@pi-spi/app 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/README.md +74 -7
  2. package/assets/icons/qr.png +0 -0
  3. package/assets/sounds/voice/search_prompt_en.mp3 +0 -0
  4. package/assets/sounds/voice/search_prompt_fr.mp3 +0 -0
  5. package/assets/sounds/voice/search_prompt_pt.mp3 +0 -0
  6. package/example/App.tsx +16 -1
  7. package/example/app.json +9 -1
  8. package/lib/module/core/assets.js +1 -0
  9. package/lib/module/core/assets.js.map +1 -1
  10. package/lib/module/core/di.js +8 -1
  11. package/lib/module/core/di.js.map +1 -1
  12. package/lib/module/core/pi_spi_custom_theme.js +63 -0
  13. package/lib/module/core/pi_spi_custom_theme.js.map +1 -0
  14. package/lib/module/core/pi_spi_sdk_config.js +15 -3
  15. package/lib/module/core/pi_spi_sdk_config.js.map +1 -1
  16. package/lib/module/core/router.js +3 -0
  17. package/lib/module/core/router.js.map +1 -1
  18. package/lib/module/core/routes.js +14 -0
  19. package/lib/module/core/routes.js.map +1 -1
  20. package/lib/module/core/storage.js +27 -29
  21. package/lib/module/core/storage.js.map +1 -1
  22. package/lib/module/core/theme.js +51 -0
  23. package/lib/module/core/theme.js.map +1 -1
  24. package/lib/module/index.js +1 -0
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/module/l10n/app_localizations.js.map +1 -1
  27. package/lib/module/l10n/app_localizations_en.js +24 -1
  28. package/lib/module/l10n/app_localizations_en.js.map +1 -1
  29. package/lib/module/l10n/app_localizations_fr.js +24 -1
  30. package/lib/module/l10n/app_localizations_fr.js.map +1 -1
  31. package/lib/module/l10n/app_localizations_pt.js +24 -1
  32. package/lib/module/l10n/app_localizations_pt.js.map +1 -1
  33. package/lib/module/modules/alias/presentation/pages/alias_page_create_options.js +3 -3
  34. package/lib/module/modules/alias/presentation/pages/alias_page_create_options.js.map +1 -1
  35. package/lib/module/modules/alias/presentation/pages/alias_page_otp.js.map +1 -1
  36. package/lib/module/modules/categorie/presentation/pages/categorie_edit_icon_btn.js +2 -2
  37. package/lib/module/modules/categorie/presentation/pages/categorie_edit_icon_btn.js.map +1 -1
  38. package/lib/module/modules/categorie/presentation/pages/categorie_select_widget.js +12 -10
  39. package/lib/module/modules/categorie/presentation/pages/categorie_select_widget.js.map +1 -1
  40. package/lib/module/modules/compte/presentation/pages/solde_widget_with_qr.js +14 -9
  41. package/lib/module/modules/compte/presentation/pages/solde_widget_with_qr.js.map +1 -1
  42. package/lib/module/modules/home/presentation/pages/home_page.js +41 -2
  43. package/lib/module/modules/home/presentation/pages/home_page.js.map +1 -1
  44. package/lib/module/modules/home/presentation/pages/home_tab_compte.js +1 -2
  45. package/lib/module/modules/home/presentation/pages/home_tab_compte.js.map +1 -1
  46. package/lib/module/modules/home/presentation/pages/home_tab_compte_more_menu.js +2 -2
  47. package/lib/module/modules/home/presentation/pages/home_tab_compte_more_menu.js.map +1 -1
  48. package/lib/module/modules/introduction/presentation/pages/introduction_bar.js +8 -1
  49. package/lib/module/modules/introduction/presentation/pages/introduction_bar.js.map +1 -1
  50. package/lib/module/modules/introduction/presentation/pages/introduction_page.js +29 -4
  51. package/lib/module/modules/introduction/presentation/pages/introduction_page.js.map +1 -1
  52. package/lib/module/modules/notification/presentation/bloc/notification_bloc.js +11 -4
  53. package/lib/module/modules/notification/presentation/bloc/notification_bloc.js.map +1 -1
  54. package/lib/module/modules/notification/presentation/bloc/notification_state.js +7 -1
  55. package/lib/module/modules/notification/presentation/bloc/notification_state.js.map +1 -1
  56. package/lib/module/modules/notification/presentation/pages/notification_page_liste.js +6 -2
  57. package/lib/module/modules/notification/presentation/pages/notification_page_liste.js.map +1 -1
  58. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js +2 -2
  59. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js.map +1 -1
  60. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.js +2 -2
  61. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.js.map +1 -1
  62. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js +156 -25
  63. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js.map +1 -1
  64. package/lib/module/modules/profile/presentation/pages/web/profile_web_page.js.map +1 -1
  65. package/lib/module/modules/qrcode/presentation/pages/qrcode_alias_page.js.map +1 -1
  66. package/lib/module/modules/qrcode/presentation/pages/qrcode_alias_page_share.js +2 -2
  67. package/lib/module/modules/qrcode/presentation/pages/qrcode_alias_page_share.js.map +1 -1
  68. package/lib/module/modules/search/domain/models/fonctionnalite.js +199 -0
  69. package/lib/module/modules/search/domain/models/fonctionnalite.js.map +1 -0
  70. package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js +119 -0
  71. package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js.map +1 -0
  72. package/lib/module/modules/search/presentation/pages/search_page.js +359 -0
  73. package/lib/module/modules/search/presentation/pages/search_page.js.map +1 -0
  74. package/lib/module/modules/search/presentation/pages/search_page_voice_button.js +335 -0
  75. package/lib/module/modules/search/presentation/pages/search_page_voice_button.js.map +1 -0
  76. package/lib/module/modules/subscription/presentation/pages/subscription_menu_widget.js +2 -2
  77. package/lib/module/modules/subscription/presentation/pages/subscription_menu_widget.js.map +1 -1
  78. package/lib/module/modules/transactions/domain/models/transaction_send/transaction_send_command.js +6 -2
  79. package/lib/module/modules/transactions/domain/models/transaction_send/transaction_send_command.js.map +1 -1
  80. package/lib/module/modules/transactions/presentation/bloc/transaction_search/transaction_search_bloc.js +13 -1
  81. package/lib/module/modules/transactions/presentation/bloc/transaction_search/transaction_search_bloc.js.map +1 -1
  82. package/lib/module/modules/transactions/presentation/bloc/transaction_search/transaction_search_state.js +7 -1
  83. package/lib/module/modules/transactions/presentation/bloc/transaction_search/transaction_search_state.js.map +1 -1
  84. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.js +3 -1
  85. package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.js.map +1 -1
  86. package/lib/module/modules/transactions/presentation/pages/transaction_jour_libelle.js +15 -0
  87. package/lib/module/modules/transactions/presentation/pages/transaction_jour_libelle.js.map +1 -0
  88. package/lib/module/modules/transactions/presentation/pages/transaction_list_item_widget.js +2 -1
  89. package/lib/module/modules/transactions/presentation/pages/transaction_list_item_widget.js.map +1 -1
  90. package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.js +5 -3
  91. package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.js.map +1 -1
  92. package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.js +216 -137
  93. package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.js.map +1 -1
  94. package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.js +8 -3
  95. package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.js.map +1 -1
  96. package/lib/module/modules/transactions/presentation/pages/transaction_send/transaction_send_page_options.js +3 -3
  97. package/lib/module/modules/transactions/presentation/pages/transaction_send/transaction_send_page_options.js.map +1 -1
  98. package/lib/module/shared/widgets/amount_widget.js +4 -1
  99. package/lib/module/shared/widgets/amount_widget.js.map +1 -1
  100. package/lib/module/shared/widgets/input_amount.js.map +1 -1
  101. package/lib/module/shared/widgets/input_text.js +2 -0
  102. package/lib/module/shared/widgets/input_text.js.map +1 -1
  103. package/lib/module/shared/widgets/menu_actions_widget.js +10 -1
  104. package/lib/module/shared/widgets/menu_actions_widget.js.map +1 -1
  105. package/lib/module/shared/widgets/pagination_loader_widget.js +29 -0
  106. package/lib/module/shared/widgets/pagination_loader_widget.js.map +1 -0
  107. package/lib/typescript/src/core/assets.d.ts +1 -0
  108. package/lib/typescript/src/core/assets.d.ts.map +1 -1
  109. package/lib/typescript/src/core/di.d.ts.map +1 -1
  110. package/lib/typescript/src/core/pi_spi_custom_theme.d.ts +49 -0
  111. package/lib/typescript/src/core/pi_spi_custom_theme.d.ts.map +1 -0
  112. package/lib/typescript/src/core/pi_spi_sdk_config.d.ts +15 -2
  113. package/lib/typescript/src/core/pi_spi_sdk_config.d.ts.map +1 -1
  114. package/lib/typescript/src/core/router.d.ts +1 -0
  115. package/lib/typescript/src/core/router.d.ts.map +1 -1
  116. package/lib/typescript/src/core/routes.d.ts.map +1 -1
  117. package/lib/typescript/src/core/storage.d.ts +11 -3
  118. package/lib/typescript/src/core/storage.d.ts.map +1 -1
  119. package/lib/typescript/src/core/theme.d.ts +19 -0
  120. package/lib/typescript/src/core/theme.d.ts.map +1 -1
  121. package/lib/typescript/src/index.d.ts +2 -1
  122. package/lib/typescript/src/index.d.ts.map +1 -1
  123. package/lib/typescript/src/l10n/app_localizations.d.ts +46 -0
  124. package/lib/typescript/src/l10n/app_localizations.d.ts.map +1 -1
  125. package/lib/typescript/src/l10n/app_localizations_en.d.ts.map +1 -1
  126. package/lib/typescript/src/l10n/app_localizations_fr.d.ts.map +1 -1
  127. package/lib/typescript/src/l10n/app_localizations_pt.d.ts.map +1 -1
  128. package/lib/typescript/src/modules/categorie/presentation/pages/categorie_select_widget.d.ts.map +1 -1
  129. package/lib/typescript/src/modules/compte/presentation/pages/solde_widget_with_qr.d.ts.map +1 -1
  130. package/lib/typescript/src/modules/home/presentation/pages/home_page.d.ts.map +1 -1
  131. package/lib/typescript/src/modules/introduction/presentation/pages/introduction_bar.d.ts.map +1 -1
  132. package/lib/typescript/src/modules/introduction/presentation/pages/introduction_page.d.ts.map +1 -1
  133. package/lib/typescript/src/modules/notification/presentation/bloc/notification_bloc.d.ts.map +1 -1
  134. package/lib/typescript/src/modules/notification/presentation/bloc/notification_state.d.ts +5 -0
  135. package/lib/typescript/src/modules/notification/presentation/bloc/notification_state.d.ts.map +1 -1
  136. package/lib/typescript/src/modules/notification/presentation/pages/notification_page_liste.d.ts.map +1 -1
  137. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page.d.ts.map +1 -1
  138. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.d.ts.map +1 -1
  139. package/lib/typescript/src/modules/profile/presentation/pages/web/profile_web_page.d.ts.map +1 -1
  140. package/lib/typescript/src/modules/search/domain/models/fonctionnalite.d.ts +70 -0
  141. package/lib/typescript/src/modules/search/domain/models/fonctionnalite.d.ts.map +1 -0
  142. package/lib/typescript/src/modules/search/presentation/pages/search_fonctionnalites.d.ts +21 -0
  143. package/lib/typescript/src/modules/search/presentation/pages/search_fonctionnalites.d.ts.map +1 -0
  144. package/lib/typescript/src/modules/search/presentation/pages/search_page.d.ts +21 -0
  145. package/lib/typescript/src/modules/search/presentation/pages/search_page.d.ts.map +1 -0
  146. package/lib/typescript/src/modules/search/presentation/pages/search_page_voice_button.d.ts +29 -0
  147. package/lib/typescript/src/modules/search/presentation/pages/search_page_voice_button.d.ts.map +1 -0
  148. package/lib/typescript/src/modules/transactions/domain/models/transaction_send/transaction_send_command.d.ts +1 -1
  149. package/lib/typescript/src/modules/transactions/domain/models/transaction_send/transaction_send_command.d.ts.map +1 -1
  150. package/lib/typescript/src/modules/transactions/presentation/bloc/transaction_search/transaction_search_bloc.d.ts.map +1 -1
  151. package/lib/typescript/src/modules/transactions/presentation/bloc/transaction_search/transaction_search_state.d.ts +5 -0
  152. package/lib/typescript/src/modules/transactions/presentation/bloc/transaction_search/transaction_search_state.d.ts.map +1 -1
  153. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.d.ts +2 -1
  154. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.d.ts.map +1 -1
  155. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_jour_libelle.d.ts +4 -0
  156. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_jour_libelle.d.ts.map +1 -0
  157. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_list_item_widget.d.ts +2 -1
  158. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_list_item_widget.d.ts.map +1 -1
  159. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.d.ts.map +1 -1
  160. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.d.ts +5 -1
  161. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.d.ts.map +1 -1
  162. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.d.ts.map +1 -1
  163. package/lib/typescript/src/shared/widgets/amount_widget.d.ts +3 -1
  164. package/lib/typescript/src/shared/widgets/amount_widget.d.ts.map +1 -1
  165. package/lib/typescript/src/shared/widgets/input_text.d.ts +3 -3
  166. package/lib/typescript/src/shared/widgets/input_text.d.ts.map +1 -1
  167. package/lib/typescript/src/shared/widgets/menu_actions_widget.d.ts +5 -1
  168. package/lib/typescript/src/shared/widgets/menu_actions_widget.d.ts.map +1 -1
  169. package/lib/typescript/src/shared/widgets/pagination_loader_widget.d.ts +7 -0
  170. package/lib/typescript/src/shared/widgets/pagination_loader_widget.d.ts.map +1 -0
  171. package/package.json +49 -35
  172. package/src/core/assets.ts +1 -0
  173. package/src/core/di.ts +8 -1
  174. package/src/core/pi_spi_custom_theme.ts +66 -0
  175. package/src/core/pi_spi_sdk_config.ts +18 -3
  176. package/src/core/router.tsx +3 -0
  177. package/src/core/routes.tsx +15 -0
  178. package/src/core/storage.ts +30 -22
  179. package/src/core/theme.ts +53 -0
  180. package/src/index.ts +2 -1
  181. package/src/l10n/app_localizations.ts +46 -0
  182. package/src/l10n/app_localizations_en.ts +23 -0
  183. package/src/l10n/app_localizations_fr.ts +23 -0
  184. package/src/l10n/app_localizations_pt.ts +23 -0
  185. package/src/modules/alias/presentation/pages/alias_page_create_options.tsx +3 -3
  186. package/src/modules/alias/presentation/pages/alias_page_otp.tsx +2 -2
  187. package/src/modules/categorie/presentation/pages/categorie_edit_icon_btn.tsx +2 -2
  188. package/src/modules/categorie/presentation/pages/categorie_select_widget.tsx +13 -9
  189. package/src/modules/compte/presentation/pages/solde_widget_with_qr.tsx +10 -3
  190. package/src/modules/home/presentation/pages/home_page.tsx +31 -2
  191. package/src/modules/home/presentation/pages/home_tab_compte.tsx +1 -1
  192. package/src/modules/home/presentation/pages/home_tab_compte_more_menu.tsx +2 -2
  193. package/src/modules/introduction/presentation/pages/introduction_bar.tsx +12 -2
  194. package/src/modules/introduction/presentation/pages/introduction_page.tsx +20 -2
  195. package/src/modules/notification/presentation/bloc/notification_bloc.ts +12 -3
  196. package/src/modules/notification/presentation/bloc/notification_state.ts +11 -1
  197. package/src/modules/notification/presentation/pages/notification_page_liste.tsx +4 -1
  198. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page.tsx +5 -2
  199. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.tsx +2 -2
  200. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.tsx +95 -15
  201. package/src/modules/profile/presentation/pages/web/profile_web_page.tsx +3 -2
  202. package/src/modules/qrcode/presentation/pages/qrcode_alias_page.tsx +2 -2
  203. package/src/modules/qrcode/presentation/pages/qrcode_alias_page_share.tsx +2 -2
  204. package/src/modules/search/domain/models/fonctionnalite.ts +217 -0
  205. package/src/modules/search/presentation/pages/search_fonctionnalites.ts +135 -0
  206. package/src/modules/search/presentation/pages/search_page.tsx +314 -0
  207. package/src/modules/search/presentation/pages/search_page_voice_button.tsx +283 -0
  208. package/src/modules/subscription/presentation/pages/subscription_menu_widget.tsx +2 -2
  209. package/src/modules/transactions/domain/models/transaction_send/transaction_send_command.ts +6 -2
  210. package/src/modules/transactions/presentation/bloc/transaction_search/transaction_search_bloc.ts +15 -1
  211. package/src/modules/transactions/presentation/bloc/transaction_search/transaction_search_state.ts +10 -1
  212. package/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.tsx +3 -0
  213. package/src/modules/transactions/presentation/pages/transaction_jour_libelle.ts +17 -0
  214. package/src/modules/transactions/presentation/pages/transaction_list_item_widget.tsx +3 -1
  215. package/src/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.tsx +7 -4
  216. package/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.tsx +204 -101
  217. package/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.tsx +6 -2
  218. package/src/modules/transactions/presentation/pages/transaction_send/transaction_send_page_options.tsx +3 -3
  219. package/src/shared/widgets/amount_widget.tsx +5 -2
  220. package/src/shared/widgets/input_amount.tsx +2 -2
  221. package/src/shared/widgets/input_text.tsx +8 -3
  222. package/src/shared/widgets/menu_actions_widget.tsx +8 -2
  223. package/src/shared/widgets/pagination_loader_widget.tsx +24 -0
