diff --git a/app.config.js b/app.config.js index 79dd1dc619..98d6931830 100644 --- a/app.config.js +++ b/app.config.js @@ -240,6 +240,7 @@ module.exports = function (_config) { plugins: [ 'expo-video', 'expo-localization', + 'expo-secure-store', 'expo-web-browser', [ 'react-native-edge-to-edge', diff --git a/jest/jestSetup.js b/jest/jestSetup.js index ca04d54261..7f44a3e89e 100644 --- a/jest/jestSetup.js +++ b/jest/jestSetup.js @@ -28,6 +28,36 @@ jest.mock('react-native-safe-area-context', () => { } }) +jest.mock('react-native-mmkv', () => ({ + MMKV: class MMKV { + _store = new Map() + + set(key, value) { + this._store.set(key, value) + } + + getString(key) { + return this._store.get(key) + } + + delete(key) { + this._store.delete(key) + } + + clearAll() { + this._store.clear() + } + + getAllKeys() { + return Array.from(this._store.keys()) + } + + addOnValueChangedListener() { + return {remove: () => {}} + } + }, +})) + jest.mock('expo-file-system/legacy', () => ({ getInfoAsync: jest.fn().mockResolvedValue({exists: true, size: 100}), deleteAsync: jest.fn(), @@ -57,6 +87,24 @@ jest.mock('expo-media-library', () => ({ usePermissions: jest.fn(() => [true]), })) +// the real module reads its constants off the native module at import time +jest.mock('expo-secure-store', () => { + const store = new Map() + return { + getItem: jest.fn(key => store.get(key) ?? null), + setItem: jest.fn((key, value) => { + store.set(key, value) + }), + AFTER_FIRST_UNLOCK: 0, + AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: 1, + ALWAYS: 2, + WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: 3, + ALWAYS_THIS_DEVICE_ONLY: 4, + WHEN_UNLOCKED: 5, + WHEN_UNLOCKED_THIS_DEVICE_ONLY: 6, + } +}) + jest.mock('@bsky.app/expo-guess-language', () => ({ guessLanguageSync: jest .fn() diff --git a/package.json b/package.json index 7f2786e0a4..73561b05c8 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,7 @@ "expo-paste-input": "^0.2.1", "expo-privacy-sensitive": "^0.2.0", "expo-screen-orientation": "~9.0.8", + "expo-secure-store": "~15.0.8", "expo-sharing": "~14.0.8", "expo-sms": "^14.0.7", "expo-splash-screen": "~31.0.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f24423953a..c90c6681da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -526,6 +526,9 @@ importers: expo-screen-orientation: specifier: ~9.0.8 version: 9.0.9(83f21d12a6c1aa99b4a65d070875cc68) + expo-secure-store: + specifier: ~15.0.8 + version: 15.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-sharing: specifier: ~14.0.8 version: 14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) @@ -5581,6 +5584,11 @@ packages: expo: '*' react-native: '*' + expo-secure-store@15.0.8: + resolution: {integrity: sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==} + peerDependencies: + expo: '*' + expo-server@1.0.7: resolution: {integrity: sha512-mcmyML3oXcqFUXUxtdtCL1O00ztNI2v76d+MdniXRUgHNxIcHZ05zo+DqBaOOT6LQnPk4vA4YHqQl7iGUfRb3g==} engines: {node: '>=20.16.0'} @@ -15168,6 +15176,10 @@ snapshots: expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + expo-secure-store@15.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): + dependencies: + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-server@1.0.7: {} expo-sharing@14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)):