@pi-spi/app 1.5.1 → 1.6.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 (107) hide show
  1. package/lib/module/l10n/app_localizations.js.map +1 -1
  2. package/lib/module/l10n/app_localizations_en.js +12 -1
  3. package/lib/module/l10n/app_localizations_en.js.map +1 -1
  4. package/lib/module/l10n/app_localizations_fr.js +12 -1
  5. package/lib/module/l10n/app_localizations_fr.js.map +1 -1
  6. package/lib/module/l10n/app_localizations_pt.js +12 -1
  7. package/lib/module/l10n/app_localizations_pt.js.map +1 -1
  8. package/lib/module/modules/alias/presentation/pages/alias_delete_btn_widget.js +15 -19
  9. package/lib/module/modules/alias/presentation/pages/alias_delete_btn_widget.js.map +1 -1
  10. package/lib/module/modules/alias/presentation/pages/alias_page_claim.js +4 -11
  11. package/lib/module/modules/alias/presentation/pages/alias_page_claim.js.map +1 -1
  12. package/lib/module/modules/compte/presentation/pages/compte_details_widget.js +6 -7
  13. package/lib/module/modules/compte/presentation/pages/compte_details_widget.js.map +1 -1
  14. package/lib/module/modules/profile/presentation/pages/compte/profile_compte_page.js +25 -11
  15. package/lib/module/modules/profile/presentation/pages/compte/profile_compte_page.js.map +1 -1
  16. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js +12 -18
  17. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js.map +1 -1
  18. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.js +109 -103
  19. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.js.map +1 -1
  20. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.js +55 -72
  21. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.js.map +1 -1
  22. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.js +31 -45
  23. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.js.map +1 -1
  24. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js +103 -67
  25. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js.map +1 -1
  26. package/lib/module/modules/profile/presentation/pages/profile_app_bar.js +26 -0
  27. package/lib/module/modules/profile/presentation/pages/profile_app_bar.js.map +1 -0
  28. package/lib/module/modules/profile/presentation/pages/profile_menu.js +143 -34
  29. package/lib/module/modules/profile/presentation/pages/profile_menu.js.map +1 -1
  30. package/lib/module/modules/profile/presentation/pages/profile_page.js +95 -63
  31. package/lib/module/modules/profile/presentation/pages/profile_page.js.map +1 -1
  32. package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js +97 -45
  33. package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js.map +1 -1
  34. package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js +9 -8
  35. package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js.map +1 -1
  36. package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js +26 -2
  37. package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js.map +1 -1
  38. package/lib/module/modules/search/presentation/pages/search_page.js +0 -1
  39. package/lib/module/modules/search/presentation/pages/search_page.js.map +1 -1
  40. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.js +4 -11
  41. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.js.map +1 -1
  42. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js +13 -10
  43. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js.map +1 -1
  44. package/lib/module/shared/utils/color.js +15 -0
  45. package/lib/module/shared/utils/color.js.map +1 -0
  46. package/lib/module/shared/widgets/error_card_widget.js +70 -0
  47. package/lib/module/shared/widgets/error_card_widget.js.map +1 -0
  48. package/lib/module/shared/widgets/selection.js +136 -0
  49. package/lib/module/shared/widgets/selection.js.map +1 -0
  50. package/lib/typescript/src/l10n/app_localizations.d.ts +22 -0
  51. package/lib/typescript/src/l10n/app_localizations.d.ts.map +1 -1
  52. package/lib/typescript/src/l10n/app_localizations_en.d.ts.map +1 -1
  53. package/lib/typescript/src/l10n/app_localizations_fr.d.ts.map +1 -1
  54. package/lib/typescript/src/l10n/app_localizations_pt.d.ts.map +1 -1
  55. package/lib/typescript/src/modules/alias/presentation/pages/alias_delete_btn_widget.d.ts.map +1 -1
  56. package/lib/typescript/src/modules/alias/presentation/pages/alias_page_claim.d.ts.map +1 -1
  57. package/lib/typescript/src/modules/compte/presentation/pages/compte_details_widget.d.ts.map +1 -1
  58. package/lib/typescript/src/modules/profile/presentation/pages/compte/profile_compte_page.d.ts.map +1 -1
  59. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page.d.ts.map +1 -1
  60. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.d.ts.map +1 -1
  61. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.d.ts.map +1 -1
  62. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.d.ts.map +1 -1
  63. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.d.ts.map +1 -1
  64. package/lib/typescript/src/modules/profile/presentation/pages/profile_app_bar.d.ts +5 -0
  65. package/lib/typescript/src/modules/profile/presentation/pages/profile_app_bar.d.ts.map +1 -0
  66. package/lib/typescript/src/modules/profile/presentation/pages/profile_menu.d.ts +31 -4
  67. package/lib/typescript/src/modules/profile/presentation/pages/profile_menu.d.ts.map +1 -1
  68. package/lib/typescript/src/modules/profile/presentation/pages/profile_page.d.ts +4 -1
  69. package/lib/typescript/src/modules/profile/presentation/pages/profile_page.d.ts.map +1 -1
  70. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_avatar.d.ts +1 -1
  71. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_avatar.d.ts.map +1 -1
  72. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_contacts.d.ts.map +1 -1
  73. package/lib/typescript/src/modules/search/presentation/pages/search_fonctionnalites.d.ts.map +1 -1
  74. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.d.ts.map +1 -1
  75. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.d.ts.map +1 -1
  76. package/lib/typescript/src/shared/utils/color.d.ts +7 -0
  77. package/lib/typescript/src/shared/utils/color.d.ts.map +1 -0
  78. package/lib/typescript/src/shared/widgets/error_card_widget.d.ts +11 -0
  79. package/lib/typescript/src/shared/widgets/error_card_widget.d.ts.map +1 -0
  80. package/lib/typescript/src/shared/widgets/selection.d.ts +21 -0
  81. package/lib/typescript/src/shared/widgets/selection.d.ts.map +1 -0
  82. package/package.json +1 -1
  83. package/src/l10n/app_localizations.ts +22 -0
  84. package/src/l10n/app_localizations_en.ts +11 -0
  85. package/src/l10n/app_localizations_fr.ts +11 -0
  86. package/src/l10n/app_localizations_pt.ts +11 -0
  87. package/src/modules/alias/presentation/pages/alias_delete_btn_widget.tsx +13 -12
  88. package/src/modules/alias/presentation/pages/alias_page_claim.tsx +5 -4
  89. package/src/modules/compte/presentation/pages/compte_details_widget.tsx +3 -4
  90. package/src/modules/profile/presentation/pages/compte/profile_compte_page.tsx +15 -7
  91. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page.tsx +10 -12
  92. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.tsx +89 -68
  93. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.tsx +84 -54
  94. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.tsx +28 -24
  95. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.tsx +77 -44
  96. package/src/modules/profile/presentation/pages/profile_app_bar.tsx +21 -0
  97. package/src/modules/profile/presentation/pages/profile_menu.tsx +126 -33
  98. package/src/modules/profile/presentation/pages/profile_page.tsx +90 -66
  99. package/src/modules/profile/presentation/pages/profile_page_avatar.tsx +69 -28
  100. package/src/modules/profile/presentation/pages/profile_page_contacts.tsx +5 -4
  101. package/src/modules/search/presentation/pages/search_fonctionnalites.ts +40 -7
  102. package/src/modules/search/presentation/pages/search_page.tsx +1 -1
  103. package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.tsx +2 -4
  104. package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.tsx +13 -6
  105. package/src/shared/utils/color.ts +14 -0
  106. package/src/shared/widgets/error_card_widget.tsx +58 -0
  107. package/src/shared/widgets/selection.tsx +98 -0
