@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.
Files changed (69) hide show
  1. package/android/src/main/java/so/oxy/identity/OxyIdentityStore.kt +8 -7
  2. package/android/src/main/java/so/oxy/storage/OxyEncryptedPrefs.kt +41 -97
  3. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +5 -3
  4. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
  5. package/lib/commonjs/ui/components/OxyAuthChooser.js +33 -23
  6. package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
  7. package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +9 -0
  8. package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
  9. package/lib/commonjs/ui/components/authChooser/primitives.js +16 -7
  10. package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
  11. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +6 -11
  12. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -1
  13. package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +1 -0
  14. package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -1
  15. package/lib/commonjs/ui/screens/ManageAccountScreen.js +17 -2
  16. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  17. package/lib/commonjs/ui/utils/accountDeletionHandoff.js +99 -0
  18. package/lib/commonjs/ui/utils/accountDeletionHandoff.js.map +1 -0
  19. package/lib/module/ui/components/OxyAccountDialogScreen.js +5 -3
  20. package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
  21. package/lib/module/ui/components/OxyAuthChooser.js +33 -23
  22. package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
  23. package/lib/module/ui/components/authChooser/SignInEntryView.js +9 -0
  24. package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
  25. package/lib/module/ui/components/authChooser/primitives.js +16 -7
  26. package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
  27. package/lib/module/ui/components/modals/DeleteAccountModal.js +6 -11
  28. package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -1
  29. package/lib/module/ui/hooks/accountDialogSnapshot.js +1 -0
  30. package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -1
  31. package/lib/module/ui/screens/ManageAccountScreen.js +17 -2
  32. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  33. package/lib/module/ui/utils/accountDeletionHandoff.js +95 -0
  34. package/lib/module/ui/utils/accountDeletionHandoff.js.map +1 -0
  35. package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
  37. package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +6 -0
  38. package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +8 -0
  40. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
  41. package/lib/typescript/commonjs/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  42. package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
  44. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/ui/utils/accountDeletionHandoff.d.ts +35 -0
  47. package/lib/typescript/commonjs/ui/utils/accountDeletionHandoff.d.ts.map +1 -0
  48. package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  49. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
  50. package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +6 -0
  51. package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
  52. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +8 -0
  53. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
  54. package/lib/typescript/module/ui/components/modals/DeleteAccountModal.d.ts.map +1 -1
  55. package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
  56. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
  57. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  58. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  59. package/lib/typescript/module/ui/utils/accountDeletionHandoff.d.ts +35 -0
  60. package/lib/typescript/module/ui/utils/accountDeletionHandoff.d.ts.map +1 -0
  61. package/package.json +4 -4
  62. package/src/ui/components/OxyAccountDialogScreen.tsx +5 -3
  63. package/src/ui/components/OxyAuthChooser.tsx +33 -23
  64. package/src/ui/components/authChooser/SignInEntryView.tsx +13 -0
  65. package/src/ui/components/authChooser/primitives.tsx +20 -8
  66. package/src/ui/components/modals/DeleteAccountModal.tsx +5 -9
  67. package/src/ui/hooks/accountDialogSnapshot.ts +1 -0
  68. package/src/ui/screens/ManageAccountScreen.tsx +16 -1
  69. 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,CA2DrD,CAAC;AAyDF,eAAe,sBAAsB,CAAC"}
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,CAsXjD,CAAC;AA8BF,eAAe,cAAc,CAAC"}
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;;;;;;;;;;;;;;;GAeG;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,CA4FnD,CAAC;AAEF,eAAe,eAAe,CAAC"}
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;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAChC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAkDA,CAAC"}
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;AAO/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,CA4FzD,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
+ {"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,qBAyB3C,CAAC"}
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"}
@@ -173,6 +173,7 @@ export declare const useUpdateAccountSettings: () => {
173
173
  name: import("@oxy.so/contracts").UserNameResponse;
174
174
  bio?: string;
175
175
  description?: string;
176
+ alsoKnownAs?: string[];
176
177
  phone?: string;
177
178
  address?: string;
178
179
  birthday?: string;
@@ -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;;;;;;;;;;;;;;;;;;;;;;;iBAvMX,CAAC;uBAC/C,CAAC;iBAAuB,CAAC;cAE5B,CAAC;;;qBACH,CAAC;qBAA2B,CAAC;;;;;;;;;;;oBA6B7B,CAAC;kBAAwB,CAAC;mBAAyB,CAAC;;;mBAClD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Kb,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB;cAMoB,OAAO,CAAC,eAAe,CAAC;aAAW,MAAM;;;;cAgJjG,CAAC;AAEF,4CAA4C;AAC5C,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;CACxI;AAED,4EAA4E;AAC5E,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,eAAO,MAAM,gCAAgC;;EAmD5C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;EA+CpC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,uFAoBjC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa;UAWd,gBAAgB;iBACT,SAAS,GAAG,QAAQ,GAAG,UAAU;eACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;iBACrB,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI;WAS5C,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;AA+C3D;;;;;;;GAOG;AACH,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA01BlD,CAAC;AAYF,eAAe,mBAAmB,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.4",
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.4.0",
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.10.0",
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.9.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
- // somebody is already signed in on this device.
79
- const hasSignedInAccounts = (snapshot.directory?.principals.length ?? 0) > 0;
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
- // Another switch or removal is already changing this device: the press is
173
- // dropped, not queued — and it is not a failure to report.
174
- if (controller.isDeviceMutationInFlight()) return;
175
- // Already live: activating it again bumps nothing server-side, so treat
176
- // the press as "yes, this one" and close.
177
- if (contextId === controller.getSnapshot().activeContext?.contextId) {
178
- onComplete?.();
179
- return;
180
- }
181
- // The controller's own verdict on the SWITCH — never `snapshot.error`,
182
- // which the directory re-read after a successful switch can also set.
183
- // Reading that as "the switch failed" left the dialog open on a false
184
- // error with the previous account's queries still cached.
185
- const switched = await controller.activateContext(contextId).catch(() => false);
186
- if (!switched) {
187
- toast.error(t('accountSwitcher.toasts.activateFailed'));
188
- return;
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 secondary = operatedBy ?? (context.handle ? `@${context.handle}` : null);
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: context.isActive ? accent : theme.colors.border,
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: context.isActive, disabled: rowDisabled }}
128
- accessibilityLabel={context.displayName}
139
+ accessibilityState={{ selected: current, disabled: rowDisabled }}
140
+ accessibilityLabel={primary}
129
141
  >
130
- <View style={[styles.avatarRing, { borderColor: context.isActive ? accent : 'transparent' }]}>
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
- {context.displayName}
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
- ) : context.isActive ? (
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
- className="p-3 rounded-lg mb-4"
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
+ }