@pi-spi/app 1.4.0 → 1.5.1
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.
- package/README.md +49 -2
- package/example/App.tsx +16 -1
- package/lib/module/core/api.js +9 -0
- package/lib/module/core/api.js.map +1 -1
- package/lib/module/core/di.js +8 -1
- package/lib/module/core/di.js.map +1 -1
- package/lib/module/core/pi_spi_custom_theme.js +63 -0
- package/lib/module/core/pi_spi_custom_theme.js.map +1 -0
- package/lib/module/core/pi_spi_sdk_config.js +25 -3
- package/lib/module/core/pi_spi_sdk_config.js.map +1 -1
- package/lib/module/core/theme.js +51 -0
- package/lib/module/core/theme.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/l10n/app_localizations.js.map +1 -1
- package/lib/module/l10n/app_localizations_en.js +3 -0
- package/lib/module/l10n/app_localizations_en.js.map +1 -1
- package/lib/module/l10n/app_localizations_fr.js +3 -0
- package/lib/module/l10n/app_localizations_fr.js.map +1 -1
- package/lib/module/l10n/app_localizations_pt.js +3 -0
- package/lib/module/l10n/app_localizations_pt.js.map +1 -1
- package/lib/module/modules/home/presentation/pages/home_tab_compte.js +1 -2
- package/lib/module/modules/home/presentation/pages/home_tab_compte.js.map +1 -1
- package/lib/module/modules/introduction/presentation/pages/introduction_bar.js +8 -1
- package/lib/module/modules/introduction/presentation/pages/introduction_bar.js.map +1 -1
- package/lib/module/modules/introduction/presentation/pages/introduction_page.js +29 -4
- package/lib/module/modules/introduction/presentation/pages/introduction_page.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js +2 -2
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js +156 -25
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js.map +1 -1
- package/lib/module/modules/transactions/domain/models/transaction_send/transaction_send_command.js +6 -2
- package/lib/module/modules/transactions/domain/models/transaction_send/transaction_send_command.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.js +3 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_jour_libelle.js +15 -0
- package/lib/module/modules/transactions/presentation/pages/transaction_jour_libelle.js.map +1 -0
- package/lib/module/modules/transactions/presentation/pages/transaction_list_item_widget.js +2 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_list_item_widget.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.js +5 -3
- package/lib/module/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.js +3 -2
- package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.js.map +1 -1
- package/lib/typescript/src/core/api.d.ts.map +1 -1
- package/lib/typescript/src/core/di.d.ts.map +1 -1
- package/lib/typescript/src/core/pi_spi_custom_theme.d.ts +49 -0
- package/lib/typescript/src/core/pi_spi_custom_theme.d.ts.map +1 -0
- package/lib/typescript/src/core/pi_spi_sdk_config.d.ts +25 -2
- package/lib/typescript/src/core/pi_spi_sdk_config.d.ts.map +1 -1
- package/lib/typescript/src/core/theme.d.ts +19 -0
- package/lib/typescript/src/core/theme.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/l10n/app_localizations.d.ts +6 -0
- package/lib/typescript/src/l10n/app_localizations.d.ts.map +1 -1
- package/lib/typescript/src/l10n/app_localizations_en.d.ts.map +1 -1
- package/lib/typescript/src/l10n/app_localizations_fr.d.ts.map +1 -1
- package/lib/typescript/src/l10n/app_localizations_pt.d.ts.map +1 -1
- package/lib/typescript/src/modules/introduction/presentation/pages/introduction_bar.d.ts.map +1 -1
- package/lib/typescript/src/modules/introduction/presentation/pages/introduction_page.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.d.ts.map +1 -1
- package/lib/typescript/src/modules/transactions/domain/models/transaction_send/transaction_send_command.d.ts +1 -1
- package/lib/typescript/src/modules/transactions/domain/models/transaction_send/transaction_send_command.d.ts.map +1 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.d.ts +2 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.d.ts.map +1 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_jour_libelle.d.ts +4 -0
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_jour_libelle.d.ts.map +1 -0
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_list_item_widget.d.ts +2 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_list_item_widget.d.ts.map +1 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.d.ts.map +1 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/api.ts +4 -0
- package/src/core/di.ts +8 -1
- package/src/core/pi_spi_custom_theme.ts +66 -0
- package/src/core/pi_spi_sdk_config.ts +32 -3
- package/src/core/theme.ts +53 -0
- package/src/index.ts +2 -1
- package/src/l10n/app_localizations.ts +6 -0
- package/src/l10n/app_localizations_en.ts +3 -0
- package/src/l10n/app_localizations_fr.ts +3 -0
- package/src/l10n/app_localizations_pt.ts +3 -0
- package/src/modules/home/presentation/pages/home_tab_compte.tsx +1 -1
- package/src/modules/introduction/presentation/pages/introduction_bar.tsx +12 -2
- package/src/modules/introduction/presentation/pages/introduction_page.tsx +20 -2
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page.tsx +5 -2
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.tsx +95 -15
- package/src/modules/transactions/domain/models/transaction_send/transaction_send_command.ts +6 -2
- package/src/modules/transactions/presentation/pages/transaction_form/transaction_form_input_motif.tsx +3 -0
- package/src/modules/transactions/presentation/pages/transaction_jour_libelle.ts +17 -0
- package/src/modules/transactions/presentation/pages/transaction_list_item_widget.tsx +3 -1
- package/src/modules/transactions/presentation/pages/transaction_recents/transaction_recents_widget.tsx +7 -4
- package/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_liste.tsx +3 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Themer, lerpColor, type ThemeData } from './theme';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thème personnalisé de l'application hôte, quand aucun des thèmes du SDK
|
|
5
|
+
* (`Themes`) ne convient. À passer dans `PiSpiAppConfig.customTheme`.
|
|
6
|
+
*
|
|
7
|
+
* Deux façons de le définir :
|
|
8
|
+
*
|
|
9
|
+
* - **Par ses couleurs** (cas courant) : le SDK construit un thème complet
|
|
10
|
+
* sur le même modèle que ses thèmes clairs colorés (rouge, violet,
|
|
11
|
+
* rose...). Seule `primary` est obligatoire ; les autres couleurs en sont
|
|
12
|
+
* dérivées si elles ne sont pas fournies.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* new PiSpiCustomTheme({ primary: '#00695C', secondary: '#B2DFDB' })
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* - **Par un `ThemeData` complet** (avancé), avec
|
|
19
|
+
* `PiSpiCustomTheme.themeData`. Partez de préférence du thème construit
|
|
20
|
+
* par le SDK, pour conserver tous les réglages dont ses écrans ont
|
|
21
|
+
* besoin (cartes, champs, boutons, textes) :
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* const base = new PiSpiCustomTheme({ primary: '#00695C' }).build();
|
|
25
|
+
* PiSpiCustomTheme.themeData({ ...base, cardColor: '#F1F8F7' })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class PiSpiCustomTheme {
|
|
29
|
+
/** Couleur principale (`#RRGGBB`) : boutons, liens, icônes, champ actif. */
|
|
30
|
+
readonly primary?: string;
|
|
31
|
+
/** Couleur claire : avatars, puces de filtre, fonds secondaires. Par défaut, `primary` éclaircie à 75 %. */
|
|
32
|
+
readonly secondary?: string;
|
|
33
|
+
/** Couleur intermédiaire (tertiaire du schéma de couleurs). Par défaut, `primary` éclaircie à 40 %. */
|
|
34
|
+
readonly tertiary?: string;
|
|
35
|
+
/** Couleur d'un bouton appuyé. Par défaut, `primary` assombrie à 20 %. */
|
|
36
|
+
readonly pressed?: string;
|
|
37
|
+
|
|
38
|
+
private themeDataValue?: ThemeData;
|
|
39
|
+
|
|
40
|
+
/** Thème construit à partir de ses couleurs. */
|
|
41
|
+
constructor(params: { primary: string; secondary?: string; tertiary?: string; pressed?: string }) {
|
|
42
|
+
this.primary = params.primary;
|
|
43
|
+
this.secondary = params.secondary;
|
|
44
|
+
this.tertiary = params.tertiary;
|
|
45
|
+
this.pressed = params.pressed;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Thème fourni entièrement par l'hôte (usage avancé, voir la documentation de la classe). */
|
|
49
|
+
static themeData(theme: ThemeData): PiSpiCustomTheme {
|
|
50
|
+
const custom = new PiSpiCustomTheme({ primary: theme.primaryColor });
|
|
51
|
+
custom.themeDataValue = theme;
|
|
52
|
+
return custom;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Le `ThemeData` appliqué aux écrans du SDK. */
|
|
56
|
+
build(): ThemeData {
|
|
57
|
+
if (this.themeDataValue != null) return this.themeDataValue;
|
|
58
|
+
const main = this.primary!;
|
|
59
|
+
return Themer.colored(
|
|
60
|
+
main,
|
|
61
|
+
this.secondary ?? lerpColor(main, '#FFFFFF', 0.75),
|
|
62
|
+
this.tertiary ?? lerpColor(main, '#FFFFFF', 0.4),
|
|
63
|
+
this.pressed ?? lerpColor(main, '#000000', 0.2),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { supportedLocales, type SupportedLocaleCode } from '../l10n/app_localizations';
|
|
2
|
+
import type { PiSpiCustomTheme } from './pi_spi_custom_theme';
|
|
2
3
|
import { Themes } from './theme';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -155,18 +156,32 @@ export class PiSpiApiConfig {
|
|
|
155
156
|
*/
|
|
156
157
|
readonly demoMode: boolean;
|
|
157
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Clé d'API optionnelle, envoyée dans l'en-tête {@link apiKeyHeader} de
|
|
161
|
+
* chaque requête (en plus du token d'accès), quand la passerelle du
|
|
162
|
+
* participant en exige une. `undefined` : aucun en-tête n'est ajouté.
|
|
163
|
+
*/
|
|
164
|
+
readonly apiKey?: string;
|
|
165
|
+
|
|
166
|
+
/** Nom de l'en-tête HTTP qui porte {@link apiKey}. Par défaut `X-API-Key`. */
|
|
167
|
+
readonly apiKeyHeader: string;
|
|
168
|
+
|
|
158
169
|
constructor(params: {
|
|
159
170
|
apiBaseUrl: string;
|
|
160
171
|
accessToken: string;
|
|
161
172
|
onAccessTokenExpired: () => Promise<string | null>;
|
|
162
173
|
onSessionExpired: () => Promise<void>;
|
|
163
174
|
demoMode?: boolean;
|
|
175
|
+
apiKey?: string;
|
|
176
|
+
apiKeyHeader?: string;
|
|
164
177
|
}) {
|
|
165
178
|
this.apiBaseUrl = params.apiBaseUrl;
|
|
166
179
|
this.accessToken = params.accessToken;
|
|
167
180
|
this.onAccessTokenExpired = params.onAccessTokenExpired;
|
|
168
181
|
this.onSessionExpired = params.onSessionExpired;
|
|
169
182
|
this.demoMode = params.demoMode ?? false;
|
|
183
|
+
this.apiKey = params.apiKey;
|
|
184
|
+
this.apiKeyHeader = params.apiKeyHeader ?? 'X-API-Key';
|
|
170
185
|
}
|
|
171
186
|
}
|
|
172
187
|
|
|
@@ -214,6 +229,18 @@ export class PiSpiAppConfig {
|
|
|
214
229
|
*/
|
|
215
230
|
readonly defaultTheme?: Themes;
|
|
216
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Thème personnalisé de l'application hôte, quand aucun des thèmes du
|
|
234
|
+
* SDK (`Themes`) ne convient : défini par ses couleurs ou par un
|
|
235
|
+
* `ThemeData` complet (voir {@link PiSpiCustomTheme}). Optionnel : si
|
|
236
|
+
* fourni, il est appliqué et **persisté** à chaque ouverture du SDK, et
|
|
237
|
+
* il prime sur {@link defaultTheme}. L'entrée « Thème » des paramètres
|
|
238
|
+
* est alors masquée, sauf si {@link canChangeTheme} vaut `true` : le
|
|
239
|
+
* client peut alors choisir entre « Thème de l'application » (ce thème)
|
|
240
|
+
* et les thèmes du SDK.
|
|
241
|
+
*/
|
|
242
|
+
readonly customTheme?: PiSpiCustomTheme;
|
|
243
|
+
|
|
217
244
|
/**
|
|
218
245
|
* Autorise le client à changer lui-même de thème depuis les écrans de
|
|
219
246
|
* paramètres du SDK ; sinon (`false`), l'entrée « Thème » y est
|
|
@@ -221,8 +248,8 @@ export class PiSpiAppConfig {
|
|
|
221
248
|
* la préférence déjà persistée, voir sa documentation) pour toute la
|
|
222
249
|
* session — comme pour {@link defaultLocale}.
|
|
223
250
|
*
|
|
224
|
-
* Non fourni, sa valeur par défaut dépend de {@link defaultTheme}
|
|
225
|
-
* `false` si l'hôte a fourni un
|
|
251
|
+
* Non fourni, sa valeur par défaut dépend de {@link defaultTheme} et de
|
|
252
|
+
* {@link customTheme} : `false` si l'hôte a fourni l'un des deux (il impose alors ce
|
|
226
253
|
* thème, cohérent avec le comportement de `defaultLocale`), `true` sinon
|
|
227
254
|
* (l'hôte n'a exprimé aucun avis sur le thème, donc autant laisser le
|
|
228
255
|
* client choisir). Passez explicitement `true`/`false` pour imposer un
|
|
@@ -241,6 +268,7 @@ export class PiSpiAppConfig {
|
|
|
241
268
|
appstoreLink?: string;
|
|
242
269
|
defaultLocale?: SupportedLocaleCode;
|
|
243
270
|
defaultTheme?: Themes;
|
|
271
|
+
customTheme?: PiSpiCustomTheme;
|
|
244
272
|
canChangeTheme?: boolean;
|
|
245
273
|
}) {
|
|
246
274
|
this.pspCodeMembre = params.pspCodeMembre;
|
|
@@ -253,7 +281,8 @@ export class PiSpiAppConfig {
|
|
|
253
281
|
this.appstoreLink = params.appstoreLink ?? '';
|
|
254
282
|
this.defaultLocale = params.defaultLocale ?? 'fr';
|
|
255
283
|
this.defaultTheme = params.defaultTheme;
|
|
256
|
-
this.
|
|
284
|
+
this.customTheme = params.customTheme;
|
|
285
|
+
this.canChangeTheme = params.canChangeTheme ?? (params.defaultTheme === undefined && params.customTheme === undefined);
|
|
257
286
|
|
|
258
287
|
devAssert(
|
|
259
288
|
(PiSpiAppConfig.supportedLocaleCodes as readonly string[]).includes(this.defaultLocale),
|
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 {
|
|
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 */
|
|
@@ -1205,6 +1209,8 @@ export interface AppLocalizations {
|
|
|
1205
1209
|
readonly appSettingPageMenuThemePurple: string;
|
|
1206
1210
|
/** Pink */
|
|
1207
1211
|
readonly appSettingPageMenuThemePink: string;
|
|
1212
|
+
/** App theme */
|
|
1213
|
+
readonly appSettingPageMenuThemeCustom: string;
|
|
1208
1214
|
/** System */
|
|
1209
1215
|
readonly appSettingPageMenuThemeDefault: string;
|
|
1210
1216
|
/** Apparence */
|
|
@@ -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.`,
|
|
@@ -614,6 +616,7 @@ Scanning will be automatic`,
|
|
|
614
616
|
appSettingPageMenuThemeOrange: `Orange`,
|
|
615
617
|
appSettingPageMenuThemePurple: `Purple`,
|
|
616
618
|
appSettingPageMenuThemePink: `Pink`,
|
|
619
|
+
appSettingPageMenuThemeCustom: `App theme`,
|
|
617
620
|
appSettingPageMenuThemeDefault: `System`,
|
|
618
621
|
appSettingPageMenuThemePageTitle: `Apparence`,
|
|
619
622
|
appSettingPageMenuQrCodeTitle: `My QR code by default`,
|
|
@@ -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`,
|
|
@@ -613,6 +615,7 @@ Le scan se fait automatiquement`,
|
|
|
613
615
|
appSettingPageMenuThemeOrange: `Orange`,
|
|
614
616
|
appSettingPageMenuThemePurple: `Violet`,
|
|
615
617
|
appSettingPageMenuThemePink: `Rose`,
|
|
618
|
+
appSettingPageMenuThemeCustom: `Thème de l'application`,
|
|
616
619
|
appSettingPageMenuThemeDefault: `Système`,
|
|
617
620
|
appSettingPageMenuThemePageTitle: `Apparence`,
|
|
618
621
|
appSettingPageMenuQrCodeTitle: `Mon QR code par défaut`,
|
|
@@ -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`,
|
|
@@ -614,6 +616,7 @@ O scan é feito automaticamente`,
|
|
|
614
616
|
appSettingPageMenuThemeOrange: `Laranja`,
|
|
615
617
|
appSettingPageMenuThemePurple: `Roxo`,
|
|
616
618
|
appSettingPageMenuThemePink: `Rosa`,
|
|
619
|
+
appSettingPageMenuThemeCustom: `Tema da aplicação`,
|
|
617
620
|
appSettingPageMenuThemeDefault: `Sistema`,
|
|
618
621
|
appSettingPageMenuThemePageTitle: `Aparência`,
|
|
619
622
|
appSettingPageMenuQrCodeTitle: `Meu QR Code por defeito`,
|
|
@@ -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'
|
|
93
|
+
actions: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
|
|
94
94
|
gap10: { height: 10 },
|
|
95
95
|
gap14: { height: 14 },
|
|
96
96
|
gap16: { height: 16 },
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { VideoPlayer } from 'expo-video';
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { StyleSheet, View } from 'react-native';
|
|
3
4
|
|
|
4
5
|
import type { IntroductionItem } from './introduction_item';
|
|
@@ -15,6 +16,11 @@ export function IntroductionBar({
|
|
|
15
16
|
pageSize: number;
|
|
16
17
|
progressBarWidth: number;
|
|
17
18
|
}) {
|
|
19
|
+
// Largeur des segments tirée de la place réellement disponible, pas de
|
|
20
|
+
// [progressBarWidth] (calculé sur tout l'écran) : sur un écran déplié
|
|
21
|
+
// (Galaxy Z Fold), les marges système la réduisent et la barre débordait
|
|
22
|
+
const [largeur, setLargeur] = useState<number | null>(null);
|
|
23
|
+
const itemWidth = largeur != null ? largeur / pageSize - 4 : progressBarWidth;
|
|
18
24
|
const widgetPosition = item.position;
|
|
19
25
|
const barList = [];
|
|
20
26
|
for (let i = 0; i < pageSize; i++) {
|
|
@@ -23,13 +29,17 @@ export function IntroductionBar({
|
|
|
23
29
|
<VideoProgressBar
|
|
24
30
|
key={i}
|
|
25
31
|
videoPlayer={i === widgetPosition ? videoPlayer : null}
|
|
26
|
-
progressBarItemWidth={
|
|
32
|
+
progressBarItemWidth={itemWidth}
|
|
27
33
|
passed={i < widgetPosition}
|
|
28
34
|
current={i === widgetPosition}
|
|
29
35
|
/>,
|
|
30
36
|
);
|
|
31
37
|
}
|
|
32
|
-
return
|
|
38
|
+
return (
|
|
39
|
+
<View style={styles.row} onLayout={(event) => setLargeur(event.nativeEvent.layout.width)}>
|
|
40
|
+
{barList}
|
|
41
|
+
</View>
|
|
42
|
+
);
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
const styles = StyleSheet.create({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useVideoPlayer } from 'expo-video';
|
|
2
|
-
import { useEffect } from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Pressable, StyleSheet, View } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import { useAppLocalizations } from '../../../../l10n/localizations';
|
|
@@ -28,6 +28,11 @@ export function IntroductionPageItem({
|
|
|
28
28
|
onNext: () => void;
|
|
29
29
|
}) {
|
|
30
30
|
const traductions = useAppLocalizations();
|
|
31
|
+
// Vidéo et titres réduits si la hauteur manque (écran déplié, paysage),
|
|
32
|
+
// jamais agrandis : hauteur disponible / hauteur naturelle du bloc
|
|
33
|
+
const [hauteurZone, setHauteurZone] = useState(0);
|
|
34
|
+
const [hauteurContenu, setHauteurContenu] = useState(0);
|
|
35
|
+
const echelle = hauteurZone > 0 && hauteurContenu > hauteurZone ? hauteurZone / hauteurContenu : 1;
|
|
31
36
|
const videoPlayer = useVideoPlayer(item.video, (player) => {
|
|
32
37
|
player.loop = true;
|
|
33
38
|
player.volume = 1.0;
|
|
@@ -59,7 +64,19 @@ export function IntroductionPageItem({
|
|
|
59
64
|
{/* barre de progression */}
|
|
60
65
|
<IntroductionBar videoPlayer={videoPlayer} item={item} pageSize={pageSize} progressBarWidth={progressBarWidth} />
|
|
61
66
|
{/* Video et titres */}
|
|
62
|
-
<
|
|
67
|
+
<View
|
|
68
|
+
style={styles.zoneVideo}
|
|
69
|
+
pointerEvents="box-none"
|
|
70
|
+
onLayout={(event) => setHauteurZone(event.nativeEvent.layout.height)}
|
|
71
|
+
>
|
|
72
|
+
<View
|
|
73
|
+
style={{ transform: [{ scale: echelle }] }}
|
|
74
|
+
pointerEvents="box-none"
|
|
75
|
+
onLayout={(event) => setHauteurContenu(event.nativeEvent.layout.height)}
|
|
76
|
+
>
|
|
77
|
+
<IntroductionVideo videoPlayer={videoPlayer} item={item} />
|
|
78
|
+
</View>
|
|
79
|
+
</View>
|
|
63
80
|
{/* Pied de la page */}
|
|
64
81
|
<IntroductionFooter item={item} pageSize={pageSize} traductions={traductions} />
|
|
65
82
|
</View>
|
|
@@ -72,4 +89,5 @@ const styles = StyleSheet.create({
|
|
|
72
89
|
side: { flex: 1 },
|
|
73
90
|
middle: { flex: 2 },
|
|
74
91
|
page: { flex: 1, paddingVertical: 12, paddingHorizontal: 20, alignItems: 'stretch', justifyContent: 'space-between' },
|
|
92
|
+
zoneVideo: { flexShrink: 1, marginVertical: 12, alignItems: 'center', justifyContent: 'center' },
|
|
75
93
|
});
|
|
@@ -5,7 +5,7 @@ import { useBloc, useBlocBuilder } from '../../../../../core/bloc_react';
|
|
|
5
5
|
import { Di } from '../../../../../core/di';
|
|
6
6
|
import { AppRouter, useAppRouter } from '../../../../../core/router';
|
|
7
7
|
import { SoundManager } from '../../../../../core/sounds';
|
|
8
|
-
import { Themes, useTheme } from '../../../../../core/theme';
|
|
8
|
+
import { Themer, Themes, useTheme } from '../../../../../core/theme';
|
|
9
9
|
import type { AppLocalizations } from '../../../../../l10n/app_localizations';
|
|
10
10
|
import { useAppLocalizations } from '../../../../../l10n/localizations';
|
|
11
11
|
import { AppBar, Scaffold } from '../../../../../shared/material/scaffold';
|
|
@@ -45,7 +45,10 @@ function menuItems(traductions: AppLocalizations, configParams: ConfigParams): P
|
|
|
45
45
|
if (Di.config.appConfig.canChangeTheme) {
|
|
46
46
|
items.push(
|
|
47
47
|
new ProfileItem(Images.iconsTheme, traductions.appSettingPageMenuThemeTitle, AppRouter.profileParametreTheme, {
|
|
48
|
-
subtitle:
|
|
48
|
+
subtitle:
|
|
49
|
+
params[ConfigKey.preferedTheme] === Themer.customThemeCode
|
|
50
|
+
? traductions.appSettingPageMenuThemeCustom
|
|
51
|
+
: Themes.label(theme, traductions),
|
|
49
52
|
}),
|
|
50
53
|
);
|
|
51
54
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Image } from 'expo-image';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
2
3
|
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
|
|
3
4
|
|
|
4
5
|
import { Images } from '../../../../../core/assets';
|
|
5
6
|
import { useBloc, useBlocBuilder } from '../../../../../core/bloc_react';
|
|
6
7
|
import { AppRouter, useAppRouter } from '../../../../../core/router';
|
|
7
|
-
import { Themes, useTheme } from '../../../../../core/theme';
|
|
8
|
+
import { Themer, Themes, useTheme, type ThemeData } from '../../../../../core/theme';
|
|
8
9
|
import type { AppLocalizations } from '../../../../../l10n/app_localizations';
|
|
9
10
|
import { useAppLocalizations } from '../../../../../l10n/localizations';
|
|
10
11
|
import { Card } from '../../../../../shared/material/card';
|
|
@@ -67,21 +68,23 @@ export function ProfileParametrePageTheme() {
|
|
|
67
68
|
<View style={styles.gap10} />
|
|
68
69
|
<Card>
|
|
69
70
|
<View style={styles.wrap}>
|
|
71
|
+
{/* Thème personnalisé de l'hôte, en tête */}
|
|
72
|
+
{Themer.customTheme != null && (
|
|
73
|
+
<ThemeOption
|
|
74
|
+
title={traductions.appSettingPageMenuThemeCustom}
|
|
75
|
+
selected={currentTheme === Themer.customThemeCode}
|
|
76
|
+
onSelect={() => configBloc.add(new ConfigChangeEvent(ConfigKey.preferedTheme, Themer.customThemeCode))}
|
|
77
|
+
preview={<CustomThemePreview theme={Themer.customTheme} />}
|
|
78
|
+
/>
|
|
79
|
+
)}
|
|
70
80
|
{Themes.list().map((item) => (
|
|
71
|
-
<
|
|
81
|
+
<ThemeOption
|
|
72
82
|
key={item}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
style={styles.
|
|
77
|
-
|
|
78
|
-
<Image source={themeIcon(item)} style={[styles.preview, { backgroundColor: theme.colorScheme.surface }]} contentFit="cover" />
|
|
79
|
-
<View style={styles.gap5} />
|
|
80
|
-
<Text style={theme.textTheme.displayMedium}>{themeTitle(traductions, item)}</Text>
|
|
81
|
-
<View style={styles.gap5} />
|
|
82
|
-
<CheckedDot value={currentTheme === item} />
|
|
83
|
-
<View style={styles.gap20} />
|
|
84
|
-
</Pressable>
|
|
83
|
+
title={themeTitle(traductions, item)}
|
|
84
|
+
selected={currentTheme === item}
|
|
85
|
+
onSelect={() => configBloc.add(new ConfigChangeEvent(ConfigKey.preferedTheme, item))}
|
|
86
|
+
preview={<Image source={themeIcon(item)} style={styles.flex} contentFit="cover" />}
|
|
87
|
+
/>
|
|
85
88
|
))}
|
|
86
89
|
</View>
|
|
87
90
|
</Card>
|
|
@@ -91,10 +94,87 @@ export function ProfileParametrePageTheme() {
|
|
|
91
94
|
);
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
/** Une option de thème : aperçu, nom et pastille de sélection */
|
|
98
|
+
function ThemeOption({
|
|
99
|
+
title,
|
|
100
|
+
selected,
|
|
101
|
+
onSelect,
|
|
102
|
+
preview,
|
|
103
|
+
}: {
|
|
104
|
+
title: string;
|
|
105
|
+
selected: boolean;
|
|
106
|
+
onSelect: () => void;
|
|
107
|
+
preview: ReactNode;
|
|
108
|
+
}) {
|
|
109
|
+
const theme = useTheme();
|
|
110
|
+
return (
|
|
111
|
+
<Pressable accessibilityRole="button" disabled={selected} onPress={onSelect} style={styles.item}>
|
|
112
|
+
<View style={[styles.preview, { backgroundColor: theme.colorScheme.surface }]}>{preview}</View>
|
|
113
|
+
<View style={styles.gap5} />
|
|
114
|
+
<Text style={theme.textTheme.displayMedium}>{title}</Text>
|
|
115
|
+
<View style={styles.gap5} />
|
|
116
|
+
<CheckedDot value={selected} />
|
|
117
|
+
<View style={styles.gap20} />
|
|
118
|
+
</Pressable>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Aperçu du thème personnalisé de l'hôte (pas d'image comme pour les
|
|
124
|
+
* thèmes du SDK) : une miniature d'accueil dessinée avec ses couleurs.
|
|
125
|
+
*/
|
|
126
|
+
function CustomThemePreview({ theme }: { theme: ThemeData }) {
|
|
127
|
+
const primary = theme.colorScheme.primary;
|
|
128
|
+
const secondary = theme.colorScheme.secondary;
|
|
129
|
+
const bar = (width: number, color: string) => <View style={[styles.bar, { width, backgroundColor: color }]} />;
|
|
130
|
+
return (
|
|
131
|
+
<View style={[styles.flex, styles.mock, { backgroundColor: theme.scaffoldBackgroundColor }]}>
|
|
132
|
+
{/* Avatar et titre */}
|
|
133
|
+
<View style={styles.mockRow}>
|
|
134
|
+
<View style={[styles.dot16, { backgroundColor: secondary }]} />
|
|
135
|
+
{bar(40, Themer.neural02Color)}
|
|
136
|
+
</View>
|
|
137
|
+
<View style={styles.gap12} />
|
|
138
|
+
{/* Carte du compte */}
|
|
139
|
+
<View style={[styles.mockCard, { backgroundColor: theme.cardColor }]}>
|
|
140
|
+
{bar(50, Themer.neural02Color)}
|
|
141
|
+
{bar(30, primary)}
|
|
142
|
+
</View>
|
|
143
|
+
<View style={styles.gap12} />
|
|
144
|
+
{/* Boutons d'action */}
|
|
145
|
+
<View style={styles.mockActions}>
|
|
146
|
+
{[0, 1, 2].map((i) => (
|
|
147
|
+
<View key={i} style={[styles.mockButton, { backgroundColor: primary }]} />
|
|
148
|
+
))}
|
|
149
|
+
</View>
|
|
150
|
+
<View style={styles.gap14} />
|
|
151
|
+
{/* Liste */}
|
|
152
|
+
{[0, 1, 2].map((i) => (
|
|
153
|
+
<View key={i} style={[styles.mockRow, styles.mockListRow]}>
|
|
154
|
+
<View style={[styles.dot14, { backgroundColor: secondary }]} />
|
|
155
|
+
{bar(46, Themer.neural02Color)}
|
|
156
|
+
</View>
|
|
157
|
+
))}
|
|
158
|
+
</View>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
94
162
|
const styles = StyleSheet.create({
|
|
163
|
+
flex: { flex: 1 },
|
|
164
|
+
mock: { padding: 10 },
|
|
165
|
+
mockRow: { flexDirection: 'row', alignItems: 'center', gap: 6 },
|
|
166
|
+
mockListRow: { marginBottom: 8 },
|
|
167
|
+
mockCard: { height: 54, padding: 8, borderRadius: 8, justifyContent: 'space-between' },
|
|
168
|
+
mockActions: { flexDirection: 'row', justifyContent: 'space-between' },
|
|
169
|
+
mockButton: { width: 26, height: 26, borderRadius: 8 },
|
|
170
|
+
bar: { height: 6, borderRadius: 3 },
|
|
171
|
+
dot16: { width: 16, height: 16, borderRadius: 8 },
|
|
172
|
+
dot14: { width: 14, height: 14, borderRadius: 7 },
|
|
173
|
+
gap12: { height: 12 },
|
|
174
|
+
gap14: { height: 14 },
|
|
95
175
|
wrap: { padding: 20, flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', columnGap: 10, rowGap: 10 },
|
|
96
176
|
item: { alignItems: 'center' },
|
|
97
|
-
preview: { width: 120, height: 200, borderRadius: 8 },
|
|
177
|
+
preview: { width: 120, height: 200, borderRadius: 8, overflow: 'hidden' },
|
|
98
178
|
gap5: { height: 5 },
|
|
99
179
|
gap10: { height: 10 },
|
|
100
180
|
gap20: { height: 20 },
|
|
@@ -4,7 +4,7 @@ import { TransactionActions } from './transaction_actions';
|
|
|
4
4
|
import { TransactionSendCommandAlias } from './transaction_send_command_alias';
|
|
5
5
|
import { TransactionSendCommandAmount } from './transaction_send_command_amount';
|
|
6
6
|
import type { TransactionSendCommandContact } from './transaction_send_command_contact';
|
|
7
|
-
import
|
|
7
|
+
import { TransactionSendCommandMotif } from './transaction_send_command_motif';
|
|
8
8
|
import { TransactionSendCommandOthr } from './transaction_send_command_othr';
|
|
9
9
|
import type { TransactionSendCommandSchedule } from './transaction_send_command_schedule';
|
|
10
10
|
import { TransactionSendMethod } from './transaction_send_method';
|
|
@@ -109,7 +109,7 @@ export class TransactionSendCommand {
|
|
|
109
109
|
longitude: this.longitude ?? null,
|
|
110
110
|
};
|
|
111
111
|
if (this.txId != null) json.txId = this.txId;
|
|
112
|
-
if (this.motif != null) json.motif = this.motif.value
|
|
112
|
+
if (this.motif?.value != null && this.motif.value.length > 0) json.motif = this.motif.value;
|
|
113
113
|
if (this.schedule != null) {
|
|
114
114
|
json.dateDebut = this.schedule.dateDebut;
|
|
115
115
|
if (this.schedule.frequence != null) {
|
|
@@ -163,6 +163,10 @@ export class TransactionSendCommand {
|
|
|
163
163
|
pspPays: transaction.clientPays,
|
|
164
164
|
pspNom: transaction.clientPSPNom,
|
|
165
165
|
amount: new TransactionSendCommandAmount({ action, value: transaction.montant }),
|
|
166
|
+
motif:
|
|
167
|
+
transaction.motif != null && !transaction.motif.startsWith('@')
|
|
168
|
+
? new TransactionSendCommandMotif({ value: transaction.motif })
|
|
169
|
+
: undefined,
|
|
166
170
|
});
|
|
167
171
|
}
|
|
168
172
|
}
|
|
@@ -12,14 +12,17 @@ import { TransactionSendFormChangedEvent } from '../../bloc/transaction_send/tra
|
|
|
12
12
|
export function TransactionFormInputMotif({
|
|
13
13
|
command,
|
|
14
14
|
transactionFormBloc,
|
|
15
|
+
readOnly = false,
|
|
15
16
|
}: {
|
|
16
17
|
command: TransactionSendCommand;
|
|
17
18
|
transactionFormBloc: TransactionSendBloc;
|
|
19
|
+
readOnly?: boolean;
|
|
18
20
|
}) {
|
|
19
21
|
const traductions = useAppLocalizations();
|
|
20
22
|
return (
|
|
21
23
|
<CustomTextMultipleInput
|
|
22
24
|
labelText={traductions.transactionFormMotifHint}
|
|
25
|
+
readOnly={readOnly}
|
|
23
26
|
defaultValue={command.motif?.value}
|
|
24
27
|
messageError={command.motif?.error === TransactionSendCommandMotifError.invalid ? traductions.transactionFormMotifInvalid : ''}
|
|
25
28
|
onChange={(value) => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AppLocalizations } from '../../../../l10n/app_localizations';
|
|
2
|
+
import { DateFormat } from '../../../../shared/utils/date_format';
|
|
3
|
+
|
|
4
|
+
/** « Aujourd'hui », « Hier » ou la date du jour [jour] (aaaa-mm-jj) */
|
|
5
|
+
export function transactionJourLibelle(traductions: AppLocalizations, jour: string): string {
|
|
6
|
+
const date = new Date(`${jour}T00:00:00`);
|
|
7
|
+
const maintenant = new Date();
|
|
8
|
+
// En UTC : pas de journée de 23 h aux changements d'heure
|
|
9
|
+
const ecart = Math.round(
|
|
10
|
+
(Date.UTC(maintenant.getFullYear(), maintenant.getMonth(), maintenant.getDate()) -
|
|
11
|
+
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())) /
|
|
12
|
+
86400000,
|
|
13
|
+
);
|
|
14
|
+
if (ecart === 0) return traductions.transactionsAujourdhui;
|
|
15
|
+
if (ecart === 1) return traductions.transactionsHier;
|
|
16
|
+
return new DateFormat('d MMMM y', traductions.localeName).format(date);
|
|
17
|
+
}
|
|
@@ -16,12 +16,14 @@ import { TransactionSens, type Transaction } from '../../domain/models/transacti
|
|
|
16
16
|
*/
|
|
17
17
|
export function TransactionListItemWidget({
|
|
18
18
|
transaction,
|
|
19
|
+
datePartern = 'd MMM, HH:mm',
|
|
19
20
|
detailsBackRoute,
|
|
20
21
|
noLink,
|
|
21
22
|
onSelect,
|
|
22
23
|
isSelected,
|
|
23
24
|
}: {
|
|
24
25
|
transaction: Transaction;
|
|
26
|
+
datePartern?: string;
|
|
25
27
|
detailsBackRoute?: string;
|
|
26
28
|
noLink?: boolean;
|
|
27
29
|
onSelect?: (value: boolean) => void;
|
|
@@ -65,7 +67,7 @@ export function TransactionListItemWidget({
|
|
|
65
67
|
{/* Date d'irrévocabilité */}
|
|
66
68
|
{transaction.dateOperation != null && (
|
|
67
69
|
<Text style={[theme.textTheme.displaySmall, { color: Themer.neural04Color }]}>
|
|
68
|
-
{new DateFormat(
|
|
70
|
+
{new DateFormat(datePartern, locale).format(transaction.dateOperation)}
|
|
69
71
|
</Text>
|
|
70
72
|
)}
|
|
71
73
|
</View>
|