@oxyhq/services 22.0.0 → 22.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/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialog.js +27 -529
- package/lib/commonjs/ui/components/OxyAccountDialog.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +881 -0
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -0
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js +158 -0
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +28 -3
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +6 -2
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +2 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/commonjs/ui/utils/commonsStoreLinks.js +42 -0
- package/lib/commonjs/ui/utils/commonsStoreLinks.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialog.js +29 -533
- package/lib/module/ui/components/OxyAccountDialog.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +878 -0
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -0
- package/lib/module/ui/components/oxyAuthChooserStyles.js +155 -0
- package/lib/module/ui/components/oxyAuthChooserStyles.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +28 -3
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +6 -2
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +2 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/module/ui/utils/commonsStoreLinks.js +37 -0
- package/lib/module/ui/utils/commonsStoreLinks.js.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +2 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts +10 -20
- package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +60 -0
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts +148 -0
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +1 -0
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/commonsStoreLinks.d.ts +24 -0
- package/lib/typescript/commonjs/ui/utils/commonsStoreLinks.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +2 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts +10 -20
- package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +60 -0
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts +148 -0
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts.map +1 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +1 -0
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/commonsStoreLinks.d.ts +24 -0
- package/lib/typescript/module/ui/utils/commonsStoreLinks.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +7 -0
- package/src/ui/components/OxyAccountDialog.tsx +33 -557
- package/src/ui/components/OxyAuthChooser.tsx +883 -0
- package/src/ui/components/oxyAuthChooserStyles.ts +152 -0
- package/src/ui/context/OxyContext.tsx +31 -3
- package/src/ui/context/useOxyAccountGraph.ts +4 -2
- package/src/ui/hooks/useSwitchableAccounts.ts +1 -0
- package/src/ui/utils/commonsStoreLinks.ts +38 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/** High-contrast QR plate background — kept in sync with `OxyAuthChooser`'s QR colors. */
|
|
4
|
+
const QR_PLATE_BG = '#FFFFFF';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared styles for `OxyAuthChooser`'s views. Extracted from `OxyAccountDialog`
|
|
8
|
+
* so both the Dialog-wrapped host and any bare host (e.g. a future
|
|
9
|
+
* auth.oxy.so hub page) render identically without duplicating a StyleSheet.
|
|
10
|
+
*/
|
|
11
|
+
export const authChooserStyles = StyleSheet.create({
|
|
12
|
+
rows: {
|
|
13
|
+
width: '100%',
|
|
14
|
+
gap: 8,
|
|
15
|
+
},
|
|
16
|
+
accountRow: {
|
|
17
|
+
flexDirection: 'row',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
gap: 12,
|
|
20
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
21
|
+
borderRadius: 16,
|
|
22
|
+
paddingVertical: 10,
|
|
23
|
+
paddingHorizontal: 12,
|
|
24
|
+
},
|
|
25
|
+
rowDisabled: {
|
|
26
|
+
opacity: 0.6,
|
|
27
|
+
},
|
|
28
|
+
avatarRing: {
|
|
29
|
+
borderRadius: 9999,
|
|
30
|
+
borderWidth: 2,
|
|
31
|
+
padding: 1,
|
|
32
|
+
},
|
|
33
|
+
rowMeta: {
|
|
34
|
+
flex: 1,
|
|
35
|
+
minWidth: 0,
|
|
36
|
+
},
|
|
37
|
+
rowName: {
|
|
38
|
+
fontSize: 15,
|
|
39
|
+
fontWeight: '600',
|
|
40
|
+
},
|
|
41
|
+
rowHandle: {
|
|
42
|
+
fontSize: 12.5,
|
|
43
|
+
marginTop: 1,
|
|
44
|
+
},
|
|
45
|
+
addRow: {
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
gap: 12,
|
|
49
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
50
|
+
borderStyle: 'dashed',
|
|
51
|
+
borderRadius: 16,
|
|
52
|
+
paddingVertical: 10,
|
|
53
|
+
paddingHorizontal: 12,
|
|
54
|
+
marginTop: 8,
|
|
55
|
+
},
|
|
56
|
+
addBadge: {
|
|
57
|
+
width: 40,
|
|
58
|
+
height: 40,
|
|
59
|
+
borderRadius: 9999,
|
|
60
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
justifyContent: 'center',
|
|
63
|
+
},
|
|
64
|
+
footerLinks: {
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
marginTop: 16,
|
|
67
|
+
},
|
|
68
|
+
footerLink: {
|
|
69
|
+
alignSelf: 'center',
|
|
70
|
+
paddingVertical: 10,
|
|
71
|
+
marginTop: 12,
|
|
72
|
+
},
|
|
73
|
+
linkText: {
|
|
74
|
+
fontSize: 14,
|
|
75
|
+
fontWeight: '600',
|
|
76
|
+
},
|
|
77
|
+
signInBlock: {
|
|
78
|
+
width: '100%',
|
|
79
|
+
},
|
|
80
|
+
primaryButton: {
|
|
81
|
+
width: '100%',
|
|
82
|
+
borderRadius: 14,
|
|
83
|
+
marginTop: 8,
|
|
84
|
+
},
|
|
85
|
+
secondaryButton: {
|
|
86
|
+
width: '100%',
|
|
87
|
+
borderRadius: 14,
|
|
88
|
+
marginTop: 10,
|
|
89
|
+
},
|
|
90
|
+
usernameInput: {
|
|
91
|
+
width: '100%',
|
|
92
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
93
|
+
borderRadius: 14,
|
|
94
|
+
paddingVertical: 12,
|
|
95
|
+
paddingHorizontal: 14,
|
|
96
|
+
fontSize: 16,
|
|
97
|
+
marginTop: 8,
|
|
98
|
+
},
|
|
99
|
+
dividerRow: {
|
|
100
|
+
flexDirection: 'row',
|
|
101
|
+
alignItems: 'center',
|
|
102
|
+
width: '100%',
|
|
103
|
+
marginVertical: 14,
|
|
104
|
+
},
|
|
105
|
+
dividerLine: {
|
|
106
|
+
flex: 1,
|
|
107
|
+
height: StyleSheet.hairlineWidth,
|
|
108
|
+
},
|
|
109
|
+
dividerText: {
|
|
110
|
+
marginHorizontal: 12,
|
|
111
|
+
fontSize: 13,
|
|
112
|
+
},
|
|
113
|
+
centeredBlock: {
|
|
114
|
+
alignItems: 'center',
|
|
115
|
+
justifyContent: 'center',
|
|
116
|
+
gap: 12,
|
|
117
|
+
paddingVertical: 24,
|
|
118
|
+
},
|
|
119
|
+
mutedText: {
|
|
120
|
+
fontSize: 14,
|
|
121
|
+
textAlign: 'center',
|
|
122
|
+
},
|
|
123
|
+
qrHeadline: {
|
|
124
|
+
fontSize: 16,
|
|
125
|
+
fontWeight: '700',
|
|
126
|
+
textAlign: 'center',
|
|
127
|
+
},
|
|
128
|
+
errorText: {
|
|
129
|
+
fontSize: 14,
|
|
130
|
+
textAlign: 'center',
|
|
131
|
+
},
|
|
132
|
+
errorBanner: {
|
|
133
|
+
flexDirection: 'row',
|
|
134
|
+
alignItems: 'center',
|
|
135
|
+
gap: 8,
|
|
136
|
+
width: '100%',
|
|
137
|
+
borderRadius: 12,
|
|
138
|
+
paddingVertical: 10,
|
|
139
|
+
paddingHorizontal: 12,
|
|
140
|
+
marginBottom: 12,
|
|
141
|
+
},
|
|
142
|
+
errorBannerText: {
|
|
143
|
+
flex: 1,
|
|
144
|
+
fontSize: 13.5,
|
|
145
|
+
lineHeight: 18,
|
|
146
|
+
},
|
|
147
|
+
qrPlate: {
|
|
148
|
+
padding: 16,
|
|
149
|
+
borderRadius: 16,
|
|
150
|
+
backgroundColor: QR_PLATE_BG,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
@@ -606,14 +606,41 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
606
606
|
[commitSession],
|
|
607
607
|
);
|
|
608
608
|
|
|
609
|
+
// Commit a minted graph-SWITCH session. Same funnel as `handleWebSession` but
|
|
610
|
+
// IN-PLACE: `hubSync: false` so switching accounts never triggers the
|
|
611
|
+
// cross-origin hub-sync full-page redirect. The switch already propagates
|
|
612
|
+
// across tabs/apps via the server's `session_state` socket broadcast.
|
|
613
|
+
const commitSwitchedSession = useCallback(
|
|
614
|
+
async (session: SessionLoginResponse): Promise<void> => {
|
|
615
|
+
if (!session?.user || !session?.sessionId || !session.accessToken) {
|
|
616
|
+
throw new Error('Session response did not include a usable session');
|
|
617
|
+
}
|
|
618
|
+
await commitSession(
|
|
619
|
+
{
|
|
620
|
+
sessionId: session.sessionId,
|
|
621
|
+
accessToken: session.accessToken,
|
|
622
|
+
deviceSecret: session.deviceSecret,
|
|
623
|
+
deviceId: session.deviceId,
|
|
624
|
+
expiresAt: session.expiresAt,
|
|
625
|
+
userId: session.user.id,
|
|
626
|
+
user: session.user,
|
|
627
|
+
},
|
|
628
|
+
{ activate: true, hubSync: false },
|
|
629
|
+
);
|
|
630
|
+
},
|
|
631
|
+
[commitSession],
|
|
632
|
+
);
|
|
633
|
+
|
|
609
634
|
// ── Unified account dialog ─────────────────────────────────────────────────
|
|
610
635
|
// The single account-chooser + sign-in surface. Built ONCE per provider mount
|
|
611
636
|
// and bound to the live `oxyServices` + `sessionClient` + this provider's
|
|
612
|
-
//
|
|
613
|
-
//
|
|
614
|
-
//
|
|
637
|
+
// commit funnels. Both funnels are threaded through refs so the controller
|
|
638
|
+
// keeps STABLE commit callbacks (rebuilding the controller on every
|
|
639
|
+
// commit-identity change would drop its subscription + state).
|
|
615
640
|
const handleWebSessionRef = useRef(handleWebSession);
|
|
616
641
|
handleWebSessionRef.current = handleWebSession;
|
|
642
|
+
const commitSwitchedSessionRef = useRef(commitSwitchedSession);
|
|
643
|
+
commitSwitchedSessionRef.current = commitSwitchedSession;
|
|
617
644
|
|
|
618
645
|
const accountDialogControllerRef = useRef<AccountDialogController | null>(null);
|
|
619
646
|
if (!accountDialogControllerRef.current) {
|
|
@@ -626,6 +653,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
626
653
|
// instant `/auth-session` `auth_update` wake instead of a slow poll.
|
|
627
654
|
socketFactory: io,
|
|
628
655
|
commitSession: (session) => handleWebSessionRef.current(session),
|
|
656
|
+
commitSwitchedSession: (session) => commitSwitchedSessionRef.current(session),
|
|
629
657
|
onSignedIn: () => setAccountDialogOpen(false),
|
|
630
658
|
openUrl: (url) => {
|
|
631
659
|
if (isWebBrowser()) {
|
|
@@ -13,7 +13,7 @@ interface UseOxyAccountGraphParams {
|
|
|
13
13
|
oxyServices: OxyServices;
|
|
14
14
|
sessionClient: SessionClient;
|
|
15
15
|
syncFromClient: () => Promise<void>;
|
|
16
|
-
commitSession: (input: CommitInput, options: { activate: boolean }) => Promise<void>;
|
|
16
|
+
commitSession: (input: CommitInput, options: { activate: boolean; hubSync?: boolean }) => Promise<void>;
|
|
17
17
|
queryClient: QueryClient;
|
|
18
18
|
accountDialogControllerRef: RefObject<AccountDialogController | null>;
|
|
19
19
|
clearSessionStateRef: RefObject<() => Promise<void>>;
|
|
@@ -89,7 +89,9 @@ export function useOxyAccountGraph({
|
|
|
89
89
|
userId: result.user.id,
|
|
90
90
|
user: result.user,
|
|
91
91
|
},
|
|
92
|
-
|
|
92
|
+
// A switch is IN-PLACE: never run the cross-origin hub-sync full-page
|
|
93
|
+
// redirect. Cross-tab/app propagation rides the `session_state` socket.
|
|
94
|
+
{ activate: true, hubSync: false },
|
|
93
95
|
);
|
|
94
96
|
await runPostAccountSwitchSideEffects();
|
|
95
97
|
},
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where to send a user who doesn't have Commons installed yet.
|
|
3
|
+
*
|
|
4
|
+
* Commons is not published to either app store as of this writing (no submit
|
|
5
|
+
* profile in its `eas.json`, no store listing) — so these are ALWAYS
|
|
6
|
+
* env-overridable and NEVER hardcoded, and the landing-page fallback exists
|
|
7
|
+
* precisely so a "Get Commons" affordance is never a dead link in the
|
|
8
|
+
* meantime. Once Commons ships to TestFlight/Play, set the two store env vars
|
|
9
|
+
* and every consumer picks them up with no code change.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Real per-platform store URLs, or `undefined` where not yet configured. */
|
|
13
|
+
export interface CommonsStoreLinks {
|
|
14
|
+
ios: string | undefined;
|
|
15
|
+
android: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Per-platform Commons store URLs, from env — `undefined` when unset. */
|
|
19
|
+
export function getCommonsStoreLinks(): CommonsStoreLinks {
|
|
20
|
+
return {
|
|
21
|
+
ios: process.env.EXPO_PUBLIC_COMMONS_APP_STORE_URL || undefined,
|
|
22
|
+
android: process.env.EXPO_PUBLIC_COMMONS_PLAY_STORE_URL || undefined,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Placeholder landing page — the fallback while Commons has no store listing. */
|
|
27
|
+
const DEFAULT_COMMONS_LANDING_URL = 'https://oxy.so/commons';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the "Get Commons" href for the current platform. Never `undefined`
|
|
31
|
+
* — falls back to a landing page so the affordance is always a working link,
|
|
32
|
+
* store listing or not.
|
|
33
|
+
*/
|
|
34
|
+
export function getCommonsAcquisitionUrl(platformOS: 'ios' | 'android' | string): string {
|
|
35
|
+
const links = getCommonsStoreLinks();
|
|
36
|
+
const storeUrl = platformOS === 'ios' ? links.ios : platformOS === 'android' ? links.android : undefined;
|
|
37
|
+
return storeUrl ?? process.env.EXPO_PUBLIC_COMMONS_LANDING_URL ?? DEFAULT_COMMONS_LANDING_URL;
|
|
38
|
+
}
|