@oxy.so/services 4.0.4 → 4.0.6
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/android/src/main/java/so/oxy/identity/OxyIdentityStore.kt +8 -7
- package/android/src/main/java/so/oxy/storage/OxyEncryptedPrefs.kt +41 -97
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +5 -3
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +33 -23
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +9 -0
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/primitives.js +16 -7
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +6 -11
- package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +1 -0
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +17 -2
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/utils/accountDeletionHandoff.js +99 -0
- package/lib/commonjs/ui/utils/accountDeletionHandoff.js.map +1 -0
- package/lib/module/ui/components/OxyAccountDialogScreen.js +5 -3
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +33 -23
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js +9 -0
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/module/ui/components/authChooser/primitives.js +16 -7
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/module/ui/components/modals/DeleteAccountModal.js +6 -11
- package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js +1 -0
- package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/module/ui/screens/ManageAccountScreen.js +17 -2
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/utils/accountDeletionHandoff.js +95 -0
- package/lib/module/ui/utils/accountDeletionHandoff.js.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +6 -0
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +8 -0
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/accountDeletionHandoff.d.ts +35 -0
- package/lib/typescript/commonjs/ui/utils/accountDeletionHandoff.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +6 -0
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +8 -0
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/accountDeletionHandoff.d.ts +35 -0
- package/lib/typescript/module/ui/utils/accountDeletionHandoff.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/ui/components/OxyAccountDialogScreen.tsx +5 -3
- package/src/ui/components/OxyAuthChooser.tsx +33 -23
- package/src/ui/components/authChooser/SignInEntryView.tsx +13 -0
- package/src/ui/components/authChooser/primitives.tsx +20 -8
- package/src/ui/components/modals/DeleteAccountModal.tsx +5 -9
- package/src/ui/hooks/accountDialogSnapshot.ts +1 -0
- package/src/ui/screens/ManageAccountScreen.tsx +16 -1
- package/src/ui/utils/accountDeletionHandoff.ts +115 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commons' own delete-account screen (`app/(tabs)/(settings)/delete-account.tsx`
|
|
3
|
+
* in packages/commons). Expo Router drops the route groups, so the scheme path
|
|
4
|
+
* `delete-account` resolves to it; `oxycommons://` is the scheme the account
|
|
5
|
+
* dialog already probes and hands sign-in approvals to.
|
|
6
|
+
*/
|
|
7
|
+
export declare const COMMONS_DELETE_ACCOUNT_URL = "oxycommons://delete-account";
|
|
8
|
+
/** The scheme root `Linking.canOpenURL` probes to tell whether Commons is installed. */
|
|
9
|
+
export declare const COMMONS_APP_SCHEME = "oxycommons://";
|
|
10
|
+
type Translate = (key: string, vars?: Record<string, string | number>) => string | undefined;
|
|
11
|
+
export interface AccountDeletionHandoffDeps {
|
|
12
|
+
/** Whether THIS app can read an identity key it could sign the deletion with. */
|
|
13
|
+
hasIdentity: () => Promise<boolean>;
|
|
14
|
+
canOpenURL: (url: string) => Promise<boolean>;
|
|
15
|
+
openURL: (url: string) => Promise<unknown>;
|
|
16
|
+
t: Translate;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `'local'`: this app holds the identity key, so the caller runs its own
|
|
20
|
+
* delete-account surface. `'handled'`: it does not, and the user has been sent
|
|
21
|
+
* to where the deletion can actually be signed (or told why not).
|
|
22
|
+
*/
|
|
23
|
+
export type AccountDeletionHandoffResult = 'local' | 'handled';
|
|
24
|
+
/**
|
|
25
|
+
* Decide where a native account deletion can happen, before anything is deleted.
|
|
26
|
+
*
|
|
27
|
+
* Deleting an account is signed with the identity private key. When the key
|
|
28
|
+
* lives in Oxy Commons rather than in this app, the API call cannot be made from
|
|
29
|
+
* here, so the deletion is delegated to Commons' own screen when Commons is
|
|
30
|
+
* installed, and explained otherwise. Nothing on the delegated paths calls the
|
|
31
|
+
* deletion API.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runAccountDeletionHandoff(deps: AccountDeletionHandoffDeps): Promise<AccountDeletionHandoffResult>;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=accountDeletionHandoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountDeletionHandoff.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/accountDeletionHandoff.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,gCAAgC,CAAC;AAExE,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;AAE7F,MAAM,WAAW,0BAA0B;IACzC,iFAAiF;IACjF,WAAW,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC,EAAE,SAAS,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,MAAM,4BAA4B,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/D;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,4BAA4B,CAAC,CAuEvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OxyAccountDialogScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyAccountDialogScreen.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAe3D;;;;;;;;;;;;GAYG;AACH,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,
|
|
1
|
+
{"version":3,"file":"OxyAccountDialogScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyAccountDialogScreen.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAe3D;;;;;;;;;;;;GAYG;AACH,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA6DrD,CAAC;AAyDF,eAAe,sBAAsB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OxyAuthChooser.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyAuthChooser.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAsD/B,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"OxyAuthChooser.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyAuthChooser.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAsD/B,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAgYjD,CAAC;AA8BF,eAAe,cAAc,CAAC"}
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Existing accounts still render ABOVE the CTA: "continue as one of these" is a
|
|
15
15
|
* choice of WHO, not of HOW.
|
|
16
|
+
*
|
|
17
|
+
* Signed in (the "Add another account" entry), a row is an account to switch
|
|
18
|
+
* to, and the current one carries its check. Signed out, nothing here is
|
|
19
|
+
* current: the device may still list an identity it holds (Commons' shared
|
|
20
|
+
* one), but THIS app has no session, so each row reads "Continue as @handle"
|
|
21
|
+
* and choosing it signs in (OxyHQ/oxy#1375 items 20 and 21).
|
|
16
22
|
*/
|
|
17
23
|
import type React from 'react';
|
|
18
24
|
import { type AccountDialogSnapshot, type SwitcherPrincipalRow } from '@oxy.so/core';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SignInEntryView.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/SignInEntryView.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SignInEntryView.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/SignInEntryView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EACV,sBAAsB,EAEtB,kBAAkB,EAClB,KAAK,EACL,SAAS,EACV,MAAM,YAAS,CAAC;AAEjB,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,qBAAqB,CAAC;IAChC,mEAAmE;IACnE,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,KAAK,EAAE,KAAK,CAAC;IACb,CAAC,EAAE,SAAS,CAAC;IACb,QAAQ,EAAE,sBAAsB,CAAC;IACjC,0FAA0F;IAC1F,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,YAAY,EAAE,kBAAkB,CAAC;CAClC;AAED,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAmGnD,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -53,11 +53,19 @@ export declare const Dividerish: React.FC<{
|
|
|
53
53
|
* directory), not from the theme and not from whoever is signed in. This surface
|
|
54
54
|
* is where a device holding two people is most visible, and it is reached while
|
|
55
55
|
* signed out — so there is no "current user" whose colour could stand in.
|
|
56
|
+
*
|
|
57
|
+
* `continueAsLabel` is the signed-out form. The device can list an identity
|
|
58
|
+
* (and even mark it active) while this app holds no session, so a check there
|
|
59
|
+
* would claim a sign-in that has not happened. Given a label, the row never
|
|
60
|
+
* reads as current: its first line is "Continue as @handle", the account's
|
|
61
|
+
* name moves to the second line, and it ends in a chevron like any other row.
|
|
56
62
|
*/
|
|
57
63
|
export declare const AccountRow: React.FC<{
|
|
58
64
|
context: SwitcherContextRow;
|
|
59
65
|
/** The person this account is reached through, when that is not obvious. */
|
|
60
66
|
operatedBy: string | null;
|
|
67
|
+
/** Signed out: the row's "Continue as @handle" line. `null` when signed in. */
|
|
68
|
+
continueAsLabel?: string | null;
|
|
61
69
|
theme: Theme;
|
|
62
70
|
activating: boolean;
|
|
63
71
|
disabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/primitives.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,SAAS,EAAQ,MAAM,6BAA6B,CAAC;AAK9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAA8B,KAAK,KAAK,EAAE,MAAM,YAAS,CAAC;AAKjE,KAAK,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,EAAE,WAAW,CAAC,GAAG;IACrF,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAcxD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAWA,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAMhE,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/primitives.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,SAAS,EAAQ,MAAM,6BAA6B,CAAC;AAK9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAA8B,KAAK,KAAK,EAAE,MAAM,YAAS,CAAC;AAKjE,KAAK,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,EAAE,WAAW,CAAC,GAAG;IACrF,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAcxD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAWA,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAMhE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAChC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAsDA,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeleteAccountModal.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/modals/DeleteAccountModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DeleteAccountModal.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/modals/DeleteAccountModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAExE,UAAU,uBAAuB;IAC7B,mEAAmE;IACnE,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;CAC3E;AAED;;;;;;GAMG;AACH,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAuFzD,CAAC;AAEF,mFAAmF;AACnF,KAAK,2BAA2B,GAAG,IAAI,CAAC,uBAAuB,EAAE,SAAS,CAAC,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,OAAO,CAAC,CAI3F;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accountDialogSnapshot.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/accountDialogSnapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,EAAE,
|
|
1
|
+
{"version":3,"file":"accountDialogSnapshot.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/accountDialogSnapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,EAAE,qBA0B3C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAccountMutations.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/mutations/useAccountMutations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,eAAe,EACf,IAAI,EACJ,eAAe,EAChB,MAAM,cAAc,CAAC;AAkBtB;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;aAipB++J,CAAC;YAAkC,CAAC;mBAAyC,CAAC;;aAA4C,CAAC;YAAkC,CAAC;mBAAyC,CAAC;;;;;;;;;;;;;;;;;;;;;;mBAAsxC,CAAC;UAAgC,CAAC;aAAmC,CAAC;aAAmC,CAAC;;;mBAAuE,CAAC;UAAgC,CAAC;aAAmC,CAAC;aAAmC,CAAC;;;;;;;;;;EAnjBpwN,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe;SAMQ,MAAM;WAAS,MAAM;WAAS,MAAM;WAAS,MAAM;;;EAuEtF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,UAAU,8BAA8B;IACtC,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAClC;;;;OAIG;IACH,WAAW,EAAE,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB;sBAoFf,OAAO,CAAC,eAAe,CAAC,KAAG,IAAI;2BAQpB,OAAO,CAAC,eAAe,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;sBARnD,OAAO,CAAC,eAAe,CAAC,KAAG,IAAI;2BAQpB,OAAO,CAAC,eAAe,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;sBARnD,OAAO,CAAC,eAAe,CAAC,KAAG,IAAI;2BAQpB,OAAO,CAAC,eAAe,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;sBARnD,OAAO,CAAC,eAAe,CAAC,KAAG,IAAI;2BAQpB,OAAO,CAAC,eAAe,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC
|
|
1
|
+
{"version":3,"file":"useAccountMutations.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/mutations/useAccountMutations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManageAccountScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/ManageAccountScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAwB/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ManageAccountScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/screens/ManageAccountScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAwB/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAqB,CAAC;AAgD3D;;;;;;;GAOG;AACH,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAw2BlD,CAAC;AAYF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commons' own delete-account screen (`app/(tabs)/(settings)/delete-account.tsx`
|
|
3
|
+
* in packages/commons). Expo Router drops the route groups, so the scheme path
|
|
4
|
+
* `delete-account` resolves to it; `oxycommons://` is the scheme the account
|
|
5
|
+
* dialog already probes and hands sign-in approvals to.
|
|
6
|
+
*/
|
|
7
|
+
export declare const COMMONS_DELETE_ACCOUNT_URL = "oxycommons://delete-account";
|
|
8
|
+
/** The scheme root `Linking.canOpenURL` probes to tell whether Commons is installed. */
|
|
9
|
+
export declare const COMMONS_APP_SCHEME = "oxycommons://";
|
|
10
|
+
type Translate = (key: string, vars?: Record<string, string | number>) => string | undefined;
|
|
11
|
+
export interface AccountDeletionHandoffDeps {
|
|
12
|
+
/** Whether THIS app can read an identity key it could sign the deletion with. */
|
|
13
|
+
hasIdentity: () => Promise<boolean>;
|
|
14
|
+
canOpenURL: (url: string) => Promise<boolean>;
|
|
15
|
+
openURL: (url: string) => Promise<unknown>;
|
|
16
|
+
t: Translate;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `'local'`: this app holds the identity key, so the caller runs its own
|
|
20
|
+
* delete-account surface. `'handled'`: it does not, and the user has been sent
|
|
21
|
+
* to where the deletion can actually be signed (or told why not).
|
|
22
|
+
*/
|
|
23
|
+
export type AccountDeletionHandoffResult = 'local' | 'handled';
|
|
24
|
+
/**
|
|
25
|
+
* Decide where a native account deletion can happen, before anything is deleted.
|
|
26
|
+
*
|
|
27
|
+
* Deleting an account is signed with the identity private key. When the key
|
|
28
|
+
* lives in Oxy Commons rather than in this app, the API call cannot be made from
|
|
29
|
+
* here, so the deletion is delegated to Commons' own screen when Commons is
|
|
30
|
+
* installed, and explained otherwise. Nothing on the delegated paths calls the
|
|
31
|
+
* deletion API.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runAccountDeletionHandoff(deps: AccountDeletionHandoffDeps): Promise<AccountDeletionHandoffResult>;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=accountDeletionHandoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountDeletionHandoff.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/accountDeletionHandoff.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,gCAAgC,CAAC;AAExE,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;AAE7F,MAAM,WAAW,0BAA0B;IACzC,iFAAiF;IACjF,WAAW,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC,EAAE,SAAS,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,MAAM,4BAA4B,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/D;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,4BAA4B,CAAC,CAuEvC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/services",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.6",
|
|
4
4
|
"description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
},
|
|
156
156
|
"dependencies": {
|
|
157
157
|
"@expo/config-plugins": "57.0.9",
|
|
158
|
-
"@oxy.so/contracts": "1.
|
|
158
|
+
"@oxy.so/contracts": "^1.5.0",
|
|
159
159
|
"@simplewebauthn/browser": "^13.3.0",
|
|
160
160
|
"color": "^4.2.3"
|
|
161
161
|
},
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
"@commitlint/cli": "^21.2.1",
|
|
165
165
|
"@commitlint/config-conventional": "^21.2.0",
|
|
166
166
|
"@expo/vector-icons": "^15.0.3",
|
|
167
|
-
"@oxy.so/core": "1.
|
|
167
|
+
"@oxy.so/core": "1.13.0",
|
|
168
168
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
169
169
|
"@react-native-community/netinfo": "^12.0.1",
|
|
170
170
|
"@react-native/babel-preset": "^0.86.2",
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
"peerDependencies": {
|
|
217
217
|
"@expo/vector-icons": "^15.0.3",
|
|
218
218
|
"@oxy.so/bloom": "^4.21.1",
|
|
219
|
-
"@oxy.so/core": "^1.
|
|
219
|
+
"@oxy.so/core": "^1.12.0",
|
|
220
220
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
221
221
|
"@react-native-community/netinfo": ">=11.4.1",
|
|
222
222
|
"@tanstack/query-async-storage-persister": "^5.101",
|
|
@@ -74,9 +74,11 @@ const OxyAccountDialogScreen: React.FC<BaseScreenProps> = ({ canGoBack }) => {
|
|
|
74
74
|
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
75
75
|
|
|
76
76
|
const { view } = snapshot;
|
|
77
|
-
// Names the entry view "Add another account" rather than "Sign in" when
|
|
78
|
-
//
|
|
79
|
-
|
|
77
|
+
// Names the entry view "Add another account" rather than "Sign in" only when
|
|
78
|
+
// THIS app is signed in. Not the directory's size: the device can go on
|
|
79
|
+
// listing a shared identity (Commons') after the app signed out, and that
|
|
80
|
+
// titled a signed-out sheet "Add another account" (OxyHQ/oxy#1375 item 21).
|
|
81
|
+
const hasSignedInAccounts = snapshot.hasSession;
|
|
80
82
|
// Where back leads is the CONTROLLER's answer (`snapshot.backView`), never a
|
|
81
83
|
// table here: a host-side "back = accounts" assumed a signed-in origin and
|
|
82
84
|
// opened the account menu for nobody on a signed-out Back from sign-up.
|
|
@@ -169,30 +169,40 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({ onComplete }) => {
|
|
|
169
169
|
const handleActivate = useCallback(
|
|
170
170
|
async (contextId: string) => {
|
|
171
171
|
if (!controller) return;
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
172
|
+
// The controller decides what choosing a row MEANS. Signed in it is a
|
|
173
|
+
// switch; signed out it is "Continue as @handle" — the same sign-in
|
|
174
|
+
// "Continue with Oxy" runs. The signed-out case is why this is not a
|
|
175
|
+
// host-side "already active → close": the device can list an identity
|
|
176
|
+
// (Commons') as active while THIS app holds no session, and closing on it
|
|
177
|
+
// left the app signed out behind a sheet that looked done (#1375 item 20).
|
|
178
|
+
const outcome = await controller.chooseContext(contextId).catch(() => 'failed' as const);
|
|
179
|
+
switch (outcome) {
|
|
180
|
+
// Another switch or removal is already changing this device: the press
|
|
181
|
+
// is dropped, not queued — and it is not a failure to report.
|
|
182
|
+
case 'busy':
|
|
183
|
+
return;
|
|
184
|
+
// The sign-in flow owns the rest: its own view while it runs, and the
|
|
185
|
+
// dialog's close (through `onSignedIn`) when it completes.
|
|
186
|
+
case 'signing-in':
|
|
187
|
+
return;
|
|
188
|
+
// Already live: activating it again bumps nothing server-side, so treat
|
|
189
|
+
// the press as "yes, this one" and close.
|
|
190
|
+
case 'current':
|
|
191
|
+
onComplete?.();
|
|
192
|
+
return;
|
|
193
|
+
// The controller's own verdict on the SWITCH — never `snapshot.error`,
|
|
194
|
+
// which the directory re-read after a successful switch can also set.
|
|
195
|
+
case 'failed':
|
|
196
|
+
toast.error(t('accountSwitcher.toasts.activateFailed'));
|
|
197
|
+
return;
|
|
198
|
+
case 'switched':
|
|
199
|
+
// The subject changed, so every account-scoped query is now about
|
|
200
|
+
// somebody else. The runtime resets its own caches between the bearer
|
|
201
|
+
// commit and the notify; this drops the Query cache the same way.
|
|
202
|
+
queryClient.invalidateQueries();
|
|
203
|
+
onComplete?.();
|
|
204
|
+
return;
|
|
189
205
|
}
|
|
190
|
-
// The subject changed, so every account-scoped query is now about
|
|
191
|
-
// somebody else. The runtime resets its own caches between the bearer
|
|
192
|
-
// commit and the notify; this drops the Query cache the same way the
|
|
193
|
-
// account switch did.
|
|
194
|
-
queryClient.invalidateQueries();
|
|
195
|
-
onComplete?.();
|
|
196
206
|
},
|
|
197
207
|
[controller, onComplete, queryClient, t],
|
|
198
208
|
);
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Existing accounts still render ABOVE the CTA: "continue as one of these" is a
|
|
15
15
|
* choice of WHO, not of HOW.
|
|
16
|
+
*
|
|
17
|
+
* Signed in (the "Add another account" entry), a row is an account to switch
|
|
18
|
+
* to, and the current one carries its check. Signed out, nothing here is
|
|
19
|
+
* current: the device may still list an identity it holds (Commons' shared
|
|
20
|
+
* one), but THIS app has no session, so each row reads "Continue as @handle"
|
|
21
|
+
* and choosing it signs in (OxyHQ/oxy#1375 items 20 and 21).
|
|
16
22
|
*/
|
|
17
23
|
|
|
18
24
|
import type React from 'react';
|
|
@@ -90,6 +96,13 @@ const SignInEntryView: React.FC<SignInEntryViewProps> = ({
|
|
|
90
96
|
key={context.contextId}
|
|
91
97
|
context={context}
|
|
92
98
|
operatedBy={operatedBy}
|
|
99
|
+
continueAsLabel={
|
|
100
|
+
snapshot.hasSession
|
|
101
|
+
? null
|
|
102
|
+
: t('signin.chooser.continueAs', {
|
|
103
|
+
name: context.handle ? `@${context.handle}` : context.displayName,
|
|
104
|
+
})
|
|
105
|
+
}
|
|
93
106
|
theme={theme}
|
|
94
107
|
activating={snapshot.activatingContextId === context.contextId}
|
|
95
108
|
disabled={snapshot.activatingContextId !== null}
|
|
@@ -96,19 +96,31 @@ export const Dividerish: React.FC<{ theme: Theme; label: string }> = ({ theme, l
|
|
|
96
96
|
* directory), not from the theme and not from whoever is signed in. This surface
|
|
97
97
|
* is where a device holding two people is most visible, and it is reached while
|
|
98
98
|
* signed out — so there is no "current user" whose colour could stand in.
|
|
99
|
+
*
|
|
100
|
+
* `continueAsLabel` is the signed-out form. The device can list an identity
|
|
101
|
+
* (and even mark it active) while this app holds no session, so a check there
|
|
102
|
+
* would claim a sign-in that has not happened. Given a label, the row never
|
|
103
|
+
* reads as current: its first line is "Continue as @handle", the account's
|
|
104
|
+
* name moves to the second line, and it ends in a chevron like any other row.
|
|
99
105
|
*/
|
|
100
106
|
export const AccountRow: React.FC<{
|
|
101
107
|
context: SwitcherContextRow;
|
|
102
108
|
/** The person this account is reached through, when that is not obvious. */
|
|
103
109
|
operatedBy: string | null;
|
|
110
|
+
/** Signed out: the row's "Continue as @handle" line. `null` when signed in. */
|
|
111
|
+
continueAsLabel?: string | null;
|
|
104
112
|
theme: Theme;
|
|
105
113
|
activating: boolean;
|
|
106
114
|
disabled: boolean;
|
|
107
115
|
onPress: () => void;
|
|
108
|
-
}> = ({ context, operatedBy, theme, activating, disabled, onPress }) => {
|
|
116
|
+
}> = ({ context, operatedBy, continueAsLabel = null, theme, activating, disabled, onPress }) => {
|
|
109
117
|
const accent = resolveAccentHex(context.color, theme.colors.primary);
|
|
110
118
|
const rowDisabled = disabled || !context.canActivate;
|
|
111
|
-
const
|
|
119
|
+
const current = continueAsLabel === null && context.isActive;
|
|
120
|
+
const primary = continueAsLabel ?? context.displayName;
|
|
121
|
+
const secondary =
|
|
122
|
+
operatedBy ??
|
|
123
|
+
(continueAsLabel !== null ? context.displayName : context.handle ? `@${context.handle}` : null);
|
|
112
124
|
|
|
113
125
|
return (
|
|
114
126
|
<BloomColorScope colorPreset={toPreset(context.color)} asChild>
|
|
@@ -116,7 +128,7 @@ export const AccountRow: React.FC<{
|
|
|
116
128
|
style={[
|
|
117
129
|
styles.accountRow,
|
|
118
130
|
{
|
|
119
|
-
borderColor:
|
|
131
|
+
borderColor: current ? accent : theme.colors.border,
|
|
120
132
|
backgroundColor: theme.colors.card,
|
|
121
133
|
},
|
|
122
134
|
rowDisabled && !activating ? styles.rowDisabled : null,
|
|
@@ -124,10 +136,10 @@ export const AccountRow: React.FC<{
|
|
|
124
136
|
onPress={onPress}
|
|
125
137
|
disabled={rowDisabled}
|
|
126
138
|
accessibilityRole="button"
|
|
127
|
-
accessibilityState={{ selected:
|
|
128
|
-
accessibilityLabel={
|
|
139
|
+
accessibilityState={{ selected: current, disabled: rowDisabled }}
|
|
140
|
+
accessibilityLabel={primary}
|
|
129
141
|
>
|
|
130
|
-
<View style={[styles.avatarRing, { borderColor:
|
|
142
|
+
<View style={[styles.avatarRing, { borderColor: current ? accent : 'transparent' }]}>
|
|
131
143
|
<Avatar
|
|
132
144
|
source={context.avatarUrl ?? undefined}
|
|
133
145
|
variant="thumb"
|
|
@@ -137,7 +149,7 @@ export const AccountRow: React.FC<{
|
|
|
137
149
|
</View>
|
|
138
150
|
<View style={styles.rowMeta}>
|
|
139
151
|
<Text style={[styles.rowName, { color: theme.colors.text }]} numberOfLines={1}>
|
|
140
|
-
{
|
|
152
|
+
{primary}
|
|
141
153
|
</Text>
|
|
142
154
|
{secondary ? (
|
|
143
155
|
<Text style={[styles.rowHandle, { color: theme.colors.textSecondary }]} numberOfLines={1}>
|
|
@@ -147,7 +159,7 @@ export const AccountRow: React.FC<{
|
|
|
147
159
|
</View>
|
|
148
160
|
{activating ? (
|
|
149
161
|
<MaterialCommunityIcons name="loading" size={20} color={accent} />
|
|
150
|
-
) :
|
|
162
|
+
) : current ? (
|
|
151
163
|
<MaterialCommunityIcons name="check-circle" size={20} color={accent} />
|
|
152
164
|
) : (
|
|
153
165
|
<MaterialCommunityIcons name="chevron-right" size={20} color={theme.colors.textSecondary} />
|
|
@@ -4,6 +4,7 @@ import { View, TextInput } from 'react-native';
|
|
|
4
4
|
import { Text } from '@oxy.so/bloom/typography';
|
|
5
5
|
import Ionicons from '../../icons/Ionicons';
|
|
6
6
|
import { Button } from '@oxy.so/bloom/button';
|
|
7
|
+
import { Admonition } from '@oxy.so/bloom/admonition';
|
|
7
8
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
8
9
|
import { surfaces, type SurfaceControls } from '@oxy.so/bloom/surfaces';
|
|
9
10
|
|
|
@@ -69,16 +70,11 @@ const DeleteAccountModal: React.FC<DeleteAccountModalProps> = ({
|
|
|
69
70
|
{t('deleteAccount.warning') || 'This action cannot be undone. Your account and all associated data will be permanently deleted.'}
|
|
70
71
|
</Text>
|
|
71
72
|
|
|
72
|
-
{error
|
|
73
|
-
<View
|
|
74
|
-
|
|
75
|
-
style={{ backgroundColor: `${theme.colors.error}20` }}
|
|
76
|
-
>
|
|
77
|
-
<Text className="text-text text-sm text-center" style={{ color: theme.colors.error }}>
|
|
78
|
-
{error}
|
|
79
|
-
</Text>
|
|
73
|
+
{error ? (
|
|
74
|
+
<View className="mb-4">
|
|
75
|
+
<Admonition type="error">{error}</Admonition>
|
|
80
76
|
</View>
|
|
81
|
-
)}
|
|
77
|
+
) : null}
|
|
82
78
|
|
|
83
79
|
<View className="mb-4">
|
|
84
80
|
<Text className="text-text-secondary text-[13px] mb-2">
|
|
@@ -15,6 +15,7 @@ import type { AccountDialogSnapshot } from '@oxy.so/core';
|
|
|
15
15
|
export const EMPTY_ACCOUNT_DIALOG_SNAPSHOT: AccountDialogSnapshot = {
|
|
16
16
|
view: 'accounts',
|
|
17
17
|
backView: null,
|
|
18
|
+
hasSession: false,
|
|
18
19
|
directory: null,
|
|
19
20
|
activeContext: null,
|
|
20
21
|
activatingContextId: null,
|
|
@@ -26,6 +26,7 @@ import type { BaseScreenProps } from '../types/navigation';
|
|
|
26
26
|
import ProfileSummaryCard from '../components/ProfileSummaryCard';
|
|
27
27
|
import { SettingsIcon } from '../components/SettingsIcon';
|
|
28
28
|
import { presentDeleteAccount } from '../components/modals/DeleteAccountModal';
|
|
29
|
+
import { runAccountDeletionHandoff } from '../utils/accountDeletionHandoff';
|
|
29
30
|
import { presentActionSheet } from '../components/surfaces/ActionSheetSurface';
|
|
30
31
|
import { useOxy } from '../context/OxyContext';
|
|
31
32
|
import { useI18n } from '../hooks/useI18n';
|
|
@@ -95,6 +96,7 @@ const ManageAccountScreen: React.FC<BaseScreenProps> = ({
|
|
|
95
96
|
openAvatarPicker,
|
|
96
97
|
accounts,
|
|
97
98
|
openAccountDialog,
|
|
99
|
+
hasIdentity,
|
|
98
100
|
} = useOxy();
|
|
99
101
|
|
|
100
102
|
const { data: userFromQuery, isLoading: userLoading } = useCurrentUser({
|
|
@@ -330,6 +332,19 @@ const ManageAccountScreen: React.FC<BaseScreenProps> = ({
|
|
|
330
332
|
});
|
|
331
333
|
return;
|
|
332
334
|
}
|
|
335
|
+
// Native: the deletion is signed with the identity key. When this app
|
|
336
|
+
// does not hold it (Commons keeps it on this device, or it lives on
|
|
337
|
+
// another device), hand off to where it can be signed instead of
|
|
338
|
+
// failing inside the confirmation surface.
|
|
339
|
+
const route = await runAccountDeletionHandoff({
|
|
340
|
+
hasIdentity,
|
|
341
|
+
canOpenURL: (url) => Linking.canOpenURL(url),
|
|
342
|
+
openURL: (url) => Linking.openURL(url),
|
|
343
|
+
t,
|
|
344
|
+
});
|
|
345
|
+
if (route !== 'local') {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
333
348
|
const deleted = await presentDeleteAccount({
|
|
334
349
|
username: user.username || '',
|
|
335
350
|
onDelete: handleConfirmDelete,
|
|
@@ -339,7 +354,7 @@ const ManageAccountScreen: React.FC<BaseScreenProps> = ({
|
|
|
339
354
|
await logout();
|
|
340
355
|
onClose?.();
|
|
341
356
|
}
|
|
342
|
-
}, [user, t, handleConfirmDelete, logout, onClose]);
|
|
357
|
+
}, [user, t, handleConfirmDelete, hasIdentity, logout, onClose]);
|
|
343
358
|
|
|
344
359
|
if (!isAuthenticated) {
|
|
345
360
|
return (
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { surfaces } from '@oxy.so/bloom/surfaces';
|
|
2
|
+
import { toast } from '@oxy.so/bloom/toast';
|
|
3
|
+
import { IDENTITY_WEB_ORIGIN, logger } from '@oxy.so/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Commons' own delete-account screen (`app/(tabs)/(settings)/delete-account.tsx`
|
|
7
|
+
* in packages/commons). Expo Router drops the route groups, so the scheme path
|
|
8
|
+
* `delete-account` resolves to it; `oxycommons://` is the scheme the account
|
|
9
|
+
* dialog already probes and hands sign-in approvals to.
|
|
10
|
+
*/
|
|
11
|
+
export const COMMONS_DELETE_ACCOUNT_URL = 'oxycommons://delete-account';
|
|
12
|
+
|
|
13
|
+
/** The scheme root `Linking.canOpenURL` probes to tell whether Commons is installed. */
|
|
14
|
+
export const COMMONS_APP_SCHEME = 'oxycommons://';
|
|
15
|
+
|
|
16
|
+
type Translate = (key: string, vars?: Record<string, string | number>) => string | undefined;
|
|
17
|
+
|
|
18
|
+
export interface AccountDeletionHandoffDeps {
|
|
19
|
+
/** Whether THIS app can read an identity key it could sign the deletion with. */
|
|
20
|
+
hasIdentity: () => Promise<boolean>;
|
|
21
|
+
canOpenURL: (url: string) => Promise<boolean>;
|
|
22
|
+
openURL: (url: string) => Promise<unknown>;
|
|
23
|
+
t: Translate;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* `'local'`: this app holds the identity key, so the caller runs its own
|
|
28
|
+
* delete-account surface. `'handled'`: it does not, and the user has been sent
|
|
29
|
+
* to where the deletion can actually be signed (or told why not).
|
|
30
|
+
*/
|
|
31
|
+
export type AccountDeletionHandoffResult = 'local' | 'handled';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Decide where a native account deletion can happen, before anything is deleted.
|
|
35
|
+
*
|
|
36
|
+
* Deleting an account is signed with the identity private key. When the key
|
|
37
|
+
* lives in Oxy Commons rather than in this app, the API call cannot be made from
|
|
38
|
+
* here, so the deletion is delegated to Commons' own screen when Commons is
|
|
39
|
+
* installed, and explained otherwise. Nothing on the delegated paths calls the
|
|
40
|
+
* deletion API.
|
|
41
|
+
*/
|
|
42
|
+
export async function runAccountDeletionHandoff(
|
|
43
|
+
deps: AccountDeletionHandoffDeps,
|
|
44
|
+
): Promise<AccountDeletionHandoffResult> {
|
|
45
|
+
const { hasIdentity, canOpenURL, openURL, t } = deps;
|
|
46
|
+
const text = (key: string, fallback: string, vars?: Record<string, string>): string => {
|
|
47
|
+
// An older `@oxy.so/core` echoes a key it has no string for; show English then.
|
|
48
|
+
const value = t(key, vars);
|
|
49
|
+
return value && value !== key ? value : fallback;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let holdsIdentity: boolean;
|
|
53
|
+
try {
|
|
54
|
+
holdsIdentity = await hasIdentity();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
// A locked or unreadable keystore is not proof the identity is elsewhere.
|
|
57
|
+
logger.warn('Identity read failed before account deletion', { component: 'accountDeletionHandoff' }, error);
|
|
58
|
+
toast.error(
|
|
59
|
+
text(
|
|
60
|
+
'deleteAccount.handoff.identityUnreadable',
|
|
61
|
+
"Couldn't read this device's identity storage. Unlock your device and try again.",
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
return 'handled';
|
|
65
|
+
}
|
|
66
|
+
if (holdsIdentity) {
|
|
67
|
+
return 'local';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let commonsInstalled = false;
|
|
71
|
+
try {
|
|
72
|
+
commonsInstalled = await canOpenURL(COMMONS_APP_SCHEME);
|
|
73
|
+
} catch {
|
|
74
|
+
commonsInstalled = false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (commonsInstalled) {
|
|
78
|
+
const open = await surfaces.confirm({
|
|
79
|
+
title: text('deleteAccount.handoff.commonsTitle', 'Delete your account in Oxy Commons'),
|
|
80
|
+
description: text(
|
|
81
|
+
'deleteAccount.handoff.commonsMessage',
|
|
82
|
+
'Your identity key is kept by Oxy Commons on this device, and deleting your account needs it. Commons will open on Settings > Delete account to finish.',
|
|
83
|
+
),
|
|
84
|
+
confirmLabel: text('deleteAccount.handoff.openCommons', 'Open Oxy Commons'),
|
|
85
|
+
cancelLabel: text('common.cancel', 'Cancel'),
|
|
86
|
+
});
|
|
87
|
+
if (open) {
|
|
88
|
+
try {
|
|
89
|
+
await openURL(COMMONS_DELETE_ACCOUNT_URL);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
logger.warn('Opening Commons delete-account failed', { component: 'accountDeletionHandoff' }, error);
|
|
92
|
+
toast.error(
|
|
93
|
+
text(
|
|
94
|
+
'deleteAccount.handoff.commonsOpenFailed',
|
|
95
|
+
"Couldn't open Oxy Commons. Open it yourself and go to Settings > Delete account.",
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return 'handled';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const site = IDENTITY_WEB_ORIGIN.replace(/^https?:\/\//, '');
|
|
104
|
+
await surfaces.confirm({
|
|
105
|
+
title: text('deleteAccount.handoff.elsewhereTitle', 'Delete your account where your identity is'),
|
|
106
|
+
description: text(
|
|
107
|
+
'deleteAccount.handoff.elsewhereMessage',
|
|
108
|
+
`Deleting your account needs your identity key, and it isn't on this device. Open Oxy Commons on the device that holds your identity and go to Settings > Delete account, or open ${site} in a browser that holds it.`,
|
|
109
|
+
{ site },
|
|
110
|
+
),
|
|
111
|
+
confirmLabel: text('deleteAccount.handoff.gotIt', 'Got it'),
|
|
112
|
+
hideCancel: true,
|
|
113
|
+
});
|
|
114
|
+
return 'handled';
|
|
115
|
+
}
|