@oxy.so/services 4.0.1 → 4.0.3
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/ui/components/OxyAccountDialogScreen.js +10 -16
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +8 -2
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyConsentScreen.js +2 -1
- package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInRequestSurface.js +1 -0
- package/lib/commonjs/ui/components/OxySignInRequestSurface.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +5 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignUpView.js +2 -0
- package/lib/commonjs/ui/components/authChooser/SignUpView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js +2 -1
- package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js +1 -0
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/styles.js +13 -3
- package/lib/commonjs/ui/components/authChooser/styles.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +52 -20
- package/lib/commonjs/ui/context/OxyContext.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/icons/Ionicons.js +3 -1
- package/lib/commonjs/ui/icons/Ionicons.js.map +1 -1
- package/lib/commonjs/ui/icons/MaterialCommunityIcons.js +3 -1
- package/lib/commonjs/ui/icons/MaterialCommunityIcons.js.map +1 -1
- package/lib/commonjs/ui/icons/decorativeIconSet.js +50 -0
- package/lib/commonjs/ui/icons/decorativeIconSet.js.map +1 -0
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +10 -7
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/session/tokenLossRecovery.js +122 -0
- package/lib/commonjs/ui/session/tokenLossRecovery.js.map +1 -0
- package/lib/commonjs/ui/utils/deviceSessionTitle.js +30 -0
- package/lib/commonjs/ui/utils/deviceSessionTitle.js.map +1 -0
- package/lib/module/ui/components/OxyAccountDialogScreen.js +10 -16
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +8 -2
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyConsentScreen.js +2 -1
- package/lib/module/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/module/ui/components/OxySignInRequestSurface.js +1 -0
- package/lib/module/ui/components/OxySignInRequestSurface.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js +5 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignUpView.js +2 -0
- package/lib/module/ui/components/authChooser/SignUpView.js.map +1 -1
- package/lib/module/ui/components/authChooser/TroubleDisclosure.js +2 -1
- package/lib/module/ui/components/authChooser/TroubleDisclosure.js.map +1 -1
- package/lib/module/ui/components/authChooser/requestSurfaces.js +1 -0
- package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -1
- package/lib/module/ui/components/authChooser/styles.js +13 -3
- package/lib/module/ui/components/authChooser/styles.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +53 -21
- package/lib/module/ui/context/OxyContext.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/icons/Ionicons.js +4 -1
- package/lib/module/ui/icons/Ionicons.js.map +1 -1
- package/lib/module/ui/icons/MaterialCommunityIcons.js +4 -1
- package/lib/module/ui/icons/MaterialCommunityIcons.js.map +1 -1
- package/lib/module/ui/icons/decorativeIconSet.js +47 -0
- package/lib/module/ui/icons/decorativeIconSet.js.map +1 -0
- package/lib/module/ui/screens/ManageAccountScreen.js +10 -7
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/session/tokenLossRecovery.js +116 -0
- package/lib/module/ui/session/tokenLossRecovery.js.map +1 -0
- package/lib/module/ui/utils/deviceSessionTitle.js +26 -0
- package/lib/module/ui/utils/deviceSessionTitle.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/OxyConsentScreen.d.ts +10 -1
- package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts +12 -2
- package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/Ionicons.d.ts +2 -1
- package/lib/typescript/commonjs/ui/icons/Ionicons.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +2 -1
- package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/decorativeIconSet.d.ts +26 -0
- package/lib/typescript/commonjs/ui/icons/decorativeIconSet.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/tokenLossRecovery.d.ts +37 -0
- package/lib/typescript/commonjs/ui/session/tokenLossRecovery.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/deviceSessionTitle.d.ts +18 -0
- package/lib/typescript/commonjs/ui/utils/deviceSessionTitle.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/OxyConsentScreen.d.ts +10 -1
- package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/styles.d.ts +12 -2
- package/lib/typescript/module/ui/components/authChooser/styles.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/Ionicons.d.ts +2 -1
- package/lib/typescript/module/ui/icons/Ionicons.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +2 -1
- package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/decorativeIconSet.d.ts +26 -0
- package/lib/typescript/module/ui/icons/decorativeIconSet.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/tokenLossRecovery.d.ts +37 -0
- package/lib/typescript/module/ui/session/tokenLossRecovery.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/deviceSessionTitle.d.ts +18 -0
- package/lib/typescript/module/ui/utils/deviceSessionTitle.d.ts.map +1 -0
- package/package.json +6 -6
- package/src/ui/components/OxyAccountDialogScreen.tsx +10 -16
- package/src/ui/components/OxyAuthChooser.tsx +8 -2
- package/src/ui/components/OxyConsentScreen.tsx +11 -1
- package/src/ui/components/OxySignInRequestSurface.tsx +1 -1
- package/src/ui/components/__tests__/OxyConsentScreen.test.tsx +16 -0
- package/src/ui/components/authChooser/SignInEntryView.tsx +3 -0
- package/src/ui/components/authChooser/SignUpView.tsx +2 -2
- package/src/ui/components/authChooser/TroubleDisclosure.tsx +5 -2
- package/src/ui/components/authChooser/requestSurfaces.tsx +1 -1
- package/src/ui/components/authChooser/styles.ts +12 -2
- package/src/ui/context/OxyContext.tsx +53 -23
- package/src/ui/hooks/accountDialogSnapshot.ts +1 -0
- package/src/ui/icons/Ionicons.ts +9 -4
- package/src/ui/icons/MaterialCommunityIcons.ts +9 -4
- package/src/ui/icons/__tests__/decorativeIconSet.test.tsx +40 -0
- package/src/ui/icons/decorativeIconSet.ts +41 -0
- package/src/ui/screens/ManageAccountScreen.tsx +13 -18
- package/src/ui/session/__tests__/tokenLossRecovery.test.ts +195 -0
- package/src/ui/session/tokenLossRecovery.ts +146 -0
- package/src/ui/utils/__tests__/deviceSessionTitle.test.ts +31 -0
- package/src/ui/utils/deviceSessionTitle.ts +29 -0
|
@@ -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,
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TroubleDisclosure.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/TroubleDisclosure.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAS,CAAC;AAExE,UAAU,sBAAsB;IAC9B,mFAAmF;IACnF,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;IACb,CAAC,EAAE,SAAS,CAAC;CACd;AAKD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,
|
|
1
|
+
{"version":3,"file":"TroubleDisclosure.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/TroubleDisclosure.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAS,CAAC;AAExE,UAAU,sBAAsB;IAC9B,mFAAmF;IACnF,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;IACb,CAAC,EAAE,SAAS,CAAC;CACd;AAKD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA0CvD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -200,10 +200,20 @@ export declare const authChooserStyles: {
|
|
|
200
200
|
width: "100%";
|
|
201
201
|
alignItems: "center";
|
|
202
202
|
};
|
|
203
|
-
/**
|
|
203
|
+
/**
|
|
204
|
+
* The disclosure spans the content width, so the alternatives it reveals are
|
|
205
|
+
* centred across the sheet rather than inside the trigger's own width. It
|
|
206
|
+
* used to be `alignSelf: 'center'` — shrink-wrapped — and on Android that
|
|
207
|
+
* both wrapped the trigger to "Having / trouble?" and squeezed the revealed
|
|
208
|
+
* links into the same narrow column (OxyHQ/oxy#1375).
|
|
209
|
+
*/
|
|
210
|
+
troubleDisclosure: {
|
|
211
|
+
width: "100%";
|
|
212
|
+
marginTop: number;
|
|
213
|
+
};
|
|
214
|
+
/** The trigger itself stays a compact, centred affordance like the links. */
|
|
204
215
|
troubleTrigger: {
|
|
205
216
|
alignSelf: "center";
|
|
206
|
-
marginTop: number;
|
|
207
217
|
};
|
|
208
218
|
};
|
|
209
219
|
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/styles.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAkC5B;;;;;OAKG;;;;;;;IAOH;;;;;;OAMG;;;;;;;IAOH;;;;;OAKG;;;;;;;;;;IAUH;;;;OAIG;;;;IAIH,0EAA0E;;;;;;;;;;IAU1E;qFACiF;;;;;;;;;;IAUjF,8EAA8E;;;;;IAK9E;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyEH;;;;OAIG;;;;;IAKH,6EAA6E
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/components/authChooser/styles.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAkC5B;;;;;OAKG;;;;;;;IAOH;;;;;;OAMG;;;;;;;IAOH;;;;;OAKG;;;;;;;;;;IAUH;;;;OAIG;;;;IAIH,0EAA0E;;;;;;;;;;IAU1E;qFACiF;;;;;;;;;;IAUjF,8EAA8E;;;;;IAK9E;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyEH;;;;OAIG;;;;;IAKH;;;;;;OAMG;;;;;IAKH,6EAA6E;;;;CAI7E,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAuF/B,OAAO,KAAK,EACV,eAAe,EACf,uBAAuB,EAExB,MAAM,sBAAmB,CAAC;AAgB3B,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,sBAAmB,CAAC;AAIhG,KAAK,0BAA0B,GAAG,IAAI,CACpC,eAAe,EACb,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,iBAAiB,GACjB,aAAa,GACb,iBAAiB,GACjB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,CACtB,CAAC;AAIF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAyvChE,CAAC;AAMF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,0BAA0B;;CAMxC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,QAAO,eAMzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,eAAe,GAAG,IAAqC,CAAC;AAE1F,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,0BAMpC,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,qBAyB3C,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/** Glyphs are decorative: hidden from assistive technology (see `decorativeIconSet`). */
|
|
2
|
+
declare const Ionicons: import("@expo/vector-icons/build/createIconSet").Icon<"code" | "link" | "search" | "ellipse" | "image" | "text" | "push" | "at" | "add" | "add-circle-outline" | "alert" | "alert-circle" | "alert-circle-outline" | "analytics" | "apps" | "archive" | "arrow-back" | "arrow-down" | "arrow-forward" | "arrow-up" | "bookmark" | "briefcase" | "briefcase-outline" | "bug" | "bulb" | "business" | "business-outline" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "cash-outline" | "chatbubble" | "checkmark" | "checkmark-circle" | "chevron-back" | "chevron-down" | "chevron-forward" | "chevron-up" | "close" | "close-circle" | "cloud-offline-outline" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "color-palette" | "construct-outline" | "copy-outline" | "cube-outline" | "desktop" | "document" | "document-outline" | "document-text" | "documents" | "download" | "download-outline" | "earth" | "ellipse-outline" | "ellipsis-horizontal" | "eye" | "eye-off-outline" | "filter-outline" | "flame" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "footsteps" | "gift" | "gift-outline" | "globe-outline" | "grid" | "hand-left" | "hardware-chip-outline" | "headset" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "hourglass-outline" | "image-outline" | "images" | "images-outline" | "infinite" | "information" | "information-circle" | "information-outline" | "key" | "leaf" | "library" | "link-outline" | "list" | "location" | "location-outline" | "lock-closed" | "log-out-outline" | "musical-notes" | "navigate" | "open" | "pencil-outline" | "people" | "people-outline" | "play" | "pricetag-outline" | "pulse" | "qr-code-outline" | "radio" | "receipt-outline" | "refresh-outline" | "remove" | "repeat-outline" | "resize" | "rocket" | "scale" | "school-outline" | "search-outline" | "send" | "server" | "settings" | "share-outline" | "shield" | "shield-checkmark" | "shield-checkmark-outline" | "star" | "swap-horizontal-outline" | "swap-vertical-outline" | "sync" | "time-outline" | "trash" | "trash-outline" | "trophy" | "trophy-outline" | "videocam" | "volume-off" | "wallet" | "wallet-outline" | "warning" | "watch" | "wifi", "OxyServicesIonicons">;
|
|
2
3
|
export default Ionicons;
|
|
3
4
|
//# sourceMappingURL=Ionicons.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Ionicons.d.ts","sourceRoot":"","sources":["../../../../../src/ui/icons/Ionicons.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Ionicons.d.ts","sourceRoot":"","sources":["../../../../../src/ui/icons/Ionicons.ts"],"names":[],"mappings":"AAKA,yFAAyF;AACzF,QAAA,MAAM,QAAQ,suEAOb,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/** Glyphs are decorative: hidden from assistive technology (see `decorativeIconSet`). */
|
|
2
|
+
declare const MaterialCommunityIcons: import("@expo/vector-icons/build/createIconSet").Icon<"function" | "label" | "link" | "ellipse" | "image" | "switch" | "text" | "sort" | "at" | "alert" | "alert-circle" | "alert-circle-outline" | "apps" | "archive" | "arrow-down" | "arrow-up" | "bookmark" | "briefcase" | "briefcase-outline" | "bug" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "chevron-down" | "chevron-up" | "close" | "close-circle" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "cube-outline" | "download" | "download-outline" | "earth" | "ellipse-outline" | "eye" | "eye-off-outline" | "filter-outline" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "gift" | "gift-outline" | "grid" | "headset" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "image-outline" | "information" | "information-outline" | "key" | "leaf" | "library" | "pencil-outline" | "play" | "pulse" | "radio" | "receipt-outline" | "resize" | "rocket" | "scale" | "school-outline" | "send" | "server" | "share-outline" | "shield" | "star" | "sync" | "trophy" | "trophy-outline" | "volume-off" | "wallet" | "wallet-outline" | "watch" | "wifi" | "solid" | "account" | "account-circle" | "account-cog-outline" | "account-eye-outline" | "account-group" | "account-multiple" | "account-multiple-outline" | "account-outline" | "account-plus" | "account-search" | "account-switch" | "android" | "archive-arrow-down" | "arrow-left" | "bell" | "bell-outline" | "bio" | "bullhorn" | "cake-variant" | "cake-variant-outline" | "cancel" | "cellphone" | "cellphone-basic" | "cellphone-message" | "cellphone-screenshot" | "chart-line" | "chat" | "check-all" | "check-circle" | "chevron-right" | "chip" | "circle-outline" | "clock" | "clock-outline" | "code-tags" | "contain" | "content-copy" | "controller" | "cookie" | "credit-card" | "database-outline" | "delete" | "delete-outline" | "devices" | "dots-horizontal" | "email" | "email-outline" | "file" | "file-document" | "file-document-edit-outline" | "file-document-outline" | "file-excel-outline" | "file-outline" | "file-pdf-box" | "folder-image" | "folder-zip-outline" | "ghost" | "history" | "hub" | "image-multiple-outline" | "lightbulb-on" | "link-variant" | "loading" | "lock-outline" | "logout" | "magnify" | "map-marker" | "map-marker-multiple" | "map-marker-outline" | "message" | "message-outline" | "message-text" | "minus" | "minus-circle" | "motion" | "music-note-outline" | "network" | "null" | "numeric" | "opacity" | "open-in-app" | "openid" | "palette" | "palette-outline" | "pan" | "phone" | "phone-outline" | "plus" | "plus-circle" | "post" | "presentation" | "react" | "read" | "security" | "shield-account-outline" | "shield-check" | "shield-search" | "signature" | "sort-alphabetical-variant" | "sort-numeric-variant" | "store" | "tag" | "tag-outline" | "text-box-outline" | "theme-light-dark" | "transfer" | "translate" | "trash-can-outline" | "tray-arrow-down" | "update" | "video-outline" | "web" | "wifi-off" | "wrap" | "wrench" | "xml", "OxyServicesMaterialCommunityIcons">;
|
|
2
3
|
export default MaterialCommunityIcons;
|
|
3
4
|
//# sourceMappingURL=MaterialCommunityIcons.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MaterialCommunityIcons.d.ts","sourceRoot":"","sources":["../../../../../src/ui/icons/MaterialCommunityIcons.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MaterialCommunityIcons.d.ts","sourceRoot":"","sources":["../../../../../src/ui/icons/MaterialCommunityIcons.ts"],"names":[],"mappings":"AAKA,yFAAyF;AACzF,QAAA,MAAM,sBAAsB,ojGAO3B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ComponentType } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Wrap an icon-font family so every glyph it renders is hidden from assistive
|
|
4
|
+
* technology.
|
|
5
|
+
*
|
|
6
|
+
* A vector-icons glyph is a `Text` node whose content is a private-use code
|
|
7
|
+
* point. A screen reader reads it like any other text: Android QA of the account
|
|
8
|
+
* menu found `"\u{F0140}"` announced next to "Switch account" (OxyHQ/oxy#1375),
|
|
9
|
+
* and on Android the code point is folded into the label of the pressable around
|
|
10
|
+
* it, because RN builds an unlabelled accessible view's description from its
|
|
11
|
+
* children's text.
|
|
12
|
+
*
|
|
13
|
+
* Every glyph in this SDK is decorative — the control or row around it carries
|
|
14
|
+
* the words — so it is hidden here, once, rather than at each call site where
|
|
15
|
+
* the next icon would forget it. `aria-hidden` is
|
|
16
|
+
* `importantForAccessibility="no-hide-descendants"` on Android,
|
|
17
|
+
* `accessibilityElementsHidden` on iOS and `aria-hidden` on web. It is applied
|
|
18
|
+
* AFTER the caller's props so it cannot be switched back on; a glyph that needs
|
|
19
|
+
* a name belongs inside a control that has one. (Mention's own wrapper,
|
|
20
|
+
* `components/common/Ionicons.tsx`, is the same rule.)
|
|
21
|
+
*
|
|
22
|
+
* The family's statics (`glyphMap`, `getImageSource`, `loadFont`, …) are carried
|
|
23
|
+
* over, so the wrapper is a drop-in for the family itself.
|
|
24
|
+
*/
|
|
25
|
+
export declare function decorativeIconSet<T extends ComponentType<any>>(IconSet: T, displayName: string): T;
|
|
26
|
+
//# sourceMappingURL=decorativeIconSet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorativeIconSet.d.ts","sourceRoot":"","sources":["../../../../../src/ui/icons/decorativeIconSet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAK/E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,CAAC,CAWlG"}
|
|
@@ -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;
|
|
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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface TokenLossRecoveryDeps {
|
|
2
|
+
/** Re-mint once through the installed refresh handler; the token, or null. */
|
|
3
|
+
remint: () => Promise<string | null>;
|
|
4
|
+
/** Whether the durable auth store still holds `deviceId` + `deviceSecret`. */
|
|
5
|
+
hasDeviceCredential: () => Promise<boolean>;
|
|
6
|
+
/**
|
|
7
|
+
* Whether a key-based lane could still re-establish the session without a
|
|
8
|
+
* device secret. Consulted only once the credential is gone.
|
|
9
|
+
*/
|
|
10
|
+
hasKeyedRecovery: () => Promise<boolean>;
|
|
11
|
+
/** Whether the UI still shows a signed-in user. */
|
|
12
|
+
isSignedIn: () => boolean;
|
|
13
|
+
/** Whether a bearer is planted right now. */
|
|
14
|
+
hasToken: () => boolean;
|
|
15
|
+
/** Tear down the local session (the provider's `clearSessionState`). */
|
|
16
|
+
signOutLocally: () => Promise<void>;
|
|
17
|
+
/** Timer seam for tests. */
|
|
18
|
+
sleep?: (ms: number) => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface TokenLossRecovery {
|
|
21
|
+
/** Start recovering, unless a recovery is already running. */
|
|
22
|
+
start(): void;
|
|
23
|
+
/** Abandon any running recovery (provider unmount). */
|
|
24
|
+
dispose(): void;
|
|
25
|
+
/** Whether a recovery is running. */
|
|
26
|
+
isRecovering(): boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Delays between re-mint attempts while the device credential is intact. Starts
|
|
30
|
+
* near the post-failure refresh cooldowns and settles at one attempt a minute,
|
|
31
|
+
* which also respects the mint's own 60s rate-limit cooldown.
|
|
32
|
+
*/
|
|
33
|
+
export declare const TOKEN_RECOVERY_BACKOFF_MS: readonly number[];
|
|
34
|
+
/** Attempts through a key-based lane once the device credential is gone. */
|
|
35
|
+
export declare const MAX_KEYED_RECOVERY_ATTEMPTS = 3;
|
|
36
|
+
export declare function createTokenLossRecovery(deps: TokenLossRecoveryDeps): TokenLossRecovery;
|
|
37
|
+
//# sourceMappingURL=tokenLossRecovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenLossRecovery.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/tokenLossRecovery.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,qBAAqB;IACpC,8EAA8E;IAC9E,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACrC,8EAA8E;IAC9E,mBAAmB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C;;;OAGG;IACH,gBAAgB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,mDAAmD;IACnD,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,wEAAwE;IACxE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,4BAA4B;IAC5B,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,KAAK,IAAI,IAAI,CAAC;IACd,uDAAuD;IACvD,OAAO,IAAI,IAAI,CAAC;IAChB,qCAAqC;IACrC,YAAY,IAAI,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAAmD,CAAC;AAE3G,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAS7C,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,qBAAqB,GAAG,iBAAiB,CAoEtF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type DeviceLinkedSession } from '@oxy.so/core';
|
|
2
|
+
type Translate = (key: string, vars?: Record<string, string | number>) => string;
|
|
3
|
+
/**
|
|
4
|
+
* The title of one row in "Sessions & devices".
|
|
5
|
+
*
|
|
6
|
+
* `GET /session/device/sessions/:sessionId` lists the sessions that share THIS
|
|
7
|
+
* physical device — one per account signed in here — and sends no
|
|
8
|
+
* `deviceName` at all, although the model declared one. The row interpolated
|
|
9
|
+
* it unconditionally and Android QA read "undefined (This device)"
|
|
10
|
+
* (OxyHQ/oxy#1375 item 11).
|
|
11
|
+
*
|
|
12
|
+
* A name the server does send is used; otherwise the current session is "This
|
|
13
|
+
* device" and any other is named by the account it belongs to, which the
|
|
14
|
+
* response does carry. Never the string "undefined".
|
|
15
|
+
*/
|
|
16
|
+
export declare function deviceSessionTitle(session: Pick<DeviceLinkedSession, 'deviceName' | 'isCurrent' | 'user'>, t: Translate): string;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=deviceSessionTitle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deviceSessionTitle.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/deviceSessionTitle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEjF,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAC;AAEjF;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,YAAY,GAAG,WAAW,GAAG,MAAM,CAAC,EACvE,CAAC,EAAE,SAAS,GACX,MAAM,CAQR"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/services",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
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.4.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.9.1",
|
|
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",
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
"@types/jest": "^30.0.0",
|
|
180
180
|
"@types/node": "^20.19.43",
|
|
181
181
|
"@types/react": "*",
|
|
182
|
-
"@oxy.so/bloom": "^4.
|
|
182
|
+
"@oxy.so/bloom": "^4.21.1",
|
|
183
183
|
"copyfiles": "^2.4.1",
|
|
184
184
|
"expo-constants": "~57.0.5",
|
|
185
185
|
"expo-file-system": "~57.0.1",
|
|
@@ -215,8 +215,8 @@
|
|
|
215
215
|
"//@oxy.so/bloom": "RE-RAISE THIS FLOOR ON EVERY BREAKING BLOOM RELEASE, in the same commit as the root package.json catalog entry and the code that requires it. The breaking position is the MAJOR, so `^3.0.1` means `>=3.0.1 <4.0.0`: 3.x minors are admitted on semver's additive promise, and Bloom 4.0.0 is refused until someone raises this deliberately. The CAP is the part that is load-bearing. An earlier revision of this key recorded the opposite trade -- an uncapped `>=` floor, accepted so that intentionally-breaking Bloom 0.x minors would not be blocked -- and that trade is closed by evidence rather than by preference: an unbounded range is exactly what let `bun install` pair this package with Bloom 1.0.0 and emit no warning at all, and 1.0.0 broke it four ways (a deleted provider, a renamed confirm option, a renamed Chip fill, and two REMOVED SUBPATHS that fail at bundle time, before any typecheck runs). A floor is a claim about what was measured; a cap is the claim that nothing beyond it was. RAISED TO 3.0.1 from `^2.1.0`. Bloom 3.0.0 breaks exactly two things -- `PageHeader` defaults to `presentation=\"floating\"` and stops hiding the title under `transparent`, and `Field disabled` now reaches the control that reads the field context -- and THIS PACKAGE IMPORTS NEITHER: it renders no `PageHeader` and no `Field`, and 3.0 removes no subpath (unlike 2.0.0, which is why that floor cost code changes and this one does not). Its six `transparent` occurrences are CSS colour values, not the PageHeader prop. The cap had to move rather than widen to a union, because leaving `^2.1.0` in place is what made `bun install` resolve a SECOND, nested Bloom 2.12.4 under this package for any app on Bloom 3 -- two copies of a library whose 3.0 contracts (`control-surface`, `TopEdgeProvider`, `ScrollOffsetProvider`) are React contexts, so a provider from one copy cannot be read by a consumer from the other. Mention's `oxyRuntimeCompatibility` test is what caught it; nothing in a typecheck can. RAISED AGAIN TO 3.2.0 from `^3.0.1`, for the same class of reason the 2.x note records, only in the other direction: 3.2.0 ADDS the per-glyph icon subpaths (`@oxy.so/bloom/icons/RiAtLine`) that this package's screens now import instead of the `@oxy.so/bloom/icons` barrel, and a missing subpath fails at bundle time, in the consumer's Metro, long after this package has typechecked and shipped. `^3.0.1` would have admitted Bloom 3.0.1 and 3.1.0, neither of which has those subpaths, so the floor is the only thing standing between a consumer and a red build. Nothing else about 3.1.0 or 3.2.0 is required here; the cap stays at the major. RAISED TO ^4.2.0: FollowButton now delegates its visuals to Bloom media-header FollowButton with loading, textStyle and large size; the new shared UI contracts require one Bloom4 context across Services and its consumer.",
|
|
216
216
|
"peerDependencies": {
|
|
217
217
|
"@expo/vector-icons": "^15.0.3",
|
|
218
|
-
"@oxy.so/bloom": "^4.
|
|
219
|
-
"@oxy.so/core": "^1.
|
|
218
|
+
"@oxy.so/bloom": "^4.21.1",
|
|
219
|
+
"@oxy.so/core": "^1.9.1",
|
|
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,22 +74,16 @@ const OxyAccountDialogScreen: React.FC<BaseScreenProps> = ({ canGoBack }) => {
|
|
|
74
74
|
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
75
75
|
|
|
76
76
|
const { view } = snapshot;
|
|
77
|
-
// "Add another account"
|
|
78
|
-
//
|
|
77
|
+
// Names the entry view "Add another account" rather than "Sign in" when
|
|
78
|
+
// somebody is already signed in on this device.
|
|
79
79
|
const hasSignedInAccounts = (snapshot.directory?.principals.length ?? 0) > 0;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
(currentView === 'qr' || currentView === 'add') &&
|
|
88
|
-
(signIn.phase === 'starting' || signIn.phase === 'waiting')
|
|
89
|
-
) {
|
|
90
|
-
controller.cancelSignIn();
|
|
91
|
-
}
|
|
92
|
-
controller.setView('accounts');
|
|
80
|
+
// Where back leads is the CONTROLLER's answer (`snapshot.backView`), never a
|
|
81
|
+
// table here: a host-side "back = accounts" assumed a signed-in origin and
|
|
82
|
+
// opened the account menu for nobody on a signed-out Back from sign-up.
|
|
83
|
+
const showBack = snapshot.backView !== null;
|
|
84
|
+
const goBack = useCallback(() => {
|
|
85
|
+
// Withdraws an active request itself before leaving it.
|
|
86
|
+
controller?.back();
|
|
93
87
|
}, [controller]);
|
|
94
88
|
// An ENTRY view (accounts / signin — no in-dialog back of its own) that is
|
|
95
89
|
// MORPHED into a host surface (ManageAccount → switcher) has a frame beneath it,
|
|
@@ -109,7 +103,7 @@ const OxyAccountDialogScreen: React.FC<BaseScreenProps> = ({ canGoBack }) => {
|
|
|
109
103
|
titleContent: copy ? undefined : NAV_LOGO,
|
|
110
104
|
title: copy?.title,
|
|
111
105
|
subtitle: copy?.subtitle ?? undefined,
|
|
112
|
-
onBack: showBack ?
|
|
106
|
+
onBack: showBack ? goBack : backsToHost ? closeAccountDialog : undefined,
|
|
113
107
|
});
|
|
114
108
|
|
|
115
109
|
if (!controller) {
|
|
@@ -370,7 +370,13 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({ onComplete }) => {
|
|
|
370
370
|
// Real storage usage for the account menu's "Oxy storage" block. Disabled
|
|
371
371
|
// (no fetch) until a private-API session exists, so it is inert on the
|
|
372
372
|
// sign-in/request/sign-up views; when present the block shows live used/total.
|
|
373
|
-
|
|
373
|
+
// The account MENU describes the signed-in account — its hero, its storage,
|
|
374
|
+
// its sign-out. The controller already refuses `accounts` without a session;
|
|
375
|
+
// this is the render-side half of the same rule, so no controller state (or
|
|
376
|
+
// an older core) can ever put "Sign out" in front of someone who is not
|
|
377
|
+
// signed in. Without an account the sign-in entry renders in its place.
|
|
378
|
+
const showsAccountMenu = view === 'accounts' && user !== null && user !== undefined;
|
|
379
|
+
const storageQuery = useAccountStorageUsage({ enabled: showsAccountMenu });
|
|
374
380
|
const storage = useMemo<AccountStorageModel | null>(
|
|
375
381
|
() =>
|
|
376
382
|
storageQuery.data
|
|
@@ -419,7 +425,7 @@ const OxyAuthChooser: React.FC<OxyAuthChooserProps> = ({ onComplete }) => {
|
|
|
419
425
|
return null;
|
|
420
426
|
}
|
|
421
427
|
|
|
422
|
-
if (
|
|
428
|
+
if (showsAccountMenu) {
|
|
423
429
|
return (
|
|
424
430
|
<AccountsMenuView
|
|
425
431
|
snapshot={snapshot}
|
|
@@ -86,6 +86,15 @@ export interface OxyConsentScreenProps {
|
|
|
86
86
|
onDeny: () => void;
|
|
87
87
|
/** True while a decision is in flight; disables both actions and spins Allow. */
|
|
88
88
|
busy?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Disables Allow WITHOUT a spinner, leaving Deny live. For a host that owns an
|
|
91
|
+
* extra gate the person must clear first — e.g. the device approval page's
|
|
92
|
+
* mandatory "I started this sign-in myself" acknowledgement, which is the
|
|
93
|
+
* login-CSRF control for a request that arrived as a bare code and cannot be
|
|
94
|
+
* skipped. `busy` would be wrong there: nothing is in flight, and a spinning
|
|
95
|
+
* button reads as "working" rather than "not yet".
|
|
96
|
+
*/
|
|
97
|
+
allowDisabled?: boolean;
|
|
89
98
|
/** A blocking error message for the request, when present. */
|
|
90
99
|
error?: string | null;
|
|
91
100
|
}
|
|
@@ -213,6 +222,7 @@ export function OxyConsentScreen({
|
|
|
213
222
|
onAllow,
|
|
214
223
|
onDeny,
|
|
215
224
|
busy = false,
|
|
225
|
+
allowDisabled = false,
|
|
216
226
|
error = null,
|
|
217
227
|
}: OxyConsentScreenProps) {
|
|
218
228
|
const theme = useTheme();
|
|
@@ -431,7 +441,7 @@ export function OxyConsentScreen({
|
|
|
431
441
|
testID="consent-allow"
|
|
432
442
|
appearance="solid" tone="accent"
|
|
433
443
|
onPress={handleAllow}
|
|
434
|
-
disabled={busy}
|
|
444
|
+
disabled={busy || allowDisabled}
|
|
435
445
|
loading={busy}
|
|
436
446
|
style={styles.actionButton}
|
|
437
447
|
>
|
|
@@ -150,7 +150,7 @@ export const OxySignInRequestSurface: React.FC<OxySignInRequestSurfaceProps> = (
|
|
|
150
150
|
return (
|
|
151
151
|
<View style={styles.centeredBlock}>
|
|
152
152
|
{onRetry ? (
|
|
153
|
-
<Button appearance="solid" tone="accent" onPress={onRetry} style={styles.primaryButton}>
|
|
153
|
+
<Button appearance="solid" tone="accent" size="lg" onPress={onRetry} style={styles.primaryButton}>
|
|
154
154
|
{t('common.actions.tryAgain')}
|
|
155
155
|
</Button>
|
|
156
156
|
) : null}
|
|
@@ -28,6 +28,7 @@ function renderScreen(props: Partial<OxyConsentScreenProps> = {}) {
|
|
|
28
28
|
onAllow={props.onAllow ?? onAllow}
|
|
29
29
|
onDeny={props.onDeny ?? onDeny}
|
|
30
30
|
busy={props.busy}
|
|
31
|
+
allowDisabled={props.allowDisabled}
|
|
31
32
|
error={props.error}
|
|
32
33
|
/>,
|
|
33
34
|
);
|
|
@@ -132,6 +133,21 @@ describe('OxyConsentScreen', () => {
|
|
|
132
133
|
expect(onDeny).not.toHaveBeenCalled();
|
|
133
134
|
});
|
|
134
135
|
|
|
136
|
+
it('allowDisabled blocks only Allow and leaves Deny usable', () => {
|
|
137
|
+
const { getByTestId, onAllow, onDeny } = renderScreen({ allowDisabled: true });
|
|
138
|
+
|
|
139
|
+
const allow = getByTestId('consent-allow') as HTMLButtonElement;
|
|
140
|
+
const deny = getByTestId('consent-deny') as HTMLButtonElement;
|
|
141
|
+
expect(allow.disabled).toBe(true);
|
|
142
|
+
expect(deny.disabled).toBe(false);
|
|
143
|
+
|
|
144
|
+
fireEvent.click(allow);
|
|
145
|
+
expect(onAllow).not.toHaveBeenCalled();
|
|
146
|
+
// Declining must stay possible while the host's own gate is uncleared.
|
|
147
|
+
fireEvent.click(deny);
|
|
148
|
+
expect(onDeny).toHaveBeenCalledTimes(1);
|
|
149
|
+
});
|
|
150
|
+
|
|
135
151
|
it('renders the authorizing account using the display name when present', () => {
|
|
136
152
|
const { getByTestId } = renderScreen({
|
|
137
153
|
user: { displayName: ' Alice A ', handle: 'alice', avatarUri: 'file-1' },
|
|
@@ -108,6 +108,9 @@ const SignInEntryView: React.FC<SignInEntryViewProps> = ({
|
|
|
108
108
|
|
|
109
109
|
<Button
|
|
110
110
|
appearance="solid" tone="accent"
|
|
111
|
+
// `lg` (44dp): the surface's ONE primary action is a full-width CTA, and
|
|
112
|
+
// the default `md` (36dp) is under the touch-target minimum on Android.
|
|
113
|
+
size="lg"
|
|
111
114
|
onPress={web ? alternatives.onSignInWithPasskey : onContinueWithOxy}
|
|
112
115
|
style={styles.primaryButton}
|
|
113
116
|
testID="continue-with-oxy"
|
|
@@ -49,7 +49,7 @@ const SignUpView: React.FC<SignUpViewProps> = ({
|
|
|
49
49
|
return (
|
|
50
50
|
<View style={styles.centeredBlock}>
|
|
51
51
|
<Button
|
|
52
|
-
appearance="solid" tone="accent"
|
|
52
|
+
appearance="solid" tone="accent" size="lg"
|
|
53
53
|
onPress={commonsInstalled ? onCreateIdentityInCommons : onGetCommons}
|
|
54
54
|
style={styles.primaryButton}
|
|
55
55
|
>
|
|
@@ -65,7 +65,7 @@ const SignUpView: React.FC<SignUpViewProps> = ({
|
|
|
65
65
|
<Text style={[styles.mutedText, { color: theme.colors.textSecondary }]}>
|
|
66
66
|
{t('accountSwitcher.passkeyHint')}
|
|
67
67
|
</Text>
|
|
68
|
-
<Button appearance="solid" tone="accent" onPress={onOpenHub} style={styles.primaryButton} testID="signup-open-identity">
|
|
68
|
+
<Button appearance="solid" tone="accent" size="lg" onPress={onOpenHub} style={styles.primaryButton} testID="signup-open-identity">
|
|
69
69
|
{t('signup.createAccount')}
|
|
70
70
|
</Button>
|
|
71
71
|
<SubtleLink label={t('signup.backToSignInLink')} theme={theme} onPress={onBackToSignIn} />
|
|
@@ -72,11 +72,14 @@ const TroubleDisclosure: React.FC<TroubleDisclosureProps> = ({ actions, revealed
|
|
|
72
72
|
type="single"
|
|
73
73
|
value={openItem}
|
|
74
74
|
onValueChange={setOpenItem}
|
|
75
|
-
style={styles.
|
|
75
|
+
style={styles.troubleDisclosure}
|
|
76
76
|
testID="trouble-disclosure"
|
|
77
77
|
>
|
|
78
78
|
<AccordionItem value={TROUBLE_ITEM}>
|
|
79
|
-
<AccordionTrigger
|
|
79
|
+
<AccordionTrigger
|
|
80
|
+
style={styles.troubleTrigger}
|
|
81
|
+
textStyle={[styles.linkText, { color: theme.colors.textSecondary }]}
|
|
82
|
+
>
|
|
80
83
|
{t('accountSwitcher.havingTrouble')}
|
|
81
84
|
</AccordionTrigger>
|
|
82
85
|
<AccordionContent>{links}</AccordionContent>
|
|
@@ -81,7 +81,7 @@ export const GetCommonsPrompt: React.FC<{
|
|
|
81
81
|
{t('accountSwitcher.commonsNotInstalled')}
|
|
82
82
|
</Text>
|
|
83
83
|
<Button
|
|
84
|
-
appearance="solid" tone="accent"
|
|
84
|
+
appearance="solid" tone="accent" size="lg"
|
|
85
85
|
onPress={onGetCommons}
|
|
86
86
|
style={styles.primaryButton}
|
|
87
87
|
testID="get-commons-button"
|
|
@@ -205,9 +205,19 @@ export const authChooserStyles = StyleSheet.create({
|
|
|
205
205
|
width: '100%',
|
|
206
206
|
alignItems: 'center',
|
|
207
207
|
},
|
|
208
|
-
/**
|
|
208
|
+
/**
|
|
209
|
+
* The disclosure spans the content width, so the alternatives it reveals are
|
|
210
|
+
* centred across the sheet rather than inside the trigger's own width. It
|
|
211
|
+
* used to be `alignSelf: 'center'` — shrink-wrapped — and on Android that
|
|
212
|
+
* both wrapped the trigger to "Having / trouble?" and squeezed the revealed
|
|
213
|
+
* links into the same narrow column (OxyHQ/oxy#1375).
|
|
214
|
+
*/
|
|
215
|
+
troubleDisclosure: {
|
|
216
|
+
width: '100%',
|
|
217
|
+
marginTop: 12,
|
|
218
|
+
},
|
|
219
|
+
/** The trigger itself stays a compact, centred affordance like the links. */
|
|
209
220
|
troubleTrigger: {
|
|
210
221
|
alignSelf: 'center',
|
|
211
|
-
marginTop: 12,
|
|
212
222
|
},
|
|
213
223
|
});
|
|
@@ -45,7 +45,8 @@ import {
|
|
|
45
45
|
import { isWebBrowser } from '../utils/isWebBrowser';
|
|
46
46
|
import { resolveDeliveryPlatform } from '../utils/deliveryPlatform';
|
|
47
47
|
import { runProviderColdBoot } from '../boot/runProviderColdBoot';
|
|
48
|
-
import { loadPersistedDeviceCredential } from '../utils/deviceCredential';
|
|
48
|
+
import { hasPersistedDeviceCredential, loadPersistedDeviceCredential } from '../utils/deviceCredential';
|
|
49
|
+
import { createTokenLossRecovery } from '../session/tokenLossRecovery';
|
|
49
50
|
import { bindAuthStoreToRuntime } from '../stores/authStore';
|
|
50
51
|
import { useLanguageManagement } from '../hooks/useLanguageManagement';
|
|
51
52
|
import { useSessionManagement } from '../hooks/useSessionManagement';
|
|
@@ -543,14 +544,52 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
543
544
|
});
|
|
544
545
|
|
|
545
546
|
|
|
546
|
-
// Token-change side effects
|
|
547
|
-
//
|
|
548
|
-
//
|
|
549
|
-
//
|
|
550
|
-
//
|
|
551
|
-
//
|
|
547
|
+
// Token-change side effects. HttpService clears the bearer and emits `null`
|
|
548
|
+
// when a request draws a 401 its refresh could not answer — which happens on a
|
|
549
|
+
// transient failure (network, a cooling-down or rate-limited mint) as well as
|
|
550
|
+
// on a real revocation. A signed-in user is therefore NOT signed out on the
|
|
551
|
+
// spot: `tokenLossRecovery` keeps the session while the durable device
|
|
552
|
+
// credential survives and re-mints it, the way a relaunch would, and signs out
|
|
553
|
+
// only once the refresh handler has dropped that credential on a definitive
|
|
554
|
+
// server verdict (OxyHQ/Mention#1140). Until a token is back, `tokenReady` is
|
|
555
|
+
// false, so private queries wait instead of 401-ing.
|
|
552
556
|
const clearingInvalidTokenRef = useRef(false);
|
|
553
557
|
useEffect(() => {
|
|
558
|
+
const signOutLocally = async (): Promise<void> => {
|
|
559
|
+
if (clearingInvalidTokenRef.current) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
clearingInvalidTokenRef.current = true;
|
|
563
|
+
try {
|
|
564
|
+
await clearSessionStateRef.current();
|
|
565
|
+
} catch (clearError) {
|
|
566
|
+
logger('Failed to clear invalidated auth session', clearError);
|
|
567
|
+
} finally {
|
|
568
|
+
clearingInvalidTokenRef.current = false;
|
|
569
|
+
if (runtime.getSnapshot().authResolved) {
|
|
570
|
+
runtime.setTokenReady(true);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const recovery = createTokenLossRecovery({
|
|
575
|
+
remint: () => oxyServices.httpService.refreshAccessToken('preflight'),
|
|
576
|
+
hasDeviceCredential: () => hasPersistedDeviceCredential(authStore),
|
|
577
|
+
hasKeyedRecovery: async () => {
|
|
578
|
+
if (Platform.OS === 'web') {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
try {
|
|
582
|
+
// The same keys the refresh handler's second arm signs with: the
|
|
583
|
+
// identity-bound client's own key, or the cross-app shared identity.
|
|
584
|
+
return identity ? await KeyManager.hasIdentity() : await KeyManager.hasSharedIdentity();
|
|
585
|
+
} catch {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
isSignedIn: () => runtime.getSnapshot().account !== null,
|
|
590
|
+
hasToken: () => Boolean(oxyServices.getAccessToken()),
|
|
591
|
+
signOutLocally,
|
|
592
|
+
});
|
|
554
593
|
const handleTokenChange = (accessToken: string | null) => {
|
|
555
594
|
runtime.setHasAccessToken(Boolean(accessToken));
|
|
556
595
|
if (accessToken) {
|
|
@@ -562,20 +601,7 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
562
601
|
}
|
|
563
602
|
if (runtime.getSnapshot().account !== null) {
|
|
564
603
|
runtime.setTokenReady(false);
|
|
565
|
-
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
clearingInvalidTokenRef.current = true;
|
|
569
|
-
clearSessionStateRef.current()
|
|
570
|
-
.catch((clearError) => {
|
|
571
|
-
logger('Failed to clear invalidated auth session', clearError);
|
|
572
|
-
})
|
|
573
|
-
.finally(() => {
|
|
574
|
-
clearingInvalidTokenRef.current = false;
|
|
575
|
-
if (runtime.getSnapshot().authResolved) {
|
|
576
|
-
runtime.setTokenReady(true);
|
|
577
|
-
}
|
|
578
|
-
});
|
|
604
|
+
recovery.start();
|
|
579
605
|
return;
|
|
580
606
|
}
|
|
581
607
|
if (runtime.getSnapshot().authResolved) {
|
|
@@ -583,8 +609,12 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
583
609
|
}
|
|
584
610
|
};
|
|
585
611
|
handleTokenChange(oxyServices.getAccessToken());
|
|
586
|
-
|
|
587
|
-
|
|
612
|
+
const unsubscribe = oxyServices.onTokensChanged(handleTokenChange);
|
|
613
|
+
return () => {
|
|
614
|
+
unsubscribe();
|
|
615
|
+
recovery.dispose();
|
|
616
|
+
};
|
|
617
|
+
}, [logger, oxyServices, runtime, sessionClient, authStore, identity]);
|
|
588
618
|
|
|
589
619
|
// Unified in-session refresh (SDK-owned; every RP inherits it). Installs the
|
|
590
620
|
// ONE core refresh handler (re-mint from the persisted zero-cookie device
|