@@ -1,5 +1,5 @@
1
1
  import { MaterialIcons } from '@expo/vector-icons';
2
- import { Pressable, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
2
+ import { Pressable, StyleSheet, useWindowDimensions, View } from 'react-native';
3
3
 
4
4
  import { useBlocBuilder, useCreateBloc } from '../../../../core/bloc_react';
5
5
  import { Di } from '../../../../core/di';
@@ -46,7 +46,6 @@ function CopyButton({ value }: { value: string }) {
46
46
  /** Informations du compte actif : type, numéro et alias. */
47
47
  export function CompteDetailsWidget() {
48
48
  const traductions = useAppLocalizations();
49
- const theme = useTheme();
50
49
  const { width } = useWindowDimensions();
51
50
  const bloc = useCreateBloc(() => {
52
51
  const created = new CompteDetailsBloc(Di.getCompteInputPort());
@@ -57,8 +56,7 @@ export function CompteDetailsWidget() {
57
56
 
58
57
  return (
59
58
  <View>
60
- <Text style={theme.textTheme.titleSmall}>{traductions.compteDetailsPageTitle}</Text>
61
- <View style={styles.gap24} />
59
+ <View style={styles.gap8} />
62
60
  {state instanceof GetCompteDetailsSuccessState && (
63
61
  <View>
64
62
  {/* Type de compte */}
@@ -113,6 +111,7 @@ export function CompteDetailsWidget() {
113
111
  }
114
112
 
115
113
  const styles = StyleSheet.create({
114
+ gap8: { height: 8 },
116
115
  copy: { padding: 8, marginRight: -8 },
117
116
  skeleton: { paddingVertical: 8 },
118
117
  gap16: { height: 16 },
@@ -1,27 +1,35 @@
1
- import { ScrollView, StyleSheet } from 'react-native';
1
+ import { ScrollView, StyleSheet, View } from 'react-native';
2
2
 
3
- import { AppRouter, useAppRouter } from '../../../../../core/router';
4
- import { AppBar, Scaffold } from '../../../../../shared/material/scaffold';
3
+ import { useAppLocalizations } from '../../../../../l10n/localizations';
4
+ import { Scaffold } from '../../../../../shared/material/scaffold';
5
5
  import { MyPageContainer } from '../../../../../shared/widgets/my_page_container';
6
6
  import { AliasDeleteBtnWidget } from '../../../../alias/presentation/pages/alias_delete_btn_widget';
7
7
  import { CompteDetailsWidget } from '../../../../compte/presentation/pages/compte_details_widget';
8
+ import { ProfileAppBar } from '../profile_app_bar';
9
+ import { ProfileSectionTitre } from '../profile_menu';
8
10
 
9
11
  /** Compte du client et suppression de l'alias (route `profileCompte`). */
10
12
  export function ProfileComptePage() {
11
- const router = useAppRouter();
13
+ const traductions = useAppLocalizations();
12
14
  return (
13
- <Scaffold appBar={<AppBar onBack={() => AppRouter.pop(router)} />}>
15
+ <Scaffold appBar={<ProfileAppBar titre={traductions.compteDetailsPageTitle} />}>
14
16
  <MyPageContainer>
15
17
  <ScrollView style={styles.flex}>
16
18
  <CompteDetailsWidget />
17
19
  </ScrollView>
20
+ <View style={styles.gap24} />
21
+
22
+ {/* Suppression de l'alias */}
23
+ <ProfileSectionTitre titre={traductions.comptePageSectionZoneSensible} />
24
+ <AliasDeleteBtnWidget />
25
+ <View style={styles.gap16} />
18
26
  </MyPageContainer>
19
- {/* Bouton supprimer l'alias */}
20
- <AliasDeleteBtnWidget />
21
27
  </Scaffold>
22
28
  );
23
29
  }
24
30
 
25
31
  const styles = StyleSheet.create({
26
32
  flex: { flex: 1 },
33
+ gap16: { height: 16 },
34
+ gap24: { height: 24 },
27
35
  });
@@ -1,20 +1,21 @@
1
- import { ScrollView, StyleSheet, Text, View } from 'react-native';
1
+ import { ScrollView, StyleSheet, View } from 'react-native';
2
2
 
3
3
  import { Images } from '../../../../../core/assets';
4
4
  import { useBloc, useBlocBuilder } from '../../../../../core/bloc_react';
5
5
  import { Di } from '../../../../../core/di';
6
- import { AppRouter, useAppRouter } from '../../../../../core/router';
6
+ import { AppRouter } from '../../../../../core/router';
7
7
  import { SoundManager } from '../../../../../core/sounds';
8
- import { Themer, Themes, useTheme } from '../../../../../core/theme';
8
+ import { Themer, Themes } from '../../../../../core/theme';
9
9
  import type { AppLocalizations } from '../../../../../l10n/app_localizations';
10
10
  import { useAppLocalizations } from '../../../../../l10n/localizations';
11
- import { AppBar, Scaffold } from '../../../../../shared/material/scaffold';
11
+ import { Scaffold } from '../../../../../shared/material/scaffold';
12
12
  import { LoadingPage } from '../../../../../shared/widgets/loading_page';
13
13
  import { MyPageContainer } from '../../../../../shared/widgets/my_page_container';
14
14
  import { ConfigBloc } from '../../../../config/adapters/ui/bloc/config_bloc';
15
15
  import { ConfigLoadedState } from '../../../../config/adapters/ui/bloc/config_state';
16
16
  import { ConfigKey } from '../../../../config/domain/models/config_keys';
17
17
  import type { ConfigParams } from '../../../../config/domain/models/config_params';
18
+ import { ProfileAppBar } from '../profile_app_bar';
18
19
  import { ProfilePageMenu, ProfileItem } from '../profile_menu';
19
20
  import { ProfileParametrePageQRcode } from './profile_parametre_page_qrcode';
20
21
 
@@ -69,8 +70,6 @@ function menuItems(traductions: AppLocalizations, configParams: ConfigParams): P
69
70
  /** Paramètres de l'application (route `profileParametre`). */
70
71
  export function ProfileParametrePage() {
71
72
  const traductions = useAppLocalizations();
72
- const theme = useTheme();
73
- const router = useAppRouter();
74
73
  const configBloc = useBloc(ConfigBloc);
75
74
  const state = useBlocBuilder(
76
75
  configBloc,
@@ -83,15 +82,15 @@ export function ProfileParametrePage() {
83
82
  );
84
83
 
85
84
  return (
86
- <Scaffold appBar={<AppBar onBack={() => AppRouter.pop(router)} />}>
85
+ <Scaffold appBar={<ProfileAppBar titre={traductions.appSettingPageTitle} />}>
87
86
  {!(state instanceof ConfigLoadedState) ? (
88
87
  <LoadingPage />
89
88
  ) : (
90
89
  <MyPageContainer>
91
90
  <ScrollView>
92
- <Text style={[theme.textTheme.titleSmall, styles.title]}>{traductions.appSettingPageTitle}</Text>
93
- <View style={styles.gap24} />
94
- <ProfilePageMenu items={menuItems(traductions, state.configParams)} />
91
+ <View style={styles.gap8} />
92
+ {/* Préférences */}
93
+ <ProfilePageMenu titre={traductions.appSettingPageSectionPreferences} items={menuItems(traductions, state.configParams)} />
95
94
  </ScrollView>
96
95
  </MyPageContainer>
97
96
  )}
@@ -100,6 +99,5 @@ export function ProfileParametrePage() {
100
99
  }
101
100
 
102
101
  const styles = StyleSheet.create({
103
- title: { fontSize: 26 },
104
- gap24: { height: 24 },
102
+ gap8: { height: 8 },
105
103
  });
@@ -1,4 +1,3 @@
1
- import { MaterialIcons } from '@expo/vector-icons';
2
1
  import { Image } from 'expo-image';
3
2
  import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
4
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -10,12 +9,13 @@ import { AppRouter, useAppRouter } from '../../../../../core/router';
10
9
  import { Themer, useTheme } from '../../../../../core/theme';
11
10
  import { useAppLocalizations } from '../../../../../l10n/localizations';
12
11
  import { Card } from '../../../../../shared/material/card';
13
- import { MenuActionItem, MenuActionsWidget } from '../../../../../shared/widgets/menu_actions_widget';
12
+ import { AppBar } from '../../../../../shared/material/scaffold';
14
13
  import { ConfigBloc } from '../../../../config/adapters/ui/bloc/config_bloc';
15
14
  import { ConfigChangeEvent } from '../../../../config/adapters/ui/bloc/config_event';
16
15
  import { ConfigLoadedState } from '../../../../config/adapters/ui/bloc/config_state';
17
16
  import { ConfigKey } from '../../../../config/domain/models/config_keys';
18
- import { SettingToggle } from './settings_widgets';
17
+ import { ProfileMenuTile, ProfilePageMenu, ProfileSectionTitre } from '../profile_menu';
18
+ import { SwitchThemed } from './settings_widgets';
19
19
 
20
20
  const NOTIFICATION_KEYS = [
21
21
  ConfigKey.notificationStatus,
@@ -58,88 +58,107 @@ export function ProfileParametrePageNotifications() {
58
58
  },
59
59
  ];
60
60
 
61
+ const basculerNotifications = () =>
62
+ configBloc.add(new ConfigChangeEvent(ConfigKey.notificationStatus, enabled ? 'disabled' : 'enabled'));
63
+ const basculerVibration = () =>
64
+ configBloc.add(new ConfigChangeEvent(ConfigKey.notificationAlertVibration, vibrationEnabled ? 'disabled' : 'enabled'));
65
+
61
66
  return (
62
67
  <View style={[styles.flex, { paddingBottom: insets.bottom }]}>
63
- <View style={styles.backRow}>
64
- <Pressable accessibilityRole="button" hitSlop={12} style={styles.back} onPress={() => AppRouter.pop(router)}>
65
- <MaterialIcons name="arrow-back" size={24} color={theme.colorScheme.onSurface} />
66
- </Pressable>
67
- </View>
68
+ {/* Titre dans la barre, comme les autres pages (dans la feuille : fond de la feuille) */}
69
+ <AppBar
70
+ backgroundColor="transparent"
71
+ onBack={() => AppRouter.pop(router)}
72
+ title={
73
+ <Text numberOfLines={1} style={theme.textTheme.headlineMedium}>
74
+ {traductions.appSettingPageMenuNotificationTitle}
75
+ </Text>
76
+ }
77
+ />
68
78
  <ScrollView style={styles.flex} contentContainerStyle={styles.content}>
69
- <Text style={theme.textTheme.headlineMedium}>{traductions.appSettingPageMenuNotificationTitle}</Text>
70
- <View style={styles.gap20} />
71
79
  {/* Activer les notifications */}
72
- <SettingToggle
73
- icon={Images.iconsCloche}
74
- title={traductions.notificationPageTitle}
75
- value={enabled}
76
- onChange={() => configBloc.add(new ConfigChangeEvent(ConfigKey.notificationStatus, enabled ? 'disabled' : 'enabled'))}
80
+ <ProfilePageMenu
81
+ items={[
82
+ <ProfileMenuTile
83
+ key="notifications"
84
+ image={Images.iconsCloche}
85
+ titre={traductions.notificationPageTitle}
86
+ onPress={basculerNotifications}
87
+ trailing={<SwitchThemed value={enabled} onChange={basculerNotifications} />}
88
+ />,
89
+ ]}
77
90
  />
91
+
78
92
  {/* Style de notification */}
79
- <View style={styles.gap10} />
80
- <Text style={theme.textTheme.headlineMedium}>{traductions.appSettingPageMenuNotificationStyleTitle}</Text>
81
- <View style={styles.gap10} />
82
- <Card>
83
- <View style={styles.wrap}>
84
- {styles_.map((item) => {
85
- const active = (!enabled && item.style == null) || currentStyle === item.style;
93
+ <View style={styles.gap24} />
94
+ <ProfileSectionTitre titre={traductions.appSettingPageMenuNotificationStyleTitle} />
95
+ <Card style={styles.card}>
96
+ <View style={styles.row}>
97
+ {styles_.map((item, i) => {
98
+ // « Aucun » quand les notifications sont désactivées
99
+ const choisi = enabled ? currentStyle === item.style : item.style == null;
86
100
  return (
87
101
  <Pressable
88
102
  key={item.title}
89
103
  accessibilityRole="button"
104
+ accessibilityState={{ selected: choisi }}
90
105
  disabled={!(enabled && item.style != null && currentStyle !== item.style)}
91
106
  onPress={() => configBloc.add(new ConfigChangeEvent(ConfigKey.notificationStyle, item.style!))}
92
- style={styles.styleItem}
107
+ style={[styles.style, i > 0 ? styles.styleGap : null]}
93
108
  >
94
- <Image
95
- source={item.icon}
96
- style={[styles.stylePreview, { backgroundColor: theme.colorScheme.surface }]}
97
- contentFit="cover"
98
- tintColor={active ? 'black' : undefined}
99
- />
100
- <View style={styles.gap10} />
101
- <View style={[styles.badge, { backgroundColor: active ? theme.colorScheme.onSurface : theme.colorScheme.secondary }]}>
102
- <Text style={[theme.textTheme.displayMedium, { color: active ? theme.colorScheme.surface : theme.colorScheme.onSecondary }]}>
103
- {item.title}
104
- </Text>
109
+ {/* Aperçu, entouré quand il est choisi */}
110
+ <View style={[styles.cadre, { borderColor: choisi ? theme.colorScheme.primary : 'transparent' }]}>
111
+ <Image
112
+ source={item.icon}
113
+ style={[styles.apercu, { backgroundColor: theme.colorScheme.surface }]}
114
+ contentFit="cover"
115
+ />
105
116
  </View>
117
+ <Text
118
+ style={[
119
+ theme.textTheme.displayMedium,
120
+ styles.libelle,
121
+ { fontWeight: choisi ? '700' : '500' },
122
+ choisi ? null : { color: Themer.neural03Color },
123
+ ]}
124
+ >
125
+ {item.title}
126
+ </Text>
106
127
  </Pressable>
107
128
  );
108
129
  })}
109
130
  </View>
110
131
  </Card>
132
+ {/* Description des styles de notification */}
111
133
  <View style={styles.gap10} />
112
- <Text style={[theme.textTheme.displayLarge, { color: Themer.neural04Color }]}>
134
+ <Text style={[theme.textTheme.displaySmall, styles.description, { color: Themer.neural03Color }]}>
113
135
  {traductions.appSettingPageMenuNotificationStyleDialogDesc}
114
136
  </Text>
115
- {/* Son */}
116
- <View style={styles.gap20} />
117
- <Text style={theme.textTheme.headlineMedium}>{traductions.appSettingPageMenuNotificationSonTitle}</Text>
118
- <View style={styles.gap10} />
119
- <MenuActionsWidget
137
+
138
+ {/* Son et vibration */}
139
+ <View style={styles.gap24} />
140
+ <ProfilePageMenu
141
+ titre={traductions.appSettingPageMenuNotificationSonTitle}
120
142
  items={[
121
- new MenuActionItem(
122
- Images.iconsCloche,
123
- traductions.notificationPageTitle,
124
- sonEnabled ? traductions.statutActive : traductions.statutDesactive,
125
- () => void AppRouter.push(router, AppRouter.profileParametreNotificationsSon),
126
- { iconSize: 20 },
127
- ),
143
+ // Son des notifications
144
+ <ProfileMenuTile
145
+ key="son"
146
+ image={Images.iconsCloche}
147
+ titre={traductions.notificationPageTitle}
148
+ sousTitre={sonEnabled ? traductions.statutActive : traductions.statutDesactive}
149
+ onPress={() => void AppRouter.push(router, AppRouter.profileParametreNotificationsSon)}
150
+ />,
151
+ // Vibration des notifications
152
+ <ProfileMenuTile
153
+ key="vibration"
154
+ icone="vibration"
155
+ titre={traductions.appSettingPageMenuNotificationVibrTitle}
156
+ sousTitre={traductions.appSettingPageMenuNotificationVibrSubtitle}
157
+ onPress={basculerVibration}
158
+ trailing={<SwitchThemed value={vibrationEnabled} onChange={basculerVibration} />}
159
+ />,
128
160
  ]}
129
161
  />
130
- {/* Vibration */}
131
- <View style={styles.gap20} />
132
- <Text style={theme.textTheme.headlineMedium}>{traductions.appSettingPageMenuNotificationVibrTitle}</Text>
133
- <View style={styles.gap10} />
134
- <SettingToggle
135
- icon={Images.iconsCloche}
136
- title={traductions.appSettingPageMenuNotificationVibrTitle}
137
- subtitle={traductions.appSettingPageMenuNotificationVibrSubtitle}
138
- value={vibrationEnabled}
139
- onChange={() =>
140
- configBloc.add(new ConfigChangeEvent(ConfigKey.notificationAlertVibration, vibrationEnabled ? 'disabled' : 'enabled'))
141
- }
142
- />
143
162
  </ScrollView>
144
163
  </View>
145
164
  );
@@ -147,13 +166,15 @@ export function ProfileParametrePageNotifications() {
147
166
 
148
167
  const styles = StyleSheet.create({
149
168
  flex: { flex: 1 },
150
- backRow: { flexDirection: 'row' },
151
- back: { padding: 12 },
152
- content: { paddingTop: 12, paddingHorizontal: 12, paddingBottom: 20 },
153
- wrap: { padding: 20, flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', rowGap: 10, columnGap: 10 },
154
- styleItem: { alignItems: 'center' },
155
- stylePreview: { width: 60, height: 120, borderRadius: 8 },
156
- badge: { borderRadius: 10, paddingHorizontal: 10, paddingVertical: 5 },
169
+ content: { paddingTop: 8, paddingHorizontal: 20, paddingBottom: 24 },
170
+ card: { margin: 0 },
171
+ row: { flexDirection: 'row', alignItems: 'flex-start', padding: 14 },
172
+ style: { flex: 1, alignItems: 'center', borderRadius: 14 },
173
+ styleGap: { marginLeft: 12 },
174
+ cadre: { alignSelf: 'stretch', padding: 3, borderWidth: 2, borderRadius: 14 },
175
+ apercu: { width: '100%', aspectRatio: 0.55, borderRadius: 10 },
176
+ libelle: { marginTop: 8, textAlign: 'center' },
177
+ description: { paddingHorizontal: 4 },
157
178
  gap10: { height: 10 },
158
- gap20: { height: 20 },
179
+ gap24: { height: 24 },
159
180
  });
@@ -1,6 +1,6 @@
1
1
  import { MaterialIcons } from '@expo/vector-icons';
2
- import { useState } from 'react';
3
- import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
2
+ import { useState, type ComponentProps } from 'react';
3
+ import { ScrollView, StyleSheet, Text, View } from 'react-native';
4
4
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
5
 
6
6
  import { Images } from '../../../../../core/assets';
@@ -10,11 +10,12 @@ import { SoundManager } from '../../../../../core/sounds';
10
10
  import { useTheme } from '../../../../../core/theme';
11
11
  import { useAppLocalizations } from '../../../../../l10n/localizations';
12
12
  import { TextButton } from '../../../../../shared/material/buttons';
13
- import { Card } from '../../../../../shared/material/card';
13
+ import { AppBar } from '../../../../../shared/material/scaffold';
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 { ConfigKey } from '../../../../config/domain/models/config_keys';
17
- import { SettingToggle } from './settings_widgets';
17
+ import { ProfileMenuTile, ProfilePageMenu } from '../profile_menu';
18
+ import { SwitchThemed } from './settings_widgets';
18
19
 
19
20
  const NONE = 'NONE';
20
21
 
@@ -44,77 +45,106 @@ export function ProfileParametrePageNotificationsSon() {
44
45
  }
45
46
  };
46
47
 
48
+ const basculer = () => setSelectedSound(enabled ? NONE : (SoundManager.liste[0]?.path ?? null));
49
+
47
50
  return (
48
51
  <View style={[styles.flex, { paddingBottom: insets.bottom }]}>
49
- <View style={styles.bar}>
50
- <Pressable accessibilityRole="button" hitSlop={12} style={styles.back} onPress={() => AppRouter.pop(router)}>
51
- <MaterialIcons name="arrow-back" size={24} color={theme.colorScheme.onSurface} />
52
- </Pressable>
53
- <View style={styles.save}>
54
- <TextButton onPress={save}>{traductions.btnTextSave}</TextButton>
55
- </View>
56
- </View>
52
+ {/* Titre dans la barre, comme les autres pages (dans la feuille : fond de la feuille) */}
53
+ <AppBar
54
+ backgroundColor="transparent"
55
+ onBack={() => AppRouter.pop(router)}
56
+ title={
57
+ <Text numberOfLines={1} style={theme.textTheme.headlineMedium}>
58
+ {traductions.appSettingPageMenuNotificationSonPageTitle}
59
+ </Text>
60
+ }
61
+ actions={
62
+ <View style={styles.save}>
63
+ <TextButton onPress={save}>{traductions.btnTextSave}</TextButton>
64
+ </View>
65
+ }
66
+ />
57
67
  <ScrollView style={styles.flex} contentContainerStyle={styles.content}>
58
- <Text style={theme.textTheme.headlineMedium}>{traductions.appSettingPageMenuNotificationSonPageTitle}</Text>
59
- <View style={styles.gap20} />
60
68
  {/* Son activé ou non */}
61
- <SettingToggle
62
- icon={Images.iconsCloche}
63
- title={traductions.notificationPageTitle}
64
- value={enabled}
65
- onChange={(value) => setSelectedSound(value ? (SoundManager.liste[0]?.path ?? null) : NONE)}
69
+ <ProfilePageMenu
70
+ items={[
71
+ <ProfileMenuTile
72
+ key="son"
73
+ image={Images.iconsCloche}
74
+ titre={traductions.notificationPageTitle}
75
+ onPress={basculer}
76
+ trailing={<SwitchThemed value={enabled} onChange={basculer} />}
77
+ />,
78
+ ]}
66
79
  />
80
+
67
81
  {/* Sons disponibles */}
68
- <View style={styles.gap20} />
69
- <Text style={theme.textTheme.headlineMedium}>{traductions.appSettingPageMenuNotificationSonTitle}</Text>
70
- <View style={styles.gap10} />
71
- <Card>
72
- {/* Aucun */}
73
- <SoundRow
74
- label={traductions.appSettingPageMenuNotificationStyleNone}
75
- checked={selectedSound === NONE}
76
- onPress={() => setSelectedSound(NONE)}
77
- />
78
- {SoundManager.liste.map((sound) => (
82
+ <View style={styles.gap24} />
83
+ <ProfilePageMenu
84
+ titre={traductions.appSettingPageMenuNotificationSonTitle}
85
+ items={[
86
+ // Aucun
79
87
  <SoundRow
80
- key={sound.path}
81
- label={sound.name}
82
- checked={selectedSound === sound.path}
83
- onPress={() => {
84
- void SoundManager.playSound(sound.path);
85
- setSelectedSound(sound.path);
86
- }}
87
- />
88
- ))}
89
- </Card>
88
+ key={NONE}
89
+ icone="music-off"
90
+ label={traductions.appSettingPageMenuNotificationStyleNone}
91
+ checked={selectedSound === NONE}
92
+ onPress={() => setSelectedSound(NONE)}
93
+ />,
94
+ ...SoundManager.liste.map((sound) => (
95
+ <SoundRow
96
+ key={sound.path}
97
+ icone="music-note"
98
+ label={sound.name}
99
+ checked={selectedSound === sound.path}
100
+ onPress={() => {
101
+ void SoundManager.playSound(sound.path);
102
+ setSelectedSound(sound.path);
103
+ }}
104
+ />
105
+ )),
106
+ ]}
107
+ />
90
108
  <View style={styles.gap20} />
91
109
  </ScrollView>
92
110
  </View>
93
111
  );
94
112
  }
95
113
 
96
- function SoundRow({ label, checked, onPress }: { label: string; checked: boolean; onPress: () => void }) {
114
+ /** Son de la liste, coché quand il est choisi */
115
+ function SoundRow({
116
+ icone,
117
+ label,
118
+ checked,
119
+ onPress,
120
+ }: {
121
+ icone: ComponentProps<typeof MaterialIcons>['name'];
122
+ label: string;
123
+ checked: boolean;
124
+ onPress: () => void;
125
+ }) {
97
126
  const theme = useTheme();
98
127
  return (
99
- <Pressable
100
- accessibilityRole="radio"
101
- accessibilityState={{ checked }}
128
+ <ProfileMenuTile
129
+ icone={icone}
130
+ titre={label}
102
131
  onPress={onPress}
103
- style={({ pressed }) => [styles.row, pressed ? { backgroundColor: theme.colorScheme.secondary } : null]}
104
- >
105
- <Text style={[theme.textTheme.bodyLarge, styles.flex]}>{label}</Text>
106
- {checked && <MaterialIcons name="check" size={18} color={theme.colorScheme.onSurface} />}
107
- </Pressable>
132
+ trailing={
133
+ checked ? (
134
+ <MaterialIcons name="check-circle" size={22} color={theme.colorScheme.primary} />
135
+ ) : (
136
+ <View style={styles.vide} />
137
+ )
138
+ }
139
+ />
108
140
  );
109
141
  }
110
142
 
111
143
  const styles = StyleSheet.create({
112
144
  flex: { flex: 1 },
113
- bar: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
114
- back: { padding: 12 },
115
145
  save: { paddingRight: 12 },
116
- content: { paddingTop: 12, paddingHorizontal: 12 },
117
- row: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, minHeight: 52 },
118
- gap10: { height: 10 },
146
+ content: { paddingTop: 8, paddingHorizontal: 20 },
147
+ vide: { width: 22 },
119
148
  gap20: { height: 20 },
149
+ gap24: { height: 24 },
120
150
  });
@@ -1,52 +1,56 @@
1
- import { ScrollView, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
2
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
1
+ import { StyleSheet, Text, View } from 'react-native';
3
2
 
4
3
  import { Images } from '../../../../../core/assets';
5
4
  import { useBloc, useBlocBuilder } from '../../../../../core/bloc_react';
6
- import { useTheme } from '../../../../../core/theme';
5
+ import { Themer, useTheme } from '../../../../../core/theme';
7
6
  import { useAppLocalizations } from '../../../../../l10n/localizations';
7
+ import { SbSelectionSheet } from '../../../../../shared/widgets/selection';
8
8
  import { ConfigBloc } from '../../../../config/adapters/ui/bloc/config_bloc';
9
9
  import { ConfigChangeEvent } from '../../../../config/adapters/ui/bloc/config_event';
10
10
  import { ConfigLoadedState } from '../../../../config/adapters/ui/bloc/config_state';
11
11
  import { ConfigKey } from '../../../../config/domain/models/config_keys';
12
- import { SettingToggle } from './settings_widgets';
12
+ import { ProfileMenuTile, ProfilePageMenu } from '../profile_menu';
13
+ import { SwitchThemed } from './settings_widgets';
13
14
 
14
15
  /** Feuille « Mon QR code » : afficher ou non le QR code de l'alias sur l'accueil. */
15
16
  export function ProfileParametrePageQRcode() {
16
17
  const traductions = useAppLocalizations();
17
18
  const theme = useTheme();
18
- const insets = useSafeAreaInsets();
19
- const { height } = useWindowDimensions();
20
19
  const configBloc = useBloc(ConfigBloc);
21
20
  const state = useBlocBuilder(
22
21
  configBloc,
23
22
  (_p, current) => current instanceof ConfigLoadedState && current.updatedKey === ConfigKey.showQRcode,
24
23
  );
25
24
  const showQRcode = state.configParams.params[ConfigKey.showQRcode] !== '0';
25
+ const basculer = () => configBloc.add(new ConfigChangeEvent(ConfigKey.showQRcode, showQRcode ? '0' : '1'));
26
26
 
27
27
  return (
28
- <View style={[styles.box, { backgroundColor: theme.colorScheme.surface, maxHeight: height * 0.5 }]}>
29
- <ScrollView contentContainerStyle={[styles.content, { paddingBottom: 20 + insets.bottom }]}>
30
- <Text style={theme.textTheme.headlineLarge}>{traductions.appSettingPageMenuQrCodeTitle}</Text>
31
- <View style={styles.gap8} />
32
- <Text style={theme.textTheme.displaySmall}>
28
+ <SbSelectionSheet
29
+ titre={traductions.appSettingPageMenuQrCodeTitle}
30
+ entete={
31
+ <Text style={[theme.textTheme.displaySmall, { color: Themer.neural03Color }]}>
33
32
  {showQRcode ? traductions.appSettingPageMenuQrCodeDeselectTitle : traductions.appSettingPageMenuQrCodeSelectTitle}
34
33
  </Text>
35
- <View style={styles.gap15} />
36
- <SettingToggle
37
- icon={Images.homeFooterQrcode}
38
- title={traductions.appSettingPageMenuQrCodeTitle}
39
- value={showQRcode}
40
- onChange={() => configBloc.add(new ConfigChangeEvent(ConfigKey.showQRcode, showQRcode ? '0' : '1'))}
41
- />
42
- </ScrollView>
43
- </View>
34
+ }
35
+ contenu={
36
+ <View style={styles.contenu}>
37
+ <ProfilePageMenu
38
+ items={[
39
+ <ProfileMenuTile
40
+ key="qrcode"
41
+ image={Images.homeFooterQrcode}
42
+ titre={traductions.appSettingPageMenuQrCodeTitle}
43
+ onPress={basculer}
44
+ trailing={<SwitchThemed value={showQRcode} onChange={basculer} />}
45
+ />,
46
+ ]}
47
+ />
48
+ </View>
49
+ }
50
+ />
44
51
  );
45
52
  }
46
53
 
47
54
  const styles = StyleSheet.create({
48
- box: { borderRadius: 20, overflow: 'hidden' },
49
- content: { padding: 20 },
50
- gap8: { height: 8 },
51
- gap15: { height: 15 },
55
+ contenu: { paddingHorizontal: 20, paddingTop: 8, paddingBottom: 20 },
52
56
  });