Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ node_modules/react-native/Libraries/polyfills/.*
; was in this module.
.*/node_modules/react-native/Libraries/ReactNative/PaperUIManager.js

; Our libdef is more complete, as of 2021-08.
.*/node_modules/@react-native-async-storage/async-storage.*

[include]
; Effectively this line warns Flow that this path might be a symlink
; (because the developer might be using `yarn link` to have this point into
Expand All @@ -49,7 +52,6 @@ node_modules/react-native/flow/
; their autogenerated versions in flow-typed/npm. (Contrast commit 6219e5a23,
; where the opposite behavior is exhibited!)
flow-typed/@sentry/react-native_v1.x.x.js
flow-typed/@react-native-async-storage/async-storage_v1.x.x.js
flow-typed/react-native-url-polyfill_vx.x.x.js
flow-typed/expo-screen-orientation_vx.x.x.js

Expand Down
20 changes: 9 additions & 11 deletions flow-typed/@react-native-async-storage/async-storage_v1.x.x.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,25 @@
declare module '@react-native-async-storage/async-storage' {
declare type ReadOnlyArrayString = $ReadOnlyArray<string>;

declare export default {
getItem(
declare export default class AsyncStorage {
static getItem(
key: string,
callback?: ?(error: ?Error, result: string | null) => void,
): Promise<string | null>,

setItem(key: string, value: string, callback?: ?(error: ?Error) => void): Promise<null>,
static setItem(key: string, value: string, callback?: ?(error: ?Error) => void): Promise<mixed>,

multiSet(
static multiSet(
keyValuePairs: Array<Array<string>>,
callback?: ?(errors: ?$ReadOnlyArray<?Error>) => void,
): Promise<null>,
): Promise<mixed>,

removeItem(key: string, callback?: ?(error: ?Error) => void): Promise<null>,
static removeItem(key: string, callback?: ?(error: ?Error) => void): Promise<mixed>,

getAllKeys(
static getAllKeys(
callback?: ?(error: ?Error, keys: ?ReadOnlyArrayString) => void,
): Promise<ReadOnlyArrayString>,

clear(callback?: ?(error: ?Error) => void): Promise<null>,

...
};
static clear(callback?: ?(error: ?Error) => void): Promise<mixed>,
}
}
9 changes: 8 additions & 1 deletion jest/jestSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,14 @@ jest.mock('react-native-reanimated', () => {
return Reanimated;
});

jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage);
jest.mock('@react-native-async-storage/async-storage', () =>
// The implementation is a class with static methods `.getItem`, etc.,
// whereas `mockAsyncStorage` is just an object with those as properties.
// So, simulate the implementation: make the class with a simple class
// expression (and give it a name), and give it its statics from
// `mockAsyncStorage`.
Object.assign(class AsyncStorage {}, mockAsyncStorage),
);

// Without this, we get lots of these errors on importing the module:
// `Invariant Violation: Native module cannot be null.`
Expand Down
30 changes: 18 additions & 12 deletions src/boot/ZulipAsyncStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
import { NativeModules } from 'react-native';
import * as logging from '../utils/logging';

export default class ZulipAsyncStorage {
static async getItem(key: string) {
const item = await AsyncStorage.getItem(key);
/**
* `AsyncStorage`, but with compression on Android.
*
* See
* https://react-native-async-storage.github.io/async-storage/docs/api
* and
* node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.js
* for the base class.
*/
export default class ZulipAsyncStorage extends AsyncStorage {
static async getItem(key: string): Promise<string | null> {
const item = await super.getItem(key);

// It's possible that getItem() is called on uncompressed state, for
// example when a user updates their app from a version without
Expand Down Expand Up @@ -46,17 +55,17 @@ export default class ZulipAsyncStorage {
return item;
}

static async setItem(key: string, value: string) {
return AsyncStorage.setItem(
static async setItem(key: string, value: string): Promise<mixed> {
return super.setItem(
key,
NativeModules.TextCompressionModule
? await NativeModules.TextCompressionModule.compress(value)
: value,
);
}

static async multiSet(keyValuePairs: Array<Array<string>>) {
return AsyncStorage.multiSet(
static async multiSet(keyValuePairs: Array<Array<string>>): Promise<mixed> {
return super.multiSet(
NativeModules.TextCompressionModule
? await Promise.all(
keyValuePairs.map(async ([key, value]) => [
Expand All @@ -68,9 +77,6 @@ export default class ZulipAsyncStorage {
);
}

static removeItem = AsyncStorage.removeItem;

static getAllKeys = AsyncStorage.getAllKeys;

static clear = AsyncStorage.clear;
// TODO: `mergeItem`, `multiGet`, and `multiMerge` would need compression
// logic if we used them.
}
2 changes: 0 additions & 2 deletions src/boot/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,6 @@ const reduxPersistConfig: Config = {

// Store data through our own wrapper for AsyncStorage, in particular
// to get compression.
/* $FlowFixMe[incompatible-variance]:
https://github.com/rt2zz/redux-persist/issues/823 */
storage: ZulipAsyncStorage,
serialize: stringify,
deserialize: parse,
Expand Down
14 changes: 6 additions & 8 deletions src/third/redux-persist/index.js.flow
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @flow
import AsyncStorage from '@react-native-async-storage/async-storage'

// redux dependent typing
declare type Action = Object
Expand All @@ -10,17 +11,14 @@ declare type StoreEnhancer = (next: Function) => Function
type Whitelist = string[]
type Blacklist = string[]

declare type Storage = {
setItem: Function,
getItem: Function,
removeItem: Function,
getAllKeys: Function,
...
}
declare type Config = {|
blacklist?: Blacklist,
whitelist?: Whitelist,
storage?: Storage,

// Flow's built-in `Class` utility type:
// https://flow.org/en/docs/types/utilities/#toc-class
storage?: Class<AsyncStorage>,

serialize?: boolean | Function,
deserialize?: boolean | Function,
keyPrefix?: string,
Expand Down