@pi-spi/app 1.5.1 → 2.0.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.
- package/README.md +64 -30
- package/example/App.tsx +27 -24
- package/lib/module/core/assets.js +0 -7
- package/lib/module/core/assets.js.map +1 -1
- package/lib/module/core/di.js +17 -8
- package/lib/module/core/di.js.map +1 -1
- package/lib/module/core/pi_spi_custom_theme.js +22 -10
- package/lib/module/core/pi_spi_custom_theme.js.map +1 -1
- package/lib/module/core/pi_spi_sdk_config.js +22 -8
- package/lib/module/core/pi_spi_sdk_config.js.map +1 -1
- package/lib/module/core/theme.js +345 -225
- package/lib/module/core/theme.js.map +1 -1
- package/lib/module/index.js +1 -1
- 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 +26 -1
- package/lib/module/l10n/app_localizations_en.js.map +1 -1
- package/lib/module/l10n/app_localizations_fr.js +26 -1
- package/lib/module/l10n/app_localizations_fr.js.map +1 -1
- package/lib/module/l10n/app_localizations_pt.js +26 -1
- package/lib/module/l10n/app_localizations_pt.js.map +1 -1
- package/lib/module/modules/alias/presentation/pages/alias_delete_btn_widget.js +15 -19
- package/lib/module/modules/alias/presentation/pages/alias_delete_btn_widget.js.map +1 -1
- package/lib/module/modules/alias/presentation/pages/alias_page_claim.js +4 -11
- package/lib/module/modules/alias/presentation/pages/alias_page_claim.js.map +1 -1
- package/lib/module/modules/compte/presentation/pages/compte_details_widget.js +6 -7
- package/lib/module/modules/compte/presentation/pages/compte_details_widget.js.map +1 -1
- package/lib/module/modules/config/domain/models/config_keys.js +3 -1
- package/lib/module/modules/config/domain/models/config_keys.js.map +1 -1
- package/lib/module/modules/config/domain/services/config_service.js +31 -2
- package/lib/module/modules/config/domain/services/config_service.js.map +1 -1
- package/lib/module/modules/home/presentation/pages/home_tab_compte.js +0 -1
- package/lib/module/modules/home/presentation/pages/home_tab_compte.js.map +1 -1
- package/lib/module/modules/home/presentation/pages/home_tab_compte_more_menu.js +38 -37
- package/lib/module/modules/home/presentation/pages/home_tab_compte_more_menu.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/compte/profile_compte_page.js +25 -11
- package/lib/module/modules/profile/presentation/pages/compte/profile_compte_page.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js +14 -20
- 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_notifications.js +109 -103
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.js +55 -72
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.js +31 -45
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js +175 -203
- package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/profile_app_bar.js +26 -0
- package/lib/module/modules/profile/presentation/pages/profile_app_bar.js.map +1 -0
- package/lib/module/modules/profile/presentation/pages/profile_menu.js +144 -34
- package/lib/module/modules/profile/presentation/pages/profile_menu.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/profile_page.js +95 -63
- package/lib/module/modules/profile/presentation/pages/profile_page.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js +97 -45
- package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js.map +1 -1
- package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js +9 -8
- package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js.map +1 -1
- package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js +26 -2
- package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js.map +1 -1
- package/lib/module/modules/search/presentation/pages/search_page.js +0 -1
- package/lib/module/modules/search/presentation/pages/search_page.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_recu.js +3 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_recu.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.js +4 -11
- package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js +13 -10
- package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.js +5 -4
- package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.js.map +1 -1
- package/lib/module/modules/transactions/presentation/pages/transaction_split/transaction_split_page_repartition.js +2 -2
- package/lib/module/modules/transactions/presentation/pages/transaction_split/transaction_split_page_repartition.js.map +1 -1
- package/lib/module/pi_spi_sdk_entry.js +7 -2
- package/lib/module/pi_spi_sdk_entry.js.map +1 -1
- package/lib/module/shared/utils/color.js +15 -0
- package/lib/module/shared/utils/color.js.map +1 -0
- package/lib/module/shared/widgets/custom_progressbar.js +3 -2
- package/lib/module/shared/widgets/custom_progressbar.js.map +1 -1
- package/lib/module/shared/widgets/error_card_widget.js +70 -0
- package/lib/module/shared/widgets/error_card_widget.js.map +1 -0
- package/lib/module/shared/widgets/selection.js +136 -0
- package/lib/module/shared/widgets/selection.js.map +1 -0
- package/lib/typescript/src/core/assets.d.ts +0 -7
- package/lib/typescript/src/core/assets.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 +17 -8
- package/lib/typescript/src/core/pi_spi_custom_theme.d.ts.map +1 -1
- package/lib/typescript/src/core/pi_spi_sdk_config.d.ts +22 -8
- package/lib/typescript/src/core/pi_spi_sdk_config.d.ts.map +1 -1
- package/lib/typescript/src/core/theme.d.ts +123 -53
- package/lib/typescript/src/core/theme.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/l10n/app_localizations.d.ts +50 -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/alias/presentation/pages/alias_delete_btn_widget.d.ts.map +1 -1
- package/lib/typescript/src/modules/alias/presentation/pages/alias_page_claim.d.ts.map +1 -1
- package/lib/typescript/src/modules/compte/presentation/pages/compte_details_widget.d.ts.map +1 -1
- package/lib/typescript/src/modules/config/domain/models/config_keys.d.ts +3 -1
- package/lib/typescript/src/modules/config/domain/models/config_keys.d.ts.map +1 -1
- package/lib/typescript/src/modules/config/domain/services/config_service.d.ts +7 -0
- package/lib/typescript/src/modules/config/domain/services/config_service.d.ts.map +1 -1
- package/lib/typescript/src/modules/home/presentation/pages/home_tab_compte_more_menu.d.ts +2 -0
- package/lib/typescript/src/modules/home/presentation/pages/home_tab_compte_more_menu.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/compte/profile_compte_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_notifications.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.d.ts +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/profile/presentation/pages/profile_app_bar.d.ts +5 -0
- package/lib/typescript/src/modules/profile/presentation/pages/profile_app_bar.d.ts.map +1 -0
- package/lib/typescript/src/modules/profile/presentation/pages/profile_menu.d.ts +31 -4
- package/lib/typescript/src/modules/profile/presentation/pages/profile_menu.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/profile_page.d.ts +4 -1
- package/lib/typescript/src/modules/profile/presentation/pages/profile_page.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/profile_page_avatar.d.ts +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/profile_page_avatar.d.ts.map +1 -1
- package/lib/typescript/src/modules/profile/presentation/pages/profile_page_contacts.d.ts.map +1 -1
- package/lib/typescript/src/modules/search/presentation/pages/search_fonctionnalites.d.ts.map +1 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.d.ts.map +1 -1
- package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.d.ts.map +1 -1
- package/lib/typescript/src/shared/utils/color.d.ts +7 -0
- package/lib/typescript/src/shared/utils/color.d.ts.map +1 -0
- package/lib/typescript/src/shared/widgets/custom_progressbar.d.ts.map +1 -1
- package/lib/typescript/src/shared/widgets/error_card_widget.d.ts +11 -0
- package/lib/typescript/src/shared/widgets/error_card_widget.d.ts.map +1 -0
- package/lib/typescript/src/shared/widgets/selection.d.ts +21 -0
- package/lib/typescript/src/shared/widgets/selection.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/core/assets.ts +0 -7
- package/src/core/di.ts +13 -8
- package/src/core/pi_spi_custom_theme.ts +22 -15
- package/src/core/pi_spi_sdk_config.ts +27 -9
- package/src/core/theme.ts +381 -269
- package/src/index.ts +1 -1
- package/src/l10n/app_localizations.ts +50 -0
- package/src/l10n/app_localizations_en.ts +25 -0
- package/src/l10n/app_localizations_fr.ts +25 -0
- package/src/l10n/app_localizations_pt.ts +25 -0
- package/src/modules/alias/presentation/pages/alias_delete_btn_widget.tsx +13 -12
- package/src/modules/alias/presentation/pages/alias_page_claim.tsx +5 -4
- package/src/modules/compte/presentation/pages/compte_details_widget.tsx +3 -4
- package/src/modules/config/domain/models/config_keys.ts +3 -1
- package/src/modules/config/domain/services/config_service.ts +31 -2
- package/src/modules/home/presentation/pages/home_tab_compte.tsx +1 -1
- package/src/modules/home/presentation/pages/home_tab_compte_more_menu.tsx +34 -42
- package/src/modules/profile/presentation/pages/compte/profile_compte_page.tsx +15 -7
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page.tsx +12 -17
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.tsx +89 -68
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.tsx +84 -54
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.tsx +28 -24
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.tsx +121 -131
- package/src/modules/profile/presentation/pages/profile_app_bar.tsx +21 -0
- package/src/modules/profile/presentation/pages/profile_menu.tsx +126 -33
- package/src/modules/profile/presentation/pages/profile_page.tsx +90 -66
- package/src/modules/profile/presentation/pages/profile_page_avatar.tsx +69 -28
- package/src/modules/profile/presentation/pages/profile_page_contacts.tsx +5 -4
- package/src/modules/search/presentation/pages/search_fonctionnalites.ts +40 -7
- package/src/modules/search/presentation/pages/search_page.tsx +1 -1
- package/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_recu.tsx +3 -1
- package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.tsx +2 -4
- package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.tsx +13 -6
- package/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.tsx +4 -4
- package/src/modules/transactions/presentation/pages/transaction_split/transaction_split_page_repartition.tsx +2 -2
- package/src/pi_spi_sdk_entry.tsx +9 -2
- package/src/shared/utils/color.ts +14 -0
- package/src/shared/widgets/custom_progressbar.tsx +3 -2
- package/src/shared/widgets/error_card_widget.tsx +58 -0
- package/src/shared/widgets/selection.tsx +98 -0
- package/assets/images/theme_light_blue.jpeg +0 -0
- package/assets/images/theme_light_green.jpeg +0 -0
- package/assets/images/theme_light_orange.jpeg +0 -0
- package/assets/images/theme_light_pink.jpeg +0 -0
- package/assets/images/theme_light_purple.jpeg +0 -0
- package/assets/images/theme_light_red.jpeg +0 -0
- package/assets/images/theme_light_yellow.jpeg +0 -0
|
@@ -1,21 +1,22 @@
|
|
|
1
|
+
import { MaterialIcons } from '@expo/vector-icons';
|
|
1
2
|
import { Image } from 'expo-image';
|
|
2
|
-
import
|
|
3
|
+
import { useState } from 'react';
|
|
3
4
|
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
|
|
4
5
|
|
|
5
6
|
import { Images } from '../../../../../core/assets';
|
|
6
7
|
import { useBloc, useBlocBuilder } from '../../../../../core/bloc_react';
|
|
7
|
-
import {
|
|
8
|
-
import { Themer, Themes, useTheme, type ThemeData } from '../../../../../core/theme';
|
|
8
|
+
import { Accents, 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 { Card } from '../../../../../shared/material/card';
|
|
12
|
-
import {
|
|
12
|
+
import { Scaffold } from '../../../../../shared/material/scaffold';
|
|
13
13
|
import { MyPageContainer } from '../../../../../shared/widgets/my_page_container';
|
|
14
14
|
import { ConfigBloc } from '../../../../config/adapters/ui/bloc/config_bloc';
|
|
15
15
|
import { ConfigChangeEvent } from '../../../../config/adapters/ui/bloc/config_event';
|
|
16
16
|
import { ConfigLoadedState } from '../../../../config/adapters/ui/bloc/config_state';
|
|
17
17
|
import { ConfigKey } from '../../../../config/domain/models/config_keys';
|
|
18
|
-
import {
|
|
18
|
+
import { ProfileAppBar } from '../profile_app_bar';
|
|
19
|
+
import { ProfileSectionTitre } from '../profile_menu';
|
|
19
20
|
|
|
20
21
|
function themeIcon(theme: Themes): number {
|
|
21
22
|
switch (theme) {
|
|
@@ -23,20 +24,6 @@ function themeIcon(theme: Themes): number {
|
|
|
23
24
|
return Images.imageThemeLight;
|
|
24
25
|
case Themes.dark:
|
|
25
26
|
return Images.imageThemeDark;
|
|
26
|
-
case Themes.yellow:
|
|
27
|
-
return Images.imageThemeLightYellow;
|
|
28
|
-
case Themes.green:
|
|
29
|
-
return Images.imageThemeLightGreen;
|
|
30
|
-
case Themes.blue:
|
|
31
|
-
return Images.imageThemeLightBlue;
|
|
32
|
-
case Themes.red:
|
|
33
|
-
return Images.imageThemeLightRed;
|
|
34
|
-
case Themes.orange:
|
|
35
|
-
return Images.imageThemeLightOrange;
|
|
36
|
-
case Themes.purple:
|
|
37
|
-
return Images.imageThemeLightPurple;
|
|
38
|
-
case Themes.pink:
|
|
39
|
-
return Images.imageThemeLightPink;
|
|
40
27
|
default:
|
|
41
28
|
return Images.imageThemeSystem;
|
|
42
29
|
}
|
|
@@ -46,46 +33,119 @@ function themeTitle(traductions: AppLocalizations, theme: Themes): string {
|
|
|
46
33
|
return theme === Themes.system ? traductions.appSettingPageMenuThemeDefault : Themes.label(theme, traductions);
|
|
47
34
|
}
|
|
48
35
|
|
|
49
|
-
/** Choix du thème de
|
|
36
|
+
/** Choix du thème (système, clair, sombre) et de la couleur d'accentuation (route `profileParametreTheme`). */
|
|
50
37
|
export function ProfileParametrePageTheme() {
|
|
51
38
|
const traductions = useAppLocalizations();
|
|
52
39
|
const theme = useTheme();
|
|
53
|
-
const router = useAppRouter();
|
|
54
40
|
const configBloc = useBloc(ConfigBloc);
|
|
55
41
|
const state = useBlocBuilder(
|
|
56
42
|
configBloc,
|
|
57
|
-
(_p, current) =>
|
|
43
|
+
(_p, current) =>
|
|
44
|
+
current instanceof ConfigLoadedState &&
|
|
45
|
+
(current.updatedKey === ConfigKey.preferedTheme || current.updatedKey === ConfigKey.preferedAccent),
|
|
58
46
|
);
|
|
59
|
-
const currentTheme = state.configParams.params[ConfigKey.preferedTheme];
|
|
47
|
+
const currentTheme = Themes.fromCode(state.configParams.params[ConfigKey.preferedTheme]);
|
|
48
|
+
const currentAccent = state.configParams.params[ConfigKey.preferedAccent] ?? Accents.parDefaut;
|
|
49
|
+
const sombre = theme.brightness === 'dark';
|
|
50
|
+
const [largeurGrille, setLargeurGrille] = useState(0);
|
|
51
|
+
|
|
52
|
+
// Trois aperçus par ligne
|
|
53
|
+
const espace = 12;
|
|
54
|
+
// Arrondi vers le bas : sinon la troisième colonne peut passer à la ligne
|
|
55
|
+
const largeur = Math.floor((largeurGrille - 2 * espace) / 3);
|
|
60
56
|
|
|
61
57
|
return (
|
|
62
|
-
<Scaffold appBar={<
|
|
58
|
+
<Scaffold appBar={<ProfileAppBar titre={traductions.appSettingPageMenuThemePageTitle} />}>
|
|
63
59
|
<MyPageContainer>
|
|
64
|
-
<ScrollView>
|
|
65
|
-
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
60
|
+
<ScrollView contentContainerStyle={styles.contenu}>
|
|
61
|
+
{/* Thème : système, clair ou sombre */}
|
|
62
|
+
<ProfileSectionTitre titre={traductions.appSettingPageThemeSection} />
|
|
63
|
+
<Card style={styles.card}>
|
|
64
|
+
<View style={styles.grille} onLayout={(e) => setLargeurGrille(e.nativeEvent.layout.width - 2 * 14)}>
|
|
65
|
+
{largeurGrille > 0 &&
|
|
66
|
+
Themes.list().map((item) => {
|
|
67
|
+
const choisi = currentTheme === item;
|
|
68
|
+
return (
|
|
69
|
+
<Pressable
|
|
70
|
+
key={item}
|
|
71
|
+
accessibilityRole="button"
|
|
72
|
+
accessibilityState={{ selected: choisi }}
|
|
73
|
+
disabled={choisi}
|
|
74
|
+
onPress={() => configBloc.add(new ConfigChangeEvent(ConfigKey.preferedTheme, item))}
|
|
75
|
+
style={[styles.item, { width: largeur }]}
|
|
76
|
+
>
|
|
77
|
+
{/* Aperçu, entouré quand il est choisi */}
|
|
78
|
+
<View style={[styles.cadre, { borderColor: choisi ? theme.colorScheme.primary : 'transparent' }]}>
|
|
79
|
+
<Image
|
|
80
|
+
source={themeIcon(item)}
|
|
81
|
+
style={[styles.apercu, { backgroundColor: theme.colorScheme.surface }]}
|
|
82
|
+
contentFit="cover"
|
|
83
|
+
/>
|
|
84
|
+
</View>
|
|
85
|
+
{choisi && (
|
|
86
|
+
<View style={[styles.coche, { backgroundColor: theme.colorScheme.primary, borderColor: theme.colorScheme.surface }]}>
|
|
87
|
+
<MaterialIcons name="check" size={14} color={theme.colorScheme.onPrimary} />
|
|
88
|
+
</View>
|
|
89
|
+
)}
|
|
90
|
+
<Text
|
|
91
|
+
style={[
|
|
92
|
+
theme.textTheme.displayMedium,
|
|
93
|
+
styles.libelle,
|
|
94
|
+
{ fontWeight: choisi ? '700' : '500' },
|
|
95
|
+
choisi ? null : { color: Themer.neural03Color },
|
|
96
|
+
]}
|
|
97
|
+
>
|
|
98
|
+
{themeTitle(traductions, item)}
|
|
99
|
+
</Text>
|
|
100
|
+
</Pressable>
|
|
101
|
+
);
|
|
102
|
+
})}
|
|
103
|
+
</View>
|
|
104
|
+
</Card>
|
|
105
|
+
|
|
106
|
+
{/* Couleur d'accentuation */}
|
|
107
|
+
<View style={styles.espace} />
|
|
108
|
+
<ProfileSectionTitre titre={traductions.appSettingPageAccentSection} />
|
|
109
|
+
<Card style={styles.card}>
|
|
110
|
+
<View style={styles.pastilles}>
|
|
111
|
+
{[
|
|
112
|
+
// Couleur du thème personnalisé de l'hôte, en tête
|
|
113
|
+
...(Themer.hasCustomTheme ? [Themer.customAccentCode] : []),
|
|
114
|
+
...Accents.list(),
|
|
115
|
+
].map((accent) => {
|
|
116
|
+
const apercu = Themer.get(sombre ? Themes.dark : Themes.light, accent).colorScheme;
|
|
117
|
+
const teintes = { accent: apercu.primary, surAccent: apercu.onPrimary };
|
|
118
|
+
const choisi = currentAccent === accent;
|
|
119
|
+
return (
|
|
120
|
+
<Pressable
|
|
121
|
+
key={accent}
|
|
122
|
+
accessibilityRole="button"
|
|
123
|
+
accessibilityLabel={Themer.accentLabel(traductions, accent)}
|
|
124
|
+
accessibilityState={{ selected: choisi }}
|
|
125
|
+
disabled={choisi}
|
|
126
|
+
onPress={() => configBloc.add(new ConfigChangeEvent(ConfigKey.preferedAccent, accent))}
|
|
127
|
+
style={styles.accent}
|
|
128
|
+
>
|
|
129
|
+
{/* Pastille, cochée quand elle est choisie */}
|
|
130
|
+
<View style={[styles.anneau, { borderColor: choisi ? theme.colorScheme.onSurface : 'transparent' }]}>
|
|
131
|
+
<View style={[styles.pastille, { backgroundColor: teintes.accent }]}>
|
|
132
|
+
{choisi && <MaterialIcons name="check" size={22} color={teintes.surAccent} />}
|
|
133
|
+
</View>
|
|
134
|
+
</View>
|
|
135
|
+
<Text
|
|
136
|
+
numberOfLines={1}
|
|
137
|
+
style={[
|
|
138
|
+
theme.textTheme.displaySmall,
|
|
139
|
+
styles.libelle,
|
|
140
|
+
{ fontWeight: choisi ? '700' : '400' },
|
|
141
|
+
{ color: choisi ? theme.colorScheme.onSurface : Themer.neural03Color },
|
|
142
|
+
]}
|
|
143
|
+
>
|
|
144
|
+
{Themer.accentLabel(traductions, accent)}
|
|
145
|
+
</Text>
|
|
146
|
+
</Pressable>
|
|
147
|
+
);
|
|
148
|
+
})}
|
|
89
149
|
</View>
|
|
90
150
|
</Card>
|
|
91
151
|
</ScrollView>
|
|
@@ -94,88 +154,18 @@ export function ProfileParametrePageTheme() {
|
|
|
94
154
|
);
|
|
95
155
|
}
|
|
96
156
|
|
|
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
|
-
|
|
162
157
|
const styles = StyleSheet.create({
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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 },
|
|
175
|
-
wrap: { padding: 20, flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', columnGap: 10, rowGap: 10 },
|
|
158
|
+
contenu: { paddingTop: 8, paddingBottom: 16 },
|
|
159
|
+
card: { margin: 0 },
|
|
160
|
+
grille: { padding: 14, flexDirection: 'row', flexWrap: 'wrap', columnGap: 12, rowGap: 16 },
|
|
176
161
|
item: { alignItems: 'center' },
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
162
|
+
cadre: { alignSelf: 'stretch', padding: 3, borderWidth: 2, borderRadius: 14 },
|
|
163
|
+
apercu: { width: '100%', aspectRatio: 0.6, borderRadius: 10 },
|
|
164
|
+
coche: { position: 'absolute', top: 8, right: 8, borderRadius: 12, borderWidth: 2, padding: 1 },
|
|
165
|
+
libelle: { marginTop: 8, textAlign: 'center' },
|
|
166
|
+
espace: { height: 24 },
|
|
167
|
+
pastilles: { paddingHorizontal: 10, paddingVertical: 16, flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-evenly', rowGap: 16 },
|
|
168
|
+
accent: { width: 64, alignItems: 'center' },
|
|
169
|
+
anneau: { padding: 3, borderWidth: 2, borderRadius: 26 },
|
|
170
|
+
pastille: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' },
|
|
181
171
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Text } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { AppRouter, useAppRouter } from '../../../../core/router';
|
|
4
|
+
import { useTheme } from '../../../../core/theme';
|
|
5
|
+
import { AppBar } from '../../../../shared/material/scaffold';
|
|
6
|
+
|
|
7
|
+
/** Barre des écrans du profil : bouton retour et [titre] de la page. */
|
|
8
|
+
export function ProfileAppBar({ titre }: { titre: string }) {
|
|
9
|
+
const theme = useTheme();
|
|
10
|
+
const router = useAppRouter();
|
|
11
|
+
return (
|
|
12
|
+
<AppBar
|
|
13
|
+
onBack={() => AppRouter.pop(router)}
|
|
14
|
+
title={
|
|
15
|
+
<Text numberOfLines={1} style={[theme.textTheme.headlineMedium, { color: theme.appBarTheme.foregroundColor }]}>
|
|
16
|
+
{titre}
|
|
17
|
+
</Text>
|
|
18
|
+
}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { MaterialIcons } from '@expo/vector-icons';
|
|
1
2
|
import { Image } from 'expo-image';
|
|
2
|
-
import type
|
|
3
|
-
import { Pressable, StyleSheet, Switch, Text, View } from 'react-native';
|
|
3
|
+
import { Fragment, isValidElement, type ComponentProps, type ReactElement, type ReactNode } from 'react';
|
|
4
|
+
import { Platform, Pressable, StyleSheet, Switch, Text, View } from 'react-native';
|
|
4
5
|
|
|
5
6
|
import { AppRouter, useAppRouter } from '../../../../core/router';
|
|
6
|
-
import { useTheme } from '../../../../core/theme';
|
|
7
|
+
import { Themer, useTheme } from '../../../../core/theme';
|
|
7
8
|
import { Card } from '../../../../shared/material/card';
|
|
8
9
|
import { showModalBottomSheet } from '../../../../shared/material/overlays';
|
|
10
|
+
import { withAlpha } from '../../../../shared/utils/color';
|
|
9
11
|
|
|
10
12
|
/** Une entrée du menu du profil : une route, une action, ou une feuille à ouvrir. */
|
|
11
13
|
export class ProfileItem {
|
|
@@ -27,45 +29,136 @@ export class ProfileItem {
|
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Groupe d'entrées du profil dans une carte : [titre] de section facultatif
|
|
34
|
+
* au-dessus, puis une ligne par élément, séparées par un trait. Les [items]
|
|
35
|
+
* sont des `ProfileItem` ou des composants déjà construits (ex. bouton de
|
|
36
|
+
* modification du code PIN, qui utilise `ProfileMenuTile`).
|
|
37
|
+
*/
|
|
38
|
+
export function ProfilePageMenu({ items, titre }: { items: (ProfileItem | ReactElement)[]; titre?: string }) {
|
|
32
39
|
const theme = useTheme();
|
|
33
40
|
const router = useAppRouter();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
const lignes = items
|
|
42
|
+
.map((item) => {
|
|
43
|
+
if (!(item instanceof ProfileItem)) return isValidElement(item) ? item : null;
|
|
44
|
+
const aSwitch = item.action != null && item.value != null;
|
|
45
|
+
return (
|
|
46
|
+
<ProfileMenuTile
|
|
47
|
+
image={item.icon}
|
|
48
|
+
titre={item.title}
|
|
49
|
+
sousTitre={item.subtitle}
|
|
40
50
|
onPress={() => {
|
|
41
51
|
if (typeof item.page === 'string') void AppRouter.push(router, item.page);
|
|
42
52
|
else if (typeof item.page === 'function') item.page();
|
|
43
53
|
// Ouvrir une feuille
|
|
44
|
-
else if (item.sheet != null) void showModalBottomSheet(router, {
|
|
54
|
+
else if (item.sheet != null) void showModalBottomSheet(router, { builder: item.sheet });
|
|
45
55
|
}}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
trailing={
|
|
57
|
+
aSwitch ? (
|
|
58
|
+
<Switch
|
|
59
|
+
value={item.value}
|
|
60
|
+
onValueChange={item.action}
|
|
61
|
+
trackColor={{ false: theme.switchTheme.inactiveTrackColor, true: theme.switchTheme.activeTrackColor }}
|
|
62
|
+
thumbColor={theme.switchTheme.thumbColor}
|
|
63
|
+
/>
|
|
64
|
+
) : undefined
|
|
65
|
+
}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
})
|
|
69
|
+
.filter((ligne): ligne is ReactElement => ligne != null);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<View>
|
|
73
|
+
{titre != null && <ProfileSectionTitre titre={titre} />}
|
|
74
|
+
<Card style={styles.card}>
|
|
75
|
+
{lignes.map((ligne, i) => (
|
|
76
|
+
<Fragment key={i}>
|
|
77
|
+
{i > 0 && <View style={[styles.separateur, { backgroundColor: withAlpha(Themer.neural03Color, 0.25) }]} />}
|
|
78
|
+
{ligne}
|
|
79
|
+
</Fragment>
|
|
80
|
+
))}
|
|
81
|
+
</Card>
|
|
82
|
+
</View>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Ligne d'un menu du profil : pastille avec l'icône, [titre], [sousTitre]
|
|
88
|
+
* facultatif et, à droite, [trailing] (ex. Switch) ou une flèche.
|
|
89
|
+
* [couleur] remplace la couleur principale (ex. rouge pour une action
|
|
90
|
+
* destructive).
|
|
91
|
+
*/
|
|
92
|
+
export function ProfileMenuTile({
|
|
93
|
+
image,
|
|
94
|
+
icone,
|
|
95
|
+
titre,
|
|
96
|
+
sousTitre,
|
|
97
|
+
onPress,
|
|
98
|
+
trailing,
|
|
99
|
+
couleur,
|
|
100
|
+
disabled,
|
|
101
|
+
}: {
|
|
102
|
+
image?: number;
|
|
103
|
+
icone?: ComponentProps<typeof MaterialIcons>['name'];
|
|
104
|
+
titre: string;
|
|
105
|
+
sousTitre?: string;
|
|
106
|
+
onPress?: () => void;
|
|
107
|
+
trailing?: ReactNode;
|
|
108
|
+
couleur?: string;
|
|
109
|
+
disabled?: boolean;
|
|
110
|
+
}) {
|
|
111
|
+
const theme = useTheme();
|
|
112
|
+
const principale = couleur ?? theme.colorScheme.primary;
|
|
113
|
+
return (
|
|
114
|
+
<Pressable
|
|
115
|
+
accessibilityRole="button"
|
|
116
|
+
disabled={disabled}
|
|
117
|
+
onPress={onPress}
|
|
118
|
+
style={({ pressed }) => [styles.tile, pressed ? { backgroundColor: theme.colorScheme.secondary } : null]}
|
|
119
|
+
>
|
|
120
|
+
{/* Pastille */}
|
|
121
|
+
<View style={[styles.pastille, { backgroundColor: withAlpha(principale, 0.14) }]}>
|
|
122
|
+
{image != null ? (
|
|
123
|
+
<Image source={image} style={styles.icon} tintColor={couleur ?? theme.colorScheme.onSurface} />
|
|
124
|
+
) : (
|
|
125
|
+
<MaterialIcons name={icone ?? 'circle'} size={20} color={principale} />
|
|
126
|
+
)}
|
|
127
|
+
</View>
|
|
128
|
+
<View style={styles.texts}>
|
|
129
|
+
<Text style={[theme.textTheme.displayLarge, styles.titre, couleur != null ? { color: couleur } : null]}>{titre}</Text>
|
|
130
|
+
{sousTitre != null && sousTitre.length > 0 && (
|
|
131
|
+
<Text style={[theme.textTheme.displaySmall, styles.sousTitre, { color: Themer.neural03Color }]}>{sousTitre}</Text>
|
|
132
|
+
)}
|
|
133
|
+
</View>
|
|
134
|
+
{trailing ?? (
|
|
135
|
+
<MaterialIcons
|
|
136
|
+
name={Platform.OS === 'ios' ? 'arrow-forward-ios' : 'arrow-forward'}
|
|
137
|
+
size={18}
|
|
138
|
+
color={couleur ?? Themer.neural03Color}
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
</Pressable>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Titre d'une section du profil, en petites capitales. */
|
|
146
|
+
export function ProfileSectionTitre({ titre }: { titre: string }) {
|
|
147
|
+
const theme = useTheme();
|
|
148
|
+
return (
|
|
149
|
+
<Text style={[theme.textTheme.displaySmall, styles.section, { color: Themer.neural03Color }]}>{titre.toUpperCase()}</Text>
|
|
64
150
|
);
|
|
65
151
|
}
|
|
66
152
|
|
|
67
153
|
const styles = StyleSheet.create({
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
154
|
+
card: { margin: 0 },
|
|
155
|
+
tile: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 14, paddingVertical: 12 },
|
|
156
|
+
pastille: { width: 40, height: 40, borderRadius: 12, alignItems: 'center', justifyContent: 'center' },
|
|
157
|
+
icon: { width: 20, height: 20 },
|
|
158
|
+
texts: { flex: 1, marginLeft: 14, marginRight: 8 },
|
|
159
|
+
titre: { fontWeight: '500' },
|
|
160
|
+
sousTitre: { marginTop: 2 },
|
|
161
|
+
section: { fontSize: 12, letterSpacing: 0.8, fontWeight: '600', marginLeft: 4, marginBottom: 8 },
|
|
162
|
+
// Trait aligné sur le texte (après la pastille)
|
|
163
|
+
separateur: { height: StyleSheet.hairlineWidth, marginLeft: 68 },
|
|
71
164
|
});
|