@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.
- package/lib/module/l10n/app_localizations.js.map +1 -1
- package/lib/module/l10n/app_localizations_en.js +12 -1
- package/lib/module/l10n/app_localizations_en.js.map +1 -1
- package/lib/module/l10n/app_localizations_fr.js +12 -1
- package/lib/module/l10n/app_localizations_fr.js.map +1 -1
- package/lib/module/l10n/app_localizations_pt.js +12 -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/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 +12 -18
- 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 +103 -67
- 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 +143 -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_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/shared/utils/color.js +15 -0
- package/lib/module/shared/utils/color.js.map +1 -0
- 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/l10n/app_localizations.d.ts +22 -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/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.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/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/l10n/app_localizations.ts +22 -0
- package/src/l10n/app_localizations_en.ts +11 -0
- package/src/l10n/app_localizations_fr.ts +11 -0
- package/src/l10n/app_localizations_pt.ts +11 -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/profile/presentation/pages/compte/profile_compte_page.tsx +15 -7
- package/src/modules/profile/presentation/pages/parametre/profile_parametre_page.tsx +10 -12
- 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 +77 -44
- 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_rtp/transaction_rtp_page.tsx +2 -4
- package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.tsx +13 -6
- package/src/shared/utils/color.ts +14 -0
- package/src/shared/widgets/error_card_widget.tsx +58 -0
- package/src/shared/widgets/selection.tsx +98 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MaterialIcons } from '@expo/vector-icons';
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useId, useState } from 'react';
|
|
3
3
|
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
4
|
+
import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg';
|
|
4
5
|
|
|
5
6
|
import { useBloc } from '../../../../core/bloc_react';
|
|
6
7
|
import { Di } from '../../../../core/di';
|
|
@@ -9,16 +10,18 @@ import { Themer, useTheme } from '../../../../core/theme';
|
|
|
9
10
|
import { useValueListenable } from '../../../../core/value_notifier';
|
|
10
11
|
import { useAppLocalizations } from '../../../../l10n/localizations';
|
|
11
12
|
import { showModalBottomSheet } from '../../../../shared/material/overlays';
|
|
12
|
-
import {
|
|
13
|
+
import { AliasPI } from '../../../../shared/models/alias_pi';
|
|
14
|
+
import { withAlpha } from '../../../../shared/utils/color';
|
|
13
15
|
import { AvatarWidget } from '../../../../shared/widgets/avatar_widget';
|
|
16
|
+
import { ClipboardWidget } from '../../../../shared/widgets/clipboard_widget';
|
|
14
17
|
import { ImageSelectWidget } from '../../../../shared/widgets/image_select_widget';
|
|
15
18
|
import { AliasBloc } from '../../../alias/presentation/bloc/alias_bloc';
|
|
16
19
|
import { AliasExistState } from '../../../alias/presentation/bloc/alias_state';
|
|
17
20
|
import { ProfileAvatar } from '../../domain/profile_avatar';
|
|
18
21
|
|
|
19
|
-
const AVATAR_SIZE =
|
|
22
|
+
const AVATAR_SIZE = 68;
|
|
20
23
|
|
|
21
|
-
/** En-tête du profil :
|
|
24
|
+
/** En-tête du profil : photo (modifiable), nom et alias. */
|
|
22
25
|
export function ProfilePageAvatar() {
|
|
23
26
|
const traductions = useAppLocalizations();
|
|
24
27
|
const theme = useTheme();
|
|
@@ -27,6 +30,7 @@ export function ProfilePageAvatar() {
|
|
|
27
30
|
const [aliasCle] = useState(() => (aliasBloc.state instanceof AliasExistState ? aliasBloc.state.alias.getCle() : ''));
|
|
28
31
|
const user = Di.config.user;
|
|
29
32
|
const avatar = useValueListenable(ProfileAvatar.value);
|
|
33
|
+
const gradientId = `entete${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
|
|
30
34
|
|
|
31
35
|
const showImagePicker = () =>
|
|
32
36
|
void showModalBottomSheet(router, {
|
|
@@ -63,49 +67,86 @@ export function ProfilePageAvatar() {
|
|
|
63
67
|
),
|
|
64
68
|
});
|
|
65
69
|
|
|
70
|
+
const primaire = theme.colorScheme.primary;
|
|
71
|
+
|
|
66
72
|
return (
|
|
67
|
-
<View style={styles.
|
|
68
|
-
{/*
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
<View style={styles.entete}>
|
|
74
|
+
{/* Dégradé doux de la couleur du thème */}
|
|
75
|
+
<Svg style={StyleSheet.absoluteFill} preserveAspectRatio="none">
|
|
76
|
+
<Defs>
|
|
77
|
+
<LinearGradient id={gradientId} x1="0" y1="0" x2="1" y2="1">
|
|
78
|
+
<Stop offset="0" stopColor={primaire} stopOpacity={0.22} />
|
|
79
|
+
<Stop offset="1" stopColor={primaire} stopOpacity={0.06} />
|
|
80
|
+
</LinearGradient>
|
|
81
|
+
</Defs>
|
|
82
|
+
<Rect width="100%" height="100%" fill={`url(#${gradientId})`} />
|
|
83
|
+
</Svg>
|
|
84
|
+
|
|
77
85
|
{/* Avatar */}
|
|
78
86
|
<Pressable accessibilityRole="imagebutton" onPress={showImagePicker}>
|
|
79
|
-
<View style={styles.
|
|
80
|
-
<AvatarWidget userInitiales={user.initiales()} photo={avatar} size={AVATAR_SIZE} rounded />
|
|
87
|
+
<View style={[styles.avatar, { backgroundColor: theme.colorScheme.surface }]}>
|
|
88
|
+
<AvatarWidget userInitiales={user.initiales()} photo={avatar} size={AVATAR_SIZE - 6} rounded />
|
|
81
89
|
</View>
|
|
82
90
|
{/* Badge caméra */}
|
|
83
|
-
<View style={[styles.camera, { backgroundColor:
|
|
84
|
-
<MaterialIcons name="photo-camera" size={
|
|
91
|
+
<View style={[styles.camera, { backgroundColor: primaire, borderColor: theme.colorScheme.surface }]}>
|
|
92
|
+
<MaterialIcons name="photo-camera" size={15} color={theme.colorScheme.onPrimary} />
|
|
85
93
|
</View>
|
|
86
94
|
</Pressable>
|
|
95
|
+
|
|
96
|
+
{/* Nom */}
|
|
97
|
+
<Text numberOfLines={2} style={[theme.textTheme.headlineMedium, styles.nom]}>
|
|
98
|
+
{user.nomComplet()}
|
|
99
|
+
</Text>
|
|
100
|
+
|
|
101
|
+
{/* Alias de l'utilisateur, copié d'un appui */}
|
|
102
|
+
<Pressable
|
|
103
|
+
accessibilityRole="button"
|
|
104
|
+
onPress={() => void ClipboardWidget.setData(aliasCle)}
|
|
105
|
+
style={[styles.alias, { backgroundColor: withAlpha(theme.colorScheme.surface, 0.7) }]}
|
|
106
|
+
>
|
|
107
|
+
<MaterialIcons name="badge" size={15} color={primaire} />
|
|
108
|
+
<Text numberOfLines={1} style={[theme.textTheme.displayMedium, styles.aliasTexte]}>
|
|
109
|
+
{AliasPI.formatPhoneNumber(aliasCle)}
|
|
110
|
+
</Text>
|
|
111
|
+
<MaterialIcons name="content-copy" size={14} color={primaire} />
|
|
112
|
+
</Pressable>
|
|
87
113
|
</View>
|
|
88
114
|
);
|
|
89
115
|
}
|
|
90
116
|
|
|
91
117
|
const styles = StyleSheet.create({
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
entete: {
|
|
119
|
+
alignItems: 'center',
|
|
120
|
+
paddingTop: 24,
|
|
121
|
+
paddingBottom: 20,
|
|
122
|
+
paddingHorizontal: 20,
|
|
123
|
+
borderRadius: 24,
|
|
124
|
+
overflow: 'hidden',
|
|
125
|
+
},
|
|
126
|
+
avatar: { padding: 3, borderRadius: AVATAR_SIZE / 2, boxShadow: '0 3px 12px rgba(0,0,0,0.12)' },
|
|
97
127
|
camera: {
|
|
98
128
|
position: 'absolute',
|
|
99
|
-
right:
|
|
100
|
-
bottom:
|
|
101
|
-
width:
|
|
102
|
-
height:
|
|
103
|
-
borderRadius:
|
|
129
|
+
right: 0,
|
|
130
|
+
bottom: 0,
|
|
131
|
+
width: 30,
|
|
132
|
+
height: 30,
|
|
133
|
+
borderRadius: 15,
|
|
104
134
|
borderWidth: 2,
|
|
105
135
|
alignItems: 'center',
|
|
106
136
|
justifyContent: 'center',
|
|
107
137
|
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
|
|
108
138
|
},
|
|
139
|
+
nom: { marginTop: 14, textAlign: 'center' },
|
|
140
|
+
alias: {
|
|
141
|
+
flexDirection: 'row',
|
|
142
|
+
alignItems: 'center',
|
|
143
|
+
marginTop: 12,
|
|
144
|
+
paddingHorizontal: 12,
|
|
145
|
+
paddingVertical: 5,
|
|
146
|
+
borderRadius: 20,
|
|
147
|
+
maxWidth: '100%',
|
|
148
|
+
},
|
|
149
|
+
aliasTexte: { flexShrink: 1, marginHorizontal: 6 },
|
|
109
150
|
deleteTile: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, minHeight: 56 },
|
|
110
151
|
deleteText: { flex: 1, marginHorizontal: 16 },
|
|
111
152
|
});
|
|
@@ -10,13 +10,14 @@ import { Themer, useTheme } from '../../../../core/theme';
|
|
|
10
10
|
import { useAppLocalizations } from '../../../../l10n/localizations';
|
|
11
11
|
import { Card } from '../../../../shared/material/card';
|
|
12
12
|
import { showModalBottomSheet } from '../../../../shared/material/overlays';
|
|
13
|
-
import {
|
|
13
|
+
import { Scaffold } from '../../../../shared/material/scaffold';
|
|
14
14
|
import { MyPageContainer } from '../../../../shared/widgets/my_page_container';
|
|
15
15
|
import type { ContactPI } from '../../../contacts/domain/contact_pi';
|
|
16
16
|
import type { PhoneContact } from '../../../contacts/domain/phone_contact';
|
|
17
17
|
import { ContactBloc } from '../../../contacts/presentation/bloc/contact_bloc';
|
|
18
18
|
import { ContactSearchEvent } from '../../../contacts/presentation/bloc/contact_event';
|
|
19
19
|
import { ContactAliasListWidget } from '../../../contacts/presentation/pages/contact_list_alias_widget';
|
|
20
|
+
import { ProfileAppBar } from './profile_app_bar';
|
|
20
21
|
import { ProfilePageContactsActions } from './profile_page_contacts_actions';
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -42,11 +43,10 @@ export function ProfilePageContacts({ contactPI }: { contactPI?: ContactPI | nul
|
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
return (
|
|
45
|
-
<Scaffold appBar={<
|
|
46
|
+
<Scaffold appBar={<ProfileAppBar titre={traductions.profileContactPageTitle} />}>
|
|
46
47
|
<MyPageContainer>
|
|
47
48
|
<Pressable style={styles.flex} onPress={Keyboard.dismiss} accessible={false}>
|
|
48
|
-
<
|
|
49
|
-
<View style={styles.gap10} />
|
|
49
|
+
<View style={styles.gap8} />
|
|
50
50
|
{/* Recherche d'un contact */}
|
|
51
51
|
<Card>
|
|
52
52
|
<View style={[styles.search, { backgroundColor: theme.inputDecorationTheme.fillColor }]}>
|
|
@@ -99,6 +99,7 @@ export function ProfilePageContacts({ contactPI }: { contactPI?: ContactPI | nul
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const styles = StyleSheet.create({
|
|
102
|
+
gap8: { height: 8 },
|
|
102
103
|
flex: { flex: 1 },
|
|
103
104
|
search: { height: 36, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12 },
|
|
104
105
|
input: { flex: 1, marginLeft: 8, paddingVertical: 0 },
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Platform } from 'react-native';
|
|
2
2
|
|
|
3
3
|
import { Images } from '../../../../core/assets';
|
|
4
|
+
import { Di } from '../../../../core/di';
|
|
5
|
+
import { AppEnv } from '../../../../core/env';
|
|
4
6
|
import { AppRouter, type SdkRouter } from '../../../../core/router';
|
|
5
7
|
import type { AppLocalizations } from '../../../../l10n/app_localizations';
|
|
6
8
|
import { Fonctionnalite } from '../../domain/models/fonctionnalite';
|
|
@@ -65,6 +67,13 @@ export function fonctionnalitesRecherchables(
|
|
|
65
67
|
motsCles: ['historique', 'paiement', 'operation', 'releve', 'history', 'payment', 'historico', 'pagamento'],
|
|
66
68
|
ouvrir: () => aller(AppRouter.transactionSearch),
|
|
67
69
|
}),
|
|
70
|
+
new Fonctionnalite({
|
|
71
|
+
icon: Images.iconsCloche,
|
|
72
|
+
titre: traductions.searchNotificationsTitle,
|
|
73
|
+
sousTitre: traductions.searchNotificationsSubTitle,
|
|
74
|
+
motsCles: ['notification', 'alerte', 'message', 'nouveau', 'alert', 'news', 'notificacao', 'alerta'],
|
|
75
|
+
ouvrir: () => aller(AppRouter.notifications),
|
|
76
|
+
}),
|
|
68
77
|
|
|
69
78
|
// Abonnements
|
|
70
79
|
new Fonctionnalite({
|
|
@@ -111,6 +120,25 @@ export function fonctionnalitesRecherchables(
|
|
|
111
120
|
motsCles: ['parametre', 'reglage', 'theme', 'langue', 'notification', 'sombre', 'settings', 'language', 'dark', 'definicoes', 'idioma'],
|
|
112
121
|
ouvrir: () => aller(AppRouter.profileParametre),
|
|
113
122
|
}),
|
|
123
|
+
// Masqué si l'hôte n'autorise pas le client à changer de thème
|
|
124
|
+
...(Di.config.appConfig.canChangeTheme
|
|
125
|
+
? [
|
|
126
|
+
new Fonctionnalite({
|
|
127
|
+
icon: Images.iconsTheme,
|
|
128
|
+
titre: traductions.searchThemeTitle,
|
|
129
|
+
sousTitre: traductions.searchThemeSubTitle,
|
|
130
|
+
motsCles: ['theme', 'apparence', 'sombre', 'clair', 'couleur', 'mode', 'appearance', 'dark', 'light', 'color', 'tema', 'aparencia', 'escuro', 'claro', 'cor'],
|
|
131
|
+
ouvrir: () => aller(AppRouter.profileParametreTheme),
|
|
132
|
+
}),
|
|
133
|
+
]
|
|
134
|
+
: []),
|
|
135
|
+
new Fonctionnalite({
|
|
136
|
+
icon: Images.iconsCloche,
|
|
137
|
+
titre: traductions.searchNotificationsParametreTitle,
|
|
138
|
+
sousTitre: traductions.searchNotificationsParametreSubTitle,
|
|
139
|
+
motsCles: ['notification', 'son', 'vibration', 'sonnerie', 'style', 'sound', 'vibrate', 'som', 'vibracao', 'estilo'],
|
|
140
|
+
ouvrir: () => aller(AppRouter.profileParametreNotifications),
|
|
141
|
+
}),
|
|
114
142
|
// Sur le web, le profil n'a pas ces pages : pas de contacts,
|
|
115
143
|
// et l'aide est un lien externe.
|
|
116
144
|
...(web
|
|
@@ -123,13 +151,18 @@ export function fonctionnalitesRecherchables(
|
|
|
123
151
|
motsCles: ['contact', 'alias', 'repertoire', 'contacts', 'contactos'],
|
|
124
152
|
ouvrir: () => aller(AppRouter.profileContacts),
|
|
125
153
|
}),
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
154
|
+
// Seulement si l'hôte fournit un lien d'aide, comme dans le profil
|
|
155
|
+
...((AppEnv.aideLink?.length ?? 0) > 0
|
|
156
|
+
? [
|
|
157
|
+
new Fonctionnalite({
|
|
158
|
+
icon: Images.iconsHelp,
|
|
159
|
+
titre: traductions.profilePageMenuHelpTitle,
|
|
160
|
+
sousTitre: traductions.searchAideSubTitle,
|
|
161
|
+
motsCles: ['aide', 'faq', 'assistance', 'support', 'help', 'ajuda'],
|
|
162
|
+
ouvrir: () => aller(AppRouter.profileHelpCenter),
|
|
163
|
+
}),
|
|
164
|
+
]
|
|
165
|
+
: []),
|
|
133
166
|
]),
|
|
134
167
|
];
|
|
135
168
|
}
|
|
@@ -289,7 +289,7 @@ export function SearchPage({
|
|
|
289
289
|
</Text>
|
|
290
290
|
</View>
|
|
291
291
|
) : (
|
|
292
|
-
<ScrollView
|
|
292
|
+
<ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={styles.liste}>
|
|
293
293
|
<MenuActionsWidget items={resultats.map(ligne)} />
|
|
294
294
|
</ScrollView>
|
|
295
295
|
)}
|
package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import { DateFormat } from '../../../../../shared/utils/date_format';
|
|
|
17
17
|
import { AliasWidget } from '../../../../../shared/widgets/alias_widget';
|
|
18
18
|
import { formatMontant } from '../../../../../shared/widgets/amount_widget';
|
|
19
19
|
import { ClipboardWidget } from '../../../../../shared/widgets/clipboard_widget';
|
|
20
|
+
import { ErrorCardWidget } from '../../../../../shared/widgets/error_card_widget';
|
|
20
21
|
import { ErrorPage, ErrorType } from '../../../../../shared/widgets/error_page';
|
|
21
22
|
import { CustomLoadingDialog, LoadingPage } from '../../../../../shared/widgets/loading_page';
|
|
22
23
|
import { NotificationDialog, NotificationType } from '../../../../../shared/widgets/notification_dialog';
|
|
@@ -260,9 +261,7 @@ export function TransactionRtpPage({
|
|
|
260
261
|
{toPay && !canDoAction() && (
|
|
261
262
|
<>
|
|
262
263
|
<View style={styles.gap16} />
|
|
263
|
-
<
|
|
264
|
-
{traductions.transactionRtpWarningMessage}
|
|
265
|
-
</Text>
|
|
264
|
+
<ErrorCardWidget icon="shield" message={traductions.transactionRtpWarningMessage} />
|
|
266
265
|
</>
|
|
267
266
|
)}
|
|
268
267
|
</ScrollView>
|
|
@@ -487,7 +486,6 @@ const styles = StyleSheet.create({
|
|
|
487
486
|
row: { flexDirection: 'row', alignItems: 'center' },
|
|
488
487
|
value: { flex: 1, paddingLeft: 10 },
|
|
489
488
|
right: { textAlign: 'right' },
|
|
490
|
-
warning: { marginLeft: 8, marginRight: 20, marginTop: 2, marginBottom: 5 },
|
|
491
489
|
gap3: { height: 3 },
|
|
492
490
|
gap10: { height: 10 },
|
|
493
491
|
gap16: { height: 16 },
|
package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MaterialIcons } from '@expo/vector-icons';
|
|
2
|
-
import {
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { Pressable, StyleSheet, View } from 'react-native';
|
|
3
4
|
|
|
4
5
|
import { BlocProvider, useBloc } from '../../../../../core/bloc_react';
|
|
5
6
|
import { Di } from '../../../../../core/di';
|
|
@@ -9,6 +10,7 @@ import { useAppLocalizations } from '../../../../../l10n/localizations';
|
|
|
9
10
|
import { ElevatedButton, FilledTonalButton } from '../../../../../shared/material/buttons';
|
|
10
11
|
import { showModalBottomSheet } from '../../../../../shared/material/overlays';
|
|
11
12
|
import { FrequenceCommand } from '../../../../../shared/models/frequence_command';
|
|
13
|
+
import { ErrorCardWidget } from '../../../../../shared/widgets/error_card_widget';
|
|
12
14
|
import type { Subscription } from '../../../../subscription/domain/models/subscription';
|
|
13
15
|
import { TransactionStatut, type Transaction } from '../../../domain/models/transaction';
|
|
14
16
|
import { TransactionSendCommand } from '../../../domain/models/transaction_send/transaction_send_command';
|
|
@@ -80,18 +82,23 @@ export function TransactionRtpPageActions({
|
|
|
80
82
|
),
|
|
81
83
|
});
|
|
82
84
|
|
|
83
|
-
const warning = (message: string) => (
|
|
84
|
-
<
|
|
85
|
+
const warning = (message: string, icon: ComponentProps<typeof ErrorCardWidget>['icon']) => (
|
|
86
|
+
<ErrorCardWidget message={message} icon={icon} />
|
|
85
87
|
);
|
|
86
88
|
|
|
87
89
|
if (isExpired(tx)) {
|
|
88
|
-
return <View style={styles.padding}>{warning(traductions.transactionRtpExpiredWarningMessage)}</View>;
|
|
90
|
+
return <View style={styles.padding}>{warning(traductions.transactionRtpExpiredWarningMessage, 'timer-off')}</View>;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
const scheduleActive = canDoAction && sub == null;
|
|
92
94
|
return (
|
|
93
95
|
<View style={styles.padding}>
|
|
94
|
-
{isSoldeInsuffisant &&
|
|
96
|
+
{isSoldeInsuffisant && (
|
|
97
|
+
<>
|
|
98
|
+
{warning(traductions.transactionFormAmountInvalid, 'account-balance-wallet')}
|
|
99
|
+
<View style={styles.gap12} />
|
|
100
|
+
</>
|
|
101
|
+
)}
|
|
95
102
|
<View style={styles.row}>
|
|
96
103
|
{tx.canSchedule() && sub == null && (
|
|
97
104
|
<>
|
|
@@ -128,7 +135,7 @@ export function TransactionRtpPageActions({
|
|
|
128
135
|
|
|
129
136
|
const styles = StyleSheet.create({
|
|
130
137
|
padding: { paddingVertical: 10, paddingHorizontal: 10 },
|
|
131
|
-
|
|
138
|
+
gap12: { height: 12 },
|
|
132
139
|
row: { flexDirection: 'row', alignItems: 'center' },
|
|
133
140
|
flex: { flex: 1 },
|
|
134
141
|
button: { height: 56 },
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Équivalent de `Color.withValues(alpha:)` : la couleur [color] avec
|
|
3
|
+
* l'opacité [alpha] (0 à 1). Accepte `#RGB`, `#RRGGBB` et `#RRGGBBAA` ;
|
|
4
|
+
* toute autre forme est rendue telle quelle.
|
|
5
|
+
*/
|
|
6
|
+
export function withAlpha(color: string, alpha: number): string {
|
|
7
|
+
let hex = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(color)?.[1];
|
|
8
|
+
if (hex == null) return color;
|
|
9
|
+
if (hex.length === 3) hex = hex.replace(/./g, (c) => c + c);
|
|
10
|
+
const a = Math.round(Math.min(1, Math.max(0, alpha)) * 255)
|
|
11
|
+
.toString(16)
|
|
12
|
+
.padStart(2, '0');
|
|
13
|
+
return `#${hex.slice(0, 6)}${a}`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { MaterialIcons } from '@expo/vector-icons';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { Themer, useTheme } from '../../core/theme';
|
|
6
|
+
import { Card } from '../material/card';
|
|
7
|
+
|
|
8
|
+
/** Couleur d'erreur du thème avec une opacité (0 à 1). */
|
|
9
|
+
function erreur(alpha: number): string {
|
|
10
|
+
const hex = Math.round(alpha * 255).toString(16).padStart(2, '0');
|
|
11
|
+
return `${Themer.systemErrorColor}${hex}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Carte d'avertissement : fond teinté, bordure et icône dans la couleur
|
|
16
|
+
* d'erreur du thème, suivies du `message`.
|
|
17
|
+
*/
|
|
18
|
+
export function ErrorCardWidget({
|
|
19
|
+
message,
|
|
20
|
+
icon = 'warning-amber',
|
|
21
|
+
}: {
|
|
22
|
+
message: string;
|
|
23
|
+
icon?: ComponentProps<typeof MaterialIcons>['name'];
|
|
24
|
+
}) {
|
|
25
|
+
const theme = useTheme();
|
|
26
|
+
const texte = theme.textTheme.bodySmall;
|
|
27
|
+
return (
|
|
28
|
+
<Card style={styles.card}>
|
|
29
|
+
<View style={styles.row}>
|
|
30
|
+
<View style={styles.icon}>
|
|
31
|
+
<MaterialIcons name={icon} size={18} color={Themer.systemErrorColor} />
|
|
32
|
+
</View>
|
|
33
|
+
<Text
|
|
34
|
+
style={[
|
|
35
|
+
texte,
|
|
36
|
+
styles.message,
|
|
37
|
+
texte?.fontSize != null && { lineHeight: texte.fontSize * 1.4 },
|
|
38
|
+
]}
|
|
39
|
+
>
|
|
40
|
+
{message}
|
|
41
|
+
</Text>
|
|
42
|
+
</View>
|
|
43
|
+
</Card>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const styles = StyleSheet.create({
|
|
48
|
+
card: {
|
|
49
|
+
margin: 0,
|
|
50
|
+
backgroundColor: erreur(0.08),
|
|
51
|
+
borderRadius: 16,
|
|
52
|
+
borderWidth: 1,
|
|
53
|
+
borderColor: erreur(0.35),
|
|
54
|
+
},
|
|
55
|
+
row: { flexDirection: 'row', alignItems: 'flex-start', padding: 14 },
|
|
56
|
+
icon: { padding: 6, borderRadius: 999, backgroundColor: erreur(0.15), marginRight: 12 },
|
|
57
|
+
message: { flex: 1, textAlign: 'left', color: Themer.systemErrorColor },
|
|
58
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { MaterialIcons } from '@expo/vector-icons';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { Pressable, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
|
|
4
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
|
+
|
|
6
|
+
import { Themer, useTheme } from '../../core/theme';
|
|
7
|
+
import { withAlpha } from '../utils/color';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Feuille de sélection : poignée, [titre], [entete] facultatif, puis le
|
|
11
|
+
* [contenu] (qui défile s'il est trop long : utiliser un ScrollView).
|
|
12
|
+
* À ouvrir avec showModalBottomSheet.
|
|
13
|
+
*/
|
|
14
|
+
export function SbSelectionSheet({
|
|
15
|
+
titre,
|
|
16
|
+
titreSize,
|
|
17
|
+
entete,
|
|
18
|
+
contenu,
|
|
19
|
+
}: {
|
|
20
|
+
titre?: string;
|
|
21
|
+
titreSize?: number;
|
|
22
|
+
entete?: ReactNode;
|
|
23
|
+
contenu: ReactNode;
|
|
24
|
+
}) {
|
|
25
|
+
const theme = useTheme();
|
|
26
|
+
const insets = useSafeAreaInsets();
|
|
27
|
+
const { height } = useWindowDimensions();
|
|
28
|
+
return (
|
|
29
|
+
<View
|
|
30
|
+
style={[
|
|
31
|
+
styles.sheet,
|
|
32
|
+
{ maxHeight: height * 0.75, backgroundColor: theme.colorScheme.surface, paddingBottom: insets.bottom },
|
|
33
|
+
]}
|
|
34
|
+
>
|
|
35
|
+
{/* Poignée */}
|
|
36
|
+
<View style={[styles.poignee, { backgroundColor: withAlpha(Themer.neural03Color, 0.4) }]} />
|
|
37
|
+
{titre != null && (
|
|
38
|
+
<Text style={[theme.textTheme.headlineLarge, styles.titre, titreSize != null ? { fontSize: titreSize } : null]}>{titre}</Text>
|
|
39
|
+
)}
|
|
40
|
+
{entete != null && <View style={styles.entete}>{entete}</View>}
|
|
41
|
+
<View style={styles.gap8} />
|
|
42
|
+
<View style={styles.contenu}>{contenu}</View>
|
|
43
|
+
</View>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Ligne d'une feuille de sélection : [pastille], [texte], [detail] facultatif et coche si elle est [choisie]. */
|
|
48
|
+
export function SbSelectionLigne({
|
|
49
|
+
pastille,
|
|
50
|
+
texte,
|
|
51
|
+
detail,
|
|
52
|
+
choisie,
|
|
53
|
+
onPress,
|
|
54
|
+
}: {
|
|
55
|
+
pastille: ReactNode;
|
|
56
|
+
texte: string;
|
|
57
|
+
detail?: string;
|
|
58
|
+
choisie: boolean;
|
|
59
|
+
onPress: () => void;
|
|
60
|
+
}) {
|
|
61
|
+
const theme = useTheme();
|
|
62
|
+
const primaire = theme.colorScheme.primary;
|
|
63
|
+
return (
|
|
64
|
+
<Pressable
|
|
65
|
+
accessibilityRole="button"
|
|
66
|
+
accessibilityState={{ selected: choisie }}
|
|
67
|
+
onPress={onPress}
|
|
68
|
+
style={({ pressed }) => [
|
|
69
|
+
styles.ligne,
|
|
70
|
+
{ backgroundColor: choisie ? withAlpha(primaire, 0.08) : pressed ? theme.colorScheme.secondary : 'transparent' },
|
|
71
|
+
]}
|
|
72
|
+
>
|
|
73
|
+
{pastille}
|
|
74
|
+
<View style={styles.textes}>
|
|
75
|
+
<Text numberOfLines={1} style={[theme.textTheme.displayLarge, { fontWeight: choisie ? '700' : '500' }]}>
|
|
76
|
+
{texte}
|
|
77
|
+
</Text>
|
|
78
|
+
{detail != null && detail.length > 0 && (
|
|
79
|
+
<Text numberOfLines={1} style={[theme.textTheme.displaySmall, { color: Themer.neural03Color }]}>
|
|
80
|
+
{detail}
|
|
81
|
+
</Text>
|
|
82
|
+
)}
|
|
83
|
+
</View>
|
|
84
|
+
{choisie && <MaterialIcons name="check-circle" size={22} color={primaire} />}
|
|
85
|
+
</Pressable>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const styles = StyleSheet.create({
|
|
90
|
+
sheet: { width: '100%', borderTopLeftRadius: 20, borderTopRightRadius: 20 },
|
|
91
|
+
poignee: { alignSelf: 'center', marginTop: 10, marginBottom: 14, width: 36, height: 4, borderRadius: 2 },
|
|
92
|
+
titre: { paddingHorizontal: 20 },
|
|
93
|
+
entete: { paddingHorizontal: 20, paddingTop: 12 },
|
|
94
|
+
contenu: { flexShrink: 1 },
|
|
95
|
+
gap8: { height: 8 },
|
|
96
|
+
ligne: { flexDirection: 'row', alignItems: 'center', marginVertical: 2, paddingHorizontal: 8, paddingVertical: 10, borderRadius: 14 },
|
|
97
|
+
textes: { flex: 1, marginHorizontal: 14 },
|
|
98
|
+
});
|