@@ -1,5 +1,5 @@
1
1
  import AsyncStorage from '@react-native-async-storage/async-storage';
2
- import CryptoJS from 'crypto-js';
2
+ import { cbc } from '@noble/ciphers/aes.js';
3
3
  import * as Crypto from 'expo-crypto';
4
4
  import * as SecureStore from 'expo-secure-store';
5
5
 
@@ -95,7 +95,7 @@ export class Box {
95
95
  constructor(
96
96
  readonly name: string,
97
97
  private readonly entries: Map<string, Item>,
98
- private readonly dek: CryptoJS.lib.WordArray,
98
+ private readonly dek: Uint8Array,
99
99
  ) {}
100
100
 
101
101
  get length(): number {
@@ -142,7 +142,7 @@ export class Box {
142
142
  return `${KEY_PREFIX}box.${name}`;
143
143
  }
144
144
 
145
- static async open(name: string, dek: CryptoJS.lib.WordArray): Promise<Box> {
145
+ static async open(name: string, dek: Uint8Array): Promise<Box> {
146
146
  const raw = await AsyncStorage.getItem(Box.storageKey(name));
147
147
  let entries = new Map<string, Item>();
148
148
  if (raw != null) {
@@ -159,26 +159,34 @@ export class Box {
159
159
  }
160
160
  }
161
161
 
162
- function encrypt(plain: string, key: CryptoJS.lib.WordArray): string {
163
- const iv = bytesToWordArray(Crypto.getRandomBytes(16));
164
- const encrypted = CryptoJS.AES.encrypt(plain, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
165
- return `${CryptoJS.enc.Base64.stringify(iv)}:${encrypted.ciphertext.toString(CryptoJS.enc.Base64)}`;
162
+ /**
163
+ * Chiffre en AES-256-CBC (padding PKCS7) : `<iv base64>:<texte chiffré base64>`.
164
+ * Même format que l'ancienne implémentation `crypto-js` : les données déjà
165
+ * enregistrées restent lisibles.
166
+ */
167
+ export function encrypt(plain: string, key: Uint8Array): string {
168
+ const iv = Crypto.getRandomBytes(16);
169
+ const ciphertext = cbc(key, iv).encrypt(new TextEncoder().encode(plain));
170
+ return `${toBase64(iv)}:${toBase64(ciphertext)}`;
166
171
  }
167
172
 
168
- function decrypt(payload: string, key: CryptoJS.lib.WordArray): string {
173
+ export function decrypt(payload: string, key: Uint8Array): string {
169
174
  const [ivB64, cipherB64] = payload.split(':');
170
- const iv = CryptoJS.enc.Base64.parse(ivB64);
171
- const params = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(cipherB64) });
172
- const plain = CryptoJS.AES.decrypt(params, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
173
- return plain.toString(CryptoJS.enc.Utf8);
175
+ const plain = cbc(key, fromBase64(ivB64)).decrypt(fromBase64(cipherB64));
176
+ return new TextDecoder().decode(plain);
174
177
  }
175
178
 
176
- function bytesToWordArray(bytes: Uint8Array): CryptoJS.lib.WordArray {
177
- const words: number[] = [];
178
- for (let i = 0; i < bytes.length; i++) {
179
- words[i >>> 2] = (words[i >>> 2] ?? 0) | (bytes[i] << (24 - (i % 4) * 8));
180
- }
181
- return CryptoJS.lib.WordArray.create(words, bytes.length);
179
+ export function toBase64(bytes: Uint8Array): string {
180
+ let binary = '';
181
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
182
+ return btoa(binary);
183
+ }
184
+
185
+ export function fromBase64(value: string): Uint8Array {
186
+ const binary = atob(value);
187
+ const bytes = new Uint8Array(binary.length);
188
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
189
+ return bytes;
182
190
  }
183
191
 
184
192
  /**
@@ -195,7 +203,7 @@ export class AppStorage {
195
203
  static readonly keyId = 'DEK';
196
204
 
197
205
  /** The Data Encryption Key */
198
- private static dek: CryptoJS.lib.WordArray;
206
+ private static dek: Uint8Array;
199
207
 
200
208
  /** Collections ouvertes (chargées entièrement en mémoire, comme les boîtes Hive). */
201
209
  static collections = new Map<string, Promise<Box>>();
@@ -206,12 +214,12 @@ export class AppStorage {
206
214
  const keyString = await secureStorage.read(AppStorage.keyId);
207
215
  if (keyString == null) {
208
216
  const key = Crypto.getRandomBytes(32);
209
- await secureStorage.write(AppStorage.keyId, CryptoJS.enc.Base64.stringify(bytesToWordArray(key)));
210
- AppStorage.dek = bytesToWordArray(key);
217
+ await secureStorage.write(AppStorage.keyId, toBase64(key));
218
+ AppStorage.dek = key;
211
219
  }
212
220
  // Sinon récupérer la clé existante
213
221
  else {
214
- AppStorage.dek = CryptoJS.enc.Base64.parse(keyString);
222
+ AppStorage.dek = fromBase64(keyString);
215
223
  }
216
224
  AppStorage.isInit = true;
217
225
  }
package/src/core/theme.ts CHANGED
@@ -384,6 +384,29 @@ export class Themer {
384
384
  switchActiveTrack: Themer.primary,
385
385
  };
386
386
 
387
+ /** Code enregistré comme thème préféré quand l'hôte fournit un thème personnalisé (`PiSpiAppConfig.customTheme`). */
388
+ static readonly customThemeCode = 'customTheme';
389
+
390
+ /** Thème personnalisé de l'hôte, construit à l'ouverture du SDK (voir `Di.init`) ; `null` sans thème personnalisé. */
391
+ static customTheme: ThemeData | null = null;
392
+
393
+ /**
394
+ * Thème clair construit à partir de couleurs (thème personnalisé de
395
+ * l'hôte, voir `PiSpiCustomTheme`) : même modèle que les thèmes rouge,
396
+ * violet ou rose, avec le texte des boutons en blanc ou en noir selon la
397
+ * luminosité de [primary].
398
+ */
399
+ static colored(primary: string, secondary: string, tertiary: string, pressed: string): ThemeData {
400
+ const onPrimary = relativeLuminance(primary) > 0.5 ? Themer.blackColor : Themer.whiteColor;
401
+ return Themer.build(
402
+ Themer.coloredSpec(primary, secondary, tertiary, {
403
+ schemeOnPrimary: onPrimary,
404
+ buttonPressed: pressed,
405
+ buttonForeground: onPrimary,
406
+ }),
407
+ );
408
+ }
409
+
387
410
  /** Variante claire monochrome (même structure pour jaune, bleu, vert, rouge, orange, violet). */
388
411
  private static coloredSpec(
389
412
  main: string,
@@ -486,6 +509,8 @@ export class Themer {
486
509
  * ponctuellement via `Appearance`.
487
510
  */
488
511
  static get(code: string | null | undefined, systemScheme?: ColorSchemeName | null): ThemeData {
512
+ // Thème personnalisé de l'hôte
513
+ if (code === Themer.customThemeCode && Themer.customTheme != null) return Themer.customTheme;
489
514
  switch (code) {
490
515
  case Themes.light:
491
516
  return Themer.lightTheme;
@@ -523,3 +548,31 @@ export const ThemeContext = createContext<ThemeData>(Themer.lightTheme);
523
548
  export function useTheme(): ThemeData {
524
549
  return useContext(ThemeContext);
525
550
  }
551
+
552
+ /** Composantes RGB (0-255) d'une couleur `#RRGGBB` */
553
+ export function hexToRgb(hex: string): [number, number, number] {
554
+ const value = hex.replace('#', '');
555
+ const full = value.length === 3 ? value.split('').map((c) => c + c).join('') : value.substring(0, 6);
556
+ return [0, 2, 4].map((i) => parseInt(full.substring(i, i + 2), 16)) as [number, number, number];
557
+ }
558
+
559
+ /** Couleur `#RRGGBB` à partir de ses composantes RGB */
560
+ export function rgbToHex([r, g, b]: [number, number, number]): string {
561
+ return `#${[r, g, b].map((c) => Math.round(Math.min(255, Math.max(0, c))).toString(16).padStart(2, '0')).join('').toUpperCase()}`;
562
+ }
563
+
564
+ /** Mélange [from] vers [to] à [t] (0 = from, 1 = to), comme `Color.lerp` en Flutter */
565
+ export function lerpColor(from: string, to: string, t: number): string {
566
+ const a = hexToRgb(from);
567
+ const b = hexToRgb(to);
568
+ return rgbToHex([0, 1, 2].map((i) => a[i] + (b[i] - a[i]) * t) as [number, number, number]);
569
+ }
570
+
571
+ /** Luminance relative (0 à 1), comme `Color.computeLuminance` en Flutter */
572
+ export function relativeLuminance(hex: string): number {
573
+ const [r, g, b] = hexToRgb(hex).map((c) => {
574
+ const s = c / 255;
575
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
576
+ });
577
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
578
+ }
package/src/index.ts CHANGED
@@ -21,7 +21,8 @@ export {
21
21
  PiSpiLogConfig,
22
22
  type PiSpiLogLevel,
23
23
  } from './core/pi_spi_sdk_config';
24
- export { Themes } from './core/theme';
24
+ export { PiSpiCustomTheme } from './core/pi_spi_custom_theme';
25
+ export { Themes, type ThemeData } from './core/theme';
25
26
  export {
26
27
  type AppLocalizations,
27
28
  type SupportedLocaleCode,
@@ -487,6 +487,10 @@ export interface AppLocalizations {
487
487
  readonly homeActionMoreSheetAWidgetSubTitle: string;
488
488
  /** See All */
489
489
  readonly transactionsSeeAll: string;
490
+ /** Today */
491
+ readonly transactionsAujourdhui: string;
492
+ /** Yesterday */
493
+ readonly transactionsHier: string;
490
494
  /** Search for a contact */
491
495
  readonly transactionsSendInputHint: string;
492
496
  /** Use Alias */
@@ -1027,6 +1031,8 @@ export interface AppLocalizations {
1027
1031
  readonly transactionSearchInputFilterCategoriesSensPayes: string;
1028
1032
  /** Apply */
1029
1033
  readonly transactionSearchInputFilterBtnAppliquer: string;
1034
+ /** Reset */
1035
+ readonly transactionSearchInputFilterBtnReinitialiser: string;
1030
1036
  /** Scan */
1031
1037
  readonly qrcodePageBtnScan: string;
1032
1038
  /** QR Code */
@@ -1203,6 +1209,8 @@ export interface AppLocalizations {
1203
1209
  readonly appSettingPageMenuThemePurple: string;
1204
1210
  /** Pink */
1205
1211
  readonly appSettingPageMenuThemePink: string;
1212
+ /** App theme */
1213
+ readonly appSettingPageMenuThemeCustom: string;
1206
1214
  /** System */
1207
1215
  readonly appSettingPageMenuThemeDefault: string;
1208
1216
  /** Apparence */
@@ -1343,6 +1351,44 @@ export interface AppLocalizations {
1343
1351
  notificationMAJMessage(systeme: string): string;
1344
1352
  /** Install */
1345
1353
  readonly notificationMAJBtn: string;
1354
+ /** What would you like to do? */
1355
+ readonly searchTitle: string;
1356
+ /** Search for a feature */
1357
+ readonly searchHint: string;
1358
+ /** No feature matches “{motCle}” */
1359
+ searchEmpty(motCle: string): string;
1360
+ /** Send money to a contact */
1361
+ readonly searchSendSubTitle: string;
1362
+ /** Request money from a contact */
1363
+ readonly searchReceiveSubTitle: string;
1364
+ /** Scan a QR code */
1365
+ readonly searchQrScanTitle: string;
1366
+ /** Pay by scanning a QR code */
1367
+ readonly searchQrScanSubTitle: string;
1368
+ /** Receive money with your QR code */
1369
+ readonly searchQrShowSubTitle: string;
1370
+ /** Find a payment sent or received */
1371
+ readonly searchTransactionsSubTitle: string;
1372
+ /** See your scheduled payments */
1373
+ readonly searchAbonnementsSubTitle: string;
1374
+ /** Your details and your account */
1375
+ readonly searchCompteSubTitle: string;
1376
+ /** Theme, notifications and home screen */
1377
+ readonly searchParametreSubTitle: string;
1378
+ /** Manage your contacts and aliases */
1379
+ readonly searchContactsSubTitle: string;
1380
+ /** Frequently asked questions and support */
1381
+ readonly searchAideSubTitle: string;
1382
+ /** Voice search */
1383
+ readonly searchVoiceStart: string;
1384
+ /** Stop listening */
1385
+ readonly searchVoiceStop: string;
1386
+ /** Listening… */
1387
+ readonly searchVoiceListening: string;
1388
+ /** Tell me what you would like to do */
1389
+ readonly searchVoicePrompt: string;
1390
+ /** Voice search is not available. Check microphone access in settings. */
1391
+ readonly searchVoiceUnavailable: string;
1346
1392
  }
1347
1393
 
1348
1394
  export const supportedLocales = ['fr', 'en', 'pt'] as const;
@@ -244,6 +244,8 @@ Check your Wi-Fi or mobile data, then tap Retry.`,
244
244
  homeActionMoreSheetWidgetTitle: `Add Widget`,
245
245
  homeActionMoreSheetAWidgetSubTitle: `Manage widgets for home`,
246
246
  transactionsSeeAll: `See All`,
247
+ transactionsAujourdhui: `Today`,
248
+ transactionsHier: `Yesterday`,
247
249
  transactionsSendInputHint: `Search for a contact`,
248
250
  transactionsSendOptionAliasTitle: `Use Alias`,
249
251
  transactionsSendOptionAliasBSubtitle: `Payment address or phone no.`,
@@ -524,6 +526,7 @@ Please try again later.`,
524
526
  transactionSearchInputFilterCategoriesSensRecus: `Received`,
525
527
  transactionSearchInputFilterCategoriesSensPayes: `Sent`,
526
528
  transactionSearchInputFilterBtnAppliquer: `Apply`,
529
+ transactionSearchInputFilterBtnReinitialiser: `Reset`,
527
530
  qrcodePageBtnScan: `Scan`,
528
531
  qrcodePageBtnMonCode: `QR Code`,
529
532
  qrcodePageBtnMonAlias: `My alias`,
@@ -613,6 +616,7 @@ Scanning will be automatic`,
613
616
  appSettingPageMenuThemeOrange: `Orange`,
614
617
  appSettingPageMenuThemePurple: `Purple`,
615
618
  appSettingPageMenuThemePink: `Pink`,
619
+ appSettingPageMenuThemeCustom: `App theme`,
616
620
  appSettingPageMenuThemeDefault: `System`,
617
621
  appSettingPageMenuThemePageTitle: `Apparence`,
618
622
  appSettingPageMenuQrCodeTitle: `My QR code by default`,
@@ -683,4 +687,23 @@ Scanning will be automatic`,
683
687
  notificationMAJDate: (date: string) => `Since ${date}`,
684
688
  notificationMAJMessage: (systeme: string) => `A new version of the application is available on ${systeme}`,
685
689
  notificationMAJBtn: `Install`,
690
+ searchTitle: `What would you like to do?`,
691
+ searchHint: `Search for a feature`,
692
+ searchEmpty: (motCle: string) => `No feature matches “${motCle}”`,
693
+ searchSendSubTitle: `Send money to a contact`,
694
+ searchReceiveSubTitle: `Request money from a contact`,
695
+ searchQrScanTitle: `Scan a QR code`,
696
+ searchQrScanSubTitle: `Pay by scanning a QR code`,
697
+ searchQrShowSubTitle: `Receive money with your QR code`,
698
+ searchTransactionsSubTitle: `Find a payment sent or received`,
699
+ searchAbonnementsSubTitle: `See your scheduled payments`,
700
+ searchCompteSubTitle: `Your details and your account`,
701
+ searchParametreSubTitle: `Theme, notifications and home screen`,
702
+ searchContactsSubTitle: `Manage your contacts and aliases`,
703
+ searchAideSubTitle: `Frequently asked questions and support`,
704
+ searchVoiceStart: `Voice search`,
705
+ searchVoiceStop: `Stop listening`,
706
+ searchVoiceListening: `Listening…`,
707
+ searchVoicePrompt: `Tell me what you would like to do`,
708
+ searchVoiceUnavailable: `Voice search is not available. Check microphone access in settings.`,
686
709
  };
@@ -244,6 +244,8 @@ Vérifiez votre Wi-Fi ou vos données mobiles puis appuyez sur Réessayer.`,
244
244
  homeActionMoreSheetWidgetTitle: `Ajouter un widget`,
245
245
  homeActionMoreSheetAWidgetSubTitle: `Gérer les widgets de l’écran principal`,
246
246
  transactionsSeeAll: `Tout afficher`,
247
+ transactionsAujourdhui: `Aujourd’hui`,
248
+ transactionsHier: `Hier`,
247
249
  transactionsSendInputHint: `Rechercher un contact`,
248
250
  transactionsSendOptionAliasTitle: `Par alias`,
249
251
  transactionsSendOptionAliasBSubtitle: `Adresse de paiement ou n° de téléphone`,
@@ -523,6 +525,7 @@ Merci de votre compréhension.`,
523
525
  transactionSearchInputFilterCategoriesSensRecus: `Reçus`,
524
526
  transactionSearchInputFilterCategoriesSensPayes: `Envoyés`,
525
527
  transactionSearchInputFilterBtnAppliquer: `Appliquer`,
528
+ transactionSearchInputFilterBtnReinitialiser: `Réinitialiser`,
526
529
  qrcodePageBtnScan: `Scan`,
527
530
  qrcodePageBtnMonCode: `Mon Code`,
528
531
  qrcodePageBtnMonAlias: `Mon alias`,
@@ -612,6 +615,7 @@ Le scan se fait automatiquement`,
612
615
  appSettingPageMenuThemeOrange: `Orange`,
613
616
  appSettingPageMenuThemePurple: `Violet`,
614
617
  appSettingPageMenuThemePink: `Rose`,
618
+ appSettingPageMenuThemeCustom: `Thème de l'application`,
615
619
  appSettingPageMenuThemeDefault: `Système`,
616
620
  appSettingPageMenuThemePageTitle: `Apparence`,
617
621
  appSettingPageMenuQrCodeTitle: `Mon QR code par défaut`,
@@ -682,4 +686,23 @@ Le scan se fait automatiquement`,
682
686
  notificationMAJDate: (date: string) => `Depuis le ${date}`,
683
687
  notificationMAJMessage: (systeme: string) => `Une nouvelle version de l'application est disponible sur ${systeme}`,
684
688
  notificationMAJBtn: `Installer`,
689
+ searchTitle: `Que voulez-vous faire ?`,
690
+ searchHint: `Rechercher une fonctionnalité`,
691
+ searchEmpty: (motCle: string) => `Aucune fonctionnalité ne correspond à « ${motCle} »`,
692
+ searchSendSubTitle: `Envoyer de l’argent à un contact`,
693
+ searchReceiveSubTitle: `Demander de l’argent à un contact`,
694
+ searchQrScanTitle: `Scanner un QR code`,
695
+ searchQrScanSubTitle: `Payer en scannant un QR code`,
696
+ searchQrShowSubTitle: `Recevoir de l’argent avec votre QR code`,
697
+ searchTransactionsSubTitle: `Retrouver un paiement envoyé ou reçu`,
698
+ searchAbonnementsSubTitle: `Voir vos paiements programmés`,
699
+ searchCompteSubTitle: `Vos informations et votre compte`,
700
+ searchParametreSubTitle: `Thème, notifications et écran d’accueil`,
701
+ searchContactsSubTitle: `Gérer vos contacts et vos alias`,
702
+ searchAideSubTitle: `Questions fréquentes et assistance`,
703
+ searchVoiceStart: `Recherche vocale`,
704
+ searchVoiceStop: `Arrêter l’écoute`,
705
+ searchVoiceListening: `Je vous écoute…`,
706
+ searchVoicePrompt: `Dites-moi ce que vous souhaitez faire`,
707
+ searchVoiceUnavailable: `La recherche vocale n’est pas disponible. Vérifiez l’accès au micro dans les réglages.`,
685
708
  };
@@ -244,6 +244,8 @@ Verifique o Wi-Fi ou os dados móveis e toque em Tentar novamente.`,
244
244
  homeActionMoreSheetWidgetTitle: `Adicionar um widget`,
245
245
  homeActionMoreSheetAWidgetSubTitle: `Gerenciar os widgets do ecrã principal`,
246
246
  transactionsSeeAll: `Todas as transações`,
247
+ transactionsAujourdhui: `Hoje`,
248
+ transactionsHier: `Ontem`,
247
249
  transactionsSendInputHint: `Pesquisar um contato`,
248
250
  transactionsSendOptionAliasTitle: `Por alias`,
249
251
  transactionsSendOptionAliasBSubtitle: `Endereço de pagamento ou n° de telefone`,
@@ -524,6 +526,7 @@ Por favor, tente novamente mais tarde`,
524
526
  transactionSearchInputFilterCategoriesSensRecus: `Recebidos`,
525
527
  transactionSearchInputFilterCategoriesSensPayes: `Enviados`,
526
528
  transactionSearchInputFilterBtnAppliquer: `Aplicar`,
529
+ transactionSearchInputFilterBtnReinitialiser: `Repor`,
527
530
  qrcodePageBtnScan: `Scan`,
528
531
  qrcodePageBtnMonCode: `Meu Código`,
529
532
  qrcodePageBtnMonAlias: `Meu alias`,
@@ -613,6 +616,7 @@ O scan é feito automaticamente`,
613
616
  appSettingPageMenuThemeOrange: `Laranja`,
614
617
  appSettingPageMenuThemePurple: `Roxo`,
615
618
  appSettingPageMenuThemePink: `Rosa`,
619
+ appSettingPageMenuThemeCustom: `Tema da aplicação`,
616
620
  appSettingPageMenuThemeDefault: `Sistema`,
617
621
  appSettingPageMenuThemePageTitle: `Aparência`,
618
622
  appSettingPageMenuQrCodeTitle: `Meu QR Code por defeito`,
@@ -683,4 +687,23 @@ O scan é feito automaticamente`,
683
687
  notificationMAJDate: (date: string) => `Desde ${date}`,
684
688
  notificationMAJMessage: (systeme: string) => `Uma nova versão da aplicação está disponível em ${systeme}`,
685
689
  notificationMAJBtn: `Instalar`,
690
+ searchTitle: `O que deseja fazer?`,
691
+ searchHint: `Pesquisar uma funcionalidade`,
692
+ searchEmpty: (motCle: string) => `Nenhuma funcionalidade corresponde a «${motCle}»`,
693
+ searchSendSubTitle: `Enviar dinheiro a um contacto`,
694
+ searchReceiveSubTitle: `Pedir dinheiro a um contacto`,
695
+ searchQrScanTitle: `Digitalizar um código QR`,
696
+ searchQrScanSubTitle: `Pagar digitalizando um código QR`,
697
+ searchQrShowSubTitle: `Receber dinheiro com o seu código QR`,
698
+ searchTransactionsSubTitle: `Encontrar um pagamento enviado ou recebido`,
699
+ searchAbonnementsSubTitle: `Ver os seus pagamentos programados`,
700
+ searchCompteSubTitle: `Os seus dados e a sua conta`,
701
+ searchParametreSubTitle: `Tema, notificações e ecrã inicial`,
702
+ searchContactsSubTitle: `Gerir os seus contactos e alias`,
703
+ searchAideSubTitle: `Perguntas frequentes e assistência`,
704
+ searchVoiceStart: `Pesquisa por voz`,
705
+ searchVoiceStop: `Parar de ouvir`,
706
+ searchVoiceListening: `Estou a ouvir…`,
707
+ searchVoicePrompt: `Diga-me o que deseja fazer`,
708
+ searchVoiceUnavailable: `A pesquisa por voz não está disponível. Verifique o acesso ao microfone nas definições.`,
686
709
  };
@@ -4,7 +4,7 @@ import { Images } from '../../../../core/assets';
4
4
  import { useBloc } from '../../../../core/bloc_react';
5
5
  import { useTheme } from '../../../../core/theme';
6
6
  import { useAppLocalizations } from '../../../../l10n/localizations';
7
- import { MenuActionItem, MenuActionsWiget } from '../../../../shared/widgets/menu_actions_widget';
7
+ import { MenuActionItem, MenuActionsWidget } from '../../../../shared/widgets/menu_actions_widget';
8
8
  import { MyPageContainer } from '../../../../shared/widgets/my_page_container';
9
9
  import { AliasBloc } from '../bloc/alias_bloc';
10
10
  import { CreateAliasMBNOEvent, CreateAliasSHIDEvent } from '../bloc/alias_event';
@@ -26,7 +26,7 @@ export function AliasPageCreateOptions({ compte }: { compte: string }) {
26
26
  <View style={styles.gap32} />
27
27
 
28
28
  {/* Bouton créer alias SHID */}
29
- <MenuActionsWiget
29
+ <MenuActionsWidget
30
30
  items={[
31
31
  new MenuActionItem(
32
32
  Images.aliasIconUser,
@@ -40,7 +40,7 @@ export function AliasPageCreateOptions({ compte }: { compte: string }) {
40
40
  <View style={styles.gap20} />
41
41
 
42
42
  {/* - type MBNO */}
43
- <MenuActionsWiget
43
+ <MenuActionsWidget
44
44
  items={[
45
45
  new MenuActionItem(
46
46
  Images.aliasIconPhone,
@@ -1,5 +1,5 @@
1
1
  import { useRef, useState, type ReactNode } from 'react';
2
- import { Keyboard, StyleSheet, Text, TextInput, useWindowDimensions, View } from 'react-native';
2
+ import { Keyboard, StyleSheet, Text, TextInput, useWindowDimensions, View, type TextInputInstance } from 'react-native';
3
3
 
4
4
  import { useTheme } from '../../../../core/theme';
5
5
 
@@ -30,7 +30,7 @@ export function AliasPageOtp({
30
30
  const theme = useTheme();
31
31
  const { width: largeur } = useWindowDimensions();
32
32
  const [values, setValues] = useState<string[]>(() => Array(pinLength).fill(''));
33
- const inputs = useRef<(TextInput | null)[]>([]);
33
+ const inputs = useRef<(TextInputInstance | null)[]>([]);
34
34
 
35
35
  const isTablet = largeur >= 600;
36
36
  const paddingX = 20;
@@ -12,7 +12,7 @@ import { ElevatedButton } from '../../../../shared/material/buttons';
12
12
  import { Card } from '../../../../shared/material/card';
13
13
  import { showModalBottomSheet } from '../../../../shared/material/overlays';
14
14
  import { EmojiSelectWidget } from '../../../../shared/widgets/emoji_select_widget';
15
- import { MenuActionItem, MenuActionsWiget } from '../../../../shared/widgets/menu_actions_widget';
15
+ import { MenuActionItem, MenuActionsWidget } from '../../../../shared/widgets/menu_actions_widget';
16
16
  import { CategorieIconType } from '../../domain/models/categorie';
17
17
  import { CategorieBloc } from '../bloc/categorie_bloc';
18
18
  import { CategorieAddValidationEvent, CategorieEditValidationEvent } from '../bloc/categorie_event';
@@ -67,7 +67,7 @@ function CategorieEditIconOptions() {
67
67
  <ScrollView contentContainerStyle={styles.sheetContent}>
68
68
  <Text style={[theme.textTheme.headlineMedium, styles.title]}>{traductions.categorieFormIconSheetTitle}</Text>
69
69
  <Card>
70
- <MenuActionsWiget
70
+ <MenuActionsWidget
71
71
  items={[
72
72
  // Emoji
73
73
  new MenuActionItem(Images.categorieIconEmoji, traductions.categorieFormIconSheetEmojiTitle, null, () => void selectEmoji(), {
@@ -17,14 +17,18 @@ export function CategorieSelectWidget({
17
17
  const state = useBlocBuilder(categorieBloc);
18
18
  return (
19
19
  <View>
20
- {/* Catégories personnalisées */}
21
- <CategorieListeWidget
22
- liste={state.categories}
23
- editable={false}
24
- defaultSelectedCategories={selectedCategories}
25
- onSelectionChanged={onSelectionChanged}
26
- />
27
- <View style={styles.gap20} />
20
+ {/* Catégories personnalisées (pas de carte vide s'il n'y en a pas) */}
21
+ {state.categories.length > 0 && (
22
+ <>
23
+ <CategorieListeWidget
24
+ liste={state.categories}
25
+ editable={false}
26
+ defaultSelectedCategories={selectedCategories}
27
+ onSelectionChanged={onSelectionChanged}
28
+ />
29
+ <View style={styles.gap16} />
30
+ </>
31
+ )}
28
32
  {/* Catégories par défaut */}
29
33
  <CategorieListeWidget
30
34
  liste={Categorie.defaultListe}
@@ -35,4 +39,4 @@ export function CategorieSelectWidget({
35
39
  );
36
40
  }
37
41
 
38
- const styles = StyleSheet.create({ gap20: { height: 20 } });
42
+ const styles = StyleSheet.create({ gap16: { height: 16 } });
@@ -157,8 +157,6 @@ function SoldeRow({ soldeState, compteSoldeBloc }: { soldeState: CompteSoldeStat
157
157
  const traductions = useAppLocalizations();
158
158
  const configBloc = useBloc(ConfigBloc);
159
159
  const displayEye = configBloc.state.configParams.params[ConfigKey.hideEye] !== '1';
160
- const solde = soldeState.solde ?? 0;
161
- const fontSize = solde < 100000000 ? 24 : solde < 1000000000 ? 20 : 18;
162
160
 
163
161
  return (
164
162
  <View>
@@ -167,7 +165,15 @@ function SoldeRow({ soldeState, compteSoldeBloc }: { soldeState: CompteSoldeStat
167
165
  {soldeState instanceof CompteSoldeStateInitial && <SkeletonWidget height={25} width={150} />}
168
166
  {soldeState instanceof CompteSoldeDisplayState && (
169
167
  <>
170
- <AmountWidget montant={soldeState.solde} numberOfLines={1} style={{ ...theme.textTheme.headlineLarge, fontSize }} />
168
+ {/* Le montant prend la place disponible et se réduit s'il est trop long (8 chiffres et plus), au lieu de déborder */}
169
+ <View style={styles.amount}>
170
+ <AmountWidget
171
+ montant={soldeState.solde}
172
+ numberOfLines={1}
173
+ adjustsFontSizeToFit
174
+ style={{ ...theme.textTheme.headlineLarge, fontSize: 24 }}
175
+ />
176
+ </View>
171
177
  {displayEye && <EyeButton compteSoldeBloc={compteSoldeBloc} />}
172
178
  </>
173
179
  )}
@@ -281,6 +287,7 @@ const styles = StyleSheet.create({
281
287
  cardContent: { paddingLeft: 10, paddingRight: 10, paddingTop: 16, paddingBottom: 10 },
282
288
  row: { flexDirection: 'row', alignItems: 'flex-start' },
283
289
  inlineRow: { flexDirection: 'row', alignItems: 'center' },
290
+ amount: { flexShrink: 1 },
284
291
  expanded: { flex: 1 },
285
292
  libelle: { color: Themer.neural04Color, letterSpacing: 0.4 },
286
293
  bold: { fontWeight: '600' },
@@ -16,6 +16,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
16
16
  import { useBloc } from '../../../../core/bloc_react';
17
17
  import { CompteAliasStatus } from '../../../../core/compte_alias_status';
18
18
  import { Di } from '../../../../core/di';
19
+ import { AppRouter, useAppRouter } from '../../../../core/router';
19
20
  import { Themer, useTheme } from '../../../../core/theme';
20
21
  import { useValueListenable } from '../../../../core/value_notifier';
21
22
  import { useAppLocalizations } from '../../../../l10n/localizations';
@@ -24,6 +25,8 @@ import { ContactBloc } from '../../../contacts/presentation/bloc/contact_bloc';
24
25
  import { ContactListEvent } from '../../../contacts/presentation/bloc/contact_event';
25
26
  import { ParametreHideAmountBloc } from '../../../profile/presentation/bloc/hide_amount/hide_amount_bloc';
26
27
  import { ParametreHideAmountInitEvent } from '../../../profile/presentation/bloc/hide_amount/hide_amount_event';
28
+ import type { Fonctionnalite } from '../../../search/domain/models/fonctionnalite';
29
+ import { fonctionnalitesRecherchables } from '../../../search/presentation/pages/search_fonctionnalites';
27
30
  import { TransactionSendBloc } from '../../../transactions/presentation/bloc/transaction_send/transaction_send_bloc';
28
31
  import { TransactionSendUpdateStateEvent } from '../../../transactions/presentation/bloc/transaction_send/transaction_send_event';
29
32
  import { HomeTabCompte } from './home_tab_compte';
@@ -36,6 +39,7 @@ import { ComingSoon } from './pending_modules';
36
39
  export function HomePage({ selectedTab }: { selectedTab?: number }) {
37
40
  const traductions = useAppLocalizations();
38
41
  const theme = useTheme();
42
+ const router = useAppRouter();
39
43
  const insets = useSafeAreaInsets();
40
44
  const { width } = useWindowDimensions();
41
45
  const status = useValueListenable(Di.activeCompteAlias);
@@ -74,6 +78,17 @@ export function HomePage({ selectedTab }: { selectedTab?: number }) {
74
78
  listRef.current?.scrollToIndex({ index, animated: true });
75
79
  };
76
80
 
81
+ /**
82
+ * La recherche renvoie la fonctionnalité choisie au lieu de l'ouvrir :
83
+ * la navigation part de l'accueil, et le retour y ramène.
84
+ */
85
+ const ouvrirRecherche = async () => {
86
+ const choisie = await AppRouter.push<Fonctionnalite>(router, AppRouter.search, {
87
+ params: fonctionnalitesRecherchables(router, traductions, { ouvrirOnglet: goToTab }),
88
+ });
89
+ choisie?.ouvrir();
90
+ };
91
+
77
92
  const onMomentumScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
78
93
  setTab(Math.round(e.nativeEvent.contentOffset.x / pageWidth));
79
94
  };
@@ -91,8 +106,20 @@ export function HomePage({ selectedTab }: { selectedTab?: number }) {
91
106
  <View style={{ backgroundColor: theme.appBarTheme.backgroundColor }}>
92
107
  <View style={styles.toolbar}>
93
108
  <HomeToolbarLeading user={Di.config.user} enabled={hasAlias} />
94
- {/* Notifications (grisé si le compte actif n'a pas d'alias) */}
95
- <NotificationBtnOpenWidget enabled={hasAlias} />
109
+ <View style={styles.actions}>
110
+ {/* Recherche dans les fonctionnalités (grisé si le compte actif n'a pas d'alias) */}
111
+ <Pressable
112
+ accessibilityRole="button"
113
+ accessibilityLabel={traductions.searchTitle}
114
+ disabled={!hasAlias}
115
+ onPress={() => void ouvrirRecherche()}
116
+ style={[styles.action, { opacity: hasAlias ? 1 : 0.38 }]}
117
+ >
118
+ <MaterialIcons name="search" size={24} color={theme.appBarTheme.foregroundColor} />
119
+ </Pressable>
120
+ {/* Notifications (grisé si le compte actif n'a pas d'alias) */}
121
+ <NotificationBtnOpenWidget enabled={hasAlias} />
122
+ </View>
96
123
  </View>
97
124
  {/* Onglets */}
98
125
  <View style={styles.tabBar}>
@@ -161,6 +188,8 @@ export function HomePage({ selectedTab }: { selectedTab?: number }) {
161
188
 
162
189
  const styles = StyleSheet.create({
163
190
  toolbar: { height: 56, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
191
+ actions: { flexDirection: 'row', alignItems: 'center' },
192
+ action: { padding: 12 },
164
193
  tabBar: { flexDirection: 'row', paddingHorizontal: 20, height: 46, alignItems: 'center' },
165
194
  tab: { flex: 1, height: 36, marginHorizontal: 2, alignItems: 'center', justifyContent: 'center', borderRadius: 12.2 },
166
195
  tabSelected: { backgroundColor: '#151413' },
@@ -90,7 +90,7 @@ export function HomeTabCompte() {
90
90
 
91
91
  const styles = StyleSheet.create({
92
92
  content: { paddingHorizontal: 16, paddingVertical: 12 },
93
- actions: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16 },
93
+ actions: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
94
94
  gap10: { height: 10 },
95
95
  gap14: { height: 14 },
96
96
  gap16: { height: 16 },
@@ -6,7 +6,7 @@ import { AppRouter, useAppRouter } from '../../../../core/router';
6
6
  import { useTheme } from '../../../../core/theme';
7
7
  import { useAppLocalizations } from '../../../../l10n/localizations';
8
8
  import { Card } from '../../../../shared/material/card';
9
- import { MenuActionItem, MenuActionsWiget } from '../../../../shared/widgets/menu_actions_widget';
9
+ import { MenuActionItem, MenuActionsWidget } from '../../../../shared/widgets/menu_actions_widget';
10
10
 
11
11
  /**
12
12
  * Menu « Plus » de l'accueil. Le participant peut personnaliser ce menu
@@ -32,7 +32,7 @@ export function HomeTabCompteMoreMenu() {
32
32
  >
33
33
  <Card>
34
34
  <ScrollView>
35
- <MenuActionsWiget
35
+ <MenuActionsWidget
36
36
  items={[
37
37
  // Programmer un paiement
38
38
  new MenuActionItem(