diff --git a/README.md b/README.md index 325a4bad1..bc367df7a 100644 --- a/README.md +++ b/README.md @@ -98,17 +98,18 @@ You must pass at least the `writeKey`. Additional configuration options are list ### Client Options -| Name | Default | Description | -| -------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| `writeKey` **(REQUIRED)** | '' | Your Segment API key. | -| `debug` | true\* | When set to false, it will not generate any logs. | -| `flushAt` | 20 | How many events to accumulate before sending events to the backend. | -| `flushInterval` | 30 | In seconds, how often to send events to the backend. | -| `maxBatchSize` | 1000 | How many events to send to the API at once | +| Name | Default | Description | +| -------------------------- | --------- | -----------------------------------------------------------------------------------------------------------------------------------------------| +| `writeKey` **(REQUIRED)** | '' | Your Segment API key. | +| `debug` | true\* | When set to false, it will not generate any logs. | +| `flushAt` | 20 | How many events to accumulate before sending events to the backend. | +| `flushInterval` | 30 | In seconds, how often to send events to the backend. | +| `maxBatchSize` | 1000 | How many events to send to the API at once | | `trackAppLifecycleEvents` | false | Enable automatic tracking for [app lifecycle events](https://segment.com/docs/connections/spec/mobile/#lifecycle-events): application installed, opened, updated, backgrounded) | | `trackDeepLinks` | false | Enable automatic tracking for when the user opens the app via a deep link (Note: Requires additional setup on iOS, [see instructions](#ios-deep-link-tracking-setup)) | -| `defaultSettings` | undefined | Settings that will be used if the request to get the settings from Segment fails | -| `autoAddSegmentDestination`| true | Set to false to skip adding the SegmentDestination plugin | +| `defaultSettings` | undefined | Settings that will be used if the request to get the settings from Segment fails | +| `autoAddSegmentDestination`| true | Set to false to skip adding the SegmentDestination plugin | +| `storePersistor` | undefined | A custom persistor for the store that `analytics-react-native` leverages. Must match `Persistor` interface exported from [sovran-react-native](https://github.com/segmentio/sovran-react-native).| \* The default value of `debug` will be false in production. diff --git a/packages/core/src/client.tsx b/packages/core/src/client.tsx index 8b49ac1f1..f9ed76ba1 100644 --- a/packages/core/src/client.tsx +++ b/packages/core/src/client.tsx @@ -17,7 +17,10 @@ export const createClient = (config: Config) => { } const clientConfig = { ...defaultConfig, ...config }; - const segmentStore = new SovranStorage(config.writeKey); + const segmentStore = new SovranStorage({ + storeId: config.writeKey, + storePersistor: config.storePersistor, + }); const client = new SegmentClient({ config: clientConfig, diff --git a/packages/core/src/storage/__tests__/sovranStorage.test.ts b/packages/core/src/storage/__tests__/sovranStorage.test.ts index feb6837ac..e7fcac284 100644 --- a/packages/core/src/storage/__tests__/sovranStorage.test.ts +++ b/packages/core/src/storage/__tests__/sovranStorage.test.ts @@ -1,3 +1,4 @@ +import type { Persistor } from '@segment/sovran-react-native'; import deepmerge from 'deepmerge'; import { createCallbackManager } from '../../__tests__/__helpers__/utils'; import { SovranStorage } from '../sovranStorage'; @@ -33,9 +34,8 @@ jest.mock('@segment/sovran-react-native', () => ({ })); describe('sovranStorage', () => { - it('works', async () => { + async function commonAssertions(sovran: SovranStorage) { // First test that the constructor works correctly - const sovran = new SovranStorage('test'); expect(sovran.isReady.get()).toBe(false); // Setup a listener for context changes @@ -98,5 +98,27 @@ describe('sovranStorage', () => { const updatedSettings = await sovran.settings.set(settingsUpdate); expect(updatedSettings).toEqual(settingsUpdate); expect(sovran.settings.get()).toEqual(settingsUpdate); + } + + it('works', async () => { + const sovran = new SovranStorage({ storeId: 'test' }); + await commonAssertions(sovran); + }); + + it('works with custom Persistor', async () => { + const customStorage: any = {}; + const CustomPersistor: Persistor = { + get: async (key: string): Promise => { + return Promise.resolve(customStorage[key] as T); + }, + set: async (key: string, state: T): Promise => { + customStorage[key] = state; + }, + }; + const sovran = new SovranStorage({ + storeId: 'custom-persistor', + storePersistor: CustomPersistor, + }); + await commonAssertions(sovran); }); }); diff --git a/packages/core/src/storage/sovranStorage.ts b/packages/core/src/storage/sovranStorage.ts index ab660e9d7..826121075 100644 --- a/packages/core/src/storage/sovranStorage.ts +++ b/packages/core/src/storage/sovranStorage.ts @@ -2,6 +2,7 @@ import { createStore, registerBridgeStore, Store, + Persistor, } from '@segment/sovran-react-native'; import deepmerge from 'deepmerge'; import type { @@ -13,7 +14,11 @@ import type { UserInfoState, } from '..'; import { getUUID } from '../uuid'; -import type { Storage, DeepLinkData } from './types'; +import type { Storage, StorageConfig, DeepLinkData } from './types'; + +// NOTE: Not exported from @segment/sovran-react-native. Must explicitly declare here. +// Also this fallback is used in store.ts in @segment/sovran-react-native yet "storeId" is required. +const DEFAULT_STORE_NAME = 'default'; type Data = { isReady: boolean; @@ -73,6 +78,7 @@ registerBridgeStore({ export class SovranStorage implements Storage { private storeId: string; + private storePersistor?: Persistor; private readinessStore: Store; private contextStore: Store<{ context: DeepPartial }>; private settingsStore: Store<{ settings: SegmentAPIIntegrations }>; @@ -80,27 +86,45 @@ export class SovranStorage implements Storage { private userInfoStore: Store<{ userInfo: UserInfoState }>; private deepLinkStore: Store = deepLinkStore; - constructor(storeId: string) { - this.storeId = storeId; - this.readinessStore = createStore({ - hasLoadedContext: false, - }); + constructor(config: StorageConfig) { + this.storeId = config.storeId; + this.storePersistor = config.storePersistor; + this.readinessStore = createStore( + { + hasLoadedContext: false, + }, + { + persist: { + storeId: DEFAULT_STORE_NAME, + persistor: this.storePersistor, + }, + } + ); this.contextStore = createStore( { context: INITIAL_VALUES.context }, { - persist: { storeId: `${this.storeId}-context` }, + persist: { + storeId: `${this.storeId}-context`, + persistor: this.storePersistor, + }, } ); this.settingsStore = createStore( { settings: INITIAL_VALUES.settings }, { - persist: { storeId: `${this.storeId}-settings` }, + persist: { + storeId: `${this.storeId}-settings`, + persistor: this.storePersistor, + }, } ); this.eventsStore = createStore( { events: INITIAL_VALUES.events }, { - persist: { storeId: `${this.storeId}-events` }, + persist: { + storeId: `${this.storeId}-events`, + persistor: this.storePersistor, + }, } ); this.userInfoStore = createStore( @@ -108,6 +132,7 @@ export class SovranStorage implements Storage { { persist: { storeId: `${this.storeId}-userInfo`, + persistor: this.storePersistor, }, } ); diff --git a/packages/core/src/storage/types.ts b/packages/core/src/storage/types.ts index 8fe7bc08c..ed00dabe2 100644 --- a/packages/core/src/storage/types.ts +++ b/packages/core/src/storage/types.ts @@ -1,4 +1,4 @@ -import type { Unsubscribe } from '@segment/sovran-react-native'; +import type { Unsubscribe, Persistor } from '@segment/sovran-react-native'; import type { SegmentEvent } from '..'; import type { Context, @@ -69,3 +69,8 @@ export interface DeepLinkData { referring_application: string; url: string; } + +export type StorageConfig = { + storeId: string; + storePersistor?: Persistor; +}; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index bf0ebcd56..97b7ad1a7 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,3 +1,5 @@ +import type { Persistor } from '@segment/sovran-react-native'; + export type JsonValue = | boolean | number @@ -123,6 +125,7 @@ export type Config = { defaultSettings?: SegmentAPISettings; autoAddSegmentDestination?: boolean; collectDeviceId?: boolean; + storePersistor?: Persistor; }; export type ClientMethods = {