@oxy.so/services 8.0.0 → 8.1.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/commonjs/assets/assets/fonts/icons/OxyServicesIonicons.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/icons/OxyServicesMaterialCommunityIcons.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/icons/manifest.json +4 -5
- package/lib/commonjs/assets/fonts/icons/OxyServicesIonicons.ttf +0 -0
- package/lib/commonjs/assets/fonts/icons/OxyServicesMaterialCommunityIcons.ttf +0 -0
- package/lib/commonjs/assets/fonts/icons/manifest.json +4 -5
- package/lib/commonjs/ui/components/authChooser/primitives.js +1 -102
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/styles.js +0 -33
- package/lib/commonjs/ui/components/authChooser/styles.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js +151 -56
- package/lib/commonjs/ui/components/signIn/OxyAccountPicker.js.map +1 -1
- package/lib/commonjs/ui/icons/subsetGlyphMaps.js +0 -1
- package/lib/commonjs/ui/icons/subsetGlyphMaps.js.map +1 -1
- package/lib/module/assets/assets/fonts/icons/OxyServicesIonicons.ttf +0 -0
- package/lib/module/assets/assets/fonts/icons/OxyServicesMaterialCommunityIcons.ttf +0 -0
- package/lib/module/assets/assets/fonts/icons/manifest.json +4 -5
- package/lib/module/assets/fonts/icons/OxyServicesIonicons.ttf +0 -0
- package/lib/module/assets/fonts/icons/OxyServicesMaterialCommunityIcons.ttf +0 -0
- package/lib/module/assets/fonts/icons/manifest.json +4 -5
- package/lib/module/ui/components/authChooser/primitives.js +2 -103
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/module/ui/components/authChooser/styles.js +0 -33
- package/lib/module/ui/components/authChooser/styles.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAccountPicker.js +154 -57
- package/lib/module/ui/components/signIn/OxyAccountPicker.js.map +1 -1
- package/lib/module/ui/icons/subsetGlyphMaps.js +0 -1
- package/lib/module/ui/icons/subsetGlyphMaps.js.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +1 -34
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts +0 -33
- package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAccountPicker.d.ts +9 -3
- package/lib/typescript/commonjs/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +1 -1
- package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/subsetGlyphMaps.d.ts +0 -1
- package/lib/typescript/commonjs/ui/icons/subsetGlyphMaps.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +1 -34
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/styles.d.ts +0 -33
- package/lib/typescript/module/ui/components/authChooser/styles.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAccountPicker.d.ts +9 -3
- package/lib/typescript/module/ui/components/signIn/OxyAccountPicker.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +1 -1
- package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/subsetGlyphMaps.d.ts +0 -1
- package/lib/typescript/module/ui/icons/subsetGlyphMaps.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/assets/fonts/icons/OxyServicesIonicons.ttf +0 -0
- package/src/assets/fonts/icons/OxyServicesMaterialCommunityIcons.ttf +0 -0
- package/src/assets/fonts/icons/manifest.json +4 -5
- package/src/ui/components/authChooser/primitives.tsx +2 -96
- package/src/ui/components/authChooser/styles.ts +0 -33
- package/src/ui/components/signIn/OxyAccountPicker.tsx +130 -65
- package/src/ui/icons/subsetGlyphMaps.ts +0 -1
|
@@ -13,39 +13,6 @@ const QR_PLATE_BG = '#FFFFFF';
|
|
|
13
13
|
* text-link rhythm every view's fallback affordances share.
|
|
14
14
|
*/
|
|
15
15
|
export const authChooserStyles = StyleSheet.create({
|
|
16
|
-
rows: {
|
|
17
|
-
width: '100%',
|
|
18
|
-
gap: 8,
|
|
19
|
-
},
|
|
20
|
-
accountRow: {
|
|
21
|
-
flexDirection: 'row',
|
|
22
|
-
alignItems: 'center',
|
|
23
|
-
gap: 12,
|
|
24
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
25
|
-
borderRadius: 16,
|
|
26
|
-
paddingVertical: 10,
|
|
27
|
-
paddingHorizontal: 12,
|
|
28
|
-
},
|
|
29
|
-
rowDisabled: {
|
|
30
|
-
opacity: 0.6,
|
|
31
|
-
},
|
|
32
|
-
avatarRing: {
|
|
33
|
-
borderRadius: 9999,
|
|
34
|
-
borderWidth: 2,
|
|
35
|
-
padding: 1,
|
|
36
|
-
},
|
|
37
|
-
rowMeta: {
|
|
38
|
-
flex: 1,
|
|
39
|
-
minWidth: 0,
|
|
40
|
-
},
|
|
41
|
-
rowName: {
|
|
42
|
-
fontSize: 15,
|
|
43
|
-
fontWeight: '600',
|
|
44
|
-
},
|
|
45
|
-
rowHandle: {
|
|
46
|
-
fontSize: 12.5,
|
|
47
|
-
marginTop: 1,
|
|
48
|
-
},
|
|
49
16
|
/**
|
|
50
17
|
* Animated-collapse container for the account switch list — the outer clip and
|
|
51
18
|
* the inner measuring wrapper. Style-based (NOT NativeWind): the container
|
|
@@ -8,20 +8,34 @@
|
|
|
8
8
|
* means — a switch, a "Continue as" sign-in, or activating the pair before an
|
|
9
9
|
* OAuth consent — and passes the pair back, never an account id, which cannot
|
|
10
10
|
* say whose route was chosen.
|
|
11
|
+
*
|
|
12
|
+
* One rounded list: each row is the account's name and `@handle` on the left,
|
|
13
|
+
* its avatar and a chevron on the right, with a hairline between rows that
|
|
14
|
+
* steps aside for a hovered row. Layout is NativeWind; hover is driven by
|
|
15
|
+
* `onHoverIn`/`onHoverOut` (web only), because this pipeline does not emit
|
|
16
|
+
* `hover:` variants — the same pattern as the account menu.
|
|
11
17
|
*/
|
|
12
18
|
|
|
13
19
|
import type React from 'react';
|
|
14
|
-
import {
|
|
20
|
+
import { Fragment, useState } from 'react';
|
|
21
|
+
import { ScrollView } from 'react-native';
|
|
22
|
+
import { Pressable, View } from 'react-native-css/components';
|
|
23
|
+
import { Avatar } from '@oxy.so/bloom/avatar';
|
|
15
24
|
import { RiArrowRightSLine } from '@oxy.so/bloom/icons/RiArrowRightSLine';
|
|
25
|
+
import { RiCheckLine } from '@oxy.so/bloom/icons/RiCheckLine';
|
|
26
|
+
import { RiLoader4Line } from '@oxy.so/bloom/icons/RiLoader4Line';
|
|
16
27
|
import { RiUserAddLine } from '@oxy.so/bloom/icons/RiUserAddLine';
|
|
17
28
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
18
29
|
import { Text } from '@oxy.so/bloom/typography';
|
|
19
30
|
import { showsPrincipalHeaders, type SwitcherContextRow, type SwitcherPrincipalRow } from '@oxy.so/core/session';
|
|
20
31
|
import { useI18n } from '../../hooks/useI18n';
|
|
21
|
-
import {
|
|
22
|
-
import { authChooserStyles } from '../authChooser/styles';
|
|
32
|
+
import { resolveAccentHex } from '../authChooser/types';
|
|
23
33
|
import { OxyAuthScreen, OxyAuthScreenHeader, OxyAuthTerms } from './OxyAuthScreen';
|
|
24
34
|
|
|
35
|
+
/** The list scrolls past this height (`max-h-96`). */
|
|
36
|
+
const LIST_MAX_HEIGHT = 384;
|
|
37
|
+
const AVATAR_SIZE = 40;
|
|
38
|
+
|
|
25
39
|
export interface OxyAccountPickerProps {
|
|
26
40
|
/** The device's people, each with the accounts they may act as (`useDeviceSwitcher`). */
|
|
27
41
|
principals: SwitcherPrincipalRow[];
|
|
@@ -35,9 +49,9 @@ export interface OxyAccountPickerProps {
|
|
|
35
49
|
/** Disables every row while a choice is in flight. */
|
|
36
50
|
isLoading?: boolean;
|
|
37
51
|
/**
|
|
38
|
-
* This origin holds no session. A row then
|
|
39
|
-
* never as the current account: the device can list an
|
|
40
|
-
* while nobody is signed in HERE.
|
|
52
|
+
* This origin holds no session. A row is then announced as "Continue as
|
|
53
|
+
* @handle" and never marked as the current account: the device can list an
|
|
54
|
+
* identity as active while nobody is signed in HERE.
|
|
41
55
|
*/
|
|
42
56
|
signedOut?: boolean;
|
|
43
57
|
}
|
|
@@ -53,77 +67,128 @@ export const OxyAccountPicker: React.FC<OxyAccountPickerProps> = ({
|
|
|
53
67
|
}) => {
|
|
54
68
|
const theme = useTheme();
|
|
55
69
|
const { t } = useI18n();
|
|
70
|
+
const [hovered, setHovered] = useState<number | null>(null);
|
|
56
71
|
// Whose route a row is only needs naming once someone holds more than one
|
|
57
72
|
// account here — the same rule the account menu's group headers follow.
|
|
58
73
|
const namesTheOperator = showsPrincipalHeaders(principals);
|
|
59
74
|
|
|
75
|
+
const rows = principals.flatMap((principal) =>
|
|
76
|
+
principal.contexts.map((context) => ({
|
|
77
|
+
context,
|
|
78
|
+
operatedBy:
|
|
79
|
+
namesTheOperator && context.isDelegated
|
|
80
|
+
? t('accountSwitcher.context.operatedBy', { name: principal.displayName })
|
|
81
|
+
: null,
|
|
82
|
+
})),
|
|
83
|
+
);
|
|
84
|
+
const useAnotherIndex = rows.length;
|
|
85
|
+
|
|
86
|
+
/** The hairline under row `index`, hidden while either neighbour is hovered. */
|
|
87
|
+
const divider = (index: number) =>
|
|
88
|
+
hovered === index || hovered === index + 1 ? (
|
|
89
|
+
<View className="h-px mx-[8px]" />
|
|
90
|
+
) : (
|
|
91
|
+
<View className="h-px mx-[8px] bg-border opacity-50" />
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const rowClassName = (index: number, disabled: boolean) =>
|
|
95
|
+
`flex-row items-center gap-[12px] rounded-[14px] p-[8px] ${hovered === index && !disabled ? 'bg-fill' : ''} ${disabled ? 'opacity-50' : ''}`;
|
|
96
|
+
|
|
60
97
|
return (
|
|
61
98
|
<OxyAuthScreen>
|
|
62
99
|
<OxyAuthScreenHeader
|
|
63
100
|
title={t('signin.chooser.title')}
|
|
64
101
|
description={appName ? t('signin.chooser.subtitleToApp', { app: appName }) : t('signin.chooser.subtitle')}
|
|
65
102
|
/>
|
|
66
|
-
<View
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
<View className="bg-fill-secondary rounded-[22px] overflow-hidden" testID="account-picker-list">
|
|
104
|
+
<ScrollView style={{ maxHeight: LIST_MAX_HEIGHT }} contentContainerStyle={{ padding: 8 }}>
|
|
105
|
+
{rows.map(({ context, operatedBy }, index) => {
|
|
106
|
+
const activating = pendingContextId === context.contextId;
|
|
107
|
+
const disabled = isLoading || !context.canActivate;
|
|
108
|
+
const current = !signedOut && context.isActive;
|
|
109
|
+
const accent = resolveAccentHex(context.color, theme.colors.primary);
|
|
110
|
+
const handle = context.handle ? `@${context.handle}` : null;
|
|
111
|
+
const label = signedOut
|
|
112
|
+
? t('signin.chooser.continueAs', { name: handle ?? context.displayName })
|
|
113
|
+
: context.displayName;
|
|
114
|
+
return (
|
|
115
|
+
<Fragment key={context.contextId}>
|
|
116
|
+
<Pressable
|
|
117
|
+
className={rowClassName(index, disabled && !activating)}
|
|
118
|
+
onPress={() => onSelectContext(context)}
|
|
119
|
+
onHoverIn={() => setHovered(index)}
|
|
120
|
+
onHoverOut={() => setHovered((value) => (value === index ? null : value))}
|
|
121
|
+
disabled={disabled}
|
|
122
|
+
accessibilityRole="button"
|
|
123
|
+
accessibilityLabel={label}
|
|
124
|
+
accessibilityState={{ selected: current, disabled, busy: activating }}
|
|
125
|
+
>
|
|
126
|
+
<View className="flex-1 min-w-0">
|
|
127
|
+
<Text className="text-body text-text" style={{ fontWeight: '500' }} numberOfLines={1}>
|
|
128
|
+
{context.displayName}
|
|
129
|
+
</Text>
|
|
130
|
+
{operatedBy || handle ? (
|
|
131
|
+
<Text className="text-bodySmall text-text-secondary" numberOfLines={1}>
|
|
132
|
+
{operatedBy ?? handle}
|
|
133
|
+
</Text>
|
|
134
|
+
) : null}
|
|
135
|
+
</View>
|
|
136
|
+
<View className="flex-row items-center gap-[8px] shrink-0">
|
|
137
|
+
<View style={{ width: AVATAR_SIZE, height: AVATAR_SIZE }}>
|
|
138
|
+
<Avatar
|
|
139
|
+
source={context.avatarUrl ?? undefined}
|
|
140
|
+
variant="thumb"
|
|
141
|
+
name={context.displayName}
|
|
142
|
+
size={AVATAR_SIZE}
|
|
143
|
+
/>
|
|
144
|
+
{current ? (
|
|
145
|
+
<View
|
|
146
|
+
className="absolute -bottom-[2px] -right-[2px] items-center justify-center rounded-full border-2 border-background"
|
|
147
|
+
style={{ width: 18, height: 18, backgroundColor: accent }}
|
|
148
|
+
>
|
|
149
|
+
<RiCheckLine size="xs" fill="#ffffff" />
|
|
150
|
+
</View>
|
|
151
|
+
) : null}
|
|
152
|
+
</View>
|
|
153
|
+
{activating ? (
|
|
154
|
+
<RiLoader4Line size="sm" fill={accent} />
|
|
155
|
+
) : (
|
|
156
|
+
<RiArrowRightSLine size="sm" fill={theme.colors.textSecondary} />
|
|
157
|
+
)}
|
|
158
|
+
</View>
|
|
159
|
+
</Pressable>
|
|
160
|
+
{divider(index)}
|
|
161
|
+
</Fragment>
|
|
162
|
+
);
|
|
163
|
+
})}
|
|
164
|
+
<Pressable
|
|
165
|
+
className={rowClassName(useAnotherIndex, isLoading)}
|
|
166
|
+
onPress={onUseAnother}
|
|
167
|
+
onHoverIn={() => setHovered(useAnotherIndex)}
|
|
168
|
+
onHoverOut={() => setHovered((value) => (value === useAnotherIndex ? null : value))}
|
|
169
|
+
disabled={isLoading}
|
|
170
|
+
accessibilityRole="button"
|
|
171
|
+
accessibilityLabel={t('signin.chooser.useAnother')}
|
|
172
|
+
testID="use-another-account"
|
|
173
|
+
>
|
|
174
|
+
<View className="flex-1 min-w-0">
|
|
175
|
+
<Text className="text-body text-text" style={{ fontWeight: '500' }} numberOfLines={1}>
|
|
176
|
+
{t('signin.chooser.useAnother')}
|
|
177
|
+
</Text>
|
|
178
|
+
</View>
|
|
179
|
+
<View className="flex-row items-center gap-[8px] shrink-0">
|
|
180
|
+
<View
|
|
181
|
+
className="bg-fill items-center justify-center rounded-full"
|
|
182
|
+
style={{ width: AVATAR_SIZE, height: AVATAR_SIZE }}
|
|
183
|
+
>
|
|
184
|
+
<RiUserAddLine size="md" fill={theme.colors.textSecondary} />
|
|
185
|
+
</View>
|
|
186
|
+
<RiArrowRightSLine size="sm" fill={theme.colors.textSecondary} />
|
|
187
|
+
</View>
|
|
188
|
+
</Pressable>
|
|
189
|
+
</ScrollView>
|
|
111
190
|
</View>
|
|
112
191
|
<OxyAuthTerms />
|
|
113
192
|
</OxyAuthScreen>
|
|
114
193
|
);
|
|
115
194
|
};
|
|
116
|
-
|
|
117
|
-
const styles = StyleSheet.create({
|
|
118
|
-
// Matches `AccountRow`'s 40px avatar plus its 2px ring and 1px gap.
|
|
119
|
-
addGlyph: {
|
|
120
|
-
width: 46,
|
|
121
|
-
height: 46,
|
|
122
|
-
borderRadius: 23,
|
|
123
|
-
alignItems: 'center',
|
|
124
|
-
justifyContent: 'center',
|
|
125
|
-
},
|
|
126
|
-
addLabel: {
|
|
127
|
-
flex: 1,
|
|
128
|
-
},
|
|
129
|
-
});
|