@oxy.so/services 13.0.0 → 13.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/ui/components/ExpoImage.js +13 -0
- package/lib/commonjs/ui/components/ExpoImage.js.map +1 -0
- package/lib/commonjs/ui/components/ExpoImage.macos.js +80 -0
- package/lib/commonjs/ui/components/ExpoImage.macos.js.map +1 -0
- package/lib/commonjs/ui/components/KeyboardBoundary.macos.js +19 -0
- package/lib/commonjs/ui/components/KeyboardBoundary.macos.js.map +1 -0
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js +3 -3
- package/lib/commonjs/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +2 -2
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +17 -15
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountQueryPersistence.js +50 -15
- package/lib/commonjs/ui/hooks/accountQueryPersistence.js.map +1 -1
- package/lib/commonjs/ui/hooks/persistedQueryCache.js +10 -4
- package/lib/commonjs/ui/hooks/persistedQueryCache.js.map +1 -1
- package/lib/commonjs/ui/hooks/queryClient.js +9 -15
- package/lib/commonjs/ui/hooks/queryClient.js.map +1 -1
- package/lib/commonjs/ui/oauth/nativeAuthTransport.js +1 -1
- package/lib/commonjs/ui/oauth/nativeAuthTransport.js.map +1 -1
- package/lib/commonjs/ui/screens/AvatarCropScreen.js +2 -2
- package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +2 -2
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +4 -4
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyOneScreen.js +2 -2
- package/lib/commonjs/ui/screens/OxyOneScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +2 -2
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/commonjs/ui/utils/imageSize.macos.js +23 -0
- package/lib/commonjs/ui/utils/imageSize.macos.js.map +1 -0
- package/lib/module/ui/components/ExpoImage.js +14 -0
- package/lib/module/ui/components/ExpoImage.js.map +1 -0
- package/lib/module/ui/components/ExpoImage.macos.js +76 -0
- package/lib/module/ui/components/ExpoImage.macos.js.map +1 -0
- package/lib/module/ui/components/KeyboardBoundary.macos.js +14 -0
- package/lib/module/ui/components/KeyboardBoundary.macos.js.map +1 -0
- package/lib/module/ui/components/fileManagement/FileViewer.js +1 -1
- package/lib/module/ui/components/fileManagement/FileViewer.js.map +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +17 -14
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/accountQueryPersistence.js +50 -15
- package/lib/module/ui/hooks/accountQueryPersistence.js.map +1 -1
- package/lib/module/ui/hooks/persistedQueryCache.js +10 -4
- package/lib/module/ui/hooks/persistedQueryCache.js.map +1 -1
- package/lib/module/ui/hooks/queryClient.js +9 -15
- package/lib/module/ui/hooks/queryClient.js.map +1 -1
- package/lib/module/ui/oauth/nativeAuthTransport.js +1 -1
- package/lib/module/ui/oauth/nativeAuthTransport.js.map +1 -1
- package/lib/module/ui/screens/AvatarCropScreen.js +1 -1
- package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/OxyOneScreen.js +1 -1
- package/lib/module/ui/screens/OxyOneScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/utils/imageSize.macos.js +18 -0
- package/lib/module/ui/utils/imageSize.macos.js.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ExpoImage.d.ts +12 -0
- package/lib/typescript/commonjs/ui/components/ExpoImage.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ExpoImage.macos.d.ts +16 -0
- package/lib/typescript/commonjs/ui/components/ExpoImage.macos.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/KeyboardBoundary.macos.d.ts +10 -0
- package/lib/typescript/commonjs/ui/components/KeyboardBoundary.macos.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts +22 -4
- package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts +5 -3
- package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts +5 -4
- package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/icons/Ionicons.d.ts +1 -1
- package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/nativeAuthTransport.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +2 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/imageSize.macos.d.ts +6 -0
- package/lib/typescript/commonjs/ui/utils/imageSize.macos.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ExpoImage.d.ts +12 -0
- package/lib/typescript/module/ui/components/ExpoImage.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ExpoImage.macos.d.ts +16 -0
- package/lib/typescript/module/ui/components/ExpoImage.macos.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/KeyboardBoundary.macos.d.ts +10 -0
- package/lib/typescript/module/ui/components/KeyboardBoundary.macos.d.ts.map +1 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts +22 -4
- package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts +5 -3
- package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queryClient.d.ts +5 -4
- package/lib/typescript/module/ui/hooks/queryClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/icons/Ionicons.d.ts +1 -1
- package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -1
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/nativeAuthTransport.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +2 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/imageSize.macos.d.ts +6 -0
- package/lib/typescript/module/ui/utils/imageSize.macos.d.ts.map +1 -0
- package/package.json +33 -32
- package/src/index.ts +1 -1
- package/src/ui/components/ExpoImage.macos.tsx +59 -0
- package/src/ui/components/ExpoImage.tsx +11 -0
- package/src/ui/components/KeyboardBoundary.macos.tsx +8 -0
- package/src/ui/components/fileManagement/FileViewer.tsx +1 -1
- package/src/ui/components/fileManagement/UploadPreview.tsx +1 -1
- package/src/ui/context/OxyContext.tsx +27 -19
- package/src/ui/hooks/accountQueryPersistence.ts +61 -16
- package/src/ui/hooks/persistedQueryCache.ts +13 -5
- package/src/ui/hooks/queryClient.ts +12 -18
- package/src/ui/index.ts +1 -1
- package/src/ui/oauth/nativeAuthTransport.ts +5 -1
- package/src/ui/screens/AvatarCropScreen.tsx +1 -1
- package/src/ui/screens/EditProfileFieldScreen.tsx +1 -1
- package/src/ui/screens/FileManagementScreen.tsx +1 -1
- package/src/ui/screens/OxyOneScreen.tsx +1 -1
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +1 -1
- package/src/ui/types/navigation.ts +2 -0
- package/src/ui/utils/imageSize.macos.ts +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/services",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.2.0",
|
|
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",
|
|
@@ -140,6 +140,7 @@
|
|
|
140
140
|
"verify:package": "node scripts/verify-package.mjs",
|
|
141
141
|
"generate:icon-fonts": "node scripts/generate-icon-font-subsets.mjs",
|
|
142
142
|
"check:icon-fonts": "node scripts/check-icon-font-subsets.mjs",
|
|
143
|
+
"check:compat-expo-54": "bun compat/expo-54/check.mjs",
|
|
143
144
|
"build:js": "bun x react-native-builder-bob build --target commonjs && bun x react-native-builder-bob build --target module && bun run copy-assets",
|
|
144
145
|
"test": "jest --passWithNoTests",
|
|
145
146
|
"test:watch": "jest --watch",
|
|
@@ -159,11 +160,10 @@
|
|
|
159
160
|
"@commitlint/config-conventional": "^21.2.0",
|
|
160
161
|
"@expo/app-integrity": "~58.0.2",
|
|
161
162
|
"@expo/vector-icons": "^15.0.3",
|
|
162
|
-
"@oxy.so/core": "7.
|
|
163
|
+
"@oxy.so/core": "7.1.0",
|
|
163
164
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
164
165
|
"@react-native-community/netinfo": "^12.0.1",
|
|
165
166
|
"@react-native/babel-preset": "^0.88.0",
|
|
166
|
-
"@release-it/conventional-changelog": "^12.0.2",
|
|
167
167
|
"@tanstack/query-async-storage-persister": "^5.101",
|
|
168
168
|
"@tanstack/query-sync-storage-persister": "^5.101",
|
|
169
169
|
"@tanstack/react-query": "^5.101",
|
|
@@ -176,26 +176,26 @@
|
|
|
176
176
|
"@types/react": "*",
|
|
177
177
|
"@oxy.so/bloom": "15.18.0",
|
|
178
178
|
"copyfiles": "^2.4.1",
|
|
179
|
-
"expo-clipboard": "~58.0.
|
|
180
|
-
"expo-constants": "~58.0.
|
|
181
|
-
"expo-file-system": "~58.0.
|
|
179
|
+
"expo-clipboard": "~58.0.4",
|
|
180
|
+
"expo-constants": "~58.0.13",
|
|
181
|
+
"expo-file-system": "~58.0.11",
|
|
182
182
|
"expo-haptics": "~58.0.7",
|
|
183
|
-
"expo-image": "~58.0.
|
|
184
|
-
"expo-image-manipulator": "~58.0.
|
|
183
|
+
"expo-image": "~58.0.17",
|
|
184
|
+
"expo-image-manipulator": "~58.0.15",
|
|
185
185
|
"expo-linear-gradient": "~58.0.3",
|
|
186
|
-
"expo-notifications": "~58.1.
|
|
187
|
-
"expo-modules-core": "~58.0.
|
|
186
|
+
"expo-notifications": "~58.1.6",
|
|
187
|
+
"expo-modules-core": "~58.0.26",
|
|
188
188
|
"expo-secure-store": "~58.0.2",
|
|
189
189
|
"expo-web-browser": "~58.0.6",
|
|
190
190
|
"husky": "^4.3.8",
|
|
191
191
|
"jest": "^30.5.1",
|
|
192
192
|
"lint-staged": "^15.2.4",
|
|
193
|
-
"nativewind": "5.0.0-
|
|
193
|
+
"nativewind": "5.0.0-rc.0",
|
|
194
194
|
"react": "19.3.0",
|
|
195
195
|
"react-dom": "19.3.0",
|
|
196
196
|
"react-native": "0.88.0",
|
|
197
197
|
"react-native-builder-bob": "^0.43.0",
|
|
198
|
-
"react-native-css": "
|
|
198
|
+
"react-native-css": "3.1.0-rc.0",
|
|
199
199
|
"react-native-gesture-handler": "~3.3.0",
|
|
200
200
|
"react-native-keyboard-controller": "1.22.7",
|
|
201
201
|
"react-native-qrcode-svg": "^6.3.0",
|
|
@@ -206,12 +206,13 @@
|
|
|
206
206
|
"ts-jest": "^29.4.12",
|
|
207
207
|
"typescript": "^5.9.2",
|
|
208
208
|
"zustand": "^5.0.14",
|
|
209
|
-
"expo-video": "~58.0.
|
|
209
|
+
"expo-video": "~58.0.12"
|
|
210
210
|
},
|
|
211
211
|
"//@oxy.so/bloom": "Bloom 11.1.0 adds the shared grouped sections, usage card, subscription plans, frame appearance and overlay navigation APIs used by this SDK. First-party Bloom peers remain open above this tested minimum.",
|
|
212
212
|
"//@oxy.so/core": "Core 6.16.0 supplies `requireServerVerdict` and `onHolderRejected`, with which the cold boot checks a session it painted from the cache; 6.13.0 supplies the `accounts.settings.*` strings (picture, categories) the account settings speak in every language; 6.12.0 supplies `fileManagement.contentLost` and the `lost` file status a lost file is listed with; 6.11.0 supplies the `follow.errors.*` strings the follow error codes are worded with; 6.4.0 the `follow.*` strings the follow buttons read for every verb and language; 6.1.0 the `oxy.auth.email.{startChange,change,remove}` and `oxy.auth.password.remove` the sign-in & security panels call. Keep one Core instance per app; raise this floor with the root catalogue when new runtime APIs are required.",
|
|
213
|
+
"//react-native": "React Native 0.81 to 0.88, React 19.1+ and Expo SDK 54 to 58. The floors are the Expo SDK 54 stack (react-native 0.81.6, react 19.1.4, react-native-web 0.21.4, expo 54.0.38 and the module versions its bundledNativeModules.json pins), which compat/expo-54 installs as an external consumer and checks on every CI run (`bun check.mjs`): every peer here admits that stack, services' published src typechecks against its types, and <OxyProvider> mounts and settles its cold boot on iOS (React Native 0.81), web (react-native-web 0.21) and react-native-macos 0.81 with the native modules macOS lacks absent. Astro, Oxy's browser, runs react-native-macos 0.81 (0.83 is its latest release), which is why the floor is not 0.85. The SDK 56 floors these replaced were the versions current when the ranges were first written, not measurements.",
|
|
213
214
|
"peerDependencies": {
|
|
214
|
-
"@expo/app-integrity": ">=
|
|
215
|
+
"@expo/app-integrity": ">=0.1.10 <59.0.0",
|
|
215
216
|
"@expo/vector-icons": "^15.0.3",
|
|
216
217
|
"@oxy.so/bloom": ">=11.1.0",
|
|
217
218
|
"@oxy.so/core": "^7.0.0",
|
|
@@ -223,31 +224,31 @@
|
|
|
223
224
|
"@tanstack/react-query-persist-client": "^5.101",
|
|
224
225
|
"@types/react": "<20.0.0",
|
|
225
226
|
"@types/react-native": "<1.0.0",
|
|
226
|
-
"expo": ">=
|
|
227
|
-
"expo-clipboard": ">=
|
|
228
|
-
"expo-constants": ">=
|
|
229
|
-
"expo-document-picker": ">=
|
|
230
|
-
"expo-file-system": ">=
|
|
231
|
-
"expo-haptics": ">=
|
|
227
|
+
"expo": ">=54.0.0 <59.0.0",
|
|
228
|
+
"expo-clipboard": ">=8.0.0 <59.0.0",
|
|
229
|
+
"expo-constants": ">=18.0.0 <59.0.0",
|
|
230
|
+
"expo-document-picker": ">=14.0.0 <59.0.0",
|
|
231
|
+
"expo-file-system": ">=19.0.0 <59.0.0",
|
|
232
|
+
"expo-haptics": ">=15.0.0 <59.0.0",
|
|
232
233
|
"expo-image": ">=2.0.0 <59.0.0",
|
|
233
|
-
"expo-image-manipulator": ">=
|
|
234
|
-
"expo-image-picker": ">=
|
|
235
|
-
"expo-linear-gradient": ">=
|
|
234
|
+
"expo-image-manipulator": ">=14.0.0 <59.0.0",
|
|
235
|
+
"expo-image-picker": ">=17.0.0 <59.0.0",
|
|
236
|
+
"expo-linear-gradient": ">=15.0.0 <59.0.0",
|
|
236
237
|
"expo-modules-core": "<59.0.0",
|
|
237
|
-
"expo-notifications": ">=
|
|
238
|
-
"expo-web-browser": ">=
|
|
238
|
+
"expo-notifications": ">=0.32.0 <59.0.0",
|
|
239
|
+
"expo-web-browser": ">=15.0.0 <59.0.0",
|
|
239
240
|
"nativewind": ">=5.0.0-preview.3 <6.0.0",
|
|
240
241
|
"react-native-css": "^3.0.6 || 3.1.0-rc.0",
|
|
241
242
|
"react": ">=18.0.0 <20.0.0",
|
|
242
|
-
"react-native": ">=0.
|
|
243
|
-
"react-native-gesture-handler": ">=2.
|
|
244
|
-
"react-native-keyboard-controller": ">=1.
|
|
243
|
+
"react-native": ">=0.81.0 <0.89.0",
|
|
244
|
+
"react-native-gesture-handler": ">=2.28.0 <4.0.0",
|
|
245
|
+
"react-native-keyboard-controller": ">=1.18.5 <2.0.0",
|
|
245
246
|
"react-native-qrcode-svg": "^6.3.0",
|
|
246
|
-
"react-native-reanimated": ">=4.
|
|
247
|
-
"react-native-safe-area-context": ">=5.
|
|
248
|
-
"react-native-svg": ">=15.
|
|
247
|
+
"react-native-reanimated": ">=4.1.0 <5.0.0",
|
|
248
|
+
"react-native-safe-area-context": ">=5.6.0 <6.0.0",
|
|
249
|
+
"react-native-svg": ">=15.12.1 <16.0.0",
|
|
249
250
|
"zustand": "^5.0.0",
|
|
250
|
-
"expo-video": ">=
|
|
251
|
+
"expo-video": ">=3.0.0 <59.0.0"
|
|
251
252
|
},
|
|
252
253
|
"peerDependenciesMeta": {
|
|
253
254
|
"nativewind": {
|
package/src/index.ts
CHANGED
|
@@ -323,7 +323,7 @@ export type {
|
|
|
323
323
|
export { RequireOxyAuth } from './ui/components/RequireOxyAuth';
|
|
324
324
|
export type { RequireOxyAuthProps, RequireOxyAuthPrompt } from './ui/components/RequireOxyAuth';
|
|
325
325
|
// `OxyProvider`'s `accountQueries`: the app's per-account offline cache.
|
|
326
|
-
export type { AccountQueriesConfig } from './ui/hooks/accountQueryPersistence';
|
|
326
|
+
export type { AccountQueriesConfig, AccountQueryMeta } from './ui/hooks/accountQueryPersistence';
|
|
327
327
|
|
|
328
328
|
export { default as FollowButton } from './ui/components/FollowButton';
|
|
329
329
|
export type {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-native-macos fork of `ExpoImage.tsx`. expo-image ships no macOS
|
|
3
|
+
* podspec, and importing it throws there (`requireNativeModule('ExpoImage')`
|
|
4
|
+
* runs at import), so on macOS the SDK draws with react-native's `Image`,
|
|
5
|
+
* which react-native-macos backs with NSImage. Fresco, the reason the other
|
|
6
|
+
* platforms avoid react-native's `Image`, is Android-only.
|
|
7
|
+
*
|
|
8
|
+
* It accepts expo-image's props, so every call site typechecks once against the
|
|
9
|
+
* default fork, and maps the ones the SDK uses; the rest (transition, cache
|
|
10
|
+
* policy, placeholders) have no macOS equivalent here and are dropped.
|
|
11
|
+
*/
|
|
12
|
+
import type { ImageContentFit, ImageProps, ImageSource } from 'expo-image';
|
|
13
|
+
import { Image, type ImageResizeMode, type ImageSourcePropType } from 'react-native';
|
|
14
|
+
|
|
15
|
+
const RESIZE_MODE: Record<ImageContentFit, ImageResizeMode> = {
|
|
16
|
+
cover: 'cover',
|
|
17
|
+
contain: 'contain',
|
|
18
|
+
fill: 'stretch',
|
|
19
|
+
none: 'center',
|
|
20
|
+
'scale-down': 'contain',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function toImageSource(source: ImageProps['source']): ImageSourcePropType | undefined {
|
|
24
|
+
if (source == null) return undefined;
|
|
25
|
+
if (typeof source === 'number') return source;
|
|
26
|
+
if (typeof source === 'string') return { uri: source };
|
|
27
|
+
if (Array.isArray(source)) return source.length > 0 ? toImageSource(source[0]) : undefined;
|
|
28
|
+
if (typeof source === 'object' && 'uri' in source) {
|
|
29
|
+
const { uri, headers, width, height } = source as ImageSource;
|
|
30
|
+
return { uri, headers, width: width ?? undefined, height: height ?? undefined };
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function ExpoImage({
|
|
36
|
+
source,
|
|
37
|
+
contentFit = 'cover',
|
|
38
|
+
onError,
|
|
39
|
+
style,
|
|
40
|
+
blurRadius,
|
|
41
|
+
accessible,
|
|
42
|
+
accessibilityLabel,
|
|
43
|
+
testID,
|
|
44
|
+
className,
|
|
45
|
+
}: ImageProps & { className?: string }) {
|
|
46
|
+
return (
|
|
47
|
+
<Image
|
|
48
|
+
source={toImageSource(source) ?? { uri: '' }}
|
|
49
|
+
resizeMode={RESIZE_MODE[contentFit]}
|
|
50
|
+
onError={onError ? () => onError({ error: 'The image could not be loaded' }) : undefined}
|
|
51
|
+
style={style}
|
|
52
|
+
blurRadius={blurRadius}
|
|
53
|
+
accessible={accessible}
|
|
54
|
+
accessibilityLabel={accessibilityLabel}
|
|
55
|
+
testID={testID}
|
|
56
|
+
{...(className ? { className } : null)}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The image every SDK surface draws with: expo-image's `Image` (Glide on
|
|
3
|
+
* Android, SDWebImage on iOS, an `<img>` on web), never react-native's
|
|
4
|
+
* (`__tests__/packaging/rnImageBoundary.test.ts`).
|
|
5
|
+
*
|
|
6
|
+
* Its one fork, `ExpoImage.macos.tsx`, exists because expo-image has no macOS
|
|
7
|
+
* implementation: its module calls `requireNativeModule('ExpoImage')` when it
|
|
8
|
+
* is first imported, so on react-native-macos merely importing this file's
|
|
9
|
+
* target would throw and take the whole SDK down with it.
|
|
10
|
+
*/
|
|
11
|
+
export { Image as ExpoImage } from 'expo-image';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FC, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* react-native-macos fork: react-native-keyboard-controller has no macOS
|
|
5
|
+
* implementation and throws when imported without its native module, and a
|
|
6
|
+
* desktop keyboard never covers the window, so there is nothing to track.
|
|
7
|
+
*/
|
|
8
|
+
export const KeyboardBoundary: FC<{ children: ReactNode }> = ({ children }) => <>{children}</>;
|
|
@@ -4,7 +4,7 @@ import { useMemo, useState, useEffect } from 'react';
|
|
|
4
4
|
import { View, ScrollView, StyleSheet } from 'react-native';
|
|
5
5
|
import { Button } from '@oxy.so/bloom/button';
|
|
6
6
|
import { Text } from '@oxy.so/bloom/typography';
|
|
7
|
-
import {
|
|
7
|
+
import { ExpoImage } from '../ExpoImage';
|
|
8
8
|
import MaterialCommunityIcons from '../../icons/MaterialCommunityIcons';
|
|
9
9
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
10
10
|
import type { FileMetadata } from '@oxy.so/core';
|
|
@@ -3,7 +3,7 @@ import { View, ScrollView, StyleSheet } from 'react-native';
|
|
|
3
3
|
import { Button } from '@oxy.so/bloom/button';
|
|
4
4
|
import { Card } from '@oxy.so/bloom/card';
|
|
5
5
|
import { Text } from '@oxy.so/bloom/typography';
|
|
6
|
-
import {
|
|
6
|
+
import { ExpoImage } from '../ExpoImage';
|
|
7
7
|
import Ionicons from '../../icons/Ionicons';
|
|
8
8
|
import { Dialog, type DialogControlProps } from '@oxy.so/bloom/dialog';
|
|
9
9
|
import { useTheme } from '@oxy.so/bloom/theme';
|
|
@@ -130,8 +130,6 @@ export {
|
|
|
130
130
|
OxyProviderMissingError,
|
|
131
131
|
} from './oxyRuntimeContext';
|
|
132
132
|
|
|
133
|
-
const subscribeNever = (): (() => void) => () => undefined;
|
|
134
|
-
|
|
135
133
|
/**
|
|
136
134
|
* The SDK's internal runtime provider — the session/account state machine that
|
|
137
135
|
* backs `useOxy()`.
|
|
@@ -350,24 +348,23 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
350
348
|
// a persistence write is never dropped because it raced storage init.
|
|
351
349
|
const queryClient = useQueryClient();
|
|
352
350
|
|
|
353
|
-
// The
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
: null;
|
|
351
|
+
// The account's offline cache: the app's `accountQueries` roots and every
|
|
352
|
+
// query marked `meta: { persist: 'account' }`. Built once, like the runtime.
|
|
353
|
+
// `handleSubjectChange` activates it BEFORE anyone is woken, so a consumer of
|
|
354
|
+
// the new account never reads the previous account's rows.
|
|
355
|
+
const accountQueryPersistenceRef = useRef<AccountQueryPersistence | null>(null);
|
|
356
|
+
if (accountQueryPersistenceRef.current === null) {
|
|
357
|
+
accountQueryPersistenceRef.current = createAccountQueryPersistence(queryClient, accountQueries);
|
|
361
358
|
}
|
|
362
359
|
const accountQueryPersistence = accountQueryPersistenceRef.current;
|
|
363
360
|
useEffect(() => {
|
|
364
|
-
accountQueryPersistence
|
|
365
|
-
return () => accountQueryPersistence
|
|
361
|
+
accountQueryPersistence.attach();
|
|
362
|
+
return () => accountQueryPersistence.detach();
|
|
366
363
|
}, [accountQueryPersistence]);
|
|
367
364
|
const isAccountCacheReady = useSyncExternalStore(
|
|
368
|
-
accountQueryPersistence
|
|
369
|
-
|
|
370
|
-
|
|
365
|
+
accountQueryPersistence.subscribe,
|
|
366
|
+
accountQueryPersistence.isReady,
|
|
367
|
+
accountQueryPersistence.isReady,
|
|
371
368
|
);
|
|
372
369
|
|
|
373
370
|
const storageRef = useRef<StorageInterface | null>(null);
|
|
@@ -409,7 +406,7 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
409
406
|
.then((storageInstance) => {
|
|
410
407
|
storageRef.current = storageInstance;
|
|
411
408
|
storageReady.resolve(storageInstance);
|
|
412
|
-
accountQueryPersistence
|
|
409
|
+
accountQueryPersistence.setStorage(storageInstance);
|
|
413
410
|
if (mounted) {
|
|
414
411
|
setStorage(storageInstance);
|
|
415
412
|
runtime.setStorageReady(true);
|
|
@@ -418,7 +415,7 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
418
415
|
.catch((err) => {
|
|
419
416
|
// Nothing persisted can be restored; the account cache still has to
|
|
420
417
|
// become ready, or RequireOxyAuth would wait forever.
|
|
421
|
-
accountQueryPersistence
|
|
418
|
+
accountQueryPersistence.setStorage(createMemoryStorage());
|
|
422
419
|
if (mounted) {
|
|
423
420
|
logger('Failed to initialize storage', err);
|
|
424
421
|
onError?.({
|
|
@@ -1378,9 +1375,20 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
1378
1375
|
runtime.setAccount(cached.user);
|
|
1379
1376
|
});
|
|
1380
1377
|
onAuthStateChangeRef.current?.(cached.user);
|
|
1378
|
+
// The account's cached queries are part of this paint: one local read,
|
|
1379
|
+
// started by the subject change above, so nothing renders without them.
|
|
1380
|
+
await accountQueryPersistence.whenReady();
|
|
1381
1381
|
markAuthResolvedRef.current();
|
|
1382
1382
|
return true;
|
|
1383
|
-
}, [
|
|
1383
|
+
}, [
|
|
1384
|
+
identity,
|
|
1385
|
+
clientIdProp,
|
|
1386
|
+
authStore,
|
|
1387
|
+
oxyServices,
|
|
1388
|
+
runtime,
|
|
1389
|
+
syncDeviceCredentialToHost,
|
|
1390
|
+
accountQueryPersistence,
|
|
1391
|
+
]);
|
|
1384
1392
|
|
|
1385
1393
|
// A painted session the server could not be asked about yet stays on screen
|
|
1386
1394
|
// and is asked again: on the lost-token backoff, and at once when the device
|
|
@@ -1687,7 +1695,7 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
1687
1695
|
({ previous, next }: SubjectTransition): void => {
|
|
1688
1696
|
// Every transition, the first sign-in included: the account cache is
|
|
1689
1697
|
// keyed by the subject, and a sign-out deletes the signed-out one's.
|
|
1690
|
-
accountQueryPersistence
|
|
1698
|
+
accountQueryPersistence.activate(next);
|
|
1691
1699
|
if (previous === null) {
|
|
1692
1700
|
return;
|
|
1693
1701
|
}
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* The SDK's own persister (`attachQueryPersistence`) keeps account-agnostic
|
|
5
5
|
* data. An app's mail, library or drafts belong to one account, and every app
|
|
6
6
|
* that wanted them to survive a restart had to scope a persister to the session
|
|
7
|
-
* itself. The SDK owns the session, so it owns this too
|
|
7
|
+
* itself. The SDK owns the session, so it owns this too, for every root an app
|
|
8
|
+
* declares (`accountQueries`) and for any single query marked
|
|
9
|
+
* `meta: { persist: 'account' }`, with no declaration at all:
|
|
8
10
|
*
|
|
9
11
|
* - Storage is keyed by the account (`oxy_account_queries:<accountId>`), so a
|
|
10
12
|
* restore can only ever hydrate the account that is signed in.
|
|
@@ -15,7 +17,9 @@
|
|
|
15
17
|
* is woken. The leaving account's latest state is written first, a sign-out
|
|
16
18
|
* then deletes it, and only then is the next account's blob read.
|
|
17
19
|
* - `isReady()` is false until the signed-in account's blob is restored, and
|
|
18
|
-
* `RequireOxyAuth` waits on it, so another account's rows never render.
|
|
20
|
+
* `RequireOxyAuth` waits on it, so another account's rows never render. A
|
|
21
|
+
* cold boot that paints a cached session waits on it too (`whenReady()`), so
|
|
22
|
+
* the first paint already has the account's rows.
|
|
19
23
|
*
|
|
20
24
|
* Every storage operation runs through one queue, in order, and a restore
|
|
21
25
|
* checks that its account is still the active one in the same tick it hydrates.
|
|
@@ -39,12 +43,29 @@ import {
|
|
|
39
43
|
type PersistFilters,
|
|
40
44
|
} from './persistedQueryCache';
|
|
41
45
|
|
|
46
|
+
/**
|
|
47
|
+
* The `meta` that makes one query the signed-in account's and keeps it across
|
|
48
|
+
* restarts, exactly like a query under a declared root: for the few reads that
|
|
49
|
+
* shape a returning user's first paint (the tabs, the theme), not for lists.
|
|
50
|
+
*
|
|
51
|
+
* `useQuery({ queryKey, queryFn, meta: { persist: 'account' } })`
|
|
52
|
+
*/
|
|
53
|
+
export interface AccountQueryMeta {
|
|
54
|
+
persist: 'account';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isAccountQuery(query: Query): boolean {
|
|
58
|
+
return (query.meta as Partial<AccountQueryMeta> | undefined)?.persist === 'account';
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
/** An app's declaration of which data is private to the signed-in account. */
|
|
43
62
|
export interface AccountQueriesConfig {
|
|
44
63
|
/**
|
|
45
64
|
* Query-key roots (`queryKey[0]`) whose data belongs to the signed-in account
|
|
46
65
|
* and survives a restart. `'all'`: every query the app runs, for an app whose
|
|
47
66
|
* every read may depend on who is signed in, so no root can be forgotten.
|
|
67
|
+
* Every root's queries are written on every change to any of them: for one
|
|
68
|
+
* or two queries under a busy root, mark those instead (`AccountQueryMeta`).
|
|
48
69
|
*
|
|
49
70
|
* The roots `accounts`, `sessions` and `devices` are reserved for the SDK's
|
|
50
71
|
* device-level data: an app must not use them. `users` and `privacy` are the
|
|
@@ -75,13 +96,19 @@ export interface AccountQueryOwnership {
|
|
|
75
96
|
/** SDK roots that hold device-level data, the same whoever is signed in. */
|
|
76
97
|
const DEVICE_ROOTS = new Set(['accounts', 'sessions', 'devices']);
|
|
77
98
|
|
|
78
|
-
|
|
99
|
+
/** No declared roots: only the queries marked `AccountQueryMeta` are the account's. */
|
|
100
|
+
const NO_ROOTS: AccountQueriesConfig = { roots: [] };
|
|
101
|
+
|
|
102
|
+
export function accountQueryOwnership(
|
|
103
|
+
config: AccountQueriesConfig = NO_ROOTS,
|
|
104
|
+
): AccountQueryOwnership {
|
|
79
105
|
const memoryOnly = new Set([...(config.memoryOnlyRoots ?? []), ...SDK_MEMORY_ONLY_ROOTS]);
|
|
80
106
|
const sdkRoots = new Set(PERSISTED_QUERY_PREFIXES);
|
|
81
107
|
const declared = config.roots === 'all' ? null : new Set(config.roots);
|
|
82
108
|
const mutationHashes = new Set((config.mutationKeys ?? []).map((key) => hashKey(key)));
|
|
83
109
|
|
|
84
110
|
const ownsQuery = (query: Query): boolean => {
|
|
111
|
+
if (isAccountQuery(query)) return true;
|
|
85
112
|
const root = rootOf(query);
|
|
86
113
|
if (root !== null && memoryOnly.has(root)) return true;
|
|
87
114
|
if (declared === null) return root === null || !DEVICE_ROOTS.has(root);
|
|
@@ -94,7 +121,9 @@ export function accountQueryOwnership(config: AccountQueriesConfig): AccountQuer
|
|
|
94
121
|
const root = rootOf(query);
|
|
95
122
|
if (root === null) return true;
|
|
96
123
|
// The SDK's own roots stay with the SDK persister.
|
|
97
|
-
return
|
|
124
|
+
return (
|
|
125
|
+
!memoryOnly.has(root) && (isAccountQuery(query) || declared !== null || !sdkRoots.has(root))
|
|
126
|
+
);
|
|
98
127
|
},
|
|
99
128
|
ownsMutation: (mutation) => {
|
|
100
129
|
const key = mutation.options.mutationKey;
|
|
@@ -118,6 +147,8 @@ export interface AccountQueryPersistence {
|
|
|
118
147
|
detach(): void;
|
|
119
148
|
/** True once the active account's cache is restored (always true signed out). */
|
|
120
149
|
isReady(): boolean;
|
|
150
|
+
/** Resolves once `isReady()`, or once the account it waited for is left. */
|
|
151
|
+
whenReady(): Promise<void>;
|
|
121
152
|
subscribe(listener: () => void): () => void;
|
|
122
153
|
}
|
|
123
154
|
|
|
@@ -127,11 +158,11 @@ function storageKeyFor(accountId: string): string {
|
|
|
127
158
|
|
|
128
159
|
export function createAccountQueryPersistence(
|
|
129
160
|
queryClient: QueryClient,
|
|
130
|
-
config
|
|
161
|
+
config?: AccountQueriesConfig,
|
|
131
162
|
): AccountQueryPersistence {
|
|
132
163
|
const { ownsQuery, persistsQuery, ownsMutation } = accountQueryOwnership(config);
|
|
133
164
|
const filters: PersistFilters = {
|
|
134
|
-
|
|
165
|
+
persistsQuery,
|
|
135
166
|
shouldDehydrateMutation: (mutation) => mutation.state.isPaused && ownsMutation(mutation),
|
|
136
167
|
};
|
|
137
168
|
const listeners = new Set<() => void>();
|
|
@@ -159,6 +190,13 @@ export function createAccountQueryPersistence(
|
|
|
159
190
|
const notify = () => {
|
|
160
191
|
for (const listener of listeners) listener();
|
|
161
192
|
};
|
|
193
|
+
const subscribe = (listener: () => void) => {
|
|
194
|
+
listeners.add(listener);
|
|
195
|
+
return () => {
|
|
196
|
+
listeners.delete(listener);
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
const isReady = () => accountId === null || restoredFor === accountId;
|
|
162
200
|
|
|
163
201
|
const dropScopedData = () => {
|
|
164
202
|
queryClient.removeQueries({ predicate: ownsQuery });
|
|
@@ -179,11 +217,12 @@ export function createAccountQueryPersistence(
|
|
|
179
217
|
const current = generation;
|
|
180
218
|
const key = storageKeyFor(id);
|
|
181
219
|
enqueue(async (storage) => {
|
|
182
|
-
|
|
220
|
+
// An unreadable blob restores nothing; it must not leave the account unready.
|
|
221
|
+
const raw = await storage.getItem(key).catch(() => null);
|
|
183
222
|
// Same tick as the hydrate: a switch in between cannot be missed.
|
|
184
223
|
if (current !== generation) return;
|
|
185
224
|
if (!hydrateSnapshot(queryClient, raw, ACCOUNT_QUERY_CACHE_MAX_AGE) && raw) {
|
|
186
|
-
await storage.removeItem(key);
|
|
225
|
+
await storage.removeItem(key).catch(() => undefined);
|
|
187
226
|
if (current !== generation) return;
|
|
188
227
|
}
|
|
189
228
|
restoredFor = id;
|
|
@@ -245,15 +284,21 @@ export function createAccountQueryPersistence(
|
|
|
245
284
|
restoredFor = null;
|
|
246
285
|
},
|
|
247
286
|
|
|
248
|
-
isReady
|
|
249
|
-
return accountId === null || restoredFor === accountId;
|
|
250
|
-
},
|
|
287
|
+
isReady,
|
|
251
288
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
289
|
+
whenReady() {
|
|
290
|
+
const waitingFor = generation;
|
|
291
|
+
const settled = () => isReady() || generation !== waitingFor;
|
|
292
|
+
if (settled()) return Promise.resolve();
|
|
293
|
+
return new Promise((resolve) => {
|
|
294
|
+
const off = subscribe(() => {
|
|
295
|
+
if (!settled()) return;
|
|
296
|
+
off();
|
|
297
|
+
resolve();
|
|
298
|
+
});
|
|
299
|
+
});
|
|
257
300
|
},
|
|
301
|
+
|
|
302
|
+
subscribe,
|
|
258
303
|
};
|
|
259
304
|
}
|
|
@@ -44,7 +44,8 @@ export const SDK_MEMORY_ONLY_ROOTS: ReadonlyArray<string> = [
|
|
|
44
44
|
];
|
|
45
45
|
|
|
46
46
|
export interface PersistFilters {
|
|
47
|
-
|
|
47
|
+
/** The queries this cache keeps, whatever their state; only successful ones are written. */
|
|
48
|
+
persistsQuery: (query: Query) => boolean;
|
|
48
49
|
shouldDehydrateMutation: (mutation: Mutation) => boolean;
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -64,7 +65,11 @@ export function snapshot(queryClient: QueryClient, filters: PersistFilters): str
|
|
|
64
65
|
const blob: PersistedBlob = {
|
|
65
66
|
buster: getOxyBuildId(),
|
|
66
67
|
timestamp: Date.now(),
|
|
67
|
-
clientState: dehydrate(queryClient,
|
|
68
|
+
clientState: dehydrate(queryClient, {
|
|
69
|
+
shouldDehydrateQuery: (query) =>
|
|
70
|
+
query.state.status === 'success' && filters.persistsQuery(query),
|
|
71
|
+
shouldDehydrateMutation: filters.shouldDehydrateMutation,
|
|
72
|
+
}),
|
|
68
73
|
};
|
|
69
74
|
return JSON.stringify(blob);
|
|
70
75
|
}
|
|
@@ -102,8 +107,9 @@ export function hydrateSnapshot(
|
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
/**
|
|
105
|
-
* Write the filtered cache
|
|
106
|
-
* `
|
|
110
|
+
* Write the filtered cache whenever one of its queries or any mutation changes,
|
|
111
|
+
* at most once per `throttleMs`. Activity in queries it does not keep (feeds,
|
|
112
|
+
* search) never serializes it. `write` receives the snapshot; `flush()` writes now.
|
|
107
113
|
*/
|
|
108
114
|
export function subscribeSnapshots(
|
|
109
115
|
queryClient: QueryClient,
|
|
@@ -120,7 +126,9 @@ export function subscribeSnapshots(
|
|
|
120
126
|
const schedule = () => {
|
|
121
127
|
if (!timer) timer = setTimeout(flush, throttleMs);
|
|
122
128
|
};
|
|
123
|
-
const offQueries = queryClient.getQueryCache().subscribe(
|
|
129
|
+
const offQueries = queryClient.getQueryCache().subscribe((event) => {
|
|
130
|
+
if (filters.persistsQuery(event.query)) schedule();
|
|
131
|
+
});
|
|
124
132
|
const offMutations = queryClient.getMutationCache().subscribe(schedule);
|
|
125
133
|
return {
|
|
126
134
|
flush,
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* (`persistedQueryCache.ts`) when introducing reads that should survive restart.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { QueryClient, onlineManager, type
|
|
24
|
+
import { QueryClient, onlineManager, type Query } from '@tanstack/react-query';
|
|
25
25
|
import type { PersistedClient } from '@tanstack/react-query-persist-client';
|
|
26
26
|
import { isDev } from '@oxy.so/core';
|
|
27
27
|
import type { StorageInterface } from '../utils/storageHelpers';
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
PERSISTED_QUERY_PREFIXES,
|
|
31
31
|
hydrateSnapshot,
|
|
32
32
|
subscribeSnapshots,
|
|
33
|
+
type PersistFilters,
|
|
33
34
|
} from './persistedQueryCache';
|
|
34
35
|
|
|
35
36
|
const QUERY_CACHE_KEY = 'oxy_query_cache_v3';
|
|
@@ -53,15 +54,8 @@ export function shouldRetryQuery(failureCount: number, error: unknown): boolean
|
|
|
53
54
|
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
/**
|
|
57
|
-
* Whether a query belongs in the account-agnostic cache: successful (a
|
|
58
|
-
* `pending`/`error` state is not worth keeping, and an error would leak
|
|
59
|
-
* failure objects across restarts) and under one of the SDK's own prefixes.
|
|
60
|
-
*/
|
|
57
|
+
/** Whether a query is under one of the SDK's own account-agnostic prefixes. */
|
|
61
58
|
function isSharedQuery(query: Query): boolean {
|
|
62
|
-
if (query.state.status !== 'success') {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
59
|
const head = query.queryKey[0];
|
|
66
60
|
return typeof head === 'string' && PERSISTED_QUERY_PREFIXES.includes(head);
|
|
67
61
|
}
|
|
@@ -135,10 +129,11 @@ export interface AttachPersistenceResult {
|
|
|
135
129
|
* Restore, then keep writing, the account-agnostic cache.
|
|
136
130
|
*
|
|
137
131
|
* Queries written by another build are dropped; queued mutations are kept
|
|
138
|
-
* (`persistedQueryCache.ts`). Whatever
|
|
139
|
-
*
|
|
140
|
-
* one of the SDK's prefixes, so
|
|
141
|
-
*
|
|
132
|
+
* (`persistedQueryCache.ts`). Whatever belongs to the account (`accountQueries`,
|
|
133
|
+
* or a query marked `meta: { persist: 'account' }`) is left to
|
|
134
|
+
* `createAccountQueryPersistence`, even under one of the SDK's prefixes, so
|
|
135
|
+
* private rows never land in the shared blob and a paused mutation cannot
|
|
136
|
+
* replay twice or under another account.
|
|
142
137
|
*
|
|
143
138
|
* Safe to no-op if `storage` is null/undefined (e.g. server-side render
|
|
144
139
|
* with no host storage).
|
|
@@ -155,13 +150,12 @@ export const attachQueryPersistence = (
|
|
|
155
150
|
};
|
|
156
151
|
}
|
|
157
152
|
|
|
158
|
-
const account =
|
|
159
|
-
const filters = {
|
|
160
|
-
|
|
161
|
-
isSharedQuery(query) && !(account?.ownsQuery(query) ?? false),
|
|
153
|
+
const account = accountQueryOwnership(accountQueries);
|
|
154
|
+
const filters: PersistFilters = {
|
|
155
|
+
persistsQuery: (query) => isSharedQuery(query) && !account.ownsQuery(query),
|
|
162
156
|
// Every other mutation, whatever its status: paused ones are exactly the
|
|
163
157
|
// ones that must survive a restart to replay when online.
|
|
164
|
-
shouldDehydrateMutation: (mutation
|
|
158
|
+
shouldDehydrateMutation: (mutation) => !account.ownsMutation(mutation),
|
|
165
159
|
};
|
|
166
160
|
|
|
167
161
|
let stopped = false;
|
package/src/ui/index.ts
CHANGED
|
@@ -34,7 +34,7 @@ export { LogoText } from './components/logo/LogoText';
|
|
|
34
34
|
export { RequireOxyAuth } from './components/RequireOxyAuth';
|
|
35
35
|
export type { RequireOxyAuthProps, RequireOxyAuthPrompt } from './components/RequireOxyAuth';
|
|
36
36
|
// `OxyProvider`'s `accountQueries`: the app's per-account offline cache.
|
|
37
|
-
export type { AccountQueriesConfig } from './hooks/accountQueryPersistence';
|
|
37
|
+
export type { AccountQueriesConfig, AccountQueryMeta } from './hooks/accountQueryPersistence';
|
|
38
38
|
export { default as FollowButton } from './components/FollowButton';
|
|
39
39
|
export { default as PeableButton } from './components/PeableButton';
|
|
40
40
|
export type { PeableButtonProps } from './components/PeableButton';
|
|
@@ -87,7 +87,11 @@ export async function startNativeOAuthSignIn(
|
|
|
87
87
|
if (!redirectUrl.startsWith(prefix) || redirectUrl.includes('#'))
|
|
88
88
|
return { status: 'failed', reason: 'native-callback-invalid' };
|
|
89
89
|
const parameters = new URLSearchParams(redirectUrl.slice(prefix.length));
|
|
90
|
-
if (
|
|
90
|
+
if (
|
|
91
|
+
Array.from(new URL(options.redirectUri).searchParams, ([key]) => key).some((key) =>
|
|
92
|
+
parameters.has(key),
|
|
93
|
+
)
|
|
94
|
+
)
|
|
91
95
|
return { status: 'failed', reason: 'native-callback-invalid' };
|
|
92
96
|
if (
|
|
93
97
|
['code', 'state', 'error', 'error_description'].some(
|