Merge branch 'master' of https://codefirst.iut.uca.fr/git/thomas.chazot2/LaSuperMeteo
commit
441ff3d59d
@ -0,0 +1,9 @@
|
|||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
|
||||||
|
export const deleteFavoriteCity = async () => {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.removeItem('favorite_city')
|
||||||
|
} catch(e) {
|
||||||
|
console.log("An error occurred", e);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
import { City } from '../data/stub';
|
||||||
|
import {FETCH_FAVORITE_CITY} from '../redux/constants';
|
||||||
|
|
||||||
|
export const getFavoriteCityStorage = async () => {
|
||||||
|
try {
|
||||||
|
const cityJson = await AsyncStorage.getItem('favorite_city')
|
||||||
|
return cityJson != null ? JSON.parse(cityJson) : null;
|
||||||
|
} catch(e) {
|
||||||
|
console.log("An error occurred", e);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
import { City } from "../data/stub";
|
||||||
|
|
||||||
|
export const storeFavoriteCity = async (city: City | null) => {
|
||||||
|
try {
|
||||||
|
const cityJson = JSON.stringify(city)
|
||||||
|
await AsyncStorage.setItem('favorite_city', cityJson);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("An error occurred", e);
|
||||||
|
}
|
||||||
|
}
|
21
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/LICENSE
generated
vendored
21
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/LICENSE
generated
vendored
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
27
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/README.md
generated
vendored
27
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/README.md
generated
vendored
@ -0,0 +1,27 @@
|
|||||||
|
# React Native Async Storage
|
||||||
|
|
||||||
|
An asynchronous, unencrypted, persistent, key-value storage system for React Native.
|
||||||
|
|
||||||
|
|
||||||
|
## Supported platforms
|
||||||
|
|
||||||
|
- iOS
|
||||||
|
- Android
|
||||||
|
- [Web](https://github.com/react-native-async-storage/async-storage/releases/tag/v1.9.0)
|
||||||
|
- [MacOS](https://github.com/react-native-async-storage/async-storage/releases/tag/v1.8.1)
|
||||||
|
- [Windows](https://github.com/react-native-async-storage/async-storage/releases/tag/v1.10.0)
|
||||||
|
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Head over to [documentation](https://react-native-async-storage.github.io/async-storage/docs/install) to learn more.
|
||||||
|
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
Pull requests are welcome. Please open an issue first to discuss what you would like to change.
|
||||||
|
|
||||||
|
See the [CONTRIBUTING](CONTRIBUTING.md) file for more information.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT.
|
@ -0,0 +1,19 @@
|
|||||||
|
require 'json'
|
||||||
|
|
||||||
|
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
||||||
|
|
||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = "RNCAsyncStorage"
|
||||||
|
s.version = package['version']
|
||||||
|
s.summary = package['description']
|
||||||
|
s.license = package['license']
|
||||||
|
|
||||||
|
s.authors = package['author']
|
||||||
|
s.homepage = package['homepage']
|
||||||
|
s.platforms = { :ios => "9.0", :tvos => "9.2", :osx => "10.14" }
|
||||||
|
|
||||||
|
s.source = { :git => "https://github.com/react-native-async-storage/async-storage.git", :tag => "v#{s.version}" }
|
||||||
|
s.source_files = "ios/**/*.{h,m}"
|
||||||
|
|
||||||
|
s.dependency 'React-Core'
|
||||||
|
end
|
146
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/android/build.gradle
generated
vendored
146
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/android/build.gradle
generated
vendored
@ -0,0 +1,146 @@
|
|||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
def resolveModulePath(packageName) {
|
||||||
|
def basePath = rootDir.toPath().normalize()
|
||||||
|
|
||||||
|
// Node's module resolution algorithm searches up to the root directory,
|
||||||
|
// after which the base path will be null
|
||||||
|
while (basePath) {
|
||||||
|
def candidatePath = Paths.get(basePath.toString(), 'node_modules', packageName)
|
||||||
|
if (candidatePath.toFile().exists()) {
|
||||||
|
return candidatePath.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
basePath = basePath.getParent()
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
def safeExtGet(prop, fallback) {
|
||||||
|
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
def getFlagOrDefault(flagName, defaultValue) {
|
||||||
|
rootProject.hasProperty(flagName) ? rootProject.properties[flagName] == "true" : defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
def getVersionOrDefault(String flagName, String defaultVersion) {
|
||||||
|
rootProject.hasProperty(flagName) ? rootProject.properties[flagName] : defaultVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
compileClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
// kotlin version is dictated by rootProject extension or property in gradle.properties
|
||||||
|
ext.asyncStorageKtVersion = rootProject.ext.has('kotlinVersion')
|
||||||
|
? rootProject.ext['kotlinVersion']
|
||||||
|
: rootProject.hasProperty('AsyncStorage_kotlinVersion')
|
||||||
|
? rootProject.properties['AsyncStorage_kotlinVersion']
|
||||||
|
: '1.6.10'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
def projectExampleDir = Paths.get(project.projectDir.getParent(), "example", "android").toString()
|
||||||
|
def rootProjectDir = rootProject.projectDir.getPath()
|
||||||
|
if (projectExampleDir == rootProjectDir) {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$asyncStorageKtVersion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncStorage has default size of 6MB.
|
||||||
|
// This is a sane limit to protect the user from the app storing too much data in the database.
|
||||||
|
// This also protects the database from filling up the disk cache and becoming malformed.
|
||||||
|
// If you really need bigger size, please keep in mind the potential consequences.
|
||||||
|
long dbSizeInMB = 6L
|
||||||
|
def newDbSize = rootProject.properties['AsyncStorage_db_size_in_MB']
|
||||||
|
if (newDbSize != null && newDbSize.isLong()) {
|
||||||
|
dbSizeInMB = newDbSize.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instead of reusing AsyncTask thread pool, AsyncStorage can use its own executor
|
||||||
|
def useDedicatedExecutor = getFlagOrDefault('AsyncStorage_dedicatedExecutor', false)
|
||||||
|
|
||||||
|
// Use next storage implementation
|
||||||
|
def useNextStorage = getFlagOrDefault("AsyncStorage_useNextStorage", false)
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
if (useNextStorage) {
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply from: './testresults.gradle'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion safeExtGet('compileSdkVersion', 31)
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion safeExtGet('minSdkVersion', 21)
|
||||||
|
targetSdkVersion safeExtGet('targetSdkVersion', 31)
|
||||||
|
buildConfigField "Long", "AsyncStorage_db_size", "${dbSizeInMB}L"
|
||||||
|
buildConfigField "boolean", "AsyncStorage_useDedicatedExecutor", "${useDedicatedExecutor}"
|
||||||
|
buildConfigField "boolean", "AsyncStorage_useNextStorage", "${useNextStorage}"
|
||||||
|
}
|
||||||
|
lintOptions {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useNextStorage) {
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
returnDefaultValues = true
|
||||||
|
includeAndroidResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||||
|
url "${resolveModulePath("react-native")}/android"
|
||||||
|
}
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
if (useNextStorage) {
|
||||||
|
def room_version = getVersionOrDefault('AsyncStorage_next_roomVersion', '2.4.2')
|
||||||
|
def coroutines_version = "1.6.0"
|
||||||
|
def coroutinesTest_version = "1.6.0"
|
||||||
|
// if we don't provide explicit dependency on reflection, kotlin plugin
|
||||||
|
// would add one automatically, probably a version that is not compatible with
|
||||||
|
// used kotlin
|
||||||
|
def kotlinReflect_version = project.ext.asyncStorageKtVersion
|
||||||
|
def junit_version = "4.13.2"
|
||||||
|
def robolectric_version = "4.7.3"
|
||||||
|
def truth_version = "1.1.3"
|
||||||
|
def androidxtest_version = "1.4.0"
|
||||||
|
def androidtest_junit_version = "1.1.3"
|
||||||
|
|
||||||
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinReflect_version"
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||||
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
|
testImplementation "junit:junit:$junit_version"
|
||||||
|
testImplementation "androidx.test:runner:$androidxtest_version"
|
||||||
|
testImplementation "androidx.test:rules:$androidxtest_version"
|
||||||
|
testImplementation "androidx.test.ext:junit:$androidtest_junit_version"
|
||||||
|
testImplementation "org.robolectric:robolectric:$robolectric_version"
|
||||||
|
testImplementation "com.google.truth:truth:$truth_version"
|
||||||
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesTest_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection GradleDynamicVersion
|
||||||
|
implementation 'com.facebook.react:react-native:+' // From node_modules
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.reactnativecommunity.asyncstorage">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reactnativecommunity.asyncstorage;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.KEY_COLUMN;
|
||||||
|
import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.TABLE_CATALYST;
|
||||||
|
import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.VALUE_COLUMN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for database operations.
|
||||||
|
*/
|
||||||
|
public class AsyncLocalStorageUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the String required for an SQL select statement:
|
||||||
|
* WHERE key IN (?, ?, ..., ?)
|
||||||
|
* without 'WHERE' and with selectionCount '?'
|
||||||
|
*/
|
||||||
|
/* package */ static String buildKeySelection(int selectionCount) {
|
||||||
|
String[] list = new String[selectionCount];
|
||||||
|
Arrays.fill(list, "?");
|
||||||
|
return KEY_COLUMN + " IN (" + TextUtils.join(", ", list) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the String[] arguments needed for an SQL selection, i.e.:
|
||||||
|
* {a, b, c}
|
||||||
|
* to be used in the SQL select statement: WHERE key in (?, ?, ?)
|
||||||
|
*/
|
||||||
|
/* package */ static String[] buildKeySelectionArgs(ReadableArray keys, int start, int count) {
|
||||||
|
String[] selectionArgs = new String[count];
|
||||||
|
for (int keyIndex = 0; keyIndex < count; keyIndex++) {
|
||||||
|
selectionArgs[keyIndex] = keys.getString(start + keyIndex);
|
||||||
|
}
|
||||||
|
return selectionArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the given key, or null if not found.
|
||||||
|
*/
|
||||||
|
public static @Nullable String getItemImpl(SQLiteDatabase db, String key) {
|
||||||
|
String[] columns = {VALUE_COLUMN};
|
||||||
|
String[] selectionArgs = {key};
|
||||||
|
|
||||||
|
Cursor cursor = db.query(
|
||||||
|
TABLE_CATALYST,
|
||||||
|
columns,
|
||||||
|
KEY_COLUMN + "=?",
|
||||||
|
selectionArgs,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!cursor.moveToFirst()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return cursor.getString(0);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for the key given, returns true if successful, false otherwise.
|
||||||
|
*/
|
||||||
|
/* package */ static boolean setItemImpl(SQLiteDatabase db, String key, String value) {
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(KEY_COLUMN, key);
|
||||||
|
contentValues.put(VALUE_COLUMN, value);
|
||||||
|
|
||||||
|
long inserted = db.insertWithOnConflict(
|
||||||
|
TABLE_CATALYST,
|
||||||
|
null,
|
||||||
|
contentValues,
|
||||||
|
SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
|
|
||||||
|
return (-1 != inserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the actual merge of the (key, value) pair with the value stored in the database.
|
||||||
|
* NB: This assumes that a database lock is already in effect!
|
||||||
|
* @return the errorCode of the operation
|
||||||
|
*/
|
||||||
|
/* package */ static boolean mergeImpl(SQLiteDatabase db, String key, String value)
|
||||||
|
throws JSONException {
|
||||||
|
String oldValue = getItemImpl(db, key);
|
||||||
|
String newValue;
|
||||||
|
|
||||||
|
if (oldValue == null) {
|
||||||
|
newValue = value;
|
||||||
|
} else {
|
||||||
|
JSONObject oldJSON = new JSONObject(oldValue);
|
||||||
|
JSONObject newJSON = new JSONObject(value);
|
||||||
|
deepMergeInto(oldJSON, newJSON);
|
||||||
|
newValue = oldJSON.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return setItemImpl(db, key, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two {@link JSONObject}s. The newJSON object will be merged with the oldJSON object by
|
||||||
|
* either overriding its values, or merging them (if the values of the same key in both objects
|
||||||
|
* are of type {@link JSONObject}). oldJSON will contain the result of this merge.
|
||||||
|
*/
|
||||||
|
private static void deepMergeInto(JSONObject oldJSON, JSONObject newJSON)
|
||||||
|
throws JSONException {
|
||||||
|
Iterator<?> keys = newJSON.keys();
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
String key = (String) keys.next();
|
||||||
|
|
||||||
|
JSONObject newJSONObject = newJSON.optJSONObject(key);
|
||||||
|
JSONObject oldJSONObject = oldJSON.optJSONObject(key);
|
||||||
|
if (newJSONObject != null && oldJSONObject != null) {
|
||||||
|
deepMergeInto(oldJSONObject, newJSONObject);
|
||||||
|
oldJSON.put(key, oldJSONObject);
|
||||||
|
} else {
|
||||||
|
oldJSON.put(key, newJSON.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* From Pie and up, Android started to use Write-ahead logging (WAL), instead of journal rollback
|
||||||
|
* for atomic commits and rollbacks.
|
||||||
|
* Basically, WAL does not write directly to the database file, rather to the supporting WAL file.
|
||||||
|
* Because of that, migration to the next storage might not be successful, because the content of
|
||||||
|
* RKStorage might be still in WAL file instead. Committing all data from WAL to db file is called
|
||||||
|
* a "checkpoint" and is done automatically (by default) when the WAL file reaches a threshold
|
||||||
|
* size of 1000 pages.
|
||||||
|
* More here: https://sqlite.org/wal.html
|
||||||
|
*
|
||||||
|
* This helper will force checkpoint on RKStorage, if Next storage file does not exists yet.
|
||||||
|
*/
|
||||||
|
public static void verifyAndForceSqliteCheckpoint(Context ctx) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||||
|
Log.i("AsyncStorage_Next", "SQLite checkpoint not required on this API version.");
|
||||||
|
}
|
||||||
|
|
||||||
|
File nextStorageFile = ctx.getDatabasePath("AsyncStorage");
|
||||||
|
File currentStorageFile = ctx.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME);
|
||||||
|
boolean isCheckpointRequired = !nextStorageFile.exists() && currentStorageFile.exists();
|
||||||
|
if (!isCheckpointRequired) {
|
||||||
|
Log.i("AsyncStorage_Next", "SQLite checkpoint not required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ReactDatabaseSupplier supplier = ReactDatabaseSupplier.getInstance(ctx);
|
||||||
|
supplier.get().rawQuery("PRAGMA wal_checkpoint", null).close();
|
||||||
|
supplier.closeDatabase();
|
||||||
|
Log.i("AsyncStorage_Next", "Forcing SQLite checkpoint successful.");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w("AsyncStorage_Next", "Could not force checkpoint on RKStorage, the Next storage might not migrate the data properly: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reactnativecommunity.asyncstorage;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for database errors.
|
||||||
|
*/
|
||||||
|
public class AsyncStorageErrorUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Error object to be passed back to the JS callback.
|
||||||
|
*/
|
||||||
|
/* package */ static WritableMap getError(@Nullable String key, String errorMessage) {
|
||||||
|
WritableMap errorMap = Arguments.createMap();
|
||||||
|
errorMap.putString("message", errorMessage);
|
||||||
|
if (key != null) {
|
||||||
|
errorMap.putString("key", key);
|
||||||
|
}
|
||||||
|
return errorMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ static WritableMap getInvalidKeyError(@Nullable String key) {
|
||||||
|
return getError(key, "Invalid key");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ static WritableMap getInvalidValueError(@Nullable String key) {
|
||||||
|
return getError(key, "Invalid Value");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ static WritableMap getDBError(@Nullable String key) {
|
||||||
|
return getError(key, "Database Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,154 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
// A utility class that migrates a scoped AsyncStorage database to RKStorage.
|
||||||
|
// This utility only runs if the RKStorage file has not been created yet.
|
||||||
|
public class AsyncStorageExpoMigration {
|
||||||
|
static final String LOG_TAG = "AsyncStorageExpoMigration";
|
||||||
|
|
||||||
|
public static void migrate(Context context) {
|
||||||
|
// Only migrate if the default async storage file does not exist.
|
||||||
|
if (isAsyncStorageDatabaseCreated(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<File> expoDatabases = getExpoDatabases(context);
|
||||||
|
|
||||||
|
File expoDatabase = getLastModifiedFile(expoDatabases);
|
||||||
|
|
||||||
|
if (expoDatabase == null) {
|
||||||
|
Log.v(LOG_TAG, "No scoped database found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create the storage file
|
||||||
|
ReactDatabaseSupplier.getInstance(context).get();
|
||||||
|
copyFile(new FileInputStream(expoDatabase), new FileOutputStream(context.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME)));
|
||||||
|
Log.v(LOG_TAG, "Migrated most recently modified database " + expoDatabase.getName() + " to RKStorage");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(LOG_TAG, "Failed to migrate scoped database " + expoDatabase.getName());
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (File file : expoDatabases) {
|
||||||
|
if (file.delete()) {
|
||||||
|
Log.v(LOG_TAG, "Deleted scoped database " + file.getName());
|
||||||
|
} else {
|
||||||
|
Log.v(LOG_TAG, "Failed to delete scoped database " + file.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v(LOG_TAG, "Completed the scoped AsyncStorage migration");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAsyncStorageDatabaseCreated(Context context) {
|
||||||
|
return context.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all database files that the user may have created while using Expo.
|
||||||
|
private static ArrayList<File> getExpoDatabases(Context context) {
|
||||||
|
ArrayList<File> scopedDatabases = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
File databaseDirectory = context.getDatabasePath("noop").getParentFile();
|
||||||
|
File[] directoryListing = databaseDirectory.listFiles();
|
||||||
|
if (directoryListing != null) {
|
||||||
|
for (File child : directoryListing) {
|
||||||
|
// Find all databases matching the Expo scoped key, and skip any database journals.
|
||||||
|
if (child.getName().startsWith("RKStorage-scoped-experience-") && !child.getName().endsWith("-journal")) {
|
||||||
|
scopedDatabases.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Just in case anything happens catch and print, file system rules can tend to be different across vendors.
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return scopedDatabases;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the most recently modified file.
|
||||||
|
// If a user publishes an app with Expo, then changes the slug
|
||||||
|
// and publishes again, a new database will be created.
|
||||||
|
// We want to select the most recent database and migrate it to RKStorage.
|
||||||
|
private static File getLastModifiedFile(ArrayList<File> files) {
|
||||||
|
if (files.size() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long lastMod = -1;
|
||||||
|
File lastModFile = null;
|
||||||
|
for (File child : files) {
|
||||||
|
long modTime = getLastModifiedTimeInMillis(child);
|
||||||
|
if (modTime > lastMod) {
|
||||||
|
lastMod = modTime;
|
||||||
|
lastModFile = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastModFile != null) {
|
||||||
|
return lastModFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return files.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getLastModifiedTimeInMillis(File file) {
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
return getLastModifiedTimeFromBasicFileAttrs(file);
|
||||||
|
} else {
|
||||||
|
return file.lastModified();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
private static long getLastModifiedTimeFromBasicFileAttrs(File file) {
|
||||||
|
try {
|
||||||
|
return Files.readAttributes(file.toPath(), BasicFileAttributes.class).creationTime().toMillis();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copyFile(FileInputStream fromFile, FileOutputStream toFile) throws IOException {
|
||||||
|
FileChannel fromChannel = null;
|
||||||
|
FileChannel toChannel = null;
|
||||||
|
try {
|
||||||
|
fromChannel = fromFile.getChannel();
|
||||||
|
toChannel = toFile.getChannel();
|
||||||
|
fromChannel.transferTo(0, fromChannel.size(), toChannel);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (fromChannel != null) {
|
||||||
|
fromChannel.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (toChannel != null) {
|
||||||
|
toChannel.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,424 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reactnativecommunity.asyncstorage;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteStatement;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
import com.facebook.react.bridge.Callback;
|
||||||
|
import com.facebook.react.bridge.GuardedAsyncTask;
|
||||||
|
import com.facebook.react.bridge.LifecycleEventListener;
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
|
import com.facebook.react.bridge.WritableArray;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
import com.facebook.react.common.ReactConstants;
|
||||||
|
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||||
|
import com.facebook.react.module.annotations.ReactModule;
|
||||||
|
import com.facebook.react.modules.common.ModuleDataCleaner;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
@ReactModule(name = AsyncStorageModule.NAME)
|
||||||
|
public final class AsyncStorageModule
|
||||||
|
extends ReactContextBaseJavaModule implements ModuleDataCleaner.Cleanable, LifecycleEventListener {
|
||||||
|
|
||||||
|
// changed name to not conflict with AsyncStorage from RN repo
|
||||||
|
public static final String NAME = "RNC_AsyncSQLiteDBStorage";
|
||||||
|
|
||||||
|
// SQL variable number limit, defined by SQLITE_LIMIT_VARIABLE_NUMBER:
|
||||||
|
// https://raw.githubusercontent.com/android/platform_external_sqlite/master/dist/sqlite3.c
|
||||||
|
private static final int MAX_SQL_KEYS = 999;
|
||||||
|
|
||||||
|
private ReactDatabaseSupplier mReactDatabaseSupplier;
|
||||||
|
private boolean mShuttingDown = false;
|
||||||
|
|
||||||
|
private final SerialExecutor executor;
|
||||||
|
|
||||||
|
public AsyncStorageModule(ReactApplicationContext reactContext) {
|
||||||
|
this(
|
||||||
|
reactContext,
|
||||||
|
BuildConfig.AsyncStorage_useDedicatedExecutor
|
||||||
|
? Executors.newSingleThreadExecutor()
|
||||||
|
: AsyncTask.THREAD_POOL_EXECUTOR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
AsyncStorageModule(ReactApplicationContext reactContext, Executor executor) {
|
||||||
|
super(reactContext);
|
||||||
|
// The migration MUST run before the AsyncStorage database is created for the first time.
|
||||||
|
AsyncStorageExpoMigration.migrate(reactContext);
|
||||||
|
|
||||||
|
this.executor = new SerialExecutor(executor);
|
||||||
|
reactContext.addLifecycleEventListener(this);
|
||||||
|
// Creating the database MUST happen after the migration.
|
||||||
|
mReactDatabaseSupplier = ReactDatabaseSupplier.getInstance(reactContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
super.initialize();
|
||||||
|
mShuttingDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCatalystInstanceDestroy() {
|
||||||
|
mShuttingDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearSensitiveData() {
|
||||||
|
// Clear local storage. If fails, crash, since the app is potentially in a bad state and could
|
||||||
|
// cause a privacy violation. We're still not recovering from this well, but at least the error
|
||||||
|
// will be reported to the server.
|
||||||
|
mReactDatabaseSupplier.clearAndCloseDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHostResume() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHostPause() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHostDestroy() {
|
||||||
|
// ensure we close database when activity is destroyed
|
||||||
|
mReactDatabaseSupplier.closeDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of keys, this returns a map of (key, value) pairs for the keys found, and
|
||||||
|
* (key, null) for the keys that haven't been found.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void multiGet(final ReadableArray keys, final Callback callback) {
|
||||||
|
if (keys == null) {
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getInvalidKeyError(null), null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||||
|
@Override
|
||||||
|
protected void doInBackgroundGuarded(Void... params) {
|
||||||
|
if (!ensureDatabase()) {
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getDBError(null), null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] columns = {ReactDatabaseSupplier.KEY_COLUMN, ReactDatabaseSupplier.VALUE_COLUMN};
|
||||||
|
HashSet<String> keysRemaining = new HashSet<>();
|
||||||
|
WritableArray data = Arguments.createArray();
|
||||||
|
for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
|
||||||
|
int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
|
||||||
|
Cursor cursor = mReactDatabaseSupplier.get().query(
|
||||||
|
ReactDatabaseSupplier.TABLE_CATALYST,
|
||||||
|
columns,
|
||||||
|
AsyncLocalStorageUtil.buildKeySelection(keyCount),
|
||||||
|
AsyncLocalStorageUtil.buildKeySelectionArgs(keys, keyStart, keyCount),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
keysRemaining.clear();
|
||||||
|
try {
|
||||||
|
if (cursor.getCount() != keys.size()) {
|
||||||
|
// some keys have not been found - insert them with null into the final array
|
||||||
|
for (int keyIndex = keyStart; keyIndex < keyStart + keyCount; keyIndex++) {
|
||||||
|
keysRemaining.add(keys.getString(keyIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
WritableArray row = Arguments.createArray();
|
||||||
|
row.pushString(cursor.getString(0));
|
||||||
|
row.pushString(cursor.getString(1));
|
||||||
|
data.pushArray(row);
|
||||||
|
keysRemaining.remove(cursor.getString(0));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String key : keysRemaining) {
|
||||||
|
WritableArray row = Arguments.createArray();
|
||||||
|
row.pushString(key);
|
||||||
|
row.pushNull();
|
||||||
|
data.pushArray(row);
|
||||||
|
}
|
||||||
|
keysRemaining.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.invoke(null, data);
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts multiple (key, value) pairs. If one or more of the pairs cannot be inserted, this will
|
||||||
|
* return AsyncLocalStorageFailure, but all other pairs will have been inserted.
|
||||||
|
* The insertion will replace conflicting (key, value) pairs.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void multiSet(final ReadableArray keyValueArray, final Callback callback) {
|
||||||
|
if (keyValueArray.size() == 0) {
|
||||||
|
callback.invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||||
|
@Override
|
||||||
|
protected void doInBackgroundGuarded(Void... params) {
|
||||||
|
if (!ensureDatabase()) {
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sql = "INSERT OR REPLACE INTO " + ReactDatabaseSupplier.TABLE_CATALYST + " VALUES (?, ?);";
|
||||||
|
SQLiteStatement statement = mReactDatabaseSupplier.get().compileStatement(sql);
|
||||||
|
WritableMap error = null;
|
||||||
|
try {
|
||||||
|
mReactDatabaseSupplier.get().beginTransaction();
|
||||||
|
for (int idx=0; idx < keyValueArray.size(); idx++) {
|
||||||
|
if (keyValueArray.getArray(idx).size() != 2) {
|
||||||
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (keyValueArray.getArray(idx).getString(0) == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getInvalidKeyError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (keyValueArray.getArray(idx).getString(1) == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
statement.clearBindings();
|
||||||
|
statement.bindString(1, keyValueArray.getArray(idx).getString(0));
|
||||||
|
statement.bindString(2, keyValueArray.getArray(idx).getString(1));
|
||||||
|
statement.execute();
|
||||||
|
}
|
||||||
|
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
mReactDatabaseSupplier.get().endTransaction();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
if (error == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
callback.invoke(error);
|
||||||
|
} else {
|
||||||
|
callback.invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all rows of the keys given.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void multiRemove(final ReadableArray keys, final Callback callback) {
|
||||||
|
if (keys.size() == 0) {
|
||||||
|
callback.invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||||
|
@Override
|
||||||
|
protected void doInBackgroundGuarded(Void... params) {
|
||||||
|
if (!ensureDatabase()) {
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WritableMap error = null;
|
||||||
|
try {
|
||||||
|
mReactDatabaseSupplier.get().beginTransaction();
|
||||||
|
for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
|
||||||
|
int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
|
||||||
|
mReactDatabaseSupplier.get().delete(
|
||||||
|
ReactDatabaseSupplier.TABLE_CATALYST,
|
||||||
|
AsyncLocalStorageUtil.buildKeySelection(keyCount),
|
||||||
|
AsyncLocalStorageUtil.buildKeySelectionArgs(keys, keyStart, keyCount));
|
||||||
|
}
|
||||||
|
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
mReactDatabaseSupplier.get().endTransaction();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
if (error == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
callback.invoke(error);
|
||||||
|
} else {
|
||||||
|
callback.invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of (key, value) pairs, this will merge the given values with the stored values
|
||||||
|
* of the given keys, if they exist.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void multiMerge(final ReadableArray keyValueArray, final Callback callback) {
|
||||||
|
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||||
|
@Override
|
||||||
|
protected void doInBackgroundGuarded(Void... params) {
|
||||||
|
if (!ensureDatabase()) {
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WritableMap error = null;
|
||||||
|
try {
|
||||||
|
mReactDatabaseSupplier.get().beginTransaction();
|
||||||
|
for (int idx = 0; idx < keyValueArray.size(); idx++) {
|
||||||
|
if (keyValueArray.getArray(idx).size() != 2) {
|
||||||
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyValueArray.getArray(idx).getString(0) == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getInvalidKeyError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyValueArray.getArray(idx).getString(1) == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AsyncLocalStorageUtil.mergeImpl(
|
||||||
|
mReactDatabaseSupplier.get(),
|
||||||
|
keyValueArray.getArray(idx).getString(0),
|
||||||
|
keyValueArray.getArray(idx).getString(1))) {
|
||||||
|
error = AsyncStorageErrorUtil.getDBError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
mReactDatabaseSupplier.get().endTransaction();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
if (error == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
callback.invoke(error);
|
||||||
|
} else {
|
||||||
|
callback.invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the database.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void clear(final Callback callback) {
|
||||||
|
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||||
|
@Override
|
||||||
|
protected void doInBackgroundGuarded(Void... params) {
|
||||||
|
if (!mReactDatabaseSupplier.ensureDatabase()) {
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mReactDatabaseSupplier.clear();
|
||||||
|
callback.invoke();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with all keys from the database.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void getAllKeys(final Callback callback) {
|
||||||
|
new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
|
||||||
|
@Override
|
||||||
|
protected void doInBackgroundGuarded(Void... params) {
|
||||||
|
if (!ensureDatabase()) {
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getDBError(null), null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WritableArray data = Arguments.createArray();
|
||||||
|
String[] columns = {ReactDatabaseSupplier.KEY_COLUMN};
|
||||||
|
Cursor cursor = mReactDatabaseSupplier.get()
|
||||||
|
.query(ReactDatabaseSupplier.TABLE_CATALYST, columns, null, null, null, null, null);
|
||||||
|
try {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
data.pushString(cursor.getString(0));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
callback.invoke(null, data);
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the database is open for reads and writes.
|
||||||
|
*/
|
||||||
|
private boolean ensureDatabase() {
|
||||||
|
return !mShuttingDown && mReactDatabaseSupplier.ensureDatabase();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reactnativecommunity.asyncstorage;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import com.facebook.react.ReactPackage;
|
||||||
|
import com.facebook.react.bridge.JavaScriptModule;
|
||||||
|
import com.facebook.react.bridge.NativeModule;
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.uimanager.ViewManager;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AsyncStoragePackage implements ReactPackage {
|
||||||
|
@Override
|
||||||
|
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||||
|
|
||||||
|
List<NativeModule> moduleList = new ArrayList<>(1);
|
||||||
|
|
||||||
|
if (BuildConfig.AsyncStorage_useNextStorage) {
|
||||||
|
try {
|
||||||
|
Class storageClass = Class.forName("com.reactnativecommunity.asyncstorage.next.StorageModule");
|
||||||
|
NativeModule inst = (NativeModule) storageClass.getDeclaredConstructor(new Class[]{ReactContext.class}).newInstance(reactContext);
|
||||||
|
moduleList.add(inst);
|
||||||
|
AsyncLocalStorageUtil.verifyAndForceSqliteCheckpoint(reactContext);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String message = "Something went wrong when initializing module:"
|
||||||
|
+ "\n"
|
||||||
|
+ e.getCause().getClass()
|
||||||
|
+ "\n"
|
||||||
|
+ "Cause:" + e.getCause().getLocalizedMessage();
|
||||||
|
Log.e("AsyncStorage_Next", message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
moduleList.add(new AsyncStorageModule(reactContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
return moduleList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated in RN 0.47
|
||||||
|
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.reactnativecommunity.asyncstorage;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.react.common.ReactConstants;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database supplier of the database used by react native. This creates, opens and deletes the
|
||||||
|
* database as necessary.
|
||||||
|
*/
|
||||||
|
public class ReactDatabaseSupplier extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
// VisibleForTesting
|
||||||
|
public static final String DATABASE_NAME = "RKStorage";
|
||||||
|
|
||||||
|
private static final int DATABASE_VERSION = 1;
|
||||||
|
private static final int SLEEP_TIME_MS = 30;
|
||||||
|
|
||||||
|
static final String TABLE_CATALYST = "catalystLocalStorage";
|
||||||
|
static final String KEY_COLUMN = "key";
|
||||||
|
static final String VALUE_COLUMN = "value";
|
||||||
|
|
||||||
|
static final String VERSION_TABLE_CREATE =
|
||||||
|
"CREATE TABLE " + TABLE_CATALYST + " (" +
|
||||||
|
KEY_COLUMN + " TEXT PRIMARY KEY, " +
|
||||||
|
VALUE_COLUMN + " TEXT NOT NULL" +
|
||||||
|
")";
|
||||||
|
|
||||||
|
private static @Nullable ReactDatabaseSupplier sReactDatabaseSupplierInstance;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private @Nullable SQLiteDatabase mDb;
|
||||||
|
private long mMaximumDatabaseSize = BuildConfig.AsyncStorage_db_size * 1024L * 1024L;
|
||||||
|
|
||||||
|
private ReactDatabaseSupplier(Context context) {
|
||||||
|
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReactDatabaseSupplier getInstance(Context context) {
|
||||||
|
if (sReactDatabaseSupplierInstance == null) {
|
||||||
|
sReactDatabaseSupplierInstance = new ReactDatabaseSupplier(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
return sReactDatabaseSupplierInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL(VERSION_TABLE_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
if (oldVersion != newVersion) {
|
||||||
|
deleteDatabase();
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the database exists and is open.
|
||||||
|
*/
|
||||||
|
/* package */ synchronized boolean ensureDatabase() {
|
||||||
|
if (mDb != null && mDb.isOpen()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Sometimes retrieving the database fails. We do 2 retries: first without database deletion
|
||||||
|
// and then with deletion.
|
||||||
|
SQLiteException lastSQLiteException = null;
|
||||||
|
for (int tries = 0; tries < 2; tries++) {
|
||||||
|
try {
|
||||||
|
if (tries > 0) {
|
||||||
|
deleteDatabase();
|
||||||
|
}
|
||||||
|
mDb = getWritableDatabase();
|
||||||
|
break;
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
lastSQLiteException = e;
|
||||||
|
}
|
||||||
|
// Wait before retrying.
|
||||||
|
try {
|
||||||
|
Thread.sleep(SLEEP_TIME_MS);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mDb == null) {
|
||||||
|
throw lastSQLiteException;
|
||||||
|
}
|
||||||
|
// This is a sane limit to protect the user from the app storing too much data in the database.
|
||||||
|
// This also protects the database from filling up the disk cache and becoming malformed
|
||||||
|
// (endTransaction() calls will throw an exception, not rollback, and leave the db malformed).
|
||||||
|
mDb.setMaximumSize(mMaximumDatabaseSize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and/or open the database.
|
||||||
|
*/
|
||||||
|
public synchronized SQLiteDatabase get() {
|
||||||
|
ensureDatabase();
|
||||||
|
return mDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void clearAndCloseDatabase() throws RuntimeException {
|
||||||
|
try {
|
||||||
|
clear();
|
||||||
|
closeDatabase();
|
||||||
|
FLog.d(ReactConstants.TAG, "Cleaned " + DATABASE_NAME);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Clearing the database has failed, delete it instead.
|
||||||
|
if (deleteDatabase()) {
|
||||||
|
FLog.d(ReactConstants.TAG, "Deleted Local Database " + DATABASE_NAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Everything failed, throw
|
||||||
|
throw new RuntimeException("Clearing and deleting database " + DATABASE_NAME + " failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ synchronized void clear() {
|
||||||
|
get().delete(TABLE_CATALYST, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum size the database will grow to. The maximum size cannot
|
||||||
|
* be set below the current size.
|
||||||
|
*/
|
||||||
|
public synchronized void setMaximumSize(long size) {
|
||||||
|
mMaximumDatabaseSize = size;
|
||||||
|
if (mDb != null) {
|
||||||
|
mDb.setMaximumSize(mMaximumDatabaseSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized boolean deleteDatabase() {
|
||||||
|
closeDatabase();
|
||||||
|
return mContext.deleteDatabase(DATABASE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void closeDatabase() {
|
||||||
|
if (mDb != null && mDb.isOpen()) {
|
||||||
|
mDb.close();
|
||||||
|
mDb = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing purposes only!
|
||||||
|
public static void deleteInstance() {
|
||||||
|
sReactDatabaseSupplierInstance = null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detox is using this implementation detail in its environment setup,
|
||||||
|
* so in order for Next storage to work, this class has been made public
|
||||||
|
*
|
||||||
|
* Adapted from https://android.googlesource.com/platform/frameworks/base.git/+/1488a3a19d4681a41fb45570c15e14d99db1cb66/core/java/android/os/AsyncTask.java#237
|
||||||
|
*/
|
||||||
|
public class SerialExecutor implements Executor {
|
||||||
|
private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
|
||||||
|
private Runnable mActive;
|
||||||
|
private final Executor executor;
|
||||||
|
|
||||||
|
public SerialExecutor(Executor executor) {
|
||||||
|
this.executor = executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void execute(final Runnable r) {
|
||||||
|
mTasks.offer(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
r.run();
|
||||||
|
} finally {
|
||||||
|
scheduleNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (mActive == null) {
|
||||||
|
scheduleNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized void scheduleNext() {
|
||||||
|
if ((mActive = mTasks.poll()) != null) {
|
||||||
|
executor.execute(mActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage.next
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Arguments
|
||||||
|
import com.facebook.react.bridge.ReadableArray
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
fun ReadableArray.toEntryList(): List<Entry> {
|
||||||
|
val list = mutableListOf<Entry>()
|
||||||
|
for (keyValue in this.toArrayList()) {
|
||||||
|
if (keyValue !is ArrayList<*> || keyValue.size != 2) {
|
||||||
|
throw AsyncStorageError.invalidKeyValueFormat()
|
||||||
|
}
|
||||||
|
val key = keyValue[0]
|
||||||
|
val value = keyValue[1]
|
||||||
|
|
||||||
|
if (key !is String) {
|
||||||
|
when (key) {
|
||||||
|
null -> throw AsyncStorageError.keyIsNull()
|
||||||
|
else -> throw AsyncStorageError.keyNotString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !is String) {
|
||||||
|
throw AsyncStorageError.valueNotString(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
list.add(Entry(key, value))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ReadableArray.toKeyList(): List<String> {
|
||||||
|
val list = this.toArrayList()
|
||||||
|
|
||||||
|
for (item in list) {
|
||||||
|
if (item !is String) {
|
||||||
|
throw AsyncStorageError.keyNotString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list as List<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Entry>.toKeyValueArgument(): ReadableArray {
|
||||||
|
val args = Arguments.createArray()
|
||||||
|
|
||||||
|
for (entry in this) {
|
||||||
|
val keyValue = Arguments.createArray()
|
||||||
|
keyValue.pushString(entry.key)
|
||||||
|
keyValue.pushString(entry.value)
|
||||||
|
args.pushArray(keyValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String?.isValidJson(): Boolean {
|
||||||
|
if (this == null) return false
|
||||||
|
|
||||||
|
return try {
|
||||||
|
JSONObject(this)
|
||||||
|
true
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JSONObject.mergeWith(newObject: JSONObject): JSONObject {
|
||||||
|
|
||||||
|
val keys = newObject.keys()
|
||||||
|
val mergedObject = JSONObject(this.toString())
|
||||||
|
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
val key = keys.next()
|
||||||
|
val curValue = this.optJSONObject(key)
|
||||||
|
val newValue = newObject.optJSONObject(key)
|
||||||
|
|
||||||
|
if (curValue != null && newValue != null) {
|
||||||
|
val merged = curValue.mergeWith(newValue)
|
||||||
|
mergedObject.put(key, merged)
|
||||||
|
} else {
|
||||||
|
mergedObject.put(key, newObject.get(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedObject
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage.next
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Arguments
|
||||||
|
import com.facebook.react.bridge.Callback
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
|
||||||
|
internal fun createExceptionHandler(cb: Callback): CoroutineExceptionHandler {
|
||||||
|
return CoroutineExceptionHandler { _, throwable ->
|
||||||
|
val error = Arguments.createMap()
|
||||||
|
if (throwable !is AsyncStorageError) {
|
||||||
|
error.putString(
|
||||||
|
"message", "Unexpected AsyncStorage error: ${throwable.localizedMessage}"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
error.putString("message", throwable.errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AsyncStorageError private constructor(val errorMessage: String) :
|
||||||
|
Throwable(errorMessage) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun keyIsNull() = AsyncStorageError("Key cannot be null.")
|
||||||
|
|
||||||
|
fun keyNotString() = AsyncStorageError("Provided key is not string. Only strings are supported as storage key.")
|
||||||
|
|
||||||
|
fun valueNotString(key: String?): AsyncStorageError {
|
||||||
|
val detail = if (key == null) "Provided value" else "Value for key \"$key\""
|
||||||
|
return AsyncStorageError("$detail is not a string. Only strings are supported as a value.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invalidKeyValueFormat() =
|
||||||
|
AsyncStorageError("Invalid key-value format. Expected a list of [key, value] list.")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage.next
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.facebook.react.bridge.Arguments
|
||||||
|
import com.facebook.react.bridge.Callback
|
||||||
|
import com.facebook.react.bridge.ReactContext
|
||||||
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||||
|
import com.facebook.react.bridge.ReactMethod
|
||||||
|
import com.facebook.react.bridge.ReadableArray
|
||||||
|
import com.reactnativecommunity.asyncstorage.SerialExecutor
|
||||||
|
import kotlinx.coroutines.CoroutineName
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.asExecutor
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), CoroutineScope {
|
||||||
|
override fun getName() = "RNC_AsyncSQLiteDBStorage"
|
||||||
|
|
||||||
|
// this executor is not used by the module, but it must exists here due to
|
||||||
|
// Detox relying on this implementation detail to run
|
||||||
|
@VisibleForTesting
|
||||||
|
private val executor = SerialExecutor(Dispatchers.Main.asExecutor())
|
||||||
|
|
||||||
|
override val coroutineContext =
|
||||||
|
Dispatchers.IO + CoroutineName("AsyncStorageScope") + SupervisorJob()
|
||||||
|
|
||||||
|
private val storage = StorageSupplier.getInstance(reactContext)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun getStorageInstance(ctx: Context): AsyncStorageAccess {
|
||||||
|
return StorageSupplier.getInstance(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun multiGet(keys: ReadableArray, cb: Callback) {
|
||||||
|
launch(createExceptionHandler(cb)) {
|
||||||
|
val entries = storage.getValues(keys.toKeyList())
|
||||||
|
cb(null, entries.toKeyValueArgument())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun multiSet(keyValueArray: ReadableArray, cb: Callback) {
|
||||||
|
launch(createExceptionHandler(cb)) {
|
||||||
|
val entries = keyValueArray.toEntryList()
|
||||||
|
storage.setValues(entries)
|
||||||
|
cb(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun multiRemove(keys: ReadableArray, cb: Callback) {
|
||||||
|
launch(createExceptionHandler(cb)) {
|
||||||
|
storage.removeValues(keys.toKeyList())
|
||||||
|
cb(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun multiMerge(keyValueArray: ReadableArray, cb: Callback) {
|
||||||
|
launch(createExceptionHandler(cb)) {
|
||||||
|
val entries = keyValueArray.toEntryList()
|
||||||
|
storage.mergeValues(entries)
|
||||||
|
cb(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun getAllKeys(cb: Callback) {
|
||||||
|
launch(createExceptionHandler(cb)) {
|
||||||
|
val keys = storage.getKeys()
|
||||||
|
val result = Arguments.createArray()
|
||||||
|
keys.forEach { result.pushString(it) }
|
||||||
|
cb.invoke(null, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun clear(cb: Callback) {
|
||||||
|
launch(createExceptionHandler(cb)) {
|
||||||
|
storage.clear()
|
||||||
|
cb(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage.next
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.room.Transaction
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
private const val DATABASE_VERSION = 2
|
||||||
|
private const val DATABASE_NAME = "AsyncStorage"
|
||||||
|
private const val TABLE_NAME = "Storage"
|
||||||
|
private const val COLUMN_KEY = "key"
|
||||||
|
private const val COLUMN_VALUE = "value"
|
||||||
|
|
||||||
|
|
||||||
|
@Entity(tableName = TABLE_NAME)
|
||||||
|
data class Entry(
|
||||||
|
@PrimaryKey @ColumnInfo(name = COLUMN_KEY) val key: String,
|
||||||
|
@ColumnInfo(name = COLUMN_VALUE) val value: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
internal interface StorageDao {
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM $TABLE_NAME WHERE `$COLUMN_KEY` IN (:keys)")
|
||||||
|
suspend fun getValues(keys: List<String>): List<Entry>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun setValues(entries: List<Entry>)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("DELETE FROM $TABLE_NAME WHERE `$COLUMN_KEY` in (:keys)")
|
||||||
|
suspend fun removeValues(keys: List<String>)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
suspend fun mergeValues(entries: List<Entry>) {
|
||||||
|
val currentDbEntries = getValues(entries.map { it.key })
|
||||||
|
val newEntries = mutableListOf<Entry>()
|
||||||
|
|
||||||
|
entries.forEach { newEntry ->
|
||||||
|
val oldEntry = currentDbEntries.find { it.key == newEntry.key }
|
||||||
|
if (oldEntry?.value == null) {
|
||||||
|
newEntries.add(newEntry)
|
||||||
|
} else if (!oldEntry.value.isValidJson() || !newEntry.value.isValidJson()) {
|
||||||
|
newEntries.add(newEntry)
|
||||||
|
} else {
|
||||||
|
val newValue =
|
||||||
|
JSONObject(oldEntry.value).mergeWith(JSONObject(newEntry.value)).toString()
|
||||||
|
newEntries.add(newEntry.copy(value = newValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setValues(newEntries)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT `$COLUMN_KEY` FROM $TABLE_NAME")
|
||||||
|
suspend fun getKeys(): List<String>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("DELETE FROM $TABLE_NAME")
|
||||||
|
suspend fun clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous version of AsyncStorage is violating the SQL standard (based on bug in SQLite),
|
||||||
|
* where PrimaryKey ('key' column) should never be null (https://www.sqlite.org/lang_createtable.html#the_primary_key).
|
||||||
|
* Because of that, we cannot reuse the old DB, because ROOM is guarded against that case (won't compile).
|
||||||
|
*
|
||||||
|
* In order to work around this, two steps are necessary:
|
||||||
|
* - Room DB pre-population from the old database file (https://developer.android.com/training/data-storage/room/prepopulate#from-asset)
|
||||||
|
* - Version migration, so that we can mark 'key' column as NOT-NULL
|
||||||
|
*
|
||||||
|
* This migration will happens only once, when developer enable this feature (when DB is still not created).
|
||||||
|
*/
|
||||||
|
@Suppress("ClassName")
|
||||||
|
private object MIGRATION_TO_NEXT : Migration(1, 2) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
val oldTableName = "catalystLocalStorage" // from ReactDatabaseSupplier
|
||||||
|
database.execSQL("CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`${COLUMN_KEY}` TEXT NOT NULL, `${COLUMN_VALUE}` TEXT, PRIMARY KEY(`${COLUMN_KEY}`));")
|
||||||
|
// even if the old AsyncStorage has checks for not nullable keys
|
||||||
|
// make sure we don't copy any, to not fail migration
|
||||||
|
database.execSQL("DELETE FROM $oldTableName WHERE `${COLUMN_KEY}` IS NULL")
|
||||||
|
database.execSQL(
|
||||||
|
"""
|
||||||
|
INSERT INTO $TABLE_NAME (`${COLUMN_KEY}`, `${COLUMN_VALUE}`)
|
||||||
|
SELECT `${COLUMN_KEY}`, `${COLUMN_VALUE}`
|
||||||
|
FROM $oldTableName;
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
Log.e("AsyncStorage_Next", "Migration to Next storage completed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Database(entities = [Entry::class], version = DATABASE_VERSION, exportSchema = true)
|
||||||
|
internal abstract class StorageDb : RoomDatabase() {
|
||||||
|
abstract fun storage(): StorageDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var instance: StorageDb? = null
|
||||||
|
|
||||||
|
fun getDatabase(context: Context): StorageDb {
|
||||||
|
var inst = instance
|
||||||
|
if (inst != null) {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
synchronized(this) {
|
||||||
|
val oldDbFile = context.getDatabasePath("RKStorage")
|
||||||
|
val db = Room.databaseBuilder(
|
||||||
|
context, StorageDb::class.java, DATABASE_NAME
|
||||||
|
)
|
||||||
|
if (oldDbFile.exists()) {
|
||||||
|
// migrate data from old database, if it exists
|
||||||
|
db.createFromFile(oldDbFile).addMigrations(MIGRATION_TO_NEXT)
|
||||||
|
}
|
||||||
|
inst = db.build()
|
||||||
|
instance = inst
|
||||||
|
return instance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AsyncStorageAccess {
|
||||||
|
suspend fun getValues(keys: List<String>): List<Entry>
|
||||||
|
suspend fun setValues(entries: List<Entry>)
|
||||||
|
suspend fun removeValues(keys: List<String>)
|
||||||
|
suspend fun getKeys(): List<String>
|
||||||
|
suspend fun clear()
|
||||||
|
suspend fun mergeValues(entries: List<Entry>)
|
||||||
|
}
|
||||||
|
|
||||||
|
class StorageSupplier internal constructor(db: StorageDb) : AsyncStorageAccess {
|
||||||
|
companion object {
|
||||||
|
fun getInstance(ctx: Context): AsyncStorageAccess {
|
||||||
|
return StorageSupplier(StorageDb.getDatabase(ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val access = db.storage()
|
||||||
|
|
||||||
|
override suspend fun getValues(keys: List<String>) = access.getValues(keys)
|
||||||
|
override suspend fun setValues(entries: List<Entry>) = access.setValues(entries)
|
||||||
|
override suspend fun removeValues(keys: List<String>) = access.removeValues(keys)
|
||||||
|
override suspend fun mergeValues(entries: List<Entry>) = access.mergeValues(entries)
|
||||||
|
override suspend fun getKeys() = access.getKeys()
|
||||||
|
override suspend fun clear() = access.clear()
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage.next
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.JavaOnlyArray
|
||||||
|
import com.facebook.react.bridge.ReadableArray
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Assert.assertThrows
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.BlockJUnit4ClassRunner
|
||||||
|
|
||||||
|
@RunWith(BlockJUnit4ClassRunner::class)
|
||||||
|
class ArgumentHelpersTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun transformsArgumentsToEntryList() {
|
||||||
|
val args = JavaOnlyArray.of(
|
||||||
|
arrayListOf("key1", "value1"),
|
||||||
|
arrayListOf("key2", "value2"),
|
||||||
|
arrayListOf("key3", "value3")
|
||||||
|
)
|
||||||
|
assertThat(args.toEntryList()).isEqualTo(
|
||||||
|
listOf(
|
||||||
|
Entry("key1", "value1"),
|
||||||
|
Entry("key2", "value2"),
|
||||||
|
Entry("key3", "value3"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun transfersArgumentsToKeyList() {
|
||||||
|
val keyList = listOf("key1", "key2", "key3")
|
||||||
|
val args = keyList.toReadableArray()
|
||||||
|
assertThat(args.toKeyList()).isEqualTo(keyList)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun throwsIfArgumentsNotValidFormat() {
|
||||||
|
val invalid = arrayListOf("invalid")
|
||||||
|
val args = JavaOnlyArray.of(invalid)
|
||||||
|
val error = assertThrows(AsyncStorageError::class.java) {
|
||||||
|
args.toEntryList()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(error is AsyncStorageError).isTrue()
|
||||||
|
assertThat(error).hasMessageThat()
|
||||||
|
.isEqualTo("Invalid key-value format. Expected a list of [key, value] list.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun throwsIfArgumentKeyIsNullOrNotString() {
|
||||||
|
val argsInvalidNull = JavaOnlyArray.of(arrayListOf(null, "invalid"))
|
||||||
|
val errorArgsInvalidNull = assertThrows(AsyncStorageError::class.java) {
|
||||||
|
argsInvalidNull.toEntryList()
|
||||||
|
}
|
||||||
|
assertThat(errorArgsInvalidNull is AsyncStorageError).isTrue()
|
||||||
|
assertThat(errorArgsInvalidNull).hasMessageThat().isEqualTo("Key cannot be null.")
|
||||||
|
|
||||||
|
val notStringArgs = JavaOnlyArray.of(arrayListOf(123, "invalid"))
|
||||||
|
val errorNotString = assertThrows(AsyncStorageError::class.java) {
|
||||||
|
notStringArgs.toEntryList()
|
||||||
|
}
|
||||||
|
assertThat(errorNotString is AsyncStorageError).isTrue()
|
||||||
|
assertThat(errorNotString).hasMessageThat()
|
||||||
|
.isEqualTo("Provided key is not string. Only strings are supported as storage key.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun throwsIfArgumentValueNotString() {
|
||||||
|
val invalidArgs = JavaOnlyArray.of(arrayListOf("my_key", 666))
|
||||||
|
val error = assertThrows(AsyncStorageError::class.java) {
|
||||||
|
invalidArgs.toEntryList()
|
||||||
|
}
|
||||||
|
assertThat(error is AsyncStorageError).isTrue()
|
||||||
|
assertThat(error).hasMessageThat()
|
||||||
|
.isEqualTo("Value for key \"my_key\" is not a string. Only strings are supported as a value.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Any?>.toReadableArray(): ReadableArray {
|
||||||
|
val arr = JavaOnlyArray()
|
||||||
|
forEach {
|
||||||
|
when (it) {
|
||||||
|
null -> arr.pushNull()
|
||||||
|
is Boolean -> arr.pushBoolean(it)
|
||||||
|
is Double -> arr.pushDouble(it)
|
||||||
|
is Int -> arr.pushInt(it)
|
||||||
|
is String -> arr.pushString(it)
|
||||||
|
else -> throw NotImplementedError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
package com.reactnativecommunity.asyncstorage.next
|
||||||
|
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class AsyncStorageAccessTest {
|
||||||
|
private lateinit var asyncStorage: AsyncStorageAccess
|
||||||
|
private lateinit var database: StorageDb
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
database = Room.inMemoryDatabaseBuilder(
|
||||||
|
InstrumentationRegistry.getInstrumentation().context, StorageDb::class.java
|
||||||
|
).allowMainThreadQueries().build()
|
||||||
|
asyncStorage = StorageSupplier(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
database.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun performsBasicGetSetRemoveOperations() = runBlocking {
|
||||||
|
val entriesCount = 10
|
||||||
|
val entries = createRandomEntries(entriesCount)
|
||||||
|
val keys = entries.map { it.key }
|
||||||
|
assertThat(asyncStorage.getValues(keys)).hasSize(0)
|
||||||
|
asyncStorage.setValues(entries)
|
||||||
|
assertThat(asyncStorage.getValues(keys)).hasSize(entriesCount)
|
||||||
|
val indicesToRemove = (1..4).map { Random.nextInt(0, entriesCount) }.distinct()
|
||||||
|
val toRemove = entries.filterIndexed { index, _ -> indicesToRemove.contains(index) }
|
||||||
|
asyncStorage.removeValues(toRemove.map { it.key })
|
||||||
|
val currentEntries = asyncStorage.getValues(keys)
|
||||||
|
assertThat(currentEntries).hasSize(entriesCount - toRemove.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun readsAllKeysAndClearsDb() = runBlocking {
|
||||||
|
val entries = createRandomEntries(8)
|
||||||
|
val keys = entries.map { it.key }
|
||||||
|
asyncStorage.setValues(entries)
|
||||||
|
val dbKeys = asyncStorage.getKeys()
|
||||||
|
assertThat(dbKeys).isEqualTo(keys)
|
||||||
|
asyncStorage.clear()
|
||||||
|
assertThat(asyncStorage.getValues(keys)).hasSize(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mergesDeeplyTwoValues() = runBlocking {
|
||||||
|
val initialEntry = Entry("key", VALUE_INITIAL)
|
||||||
|
val overrideEntry = Entry("key", VALUE_OVERRIDES)
|
||||||
|
asyncStorage.setValues(listOf(initialEntry))
|
||||||
|
asyncStorage.mergeValues(listOf(overrideEntry))
|
||||||
|
val current = asyncStorage.getValues(listOf("key"))[0]
|
||||||
|
assertThat(current.value).isEqualTo(VALUE_MERGED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updatesExistingValues() = runBlocking {
|
||||||
|
val key = "test_key"
|
||||||
|
val value = "test_value"
|
||||||
|
val entries = listOf(Entry(key, value))
|
||||||
|
assertThat(asyncStorage.getValues(listOf(key))).hasSize(0)
|
||||||
|
asyncStorage.setValues(entries)
|
||||||
|
assertThat(asyncStorage.getValues(listOf(key))).isEqualTo(entries)
|
||||||
|
val modifiedEntries = listOf(Entry(key, "updatedValue"))
|
||||||
|
asyncStorage.setValues(modifiedEntries)
|
||||||
|
assertThat(asyncStorage.getValues(listOf(key))).isEqualTo(modifiedEntries)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test Helpers
|
||||||
|
private fun createRandomEntries(count: Int = Random.nextInt(10)): List<Entry> {
|
||||||
|
val entries = mutableListOf<Entry>()
|
||||||
|
for (i in 0 until count) {
|
||||||
|
entries.add(Entry("key$i", "value$i"))
|
||||||
|
}
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
private val VALUE_INITIAL = JSONObject(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"key":"value",
|
||||||
|
"key2":"override",
|
||||||
|
"key3":{
|
||||||
|
"key4":"value4",
|
||||||
|
"key6":{
|
||||||
|
"key7":"value7",
|
||||||
|
"key8":"override"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimMargin()
|
||||||
|
).toString()
|
||||||
|
|
||||||
|
private val VALUE_OVERRIDES = JSONObject(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"key2":"value2",
|
||||||
|
"key3":{
|
||||||
|
"key5":"value5",
|
||||||
|
"key6":{
|
||||||
|
"key8":"value8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
).toString()
|
||||||
|
|
||||||
|
|
||||||
|
private val VALUE_MERGED = JSONObject(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"key":"value",
|
||||||
|
"key2":"value2",
|
||||||
|
"key3":{
|
||||||
|
"key4":"value4",
|
||||||
|
"key6":{
|
||||||
|
"key7":"value7",
|
||||||
|
"key8":"value8"
|
||||||
|
},
|
||||||
|
"key5":"value5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimMargin()
|
||||||
|
).toString()
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
// pretty print test results
|
||||||
|
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||||
|
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||||
|
tasks.withType(Test) {
|
||||||
|
testLogging {
|
||||||
|
|
||||||
|
events TestLogEvent.FAILED,
|
||||||
|
TestLogEvent.PASSED,
|
||||||
|
TestLogEvent.SKIPPED,
|
||||||
|
TestLogEvent.STANDARD_OUT
|
||||||
|
exceptionFormat TestExceptionFormat.FULL
|
||||||
|
showExceptions true
|
||||||
|
showCauses true
|
||||||
|
showStackTraces true
|
||||||
|
|
||||||
|
debug {
|
||||||
|
events TestLogEvent.STARTED,
|
||||||
|
TestLogEvent.FAILED,
|
||||||
|
TestLogEvent.PASSED,
|
||||||
|
TestLogEvent.SKIPPED,
|
||||||
|
TestLogEvent.STANDARD_ERROR,
|
||||||
|
TestLogEvent.STANDARD_OUT
|
||||||
|
exceptionFormat TestExceptionFormat.FULL
|
||||||
|
}
|
||||||
|
info.events = debug.events
|
||||||
|
info.exceptionFormat = debug.exceptionFormat
|
||||||
|
|
||||||
|
afterSuite { desc, result ->
|
||||||
|
if (!desc.parent) { // will match the outermost suite
|
||||||
|
def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
|
||||||
|
def startItem = '| ', endItem = ' |'
|
||||||
|
def repeatLength = startItem.length() + output.length() + endItem.length()
|
||||||
|
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#import <React/RCTInvalidating.h>
|
||||||
|
|
||||||
|
#import "RNCAsyncStorageDelegate.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple, asynchronous, persistent, key-value storage system designed as a
|
||||||
|
* backend to the AsyncStorage JS module, which is modeled after LocalStorage.
|
||||||
|
*
|
||||||
|
* Current implementation stores small values in serialized dictionary and
|
||||||
|
* larger values in separate files. Since we use a serial file queue
|
||||||
|
* `RKFileQueue`, reading/writing from multiple threads should be perceived as
|
||||||
|
* being atomic, unless someone bypasses the `RNCAsyncStorage` API.
|
||||||
|
*
|
||||||
|
* Keys and values must always be strings or an error is returned.
|
||||||
|
*/
|
||||||
|
@interface RNCAsyncStorage : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||||
|
|
||||||
|
@property (nonatomic, weak, nullable) id<RNCAsyncStorageDelegate> delegate;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL clearOnInvalidate;
|
||||||
|
|
||||||
|
@property (nonatomic, readonly, getter=isValid) BOOL valid;
|
||||||
|
|
||||||
|
// Clear the RNCAsyncStorage data from native code
|
||||||
|
- (void)clearAllData;
|
||||||
|
|
||||||
|
// For clearing data when the bridge may not exist, e.g. when logging out.
|
||||||
|
+ (void)clearAllData;
|
||||||
|
|
||||||
|
// Grab data from the cache. ResponseBlock result array will have an error at position 0, and an
|
||||||
|
// array of arrays at position 1.
|
||||||
|
- (void)multiGet:(NSArray<NSString *> *)keys callback:(RCTResponseSenderBlock)callback;
|
||||||
|
|
||||||
|
// Add multiple key value pairs to the cache.
|
||||||
|
- (void)multiSet:(NSArray<NSArray<NSString *> *> *)kvPairs
|
||||||
|
callback:(RCTResponseSenderBlock)callback;
|
||||||
|
|
||||||
|
// Interface for natively fetching all the keys from the storage data.
|
||||||
|
- (void)getAllKeys:(RCTResponseSenderBlock)callback;
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,898 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RNCAsyncStorage.h"
|
||||||
|
|
||||||
|
#import <CommonCrypto/CommonCryptor.h>
|
||||||
|
#import <CommonCrypto/CommonDigest.h>
|
||||||
|
|
||||||
|
#import <React/RCTConvert.h>
|
||||||
|
#import <React/RCTLog.h>
|
||||||
|
#import <React/RCTUtils.h>
|
||||||
|
|
||||||
|
static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1";
|
||||||
|
static NSString *const RCTOldStorageDirectory = @"RNCAsyncLocalStorage_V1";
|
||||||
|
static NSString *const RCTExpoStorageDirectory = @"RCTAsyncLocalStorage";
|
||||||
|
static NSString *const RCTManifestFileName = @"manifest.json";
|
||||||
|
static const NSUInteger RCTInlineValueThreshold = 1024;
|
||||||
|
|
||||||
|
#pragma mark - Static helper functions
|
||||||
|
|
||||||
|
static NSDictionary *RCTErrorForKey(NSString *key)
|
||||||
|
{
|
||||||
|
if (![key isKindOfClass:[NSString class]]) {
|
||||||
|
return RCTMakeAndLogError(@"Invalid key - must be a string. Key: ", key, @{@"key": key});
|
||||||
|
} else if (key.length < 1) {
|
||||||
|
return RCTMakeAndLogError(
|
||||||
|
@"Invalid key - must be at least one character. Key: ", key, @{@"key": key});
|
||||||
|
} else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL RCTAsyncStorageSetExcludedFromBackup(NSString *path, NSNumber *isExcluded)
|
||||||
|
{
|
||||||
|
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
||||||
|
|
||||||
|
BOOL isDir;
|
||||||
|
BOOL exists = [fileManager fileExistsAtPath:path isDirectory:&isDir];
|
||||||
|
BOOL success = false;
|
||||||
|
|
||||||
|
if (isDir && exists) {
|
||||||
|
NSURL *pathUrl = [NSURL fileURLWithPath:path];
|
||||||
|
NSError *error = nil;
|
||||||
|
success = [pathUrl setResourceValue:isExcluded
|
||||||
|
forKey:NSURLIsExcludedFromBackupKey
|
||||||
|
error:&error];
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
NSLog(@"Could not exclude AsyncStorage dir from backup %@", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> **errors)
|
||||||
|
{
|
||||||
|
if (error && errors) {
|
||||||
|
if (!*errors) {
|
||||||
|
*errors = [NSMutableArray new];
|
||||||
|
}
|
||||||
|
[*errors addObject:error];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSArray<NSDictionary *> *RCTMakeErrors(NSArray<id<NSObject>> *results)
|
||||||
|
{
|
||||||
|
NSMutableArray<NSDictionary *> *errors;
|
||||||
|
for (id object in results) {
|
||||||
|
if ([object isKindOfClass:[NSError class]]) {
|
||||||
|
NSError *error = (NSError *)object;
|
||||||
|
NSDictionary *keyError = RCTMakeError(error.localizedDescription, error, nil);
|
||||||
|
RCTAppendError(keyError, &errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSString *RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut)
|
||||||
|
{
|
||||||
|
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
|
||||||
|
NSError *error;
|
||||||
|
NSStringEncoding encoding;
|
||||||
|
NSString *entryString = [NSString stringWithContentsOfFile:filePath
|
||||||
|
usedEncoding:&encoding
|
||||||
|
error:&error];
|
||||||
|
NSDictionary *extraData = @{@"key": RCTNullIfNil(key)};
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
if (errorOut) {
|
||||||
|
*errorOut = RCTMakeError(@"Failed to read storage file.", error, extraData);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoding != NSUTF8StringEncoding) {
|
||||||
|
if (errorOut) {
|
||||||
|
*errorOut =
|
||||||
|
RCTMakeError(@"Incorrect encoding of storage file: ", @(encoding), extraData);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return entryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO NOT USE
|
||||||
|
// This is used internally to migrate data from the old file location to the new one.
|
||||||
|
// Please use `RCTCreateStorageDirectoryPath` instead
|
||||||
|
static NSString *RCTCreateStorageDirectoryPath_deprecated(NSString *storageDir)
|
||||||
|
{
|
||||||
|
NSString *storageDirectoryPath;
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
storageDirectoryPath =
|
||||||
|
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
||||||
|
#else
|
||||||
|
storageDirectoryPath =
|
||||||
|
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
|
||||||
|
#endif
|
||||||
|
storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
|
||||||
|
return storageDirectoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSString *RCTCreateStorageDirectoryPath(NSString *storageDir)
|
||||||
|
{
|
||||||
|
NSString *storageDirectoryPath = @"";
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
storageDirectoryPath =
|
||||||
|
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
||||||
|
#else
|
||||||
|
storageDirectoryPath =
|
||||||
|
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
|
||||||
|
.firstObject;
|
||||||
|
// We should use the "Application Support/[bundleID]" folder for persistent data storage that's
|
||||||
|
// hidden from users
|
||||||
|
storageDirectoryPath = [storageDirectoryPath
|
||||||
|
stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Per Apple's docs, all app content in Application Support must be within a subdirectory of the
|
||||||
|
// app's bundle identifier
|
||||||
|
storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
|
||||||
|
|
||||||
|
return storageDirectoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSString *RCTGetStorageDirectory()
|
||||||
|
{
|
||||||
|
static NSString *storageDirectory = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
storageDirectory = RCTCreateStorageDirectoryPath(RCTStorageDirectory);
|
||||||
|
});
|
||||||
|
return storageDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSString *RCTCreateManifestFilePath(NSString *storageDirectory)
|
||||||
|
{
|
||||||
|
return [storageDirectory stringByAppendingPathComponent:RCTManifestFileName];
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSString *RCTGetManifestFilePath()
|
||||||
|
{
|
||||||
|
static NSString *manifestFilePath = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
manifestFilePath = RCTCreateManifestFilePath(RCTStorageDirectory);
|
||||||
|
});
|
||||||
|
return manifestFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only merges objects - all other types are just clobbered (including arrays)
|
||||||
|
static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source)
|
||||||
|
{
|
||||||
|
BOOL modified = NO;
|
||||||
|
for (NSString *key in source) {
|
||||||
|
id sourceValue = source[key];
|
||||||
|
id destinationValue = destination[key];
|
||||||
|
if ([sourceValue isKindOfClass:[NSDictionary class]]) {
|
||||||
|
if ([destinationValue isKindOfClass:[NSDictionary class]]) {
|
||||||
|
if ([destinationValue classForCoder] != [NSMutableDictionary class]) {
|
||||||
|
destinationValue = [destinationValue mutableCopy];
|
||||||
|
}
|
||||||
|
if (RCTMergeRecursive(destinationValue, sourceValue)) {
|
||||||
|
destination[key] = destinationValue;
|
||||||
|
modified = YES;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
destination[key] = [sourceValue copy];
|
||||||
|
modified = YES;
|
||||||
|
}
|
||||||
|
} else if (![source isEqual:destinationValue]) {
|
||||||
|
destination[key] = [sourceValue copy];
|
||||||
|
modified = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dispatch_queue_t RCTGetMethodQueue()
|
||||||
|
{
|
||||||
|
// We want all instances to share the same queue since they will be reading/writing the same
|
||||||
|
// files.
|
||||||
|
static dispatch_queue_t queue;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
queue =
|
||||||
|
dispatch_queue_create("com.facebook.react.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
});
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSCache *RCTGetCache()
|
||||||
|
{
|
||||||
|
// We want all instances to share the same cache since they will be reading/writing the same
|
||||||
|
// files.
|
||||||
|
static NSCache *cache;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
cache = [NSCache new];
|
||||||
|
cache.totalCostLimit = 2 * 1024 * 1024; // 2MB
|
||||||
|
|
||||||
|
#if !TARGET_OS_OSX
|
||||||
|
// Clear cache in the event of a memory warning
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
|
||||||
|
object:nil
|
||||||
|
queue:nil
|
||||||
|
usingBlock:^(__unused NSNotification *note) {
|
||||||
|
[cache removeAllObjects];
|
||||||
|
}];
|
||||||
|
#endif // !TARGET_OS_OSX
|
||||||
|
});
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL RCTHasCreatedStorageDirectory = NO;
|
||||||
|
static NSDictionary *RCTDeleteStorageDirectory()
|
||||||
|
{
|
||||||
|
NSError *error;
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:RCTGetStorageDirectory() error:&error];
|
||||||
|
RCTHasCreatedStorageDirectory = NO;
|
||||||
|
return error ? RCTMakeError(@"Failed to delete storage directory.", error, nil) : nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSDate *RCTManifestModificationDate(NSString *manifestFilePath)
|
||||||
|
{
|
||||||
|
NSDictionary *attributes =
|
||||||
|
[[NSFileManager defaultManager] attributesOfItemAtPath:manifestFilePath error:nil];
|
||||||
|
return [attributes fileModificationDate];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an NSException used during Storage Directory Migration.
|
||||||
|
*/
|
||||||
|
static void RCTStorageDirectoryMigrationLogError(NSString *reason, NSError *error)
|
||||||
|
{
|
||||||
|
RCTLogWarn(@"%@: %@", reason, error ? error.description : @"");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RCTStorageDirectoryCleanupOld(NSString *oldDirectoryPath)
|
||||||
|
{
|
||||||
|
NSError *error;
|
||||||
|
if (![[NSFileManager defaultManager] removeItemAtPath:oldDirectoryPath error:&error]) {
|
||||||
|
RCTStorageDirectoryMigrationLogError(
|
||||||
|
@"Failed to remove old storage directory during migration", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _createStorageDirectory(NSString *storageDirectory, NSError **error)
|
||||||
|
{
|
||||||
|
[[NSFileManager defaultManager] createDirectoryAtPath:storageDirectory
|
||||||
|
withIntermediateDirectories:YES
|
||||||
|
attributes:nil
|
||||||
|
error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath,
|
||||||
|
NSString *newDirectoryPath,
|
||||||
|
BOOL shouldCleanupOldDirectory)
|
||||||
|
{
|
||||||
|
NSError *error;
|
||||||
|
// Migrate data by copying old storage directory to new storage directory location
|
||||||
|
if (![[NSFileManager defaultManager] copyItemAtPath:oldDirectoryPath
|
||||||
|
toPath:newDirectoryPath
|
||||||
|
error:&error]) {
|
||||||
|
// the new storage directory "Application Support/[bundleID]/RCTAsyncLocalStorage_V1" seems
|
||||||
|
// unable to migrate because folder "Application Support/[bundleID]" doesn't exist.. create
|
||||||
|
// this folder and attempt folder copying again
|
||||||
|
if (error != nil && error.code == 4 &&
|
||||||
|
[newDirectoryPath isEqualToString:RCTGetStorageDirectory()]) {
|
||||||
|
NSError *error = nil;
|
||||||
|
_createStorageDirectory(RCTCreateStorageDirectoryPath(@""), &error);
|
||||||
|
if (error == nil) {
|
||||||
|
RCTStorageDirectoryMigrate(
|
||||||
|
oldDirectoryPath, newDirectoryPath, shouldCleanupOldDirectory);
|
||||||
|
} else {
|
||||||
|
RCTStorageDirectoryMigrationLogError(
|
||||||
|
@"Failed to create storage directory during migration.", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RCTStorageDirectoryMigrationLogError(
|
||||||
|
@"Failed to copy old storage directory to new storage directory location during "
|
||||||
|
@"migration",
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
} else if (shouldCleanupOldDirectory) {
|
||||||
|
// If copying succeeds, remove old storage directory
|
||||||
|
RCTStorageDirectoryCleanupOld(oldDirectoryPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine which of RCTOldStorageDirectory or RCTExpoStorageDirectory needs to migrated.
|
||||||
|
* If both exist, we remove the least recently modified and return the most recently modified.
|
||||||
|
* Otherwise, this will return the path to whichever directory exists.
|
||||||
|
* If no directory exists, then return nil.
|
||||||
|
*/
|
||||||
|
static NSString *RCTGetStoragePathForMigration()
|
||||||
|
{
|
||||||
|
BOOL isDir;
|
||||||
|
NSString *oldStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTOldStorageDirectory);
|
||||||
|
NSString *expoStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTExpoStorageDirectory);
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
BOOL oldStorageDirectoryExists =
|
||||||
|
[fileManager fileExistsAtPath:oldStoragePath isDirectory:&isDir] && isDir;
|
||||||
|
BOOL expoStorageDirectoryExists =
|
||||||
|
[fileManager fileExistsAtPath:expoStoragePath isDirectory:&isDir] && isDir;
|
||||||
|
|
||||||
|
// Check if both the old storage directory and Expo storage directory exist
|
||||||
|
if (oldStorageDirectoryExists && expoStorageDirectoryExists) {
|
||||||
|
// If the old storage has been modified more recently than Expo storage, then clear Expo
|
||||||
|
// storage. Otherwise, clear the old storage.
|
||||||
|
if ([RCTManifestModificationDate(RCTCreateManifestFilePath(oldStoragePath))
|
||||||
|
compare:RCTManifestModificationDate(RCTCreateManifestFilePath(expoStoragePath))] ==
|
||||||
|
NSOrderedDescending) {
|
||||||
|
RCTStorageDirectoryCleanupOld(expoStoragePath);
|
||||||
|
return oldStoragePath;
|
||||||
|
} else {
|
||||||
|
RCTStorageDirectoryCleanupOld(oldStoragePath);
|
||||||
|
return expoStoragePath;
|
||||||
|
}
|
||||||
|
} else if (oldStorageDirectoryExists) {
|
||||||
|
return oldStoragePath;
|
||||||
|
} else if (expoStorageDirectoryExists) {
|
||||||
|
return expoStoragePath;
|
||||||
|
} else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This check is added to make sure that anyone coming from pre-1.2.2 does not lose cached data.
|
||||||
|
* Check that data is migrated from the old location to the new location
|
||||||
|
* fromStorageDirectory: the directory where the older data lives
|
||||||
|
* toStorageDirectory: the directory where the new data should live
|
||||||
|
* shouldCleanupOldDirectoryAndOverwriteNewDirectory: YES if we should delete the old directory's
|
||||||
|
* contents and overwrite the new directory's contents during the migration to the new directory
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
RCTStorageDirectoryMigrationCheck(NSString *fromStorageDirectory,
|
||||||
|
NSString *toStorageDirectory,
|
||||||
|
BOOL shouldCleanupOldDirectoryAndOverwriteNewDirectory)
|
||||||
|
{
|
||||||
|
NSError *error;
|
||||||
|
BOOL isDir;
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
// If the old directory exists, it means we may need to migrate old data to the new directory
|
||||||
|
if ([fileManager fileExistsAtPath:fromStorageDirectory isDirectory:&isDir] && isDir) {
|
||||||
|
// Check if the new storage directory location already exists
|
||||||
|
if ([fileManager fileExistsAtPath:toStorageDirectory]) {
|
||||||
|
// If new storage location exists, check if the new storage has been modified sooner in
|
||||||
|
// which case we may want to cleanup the old location
|
||||||
|
if ([RCTManifestModificationDate(RCTCreateManifestFilePath(toStorageDirectory))
|
||||||
|
compare:RCTManifestModificationDate(
|
||||||
|
RCTCreateManifestFilePath(fromStorageDirectory))] == 1) {
|
||||||
|
// If new location has been modified more recently, simply clean out old data
|
||||||
|
if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
|
||||||
|
RCTStorageDirectoryCleanupOld(fromStorageDirectory);
|
||||||
|
}
|
||||||
|
} else if (shouldCleanupOldDirectoryAndOverwriteNewDirectory) {
|
||||||
|
// If old location has been modified more recently, remove new storage and migrate
|
||||||
|
if (![fileManager removeItemAtPath:toStorageDirectory error:&error]) {
|
||||||
|
RCTStorageDirectoryMigrationLogError(
|
||||||
|
@"Failed to remove new storage directory during migration", error);
|
||||||
|
} else {
|
||||||
|
RCTStorageDirectoryMigrate(fromStorageDirectory,
|
||||||
|
toStorageDirectory,
|
||||||
|
shouldCleanupOldDirectoryAndOverwriteNewDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If new storage location doesn't exist, migrate data
|
||||||
|
RCTStorageDirectoryMigrate(fromStorageDirectory,
|
||||||
|
toStorageDirectory,
|
||||||
|
shouldCleanupOldDirectoryAndOverwriteNewDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RNCAsyncStorage
|
||||||
|
|
||||||
|
@implementation RNCAsyncStorage {
|
||||||
|
BOOL _haveSetup;
|
||||||
|
// The manifest is a dictionary of all keys with small values inlined. Null values indicate
|
||||||
|
// values that are stored in separate files (as opposed to nil values which don't exist). The
|
||||||
|
// manifest is read off disk at startup, and written to disk after all mutations.
|
||||||
|
NSMutableDictionary<NSString *, NSString *> *_manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL)requiresMainQueueSetup
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if (!(self = [super init])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the path to any old storage directory that needs to be migrated. If multiple exist,
|
||||||
|
// the oldest are removed and the most recently modified is returned.
|
||||||
|
NSString *oldStoragePath = RCTGetStoragePathForMigration();
|
||||||
|
if (oldStoragePath != nil) {
|
||||||
|
// Migrate our deprecated path "Documents/.../RNCAsyncLocalStorage_V1" or
|
||||||
|
// "Documents/.../RCTAsyncLocalStorage" to "Documents/.../RCTAsyncLocalStorage_V1"
|
||||||
|
RCTStorageDirectoryMigrationCheck(
|
||||||
|
oldStoragePath, RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), YES);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate what's in "Documents/.../RCTAsyncLocalStorage_V1" to
|
||||||
|
// "Application Support/[bundleID]/RCTAsyncLocalStorage_V1"
|
||||||
|
RCTStorageDirectoryMigrationCheck(RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory),
|
||||||
|
RCTCreateStorageDirectoryPath(RCTStorageDirectory),
|
||||||
|
NO);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (dispatch_queue_t)methodQueue
|
||||||
|
{
|
||||||
|
return RCTGetMethodQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearAllData
|
||||||
|
{
|
||||||
|
dispatch_async(RCTGetMethodQueue(), ^{
|
||||||
|
[self->_manifest removeAllObjects];
|
||||||
|
[RCTGetCache() removeAllObjects];
|
||||||
|
RCTDeleteStorageDirectory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)clearAllData
|
||||||
|
{
|
||||||
|
dispatch_async(RCTGetMethodQueue(), ^{
|
||||||
|
[RCTGetCache() removeAllObjects];
|
||||||
|
RCTDeleteStorageDirectory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)invalidate
|
||||||
|
{
|
||||||
|
if (_clearOnInvalidate) {
|
||||||
|
[RCTGetCache() removeAllObjects];
|
||||||
|
RCTDeleteStorageDirectory();
|
||||||
|
}
|
||||||
|
_clearOnInvalidate = NO;
|
||||||
|
[_manifest removeAllObjects];
|
||||||
|
_haveSetup = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isValid
|
||||||
|
{
|
||||||
|
return _haveSetup;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self invalidate];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)_filePathForKey:(NSString *)key
|
||||||
|
{
|
||||||
|
NSString *safeFileName = RCTMD5Hash(key);
|
||||||
|
return [RCTGetStorageDirectory() stringByAppendingPathComponent:safeFileName];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)_ensureSetup
|
||||||
|
{
|
||||||
|
RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread");
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
RCTLogWarn(
|
||||||
|
@"Persistent storage is not supported on tvOS, your data may be removed at any point.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSError *error = nil;
|
||||||
|
if (!RCTHasCreatedStorageDirectory) {
|
||||||
|
_createStorageDirectory(RCTGetStorageDirectory(), &error);
|
||||||
|
if (error) {
|
||||||
|
return RCTMakeError(@"Failed to create storage directory.", error, nil);
|
||||||
|
}
|
||||||
|
RCTHasCreatedStorageDirectory = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_haveSetup) {
|
||||||
|
// iCloud backup exclusion
|
||||||
|
NSNumber *isExcludedFromBackup =
|
||||||
|
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTAsyncStorageExcludeFromBackup"];
|
||||||
|
if (isExcludedFromBackup == nil) {
|
||||||
|
// by default, we want to exclude AsyncStorage data from backup
|
||||||
|
isExcludedFromBackup = @YES;
|
||||||
|
}
|
||||||
|
RCTAsyncStorageSetExcludedFromBackup(RCTCreateStorageDirectoryPath(RCTStorageDirectory),
|
||||||
|
isExcludedFromBackup);
|
||||||
|
|
||||||
|
NSDictionary *errorOut = nil;
|
||||||
|
NSString *serialized = RCTReadFile(RCTCreateStorageDirectoryPath(RCTGetManifestFilePath()),
|
||||||
|
RCTManifestFileName,
|
||||||
|
&errorOut);
|
||||||
|
if (!serialized) {
|
||||||
|
if (errorOut) {
|
||||||
|
// We cannot simply create a new manifest in case the file does exist but we have no
|
||||||
|
// access to it. This can happen when data protection is enabled for the app and we
|
||||||
|
// are trying to read the manifest while the device is locked. (The app can be
|
||||||
|
// started by the system even if the device is locked due to e.g. a geofence event.)
|
||||||
|
RCTLogError(
|
||||||
|
@"Could not open the existing manifest, perhaps data protection is "
|
||||||
|
@"enabled?\n\n%@",
|
||||||
|
errorOut);
|
||||||
|
return errorOut;
|
||||||
|
} else {
|
||||||
|
// We can get nil without errors only when the file does not exist.
|
||||||
|
RCTLogTrace(@"Manifest does not exist - creating a new one.\n\n%@", errorOut);
|
||||||
|
_manifest = [NSMutableDictionary new];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_manifest = RCTJSONParseMutable(serialized, &error);
|
||||||
|
if (!_manifest) {
|
||||||
|
RCTLogError(@"Failed to parse manifest - creating a new one.\n\n%@", error);
|
||||||
|
_manifest = [NSMutableDictionary new];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_haveSetup = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)_writeManifest:(NSMutableArray<NSDictionary *> **)errors
|
||||||
|
{
|
||||||
|
NSError *error;
|
||||||
|
NSString *serialized = RCTJSONStringify(_manifest, &error);
|
||||||
|
[serialized writeToFile:RCTCreateStorageDirectoryPath(RCTGetManifestFilePath())
|
||||||
|
atomically:YES
|
||||||
|
encoding:NSUTF8StringEncoding
|
||||||
|
error:&error];
|
||||||
|
NSDictionary *errorOut;
|
||||||
|
if (error) {
|
||||||
|
errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil);
|
||||||
|
RCTAppendError(errorOut, errors);
|
||||||
|
}
|
||||||
|
return errorOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)_appendItemForKey:(NSString *)key
|
||||||
|
toArray:(NSMutableArray<NSArray<NSString *> *> *)result
|
||||||
|
{
|
||||||
|
NSDictionary *errorOut = RCTErrorForKey(key);
|
||||||
|
if (errorOut) {
|
||||||
|
return errorOut;
|
||||||
|
}
|
||||||
|
NSString *value = [self _getValueForKey:key errorOut:&errorOut];
|
||||||
|
[result addObject:@[key, RCTNullIfNil(value)]]; // Insert null if missing or failure.
|
||||||
|
return errorOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut
|
||||||
|
{
|
||||||
|
NSString *value =
|
||||||
|
_manifest[key]; // nil means missing, null means there may be a data file, else: NSString
|
||||||
|
if (value == (id)kCFNull) {
|
||||||
|
value = [RCTGetCache() objectForKey:key];
|
||||||
|
if (!value) {
|
||||||
|
NSString *filePath = [self _filePathForKey:key];
|
||||||
|
value = RCTReadFile(filePath, key, errorOut);
|
||||||
|
if (value) {
|
||||||
|
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
||||||
|
} else {
|
||||||
|
// file does not exist after all, so remove from manifest (no need to save
|
||||||
|
// manifest immediately though, as cost of checking again next time is negligible)
|
||||||
|
[_manifest removeObjectForKey:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL *)changedManifest
|
||||||
|
{
|
||||||
|
if (entry.count != 2) {
|
||||||
|
return RCTMakeAndLogError(
|
||||||
|
@"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil);
|
||||||
|
}
|
||||||
|
NSString *key = entry[0];
|
||||||
|
NSDictionary *errorOut = RCTErrorForKey(key);
|
||||||
|
if (errorOut) {
|
||||||
|
return errorOut;
|
||||||
|
}
|
||||||
|
NSString *value = entry[1];
|
||||||
|
NSString *filePath = [self _filePathForKey:key];
|
||||||
|
NSError *error;
|
||||||
|
if (value.length <= RCTInlineValueThreshold) {
|
||||||
|
if (_manifest[key] == (id)kCFNull) {
|
||||||
|
// If the value already existed but wasn't inlined, remove the old file.
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
||||||
|
[RCTGetCache() removeObjectForKey:key];
|
||||||
|
}
|
||||||
|
*changedManifest = YES;
|
||||||
|
_manifest[key] = value;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
[value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
|
||||||
|
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
||||||
|
if (error) {
|
||||||
|
errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key});
|
||||||
|
} else if (_manifest[key] != (id)kCFNull) {
|
||||||
|
*changedManifest = YES;
|
||||||
|
_manifest[key] = (id)kCFNull;
|
||||||
|
}
|
||||||
|
return errorOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_multiGet:(NSArray<NSString *> *)keys
|
||||||
|
callback:(RCTResponseSenderBlock)callback
|
||||||
|
getter:(NSString * (^)(NSUInteger i, NSString *key, NSDictionary **errorOut))getValue
|
||||||
|
{
|
||||||
|
NSMutableArray<NSDictionary *> *errors;
|
||||||
|
NSMutableArray<NSArray<NSString *> *> *result = [NSMutableArray arrayWithCapacity:keys.count];
|
||||||
|
for (NSUInteger i = 0; i < keys.count; ++i) {
|
||||||
|
NSString *key = keys[i];
|
||||||
|
id keyError;
|
||||||
|
id value = getValue(i, key, &keyError);
|
||||||
|
[result addObject:@[key, RCTNullIfNil(value)]];
|
||||||
|
RCTAppendError(keyError, &errors);
|
||||||
|
}
|
||||||
|
callback(@[RCTNullIfNil(errors), result]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_passthroughDelegate
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[self.delegate respondsToSelector:@selector(isPassthrough)] && self.delegate.isPassthrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Exported JS Functions
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
RCT_EXPORT_METHOD(multiGet:(NSArray<NSString *> *)keys
|
||||||
|
callback:(RCTResponseSenderBlock)callback)
|
||||||
|
// clang-format on
|
||||||
|
{
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
[self.delegate
|
||||||
|
valuesForKeys:keys
|
||||||
|
completion:^(NSArray<id<NSObject>> *valuesOrErrors) {
|
||||||
|
[self _multiGet:keys
|
||||||
|
callback:callback
|
||||||
|
getter:^NSString *(NSUInteger i, NSString *key, NSDictionary **errorOut) {
|
||||||
|
id valueOrError = valuesOrErrors[i];
|
||||||
|
if ([valueOrError isKindOfClass:[NSError class]]) {
|
||||||
|
NSError *error = (NSError *)valueOrError;
|
||||||
|
NSDictionary *extraData = @{@"key": RCTNullIfNil(key)};
|
||||||
|
*errorOut =
|
||||||
|
RCTMakeError(error.localizedDescription, error, extraData);
|
||||||
|
return nil;
|
||||||
|
} else {
|
||||||
|
return [valueOrError isKindOfClass:[NSString class]]
|
||||||
|
? (NSString *)valueOrError
|
||||||
|
: nil;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (![self _passthroughDelegate]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *errorOut = [self _ensureSetup];
|
||||||
|
if (errorOut) {
|
||||||
|
callback(@[@[errorOut], (id)kCFNull]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[self _multiGet:keys
|
||||||
|
callback:callback
|
||||||
|
getter:^(NSUInteger i, NSString *key, NSDictionary **errorOut) {
|
||||||
|
return [self _getValueForKey:key errorOut:errorOut];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
RCT_EXPORT_METHOD(multiSet:(NSArray<NSArray<NSString *> *> *)kvPairs
|
||||||
|
callback:(RCTResponseSenderBlock)callback)
|
||||||
|
// clang-format on
|
||||||
|
{
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||||
|
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||||
|
for (NSArray<NSString *> *entry in kvPairs) {
|
||||||
|
[keys addObject:entry[0]];
|
||||||
|
[values addObject:entry[1]];
|
||||||
|
}
|
||||||
|
[self.delegate setValues:values
|
||||||
|
forKeys:keys
|
||||||
|
completion:^(NSArray<id<NSObject>> *results) {
|
||||||
|
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
||||||
|
callback(@[RCTNullIfNil(errors)]);
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (![self _passthroughDelegate]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *errorOut = [self _ensureSetup];
|
||||||
|
if (errorOut) {
|
||||||
|
callback(@[@[errorOut]]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BOOL changedManifest = NO;
|
||||||
|
NSMutableArray<NSDictionary *> *errors;
|
||||||
|
for (NSArray<NSString *> *entry in kvPairs) {
|
||||||
|
NSDictionary *keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
||||||
|
RCTAppendError(keyError, &errors);
|
||||||
|
}
|
||||||
|
if (changedManifest) {
|
||||||
|
[self _writeManifest:&errors];
|
||||||
|
}
|
||||||
|
callback(@[RCTNullIfNil(errors)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
RCT_EXPORT_METHOD(multiMerge:(NSArray<NSArray<NSString *> *> *)kvPairs
|
||||||
|
callback:(RCTResponseSenderBlock)callback)
|
||||||
|
// clang-format on
|
||||||
|
{
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||||
|
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
|
||||||
|
for (NSArray<NSString *> *entry in kvPairs) {
|
||||||
|
[keys addObject:entry[0]];
|
||||||
|
[values addObject:entry[1]];
|
||||||
|
}
|
||||||
|
[self.delegate mergeValues:values
|
||||||
|
forKeys:keys
|
||||||
|
completion:^(NSArray<id<NSObject>> *results) {
|
||||||
|
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
||||||
|
callback(@[RCTNullIfNil(errors)]);
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (![self _passthroughDelegate]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *errorOut = [self _ensureSetup];
|
||||||
|
if (errorOut) {
|
||||||
|
callback(@[@[errorOut]]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BOOL changedManifest = NO;
|
||||||
|
NSMutableArray<NSDictionary *> *errors;
|
||||||
|
for (__strong NSArray<NSString *> *entry in kvPairs) {
|
||||||
|
NSDictionary *keyError;
|
||||||
|
NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError];
|
||||||
|
if (!keyError) {
|
||||||
|
if (value) {
|
||||||
|
NSError *jsonError;
|
||||||
|
NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &jsonError);
|
||||||
|
if (RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &jsonError))) {
|
||||||
|
entry = @[entry[0], RCTNullIfNil(RCTJSONStringify(mergedVal, NULL))];
|
||||||
|
}
|
||||||
|
if (jsonError) {
|
||||||
|
keyError = RCTJSErrorFromNSError(jsonError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!keyError) {
|
||||||
|
keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RCTAppendError(keyError, &errors);
|
||||||
|
}
|
||||||
|
if (changedManifest) {
|
||||||
|
[self _writeManifest:&errors];
|
||||||
|
}
|
||||||
|
callback(@[RCTNullIfNil(errors)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
RCT_EXPORT_METHOD(multiRemove:(NSArray<NSString *> *)keys
|
||||||
|
callback:(RCTResponseSenderBlock)callback)
|
||||||
|
// clang-format on
|
||||||
|
{
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
[self.delegate removeValuesForKeys:keys
|
||||||
|
completion:^(NSArray<id<NSObject>> *results) {
|
||||||
|
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
|
||||||
|
callback(@[RCTNullIfNil(errors)]);
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (![self _passthroughDelegate]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *errorOut = [self _ensureSetup];
|
||||||
|
if (errorOut) {
|
||||||
|
callback(@[@[errorOut]]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSMutableArray<NSDictionary *> *errors;
|
||||||
|
BOOL changedManifest = NO;
|
||||||
|
for (NSString *key in keys) {
|
||||||
|
NSDictionary *keyError = RCTErrorForKey(key);
|
||||||
|
if (!keyError) {
|
||||||
|
if (_manifest[key] == (id)kCFNull) {
|
||||||
|
NSString *filePath = [self _filePathForKey:key];
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
||||||
|
[RCTGetCache() removeObjectForKey:key];
|
||||||
|
}
|
||||||
|
if (_manifest[key]) {
|
||||||
|
changedManifest = YES;
|
||||||
|
[_manifest removeObjectForKey:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RCTAppendError(keyError, &errors);
|
||||||
|
}
|
||||||
|
if (changedManifest) {
|
||||||
|
[self _writeManifest:&errors];
|
||||||
|
}
|
||||||
|
callback(@[RCTNullIfNil(errors)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback)
|
||||||
|
// clang-format on
|
||||||
|
{
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
[self.delegate removeAllValues:^(NSError *error) {
|
||||||
|
NSDictionary *result = nil;
|
||||||
|
if (error != nil) {
|
||||||
|
result = RCTMakeError(error.localizedDescription, error, nil);
|
||||||
|
}
|
||||||
|
callback(@[RCTNullIfNil(result)]);
|
||||||
|
}];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_manifest removeAllObjects];
|
||||||
|
[RCTGetCache() removeAllObjects];
|
||||||
|
NSDictionary *error = RCTDeleteStorageDirectory();
|
||||||
|
callback(@[RCTNullIfNil(error)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback)
|
||||||
|
// clang-format on
|
||||||
|
{
|
||||||
|
if (self.delegate != nil) {
|
||||||
|
[self.delegate allKeys:^(NSArray<id<NSObject>> *keys) {
|
||||||
|
callback(@[(id)kCFNull, keys]);
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (![self _passthroughDelegate]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *errorOut = [self _ensureSetup];
|
||||||
|
if (errorOut) {
|
||||||
|
callback(@[errorOut, (id)kCFNull]);
|
||||||
|
} else {
|
||||||
|
callback(@[(id)kCFNull, _manifest.allKeys]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,283 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||||
|
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||||
|
747102F0243FFB7400D4F466 /* RNCAsyncStorage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||||
|
747102F1243FFB7400D4F466 /* RNCAsyncStorageDelegate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||||
|
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "include/$(PRODUCT_NAME)";
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
747102F0243FFB7400D4F466 /* RNCAsyncStorage.h in CopyFiles */,
|
||||||
|
747102F1243FFB7400D4F466 /* RNCAsyncStorageDelegate.h in CopyFiles */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCAsyncStorage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCAsyncStorageDelegate.h; sourceTree = "<group>"; };
|
||||||
|
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCAsyncStorage.h; sourceTree = "<group>"; };
|
||||||
|
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCAsyncStorage.m; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
58B511D21A9E6C8500147676 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */,
|
||||||
|
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */,
|
||||||
|
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */,
|
||||||
|
134814211AA4EA7D00B7C361 /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXHeadersBuildPhase section */
|
||||||
|
19F94B1D2239A948006921A9 /* Headers */ = {
|
||||||
|
isa = PBXHeadersBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */,
|
||||||
|
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXHeadersBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */;
|
||||||
|
buildPhases = (
|
||||||
|
19F94B1D2239A948006921A9 /* Headers */,
|
||||||
|
58B511D71A9E6C8500147676 /* Sources */,
|
||||||
|
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||||
|
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = RNCAsyncStorage;
|
||||||
|
productName = RCTDataManager;
|
||||||
|
productReference = 134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */;
|
||||||
|
productType = "com.apple.product-type.library.static";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
58B511D31A9E6C8500147676 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 0830;
|
||||||
|
ORGANIZATIONNAME = Facebook;
|
||||||
|
TargetAttributes = {
|
||||||
|
58B511DA1A9E6C8500147676 = {
|
||||||
|
CreatedOnToolsVersion = 6.1.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */;
|
||||||
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = English;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
English,
|
||||||
|
en,
|
||||||
|
);
|
||||||
|
mainGroup = 58B511D21A9E6C8500147676;
|
||||||
|
productRefGroup = 58B511D21A9E6C8500147676;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
58B511D71A9E6C8500147676 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
58B511ED1A9E6C8500147676 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
58B511EE1A9E6C8500147676 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = YES;
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
58B511F01A9E6C8500147676 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/../../../React/**",
|
||||||
|
"$(SRCROOT)/../../react-native/React/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = RNCAsyncStorage;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
58B511F11A9E6C8500147676 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/../../../React/**",
|
||||||
|
"$(SRCROOT)/../../react-native/React/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = RNCAsyncStorage;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
58B511ED1A9E6C8500147676 /* Debug */,
|
||||||
|
58B511EE1A9E6C8500147676 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
58B511F01A9E6C8500147676 /* Debug */,
|
||||||
|
58B511F11A9E6C8500147676 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef void (^RNCAsyncStorageCompletion)(NSError *_Nullable error);
|
||||||
|
typedef void (^RNCAsyncStorageResultCallback)(NSArray<id<NSObject>> *valuesOrErrors);
|
||||||
|
|
||||||
|
@protocol RNCAsyncStorageDelegate <NSObject>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns all keys currently stored. If none, an empty array is returned.
|
||||||
|
* @param block Block to call with result.
|
||||||
|
*/
|
||||||
|
- (void)allKeys:(RNCAsyncStorageResultCallback)block;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Merges values with the corresponding values stored at specified keys.
|
||||||
|
* @param values Values to merge.
|
||||||
|
* @param keys Keys to the values that should be merged with.
|
||||||
|
* @param block Block to call with merged result.
|
||||||
|
*/
|
||||||
|
- (void)mergeValues:(NSArray<NSString *> *)values
|
||||||
|
forKeys:(NSArray<NSString *> *)keys
|
||||||
|
completion:(RNCAsyncStorageResultCallback)block;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Removes all values from the store.
|
||||||
|
* @param block Block to call with result.
|
||||||
|
*/
|
||||||
|
- (void)removeAllValues:(RNCAsyncStorageCompletion)block;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Removes all values associated with specified keys.
|
||||||
|
* @param keys Keys of values to remove.
|
||||||
|
* @param block Block to call with result.
|
||||||
|
*/
|
||||||
|
- (void)removeValuesForKeys:(NSArray<NSString *> *)keys
|
||||||
|
completion:(RNCAsyncStorageResultCallback)block;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Sets specified key-value pairs.
|
||||||
|
* @param values Values to set.
|
||||||
|
* @param keys Keys of specified values to set.
|
||||||
|
* @param block Block to call with result.
|
||||||
|
*/
|
||||||
|
- (void)setValues:(NSArray<NSString *> *)values
|
||||||
|
forKeys:(NSArray<NSString *> *)keys
|
||||||
|
completion:(RNCAsyncStorageResultCallback)block;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns values associated with specified keys.
|
||||||
|
* @param keys Keys of values to return.
|
||||||
|
* @param block Block to call with result.
|
||||||
|
*/
|
||||||
|
- (void)valuesForKeys:(NSArray<NSString *> *)keys completion:(RNCAsyncStorageResultCallback)block;
|
||||||
|
|
||||||
|
@optional
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns whether the delegate should be treated as a passthrough.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, readonly, getter=isPassthrough) BOOL passthrough;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,9 @@
|
|||||||
|
import type {
|
||||||
|
AsyncStorageHook,
|
||||||
|
AsyncStorageStatic,
|
||||||
|
} from '../lib/typescript/types';
|
||||||
|
|
||||||
|
export function useAsyncStorage(key: string): AsyncStorageHook;
|
||||||
|
|
||||||
|
declare const AsyncStorage: AsyncStorageStatic;
|
||||||
|
export default AsyncStorage;
|
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
const merge = require('merge-options').bind({
|
||||||
|
concatArrays: true,
|
||||||
|
ignoreUndefined: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const asMock = {
|
||||||
|
__INTERNAL_MOCK_STORAGE__: {},
|
||||||
|
|
||||||
|
setItem: jest.fn(async (key, value, callback) => {
|
||||||
|
const setResult = await asMock.multiSet([[key, value]], undefined);
|
||||||
|
|
||||||
|
callback && callback(setResult);
|
||||||
|
return setResult;
|
||||||
|
}),
|
||||||
|
|
||||||
|
getItem: jest.fn(async (key, callback) => {
|
||||||
|
const getResult = await asMock.multiGet([key], undefined);
|
||||||
|
|
||||||
|
const result = getResult[0] ? getResult[0][1] : null;
|
||||||
|
|
||||||
|
callback && callback(null, result);
|
||||||
|
return result;
|
||||||
|
}),
|
||||||
|
|
||||||
|
removeItem: jest.fn((key, callback) => asMock.multiRemove([key], callback)),
|
||||||
|
mergeItem: jest.fn((key, value, callback) =>
|
||||||
|
asMock.multiMerge([[key, value]], callback)
|
||||||
|
),
|
||||||
|
|
||||||
|
clear: jest.fn(_clear),
|
||||||
|
getAllKeys: jest.fn(_getAllKeys),
|
||||||
|
flushGetRequests: jest.fn(),
|
||||||
|
|
||||||
|
multiGet: jest.fn(_multiGet),
|
||||||
|
multiSet: jest.fn(_multiSet),
|
||||||
|
multiRemove: jest.fn(_multiRemove),
|
||||||
|
multiMerge: jest.fn(_multiMerge),
|
||||||
|
useAsyncStorage: jest.fn((key) => {
|
||||||
|
return {
|
||||||
|
getItem: (...args) => asMock.getItem(key, ...args),
|
||||||
|
setItem: (...args) => asMock.setItem(key, ...args),
|
||||||
|
mergeItem: (...args) => asMock.mergeItem(key, ...args),
|
||||||
|
removeItem: (...args) => asMock.removeItem(key, ...args),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
async function _multiSet(keyValuePairs, callback) {
|
||||||
|
keyValuePairs.forEach((keyValue) => {
|
||||||
|
const key = keyValue[0];
|
||||||
|
|
||||||
|
asMock.__INTERNAL_MOCK_STORAGE__[key] = keyValue[1];
|
||||||
|
});
|
||||||
|
callback && callback(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _multiGet(keys, callback) {
|
||||||
|
const values = keys.map((key) => [
|
||||||
|
key,
|
||||||
|
asMock.__INTERNAL_MOCK_STORAGE__[key] || null,
|
||||||
|
]);
|
||||||
|
callback && callback(null, values);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _multiRemove(keys, callback) {
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (asMock.__INTERNAL_MOCK_STORAGE__[key]) {
|
||||||
|
delete asMock.__INTERNAL_MOCK_STORAGE__[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
callback && callback(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _clear(callback) {
|
||||||
|
asMock.__INTERNAL_MOCK_STORAGE__ = {};
|
||||||
|
|
||||||
|
callback && callback(null);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _getAllKeys() {
|
||||||
|
return Object.keys(asMock.__INTERNAL_MOCK_STORAGE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _multiMerge(keyValuePairs, callback) {
|
||||||
|
keyValuePairs.forEach((keyValue) => {
|
||||||
|
const [key, value] = keyValue;
|
||||||
|
const oldValue = asMock.__INTERNAL_MOCK_STORAGE__[key];
|
||||||
|
asMock.__INTERNAL_MOCK_STORAGE__[key] =
|
||||||
|
oldValue != null
|
||||||
|
? JSON.stringify(merge(JSON.parse(oldValue), JSON.parse(value)))
|
||||||
|
: value;
|
||||||
|
});
|
||||||
|
|
||||||
|
callback && callback(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = asMock;
|
@ -0,0 +1,164 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _mergeOptions = _interopRequireDefault(require("merge-options"));
|
||||||
|
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) Nicolas Gallagher.
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
// @ts-ignore Cannot find module 'merge-options' or its corresponding type declarations
|
||||||
|
const merge = _mergeOptions.default.bind({
|
||||||
|
concatArrays: true,
|
||||||
|
ignoreUndefined: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function mergeLocalStorageItem(key, value) {
|
||||||
|
const oldValue = window.localStorage.getItem(key);
|
||||||
|
|
||||||
|
if (oldValue) {
|
||||||
|
const oldObject = JSON.parse(oldValue);
|
||||||
|
const newObject = JSON.parse(value);
|
||||||
|
const nextValue = JSON.stringify(merge(oldObject, newObject));
|
||||||
|
window.localStorage.setItem(key, nextValue);
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPromise(getValue, callback) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const value = getValue();
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||||
|
resolve(value);
|
||||||
|
} catch (err) {
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPromiseAll(promises, callback, processResult) {
|
||||||
|
return Promise.all(promises).then(result => {
|
||||||
|
const value = (processResult === null || processResult === void 0 ? void 0 : processResult(result)) ?? null;
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||||
|
return Promise.resolve(value);
|
||||||
|
}, errors => {
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errors);
|
||||||
|
return Promise.reject(errors);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const AsyncStorage = {
|
||||||
|
/**
|
||||||
|
* Fetches `key` value.
|
||||||
|
*/
|
||||||
|
getItem: (key, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.getItem(key), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets `value` for `key`.
|
||||||
|
*/
|
||||||
|
setItem: (key, value, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.setItem(key, value), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a `key`
|
||||||
|
*/
|
||||||
|
removeItem: (key, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.removeItem(key), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges existing value with input value, assuming they are stringified JSON.
|
||||||
|
*/
|
||||||
|
mergeItem: (key, value, callback) => {
|
||||||
|
return createPromise(() => mergeLocalStorageItem(key, value), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erases *all* AsyncStorage for the domain.
|
||||||
|
*/
|
||||||
|
clear: callback => {
|
||||||
|
return createPromise(() => window.localStorage.clear(), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to the app, for all callers, libraries, etc.
|
||||||
|
*/
|
||||||
|
getAllKeys: callback => {
|
||||||
|
return createPromise(() => {
|
||||||
|
const numberOfKeys = window.localStorage.length;
|
||||||
|
const keys = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < numberOfKeys; i += 1) {
|
||||||
|
const key = window.localStorage.key(i) || '';
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (stub) Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*/
|
||||||
|
flushGetRequests: () => undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* multiGet resolves to an array of key-value pair arrays that matches the
|
||||||
|
* input format of multiSet.
|
||||||
|
*
|
||||||
|
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||||
|
*/
|
||||||
|
multiGet: (keys, callback) => {
|
||||||
|
const promises = keys.map(key => AsyncStorage.getItem(key));
|
||||||
|
|
||||||
|
const processResult = result => result.map((value, i) => [keys[i], value]);
|
||||||
|
|
||||||
|
return createPromiseAll(promises, callback, processResult);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of key-value array pairs.
|
||||||
|
* multiSet([['k1', 'val1'], ['k2', 'val2']])
|
||||||
|
*/
|
||||||
|
multiSet: (keyValuePairs, callback) => {
|
||||||
|
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all the keys in the `keys` array.
|
||||||
|
*/
|
||||||
|
multiRemove: (keys, callback) => {
|
||||||
|
const promises = keys.map(key => AsyncStorage.removeItem(key));
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of key-value array pairs and merges them with existing
|
||||||
|
* values, assuming they are stringified JSON.
|
||||||
|
*
|
||||||
|
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
|
||||||
|
*/
|
||||||
|
multiMerge: (keyValuePairs, callback) => {
|
||||||
|
const promises = keyValuePairs.map(item => AsyncStorage.mergeItem(item[0], item[1]));
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var _default = AsyncStorage;
|
||||||
|
exports.default = _default;
|
||||||
|
//# sourceMappingURL=AsyncStorage.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,366 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _helpers = require("./helpers");
|
||||||
|
|
||||||
|
var _RCTAsyncStorage = _interopRequireDefault(require("./RCTAsyncStorage"));
|
||||||
|
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
if (!_RCTAsyncStorage.default) {
|
||||||
|
throw new Error(`[@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.
|
||||||
|
|
||||||
|
To fix this issue try these steps:
|
||||||
|
|
||||||
|
• Rebuild and restart the app.
|
||||||
|
|
||||||
|
• Run the packager with \`--reset-cache\` flag.
|
||||||
|
|
||||||
|
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.
|
||||||
|
|
||||||
|
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest
|
||||||
|
|
||||||
|
If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||||
|
* storage system that is global to the app. It should be used instead of
|
||||||
|
* LocalStorage.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const AsyncStorage = (() => {
|
||||||
|
let _getRequests = [];
|
||||||
|
let _getKeys = [];
|
||||||
|
let _immediate = null;
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||||
|
*/
|
||||||
|
getItem: (key, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
(0, _helpers.checkValidInput)(key);
|
||||||
|
|
||||||
|
_RCTAsyncStorage.default.multiGet([key], (errors, result) => {
|
||||||
|
var _result$;
|
||||||
|
|
||||||
|
// Unpack result to get value from [[key,value]]
|
||||||
|
const value = result !== null && result !== void 0 && (_result$ = result[0]) !== null && _result$ !== void 0 && _result$[1] ? result[0][1] : null;
|
||||||
|
const errs = (0, _helpers.convertErrors)(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0], value);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||||
|
*/
|
||||||
|
setItem: (key, value, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
(0, _helpers.checkValidInput)(key, value);
|
||||||
|
|
||||||
|
_RCTAsyncStorage.default.multiSet([[key, value]], errors => {
|
||||||
|
const errs = (0, _helpers.convertErrors)(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||||
|
*/
|
||||||
|
removeItem: (key, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
(0, _helpers.checkValidInput)(key);
|
||||||
|
|
||||||
|
_RCTAsyncStorage.default.multiRemove([key], errors => {
|
||||||
|
const errs = (0, _helpers.convertErrors)(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges an existing `key` value with an input value, assuming both values
|
||||||
|
* are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||||
|
*/
|
||||||
|
mergeItem: (key, value, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
(0, _helpers.checkValidInput)(key, value);
|
||||||
|
|
||||||
|
_RCTAsyncStorage.default.multiMerge([[key, value]], errors => {
|
||||||
|
const errs = (0, _helpers.convertErrors)(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||||
|
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||||
|
* your app's keys.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||||
|
*/
|
||||||
|
clear: callback => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
_RCTAsyncStorage.default.clear(error => {
|
||||||
|
const err = (0, _helpers.convertError)(error);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||||
|
*/
|
||||||
|
getAllKeys: callback => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
_RCTAsyncStorage.default.getAllKeys((error, keys) => {
|
||||||
|
const err = (0, _helpers.convertError)(error);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(err, keys);
|
||||||
|
|
||||||
|
if (keys) {
|
||||||
|
resolve(keys);
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following batched functions are useful for executing a lot of
|
||||||
|
* operations at once, allowing for native optimizations and provide the
|
||||||
|
* convenience of a single callback after all operations are complete.
|
||||||
|
*
|
||||||
|
* These functions return arrays of errors, potentially one for every key.
|
||||||
|
* For key-specific errors, the Error object will have a key property to
|
||||||
|
* indicate which key caused the error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||||
|
* */
|
||||||
|
flushGetRequests: () => {
|
||||||
|
const getRequests = _getRequests;
|
||||||
|
const getKeys = _getKeys;
|
||||||
|
_getRequests = [];
|
||||||
|
_getKeys = [];
|
||||||
|
|
||||||
|
_RCTAsyncStorage.default.multiGet(getKeys, (errors, result) => {
|
||||||
|
// Even though the runtime complexity of this is theoretically worse vs if we used a map,
|
||||||
|
// it's much, much faster in practice for the data sets we deal with (we avoid
|
||||||
|
// allocating result pair arrays). This was heavily benchmarked.
|
||||||
|
//
|
||||||
|
// Is there a way to avoid using the map but fix the bug in this breaking test?
|
||||||
|
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
|
||||||
|
const map = {};
|
||||||
|
result === null || result === void 0 ? void 0 : result.forEach(_ref => {
|
||||||
|
let [key, value] = _ref;
|
||||||
|
map[key] = value;
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
const reqLength = getRequests.length;
|
||||||
|
/**
|
||||||
|
* As mentioned few lines above, this method could be called with the array of potential error,
|
||||||
|
* in case of anything goes wrong. The problem is, if any of the batched calls fails
|
||||||
|
* the rest of them would fail too, but the error would be consumed by just one. The rest
|
||||||
|
* would simply return `undefined` as their result, rendering false negatives.
|
||||||
|
*
|
||||||
|
* In order to avoid this situation, in case of any call failing,
|
||||||
|
* the rest of them will be rejected as well (with the same error).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const errorList = (0, _helpers.convertErrors)(errors);
|
||||||
|
const error = errorList !== null && errorList !== void 0 && errorList.length ? errorList[0] : null;
|
||||||
|
|
||||||
|
for (let i = 0; i < reqLength; i++) {
|
||||||
|
var _request$callback2, _request$resolve;
|
||||||
|
|
||||||
|
const request = getRequests[i];
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
var _request$callback, _request$reject;
|
||||||
|
|
||||||
|
(_request$callback = request.callback) === null || _request$callback === void 0 ? void 0 : _request$callback.call(request, errorList);
|
||||||
|
(_request$reject = request.reject) === null || _request$reject === void 0 ? void 0 : _request$reject.call(request, error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestResult = request.keys.map(key => [key, map[key]]);
|
||||||
|
(_request$callback2 = request.callback) === null || _request$callback2 === void 0 ? void 0 : _request$callback2.call(request, null, requestResult);
|
||||||
|
(_request$resolve = request.resolve) === null || _request$resolve === void 0 ? void 0 : _request$resolve.call(request, requestResult);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This allows you to batch the fetching of items given an array of `key`
|
||||||
|
* inputs. Your callback will be invoked with an array of corresponding
|
||||||
|
* key-value pairs found.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||||
|
*/
|
||||||
|
multiGet: (keys, callback) => {
|
||||||
|
if (!_immediate) {
|
||||||
|
_immediate = setImmediate(() => {
|
||||||
|
_immediate = null;
|
||||||
|
AsyncStorage.flushGetRequests();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRequest = {
|
||||||
|
keys: keys,
|
||||||
|
callback: callback,
|
||||||
|
// do we need this?
|
||||||
|
keyIndex: _getKeys.length,
|
||||||
|
resolve: null,
|
||||||
|
reject: null
|
||||||
|
};
|
||||||
|
const promiseResult = new Promise((resolve, reject) => {
|
||||||
|
getRequest.resolve = resolve;
|
||||||
|
getRequest.reject = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
_getRequests.push(getRequest); // avoid fetching duplicates
|
||||||
|
|
||||||
|
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (_getKeys.indexOf(key) === -1) {
|
||||||
|
_getKeys.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return promiseResult;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||||
|
* the operation completes you'll get a single callback with any errors.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||||
|
*/
|
||||||
|
multiSet: (keyValuePairs, callback) => {
|
||||||
|
(0, _helpers.checkValidArgs)(keyValuePairs, callback);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
keyValuePairs.forEach(_ref2 => {
|
||||||
|
let [key, value] = _ref2;
|
||||||
|
(0, _helpers.checkValidInput)(key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
_RCTAsyncStorage.default.multiSet(keyValuePairs, errors => {
|
||||||
|
const error = (0, _helpers.convertErrors)(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this to batch the deletion of all keys in the `keys` array.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||||
|
*/
|
||||||
|
multiRemove: (keys, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
keys.forEach(key => (0, _helpers.checkValidInput)(key));
|
||||||
|
|
||||||
|
_RCTAsyncStorage.default.multiRemove(keys, errors => {
|
||||||
|
const error = (0, _helpers.convertErrors)(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch operation to merge in existing and new values for a given set of
|
||||||
|
* keys. This assumes that the values are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||||
|
*/
|
||||||
|
multiMerge: (keyValuePairs, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
_RCTAsyncStorage.default.multiMerge(keyValuePairs, errors => {
|
||||||
|
const error = (0, _helpers.convertErrors)(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
var _default = AsyncStorage;
|
||||||
|
exports.default = _default;
|
||||||
|
//# sourceMappingURL=AsyncStorage.native.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,29 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = void 0;
|
||||||
|
|
||||||
|
var _reactNative = require("react-native");
|
||||||
|
|
||||||
|
var _shouldFallbackToLegacyNativeModule = require("./shouldFallbackToLegacyNativeModule");
|
||||||
|
|
||||||
|
// @ts-ignore Module '"react-native"' has no exported member 'TurboModuleRegistry'.
|
||||||
|
let RCTAsyncStorage = _reactNative.NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows
|
||||||
|
_reactNative.NativeModules['RNC_AsyncSQLiteDBStorage'] || _reactNative.NativeModules['RNCAsyncStorage'];
|
||||||
|
|
||||||
|
if (!RCTAsyncStorage && (0, _shouldFallbackToLegacyNativeModule.shouldFallbackToLegacyNativeModule)()) {
|
||||||
|
// TurboModuleRegistry falls back to NativeModules so we don't have to try go
|
||||||
|
// assign NativeModules' counterparts if TurboModuleRegistry would resolve
|
||||||
|
// with undefined.
|
||||||
|
if (_reactNative.TurboModuleRegistry) {
|
||||||
|
RCTAsyncStorage = _reactNative.TurboModuleRegistry.get('AsyncSQLiteDBStorage') || _reactNative.TurboModuleRegistry.get('AsyncLocalStorage');
|
||||||
|
} else {
|
||||||
|
RCTAsyncStorage = _reactNative.NativeModules['AsyncSQLiteDBStorage'] || _reactNative.NativeModules['AsyncLocalStorage'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _default = RCTAsyncStorage;
|
||||||
|
exports.default = _default;
|
||||||
|
//# sourceMappingURL=RCTAsyncStorage.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["RCTAsyncStorage","NativeModules","shouldFallbackToLegacyNativeModule","TurboModuleRegistry","get"],"sources":["RCTAsyncStorage.ts"],"sourcesContent":["// @ts-ignore Module '\"react-native\"' has no exported member 'TurboModuleRegistry'.\nimport { NativeModules, TurboModuleRegistry } from 'react-native';\nimport { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';\n\nlet RCTAsyncStorage =\n NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows\n NativeModules['RNC_AsyncSQLiteDBStorage'] ||\n NativeModules['RNCAsyncStorage'];\n\nif (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {\n // TurboModuleRegistry falls back to NativeModules so we don't have to try go\n // assign NativeModules' counterparts if TurboModuleRegistry would resolve\n // with undefined.\n if (TurboModuleRegistry) {\n RCTAsyncStorage =\n TurboModuleRegistry.get('AsyncSQLiteDBStorage') ||\n TurboModuleRegistry.get('AsyncLocalStorage');\n } else {\n RCTAsyncStorage =\n NativeModules['AsyncSQLiteDBStorage'] ||\n NativeModules['AsyncLocalStorage'];\n }\n}\n\nexport default RCTAsyncStorage;\n"],"mappings":";;;;;;;AACA;;AACA;;AAFA;AAIA,IAAIA,eAAe,GACjBC,0BAAA,CAAc,sBAAd,KAAyC;AACzCA,0BAAA,CAAc,0BAAd,CADA,IAEAA,0BAAA,CAAc,iBAAd,CAHF;;AAKA,IAAI,CAACD,eAAD,IAAoB,IAAAE,sEAAA,GAAxB,EAA8D;EAC5D;EACA;EACA;EACA,IAAIC,gCAAJ,EAAyB;IACvBH,eAAe,GACbG,gCAAA,CAAoBC,GAApB,CAAwB,sBAAxB,KACAD,gCAAA,CAAoBC,GAApB,CAAwB,mBAAxB,CAFF;EAGD,CAJD,MAIO;IACLJ,eAAe,GACbC,0BAAA,CAAc,sBAAd,KACAA,0BAAA,CAAc,mBAAd,CAFF;EAGD;AACF;;eAEcD,e"}
|
@ -0,0 +1,69 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.checkValidArgs = checkValidArgs;
|
||||||
|
exports.checkValidInput = checkValidInput;
|
||||||
|
exports.convertError = convertError;
|
||||||
|
exports.convertErrors = convertErrors;
|
||||||
|
|
||||||
|
function checkValidArgs(keyValuePairs, callback) {
|
||||||
|
if (!Array.isArray(keyValuePairs) || keyValuePairs.length === 0 || !Array.isArray(keyValuePairs[0])) {
|
||||||
|
throw new Error('[AsyncStorage] Expected array of key-value pairs as first argument to multiSet');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback && typeof callback !== 'function') {
|
||||||
|
if (Array.isArray(callback)) {
|
||||||
|
throw new Error('[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?');
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('[AsyncStorage] Expected function as second argument to multiSet');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkValidInput() {
|
||||||
|
for (var _len = arguments.length, input = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||||
|
input[_key] = arguments[_key];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [key, value] = input;
|
||||||
|
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
console.warn(`[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${key}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.length > 1 && typeof value !== 'string') {
|
||||||
|
if (value == null) {
|
||||||
|
throw new Error(`[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||||
|
} else {
|
||||||
|
console.warn(`[AsyncStorage] The value for key "${key}" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertError(error) {
|
||||||
|
if (!error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const out = new Error(error.message);
|
||||||
|
out.key = error.key;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertErrors(errs) {
|
||||||
|
const errors = ensureArray(errs);
|
||||||
|
return errors ? errors.map(e => convertError(e)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureArray(e) {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return e.length === 0 ? null : e;
|
||||||
|
} else if (e) {
|
||||||
|
return [e];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=helpers.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["checkValidArgs","keyValuePairs","callback","Array","isArray","length","Error","checkValidInput","input","key","value","console","warn","convertError","error","out","message","convertErrors","errs","errors","ensureArray","map","e"],"sources":["helpers.ts"],"sourcesContent":["import type { ErrorLike } from './types';\n\nexport function checkValidArgs(keyValuePairs: unknown[], callback: unknown) {\n if (\n !Array.isArray(keyValuePairs) ||\n keyValuePairs.length === 0 ||\n !Array.isArray(keyValuePairs[0])\n ) {\n throw new Error(\n '[AsyncStorage] Expected array of key-value pairs as first argument to multiSet'\n );\n }\n\n if (callback && typeof callback !== 'function') {\n if (Array.isArray(callback)) {\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?'\n );\n }\n\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet'\n );\n }\n}\n\nexport function checkValidInput(...input: unknown[]) {\n const [key, value] = input;\n\n if (typeof key !== 'string') {\n console.warn(\n `[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\\nKey passed: ${key}\\n`\n );\n }\n\n if (input.length > 1 && typeof value !== 'string') {\n if (value == null) {\n throw new Error(\n `[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n } else {\n console.warn(\n `[AsyncStorage] The value for key \"${key}\" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n }\n }\n}\n\nexport function convertError(error?: ErrorLike): Error | null {\n if (!error) {\n return null;\n }\n\n const out = new Error(error.message);\n (out as any).key = error.key;\n return out;\n}\n\nexport function convertErrors(\n errs?: ErrorLike[]\n): ReadonlyArray<Error | null> | null {\n const errors = ensureArray(errs);\n return errors ? errors.map((e) => convertError(e)) : null;\n}\n\nfunction ensureArray(e?: ErrorLike | ErrorLike[]): ErrorLike[] | null {\n if (Array.isArray(e)) {\n return e.length === 0 ? null : e;\n } else if (e) {\n return [e];\n } else {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;AAEO,SAASA,cAAT,CAAwBC,aAAxB,EAAkDC,QAAlD,EAAqE;EAC1E,IACE,CAACC,KAAK,CAACC,OAAN,CAAcH,aAAd,CAAD,IACAA,aAAa,CAACI,MAAd,KAAyB,CADzB,IAEA,CAACF,KAAK,CAACC,OAAN,CAAcH,aAAa,CAAC,CAAD,CAA3B,CAHH,EAIE;IACA,MAAM,IAAIK,KAAJ,CACJ,gFADI,CAAN;EAGD;;EAED,IAAIJ,QAAQ,IAAI,OAAOA,QAAP,KAAoB,UAApC,EAAgD;IAC9C,IAAIC,KAAK,CAACC,OAAN,CAAcF,QAAd,CAAJ,EAA6B;MAC3B,MAAM,IAAII,KAAJ,CACJ,6IADI,CAAN;IAGD;;IAED,MAAM,IAAIA,KAAJ,CACJ,iEADI,CAAN;EAGD;AACF;;AAEM,SAASC,eAAT,GAA8C;EAAA,kCAAlBC,KAAkB;IAAlBA,KAAkB;EAAA;;EACnD,MAAM,CAACC,GAAD,EAAMC,KAAN,IAAeF,KAArB;;EAEA,IAAI,OAAOC,GAAP,KAAe,QAAnB,EAA6B;IAC3BE,OAAO,CAACC,IAAR,CACG,wBAAuB,OAAOH,GAAI,iHAAgHA,GAAI,IADzJ;EAGD;;EAED,IAAID,KAAK,CAACH,MAAN,GAAe,CAAf,IAAoB,OAAOK,KAAP,KAAiB,QAAzC,EAAmD;IACjD,IAAIA,KAAK,IAAI,IAAb,EAAmB;MACjB,MAAM,IAAIJ,KAAJ,CACH,gJAA+II,KAAM,iBAAgBD,GAAI,IADtK,CAAN;IAGD,CAJD,MAIO;MACLE,OAAO,CAACC,IAAR,CACG,qCAAoCH,GAAI,4GAA2GC,KAAM,iBAAgBD,GAAI,IADhL;IAGD;EACF;AACF;;AAEM,SAASI,YAAT,CAAsBC,KAAtB,EAAuD;EAC5D,IAAI,CAACA,KAAL,EAAY;IACV,OAAO,IAAP;EACD;;EAED,MAAMC,GAAG,GAAG,IAAIT,KAAJ,CAAUQ,KAAK,CAACE,OAAhB,CAAZ;EACCD,GAAD,CAAaN,GAAb,GAAmBK,KAAK,CAACL,GAAzB;EACA,OAAOM,GAAP;AACD;;AAEM,SAASE,aAAT,CACLC,IADK,EAE+B;EACpC,MAAMC,MAAM,GAAGC,WAAW,CAACF,IAAD,CAA1B;EACA,OAAOC,MAAM,GAAGA,MAAM,CAACE,GAAP,CAAYC,CAAD,IAAOT,YAAY,CAACS,CAAD,CAA9B,CAAH,GAAwC,IAArD;AACD;;AAED,SAASF,WAAT,CAAqBE,CAArB,EAAsE;EACpE,IAAInB,KAAK,CAACC,OAAN,CAAckB,CAAd,CAAJ,EAAsB;IACpB,OAAOA,CAAC,CAACjB,MAAF,KAAa,CAAb,GAAiB,IAAjB,GAAwBiB,CAA/B;EACD,CAFD,MAEO,IAAIA,CAAJ,EAAO;IACZ,OAAO,CAACA,CAAD,CAAP;EACD,CAFM,MAEA;IACL,OAAO,IAAP;EACD;AACF"}
|
@ -0,0 +1,44 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.useAsyncStorage = useAsyncStorage;
|
||||||
|
|
||||||
|
var _AsyncStorage = _interopRequireDefault(require("./AsyncStorage"));
|
||||||
|
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
|
||||||
|
function useAsyncStorage(key) {
|
||||||
|
return {
|
||||||
|
getItem: function () {
|
||||||
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||||
|
args[_key] = arguments[_key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _AsyncStorage.default.getItem(key, ...args);
|
||||||
|
},
|
||||||
|
setItem: function () {
|
||||||
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||||
|
args[_key2] = arguments[_key2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _AsyncStorage.default.setItem(key, ...args);
|
||||||
|
},
|
||||||
|
mergeItem: function () {
|
||||||
|
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
|
||||||
|
args[_key3] = arguments[_key3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _AsyncStorage.default.mergeItem(key, ...args);
|
||||||
|
},
|
||||||
|
removeItem: function () {
|
||||||
|
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
||||||
|
args[_key4] = arguments[_key4];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _AsyncStorage.default.removeItem(key, ...args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=hooks.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["useAsyncStorage","key","getItem","args","AsyncStorage","setItem","mergeItem","removeItem"],"sources":["hooks.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\nimport type { AsyncStorageHook } from './types';\n\nexport function useAsyncStorage(key: string): AsyncStorageHook {\n return {\n getItem: (...args) => AsyncStorage.getItem(key, ...args),\n setItem: (...args) => AsyncStorage.setItem(key, ...args),\n mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),\n removeItem: (...args) => AsyncStorage.removeItem(key, ...args),\n };\n}\n"],"mappings":";;;;;;;AAAA;;;;AAGO,SAASA,eAAT,CAAyBC,GAAzB,EAAwD;EAC7D,OAAO;IACLC,OAAO,EAAE;MAAA,kCAAIC,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaF,OAAb,CAAqBD,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CADJ;IAELE,OAAO,EAAE;MAAA,mCAAIF,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaC,OAAb,CAAqBJ,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CAFJ;IAGLG,SAAS,EAAE;MAAA,mCAAIH,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaE,SAAb,CAAuBL,GAAvB,EAA4B,GAAGE,IAA/B,CAAb;IAAA,CAHN;IAILI,UAAU,EAAE;MAAA,mCAAIJ,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaC,qBAAA,CAAaG,UAAb,CAAwBN,GAAxB,EAA6B,GAAGE,IAAhC,CAAb;IAAA;EAJP,CAAP;AAMD"}
|
@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = void 0;
|
||||||
|
Object.defineProperty(exports, "useAsyncStorage", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _hooks.useAsyncStorage;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var _AsyncStorage = _interopRequireDefault(require("./AsyncStorage"));
|
||||||
|
|
||||||
|
var _hooks = require("./hooks");
|
||||||
|
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
|
||||||
|
var _default = _AsyncStorage.default;
|
||||||
|
exports.default = _default;
|
||||||
|
//# sourceMappingURL=index.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["AsyncStorage"],"sources":["index.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\n\nexport { useAsyncStorage } from './hooks';\n\nexport type { AsyncStorageStatic } from './types';\n\nexport default AsyncStorage;\n"],"mappings":";;;;;;;;;;;;;AAAA;;AAEA;;;;eAIeA,qB"}
|
@ -0,0 +1,39 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.shouldFallbackToLegacyNativeModule = shouldFallbackToLegacyNativeModule;
|
||||||
|
|
||||||
|
var _reactNative = require("react-native");
|
||||||
|
|
||||||
|
function shouldFallbackToLegacyNativeModule() {
|
||||||
|
var _NativeModules$Native, _NativeModules$Native2;
|
||||||
|
|
||||||
|
const expoConstants = (_NativeModules$Native = _reactNative.NativeModules['NativeUnimoduleProxy']) === null || _NativeModules$Native === void 0 ? void 0 : (_NativeModules$Native2 = _NativeModules$Native.modulesConstants) === null || _NativeModules$Native2 === void 0 ? void 0 : _NativeModules$Native2.ExponentConstants;
|
||||||
|
|
||||||
|
if (expoConstants) {
|
||||||
|
/**
|
||||||
|
* In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.
|
||||||
|
* In bare React Native apps using expo-constants, appOwnership is never defined, so
|
||||||
|
* isLegacySdkVersion will be false in that context.
|
||||||
|
*/
|
||||||
|
const isLegacySdkVersion = expoConstants.appOwnership && !expoConstants.executionEnvironment;
|
||||||
|
/**
|
||||||
|
* Expo managed apps don't include the @react-native-async-storage/async-storage
|
||||||
|
* native modules yet, but the API interface is the same, so we can use the version
|
||||||
|
* exported from React Native still.
|
||||||
|
*
|
||||||
|
* If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this
|
||||||
|
* will likely not be valid anymore, and the package will need to be included in the Expo SDK
|
||||||
|
* to continue to work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (isLegacySdkVersion || ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=shouldFallbackToLegacyNativeModule.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["shouldFallbackToLegacyNativeModule","expoConstants","NativeModules","modulesConstants","ExponentConstants","isLegacySdkVersion","appOwnership","executionEnvironment","includes"],"sources":["shouldFallbackToLegacyNativeModule.ts"],"sourcesContent":["import { NativeModules } from 'react-native';\n\nexport function shouldFallbackToLegacyNativeModule(): boolean {\n const expoConstants =\n NativeModules['NativeUnimoduleProxy']?.modulesConstants?.ExponentConstants;\n\n if (expoConstants) {\n /**\n * In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.\n * In bare React Native apps using expo-constants, appOwnership is never defined, so\n * isLegacySdkVersion will be false in that context.\n */\n const isLegacySdkVersion =\n expoConstants.appOwnership && !expoConstants.executionEnvironment;\n\n /**\n * Expo managed apps don't include the @react-native-async-storage/async-storage\n * native modules yet, but the API interface is the same, so we can use the version\n * exported from React Native still.\n *\n * If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this\n * will likely not be valid anymore, and the package will need to be included in the Expo SDK\n * to continue to work.\n */\n if (\n isLegacySdkVersion ||\n ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)\n ) {\n return true;\n }\n }\n\n return false;\n}\n"],"mappings":";;;;;;;AAAA;;AAEO,SAASA,kCAAT,GAAuD;EAAA;;EAC5D,MAAMC,aAAa,4BACjBC,0BAAA,CAAc,sBAAd,CADiB,oFACjB,sBAAuCC,gBADtB,2DACjB,uBAAyDC,iBAD3D;;EAGA,IAAIH,aAAJ,EAAmB;IACjB;AACJ;AACA;AACA;AACA;IACI,MAAMI,kBAAkB,GACtBJ,aAAa,CAACK,YAAd,IAA8B,CAACL,aAAa,CAACM,oBAD/C;IAGA;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;IACI,IACEF,kBAAkB,IAClB,CAAC,aAAD,EAAgB,YAAhB,EAA8BG,QAA9B,CAAuCP,aAAa,CAACM,oBAArD,CAFF,EAGE;MACA,OAAO,IAAP;IACD;EACF;;EAED,OAAO,KAAP;AACD"}
|
@ -0,0 +1,2 @@
|
|||||||
|
"use strict";
|
||||||
|
//# sourceMappingURL=types.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Nicolas Gallagher.
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
// @ts-ignore Cannot find module 'merge-options' or its corresponding type declarations
|
||||||
|
import mergeOptions from 'merge-options';
|
||||||
|
const merge = mergeOptions.bind({
|
||||||
|
concatArrays: true,
|
||||||
|
ignoreUndefined: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function mergeLocalStorageItem(key, value) {
|
||||||
|
const oldValue = window.localStorage.getItem(key);
|
||||||
|
|
||||||
|
if (oldValue) {
|
||||||
|
const oldObject = JSON.parse(oldValue);
|
||||||
|
const newObject = JSON.parse(value);
|
||||||
|
const nextValue = JSON.stringify(merge(oldObject, newObject));
|
||||||
|
window.localStorage.setItem(key, nextValue);
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPromise(getValue, callback) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const value = getValue();
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||||
|
resolve(value);
|
||||||
|
} catch (err) {
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPromiseAll(promises, callback, processResult) {
|
||||||
|
return Promise.all(promises).then(result => {
|
||||||
|
const value = (processResult === null || processResult === void 0 ? void 0 : processResult(result)) ?? null;
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(null, value);
|
||||||
|
return Promise.resolve(value);
|
||||||
|
}, errors => {
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errors);
|
||||||
|
return Promise.reject(errors);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const AsyncStorage = {
|
||||||
|
/**
|
||||||
|
* Fetches `key` value.
|
||||||
|
*/
|
||||||
|
getItem: (key, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.getItem(key), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets `value` for `key`.
|
||||||
|
*/
|
||||||
|
setItem: (key, value, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.setItem(key, value), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a `key`
|
||||||
|
*/
|
||||||
|
removeItem: (key, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.removeItem(key), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges existing value with input value, assuming they are stringified JSON.
|
||||||
|
*/
|
||||||
|
mergeItem: (key, value, callback) => {
|
||||||
|
return createPromise(() => mergeLocalStorageItem(key, value), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erases *all* AsyncStorage for the domain.
|
||||||
|
*/
|
||||||
|
clear: callback => {
|
||||||
|
return createPromise(() => window.localStorage.clear(), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to the app, for all callers, libraries, etc.
|
||||||
|
*/
|
||||||
|
getAllKeys: callback => {
|
||||||
|
return createPromise(() => {
|
||||||
|
const numberOfKeys = window.localStorage.length;
|
||||||
|
const keys = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < numberOfKeys; i += 1) {
|
||||||
|
const key = window.localStorage.key(i) || '';
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (stub) Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*/
|
||||||
|
flushGetRequests: () => undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* multiGet resolves to an array of key-value pair arrays that matches the
|
||||||
|
* input format of multiSet.
|
||||||
|
*
|
||||||
|
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||||
|
*/
|
||||||
|
multiGet: (keys, callback) => {
|
||||||
|
const promises = keys.map(key => AsyncStorage.getItem(key));
|
||||||
|
|
||||||
|
const processResult = result => result.map((value, i) => [keys[i], value]);
|
||||||
|
|
||||||
|
return createPromiseAll(promises, callback, processResult);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of key-value array pairs.
|
||||||
|
* multiSet([['k1', 'val1'], ['k2', 'val2']])
|
||||||
|
*/
|
||||||
|
multiSet: (keyValuePairs, callback) => {
|
||||||
|
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all the keys in the `keys` array.
|
||||||
|
*/
|
||||||
|
multiRemove: (keys, callback) => {
|
||||||
|
const promises = keys.map(key => AsyncStorage.removeItem(key));
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of key-value array pairs and merges them with existing
|
||||||
|
* values, assuming they are stringified JSON.
|
||||||
|
*
|
||||||
|
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
|
||||||
|
*/
|
||||||
|
multiMerge: (keyValuePairs, callback) => {
|
||||||
|
const promises = keyValuePairs.map(item => AsyncStorage.mergeItem(item[0], item[1]));
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default AsyncStorage;
|
||||||
|
//# sourceMappingURL=AsyncStorage.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,348 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
import { checkValidArgs, checkValidInput, convertError, convertErrors } from './helpers';
|
||||||
|
import RCTAsyncStorage from './RCTAsyncStorage';
|
||||||
|
|
||||||
|
if (!RCTAsyncStorage) {
|
||||||
|
throw new Error(`[@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.
|
||||||
|
|
||||||
|
To fix this issue try these steps:
|
||||||
|
|
||||||
|
• Rebuild and restart the app.
|
||||||
|
|
||||||
|
• Run the packager with \`--reset-cache\` flag.
|
||||||
|
|
||||||
|
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.
|
||||||
|
|
||||||
|
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest
|
||||||
|
|
||||||
|
If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||||
|
* storage system that is global to the app. It should be used instead of
|
||||||
|
* LocalStorage.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const AsyncStorage = (() => {
|
||||||
|
let _getRequests = [];
|
||||||
|
let _getKeys = [];
|
||||||
|
let _immediate = null;
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||||
|
*/
|
||||||
|
getItem: (key, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key);
|
||||||
|
RCTAsyncStorage.multiGet([key], (errors, result) => {
|
||||||
|
var _result$;
|
||||||
|
|
||||||
|
// Unpack result to get value from [[key,value]]
|
||||||
|
const value = result !== null && result !== void 0 && (_result$ = result[0]) !== null && _result$ !== void 0 && _result$[1] ? result[0][1] : null;
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0], value);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||||
|
*/
|
||||||
|
setItem: (key, value, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key, value);
|
||||||
|
RCTAsyncStorage.multiSet([[key, value]], errors => {
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||||
|
*/
|
||||||
|
removeItem: (key, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key);
|
||||||
|
RCTAsyncStorage.multiRemove([key], errors => {
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges an existing `key` value with an input value, assuming both values
|
||||||
|
* are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||||
|
*/
|
||||||
|
mergeItem: (key, value, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key, value);
|
||||||
|
RCTAsyncStorage.multiMerge([[key, value]], errors => {
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(errs === null || errs === void 0 ? void 0 : errs[0]);
|
||||||
|
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||||
|
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||||
|
* your app's keys.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||||
|
*/
|
||||||
|
clear: callback => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
RCTAsyncStorage.clear(error => {
|
||||||
|
const err = convertError(error);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(err);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||||
|
*/
|
||||||
|
getAllKeys: callback => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
RCTAsyncStorage.getAllKeys((error, keys) => {
|
||||||
|
const err = convertError(error);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(err, keys);
|
||||||
|
|
||||||
|
if (keys) {
|
||||||
|
resolve(keys);
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following batched functions are useful for executing a lot of
|
||||||
|
* operations at once, allowing for native optimizations and provide the
|
||||||
|
* convenience of a single callback after all operations are complete.
|
||||||
|
*
|
||||||
|
* These functions return arrays of errors, potentially one for every key.
|
||||||
|
* For key-specific errors, the Error object will have a key property to
|
||||||
|
* indicate which key caused the error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||||
|
* */
|
||||||
|
flushGetRequests: () => {
|
||||||
|
const getRequests = _getRequests;
|
||||||
|
const getKeys = _getKeys;
|
||||||
|
_getRequests = [];
|
||||||
|
_getKeys = [];
|
||||||
|
RCTAsyncStorage.multiGet(getKeys, (errors, result) => {
|
||||||
|
// Even though the runtime complexity of this is theoretically worse vs if we used a map,
|
||||||
|
// it's much, much faster in practice for the data sets we deal with (we avoid
|
||||||
|
// allocating result pair arrays). This was heavily benchmarked.
|
||||||
|
//
|
||||||
|
// Is there a way to avoid using the map but fix the bug in this breaking test?
|
||||||
|
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
|
||||||
|
const map = {};
|
||||||
|
result === null || result === void 0 ? void 0 : result.forEach(_ref => {
|
||||||
|
let [key, value] = _ref;
|
||||||
|
map[key] = value;
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
const reqLength = getRequests.length;
|
||||||
|
/**
|
||||||
|
* As mentioned few lines above, this method could be called with the array of potential error,
|
||||||
|
* in case of anything goes wrong. The problem is, if any of the batched calls fails
|
||||||
|
* the rest of them would fail too, but the error would be consumed by just one. The rest
|
||||||
|
* would simply return `undefined` as their result, rendering false negatives.
|
||||||
|
*
|
||||||
|
* In order to avoid this situation, in case of any call failing,
|
||||||
|
* the rest of them will be rejected as well (with the same error).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const errorList = convertErrors(errors);
|
||||||
|
const error = errorList !== null && errorList !== void 0 && errorList.length ? errorList[0] : null;
|
||||||
|
|
||||||
|
for (let i = 0; i < reqLength; i++) {
|
||||||
|
var _request$callback2, _request$resolve;
|
||||||
|
|
||||||
|
const request = getRequests[i];
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
var _request$callback, _request$reject;
|
||||||
|
|
||||||
|
(_request$callback = request.callback) === null || _request$callback === void 0 ? void 0 : _request$callback.call(request, errorList);
|
||||||
|
(_request$reject = request.reject) === null || _request$reject === void 0 ? void 0 : _request$reject.call(request, error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestResult = request.keys.map(key => [key, map[key]]);
|
||||||
|
(_request$callback2 = request.callback) === null || _request$callback2 === void 0 ? void 0 : _request$callback2.call(request, null, requestResult);
|
||||||
|
(_request$resolve = request.resolve) === null || _request$resolve === void 0 ? void 0 : _request$resolve.call(request, requestResult);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This allows you to batch the fetching of items given an array of `key`
|
||||||
|
* inputs. Your callback will be invoked with an array of corresponding
|
||||||
|
* key-value pairs found.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||||
|
*/
|
||||||
|
multiGet: (keys, callback) => {
|
||||||
|
if (!_immediate) {
|
||||||
|
_immediate = setImmediate(() => {
|
||||||
|
_immediate = null;
|
||||||
|
AsyncStorage.flushGetRequests();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRequest = {
|
||||||
|
keys: keys,
|
||||||
|
callback: callback,
|
||||||
|
// do we need this?
|
||||||
|
keyIndex: _getKeys.length,
|
||||||
|
resolve: null,
|
||||||
|
reject: null
|
||||||
|
};
|
||||||
|
const promiseResult = new Promise((resolve, reject) => {
|
||||||
|
getRequest.resolve = resolve;
|
||||||
|
getRequest.reject = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
_getRequests.push(getRequest); // avoid fetching duplicates
|
||||||
|
|
||||||
|
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (_getKeys.indexOf(key) === -1) {
|
||||||
|
_getKeys.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return promiseResult;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||||
|
* the operation completes you'll get a single callback with any errors.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||||
|
*/
|
||||||
|
multiSet: (keyValuePairs, callback) => {
|
||||||
|
checkValidArgs(keyValuePairs, callback);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
keyValuePairs.forEach(_ref2 => {
|
||||||
|
let [key, value] = _ref2;
|
||||||
|
checkValidInput(key, value);
|
||||||
|
});
|
||||||
|
RCTAsyncStorage.multiSet(keyValuePairs, errors => {
|
||||||
|
const error = convertErrors(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this to batch the deletion of all keys in the `keys` array.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||||
|
*/
|
||||||
|
multiRemove: (keys, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
keys.forEach(key => checkValidInput(key));
|
||||||
|
RCTAsyncStorage.multiRemove(keys, errors => {
|
||||||
|
const error = convertErrors(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch operation to merge in existing and new values for a given set of
|
||||||
|
* keys. This assumes that the values are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||||
|
*/
|
||||||
|
multiMerge: (keyValuePairs, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
RCTAsyncStorage.multiMerge(keyValuePairs, errors => {
|
||||||
|
const error = convertErrors(errors);
|
||||||
|
callback === null || callback === void 0 ? void 0 : callback(error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default AsyncStorage;
|
||||||
|
//# sourceMappingURL=AsyncStorage.native.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,19 @@
|
|||||||
|
// @ts-ignore Module '"react-native"' has no exported member 'TurboModuleRegistry'.
|
||||||
|
import { NativeModules, TurboModuleRegistry } from 'react-native';
|
||||||
|
import { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';
|
||||||
|
let RCTAsyncStorage = NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows
|
||||||
|
NativeModules['RNC_AsyncSQLiteDBStorage'] || NativeModules['RNCAsyncStorage'];
|
||||||
|
|
||||||
|
if (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {
|
||||||
|
// TurboModuleRegistry falls back to NativeModules so we don't have to try go
|
||||||
|
// assign NativeModules' counterparts if TurboModuleRegistry would resolve
|
||||||
|
// with undefined.
|
||||||
|
if (TurboModuleRegistry) {
|
||||||
|
RCTAsyncStorage = TurboModuleRegistry.get('AsyncSQLiteDBStorage') || TurboModuleRegistry.get('AsyncLocalStorage');
|
||||||
|
} else {
|
||||||
|
RCTAsyncStorage = NativeModules['AsyncSQLiteDBStorage'] || NativeModules['AsyncLocalStorage'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RCTAsyncStorage;
|
||||||
|
//# sourceMappingURL=RCTAsyncStorage.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["NativeModules","TurboModuleRegistry","shouldFallbackToLegacyNativeModule","RCTAsyncStorage","get"],"sources":["RCTAsyncStorage.ts"],"sourcesContent":["// @ts-ignore Module '\"react-native\"' has no exported member 'TurboModuleRegistry'.\nimport { NativeModules, TurboModuleRegistry } from 'react-native';\nimport { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';\n\nlet RCTAsyncStorage =\n NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows\n NativeModules['RNC_AsyncSQLiteDBStorage'] ||\n NativeModules['RNCAsyncStorage'];\n\nif (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {\n // TurboModuleRegistry falls back to NativeModules so we don't have to try go\n // assign NativeModules' counterparts if TurboModuleRegistry would resolve\n // with undefined.\n if (TurboModuleRegistry) {\n RCTAsyncStorage =\n TurboModuleRegistry.get('AsyncSQLiteDBStorage') ||\n TurboModuleRegistry.get('AsyncLocalStorage');\n } else {\n RCTAsyncStorage =\n NativeModules['AsyncSQLiteDBStorage'] ||\n NativeModules['AsyncLocalStorage'];\n }\n}\n\nexport default RCTAsyncStorage;\n"],"mappings":"AAAA;AACA,SAASA,aAAT,EAAwBC,mBAAxB,QAAmD,cAAnD;AACA,SAASC,kCAAT,QAAmD,sCAAnD;AAEA,IAAIC,eAAe,GACjBH,aAAa,CAAC,sBAAD,CAAb,IAAyC;AACzCA,aAAa,CAAC,0BAAD,CADb,IAEAA,aAAa,CAAC,iBAAD,CAHf;;AAKA,IAAI,CAACG,eAAD,IAAoBD,kCAAkC,EAA1D,EAA8D;EAC5D;EACA;EACA;EACA,IAAID,mBAAJ,EAAyB;IACvBE,eAAe,GACbF,mBAAmB,CAACG,GAApB,CAAwB,sBAAxB,KACAH,mBAAmB,CAACG,GAApB,CAAwB,mBAAxB,CAFF;EAGD,CAJD,MAIO;IACLD,eAAe,GACbH,aAAa,CAAC,sBAAD,CAAb,IACAA,aAAa,CAAC,mBAAD,CAFf;EAGD;AACF;;AAED,eAAeG,eAAf"}
|
@ -0,0 +1,56 @@
|
|||||||
|
export function checkValidArgs(keyValuePairs, callback) {
|
||||||
|
if (!Array.isArray(keyValuePairs) || keyValuePairs.length === 0 || !Array.isArray(keyValuePairs[0])) {
|
||||||
|
throw new Error('[AsyncStorage] Expected array of key-value pairs as first argument to multiSet');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback && typeof callback !== 'function') {
|
||||||
|
if (Array.isArray(callback)) {
|
||||||
|
throw new Error('[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?');
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('[AsyncStorage] Expected function as second argument to multiSet');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function checkValidInput() {
|
||||||
|
for (var _len = arguments.length, input = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||||
|
input[_key] = arguments[_key];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [key, value] = input;
|
||||||
|
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
console.warn(`[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${key}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.length > 1 && typeof value !== 'string') {
|
||||||
|
if (value == null) {
|
||||||
|
throw new Error(`[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||||
|
} else {
|
||||||
|
console.warn(`[AsyncStorage] The value for key "${key}" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\nPassed value: ${value}\nPassed key: ${key}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function convertError(error) {
|
||||||
|
if (!error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const out = new Error(error.message);
|
||||||
|
out.key = error.key;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
export function convertErrors(errs) {
|
||||||
|
const errors = ensureArray(errs);
|
||||||
|
return errors ? errors.map(e => convertError(e)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureArray(e) {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return e.length === 0 ? null : e;
|
||||||
|
} else if (e) {
|
||||||
|
return [e];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=helpers.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["checkValidArgs","keyValuePairs","callback","Array","isArray","length","Error","checkValidInput","input","key","value","console","warn","convertError","error","out","message","convertErrors","errs","errors","ensureArray","map","e"],"sources":["helpers.ts"],"sourcesContent":["import type { ErrorLike } from './types';\n\nexport function checkValidArgs(keyValuePairs: unknown[], callback: unknown) {\n if (\n !Array.isArray(keyValuePairs) ||\n keyValuePairs.length === 0 ||\n !Array.isArray(keyValuePairs[0])\n ) {\n throw new Error(\n '[AsyncStorage] Expected array of key-value pairs as first argument to multiSet'\n );\n }\n\n if (callback && typeof callback !== 'function') {\n if (Array.isArray(callback)) {\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?'\n );\n }\n\n throw new Error(\n '[AsyncStorage] Expected function as second argument to multiSet'\n );\n }\n}\n\nexport function checkValidInput(...input: unknown[]) {\n const [key, value] = input;\n\n if (typeof key !== 'string') {\n console.warn(\n `[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\\nKey passed: ${key}\\n`\n );\n }\n\n if (input.length > 1 && typeof value !== 'string') {\n if (value == null) {\n throw new Error(\n `[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n } else {\n console.warn(\n `[AsyncStorage] The value for key \"${key}\" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\\nPassed value: ${value}\\nPassed key: ${key}\\n`\n );\n }\n }\n}\n\nexport function convertError(error?: ErrorLike): Error | null {\n if (!error) {\n return null;\n }\n\n const out = new Error(error.message);\n (out as any).key = error.key;\n return out;\n}\n\nexport function convertErrors(\n errs?: ErrorLike[]\n): ReadonlyArray<Error | null> | null {\n const errors = ensureArray(errs);\n return errors ? errors.map((e) => convertError(e)) : null;\n}\n\nfunction ensureArray(e?: ErrorLike | ErrorLike[]): ErrorLike[] | null {\n if (Array.isArray(e)) {\n return e.length === 0 ? null : e;\n } else if (e) {\n return [e];\n } else {\n return null;\n }\n}\n"],"mappings":"AAEA,OAAO,SAASA,cAAT,CAAwBC,aAAxB,EAAkDC,QAAlD,EAAqE;EAC1E,IACE,CAACC,KAAK,CAACC,OAAN,CAAcH,aAAd,CAAD,IACAA,aAAa,CAACI,MAAd,KAAyB,CADzB,IAEA,CAACF,KAAK,CAACC,OAAN,CAAcH,aAAa,CAAC,CAAD,CAA3B,CAHH,EAIE;IACA,MAAM,IAAIK,KAAJ,CACJ,gFADI,CAAN;EAGD;;EAED,IAAIJ,QAAQ,IAAI,OAAOA,QAAP,KAAoB,UAApC,EAAgD;IAC9C,IAAIC,KAAK,CAACC,OAAN,CAAcF,QAAd,CAAJ,EAA6B;MAC3B,MAAM,IAAII,KAAJ,CACJ,6IADI,CAAN;IAGD;;IAED,MAAM,IAAIA,KAAJ,CACJ,iEADI,CAAN;EAGD;AACF;AAED,OAAO,SAASC,eAAT,GAA8C;EAAA,kCAAlBC,KAAkB;IAAlBA,KAAkB;EAAA;;EACnD,MAAM,CAACC,GAAD,EAAMC,KAAN,IAAeF,KAArB;;EAEA,IAAI,OAAOC,GAAP,KAAe,QAAnB,EAA6B;IAC3BE,OAAO,CAACC,IAAR,CACG,wBAAuB,OAAOH,GAAI,iHAAgHA,GAAI,IADzJ;EAGD;;EAED,IAAID,KAAK,CAACH,MAAN,GAAe,CAAf,IAAoB,OAAOK,KAAP,KAAiB,QAAzC,EAAmD;IACjD,IAAIA,KAAK,IAAI,IAAb,EAAmB;MACjB,MAAM,IAAIJ,KAAJ,CACH,gJAA+II,KAAM,iBAAgBD,GAAI,IADtK,CAAN;IAGD,CAJD,MAIO;MACLE,OAAO,CAACC,IAAR,CACG,qCAAoCH,GAAI,4GAA2GC,KAAM,iBAAgBD,GAAI,IADhL;IAGD;EACF;AACF;AAED,OAAO,SAASI,YAAT,CAAsBC,KAAtB,EAAuD;EAC5D,IAAI,CAACA,KAAL,EAAY;IACV,OAAO,IAAP;EACD;;EAED,MAAMC,GAAG,GAAG,IAAIT,KAAJ,CAAUQ,KAAK,CAACE,OAAhB,CAAZ;EACCD,GAAD,CAAaN,GAAb,GAAmBK,KAAK,CAACL,GAAzB;EACA,OAAOM,GAAP;AACD;AAED,OAAO,SAASE,aAAT,CACLC,IADK,EAE+B;EACpC,MAAMC,MAAM,GAAGC,WAAW,CAACF,IAAD,CAA1B;EACA,OAAOC,MAAM,GAAGA,MAAM,CAACE,GAAP,CAAYC,CAAD,IAAOT,YAAY,CAACS,CAAD,CAA9B,CAAH,GAAwC,IAArD;AACD;;AAED,SAASF,WAAT,CAAqBE,CAArB,EAAsE;EACpE,IAAInB,KAAK,CAACC,OAAN,CAAckB,CAAd,CAAJ,EAAsB;IACpB,OAAOA,CAAC,CAACjB,MAAF,KAAa,CAAb,GAAiB,IAAjB,GAAwBiB,CAA/B;EACD,CAFD,MAEO,IAAIA,CAAJ,EAAO;IACZ,OAAO,CAACA,CAAD,CAAP;EACD,CAFM,MAEA;IACL,OAAO,IAAP;EACD;AACF"}
|
@ -0,0 +1,34 @@
|
|||||||
|
import AsyncStorage from './AsyncStorage';
|
||||||
|
export function useAsyncStorage(key) {
|
||||||
|
return {
|
||||||
|
getItem: function () {
|
||||||
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||||
|
args[_key] = arguments[_key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return AsyncStorage.getItem(key, ...args);
|
||||||
|
},
|
||||||
|
setItem: function () {
|
||||||
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||||
|
args[_key2] = arguments[_key2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return AsyncStorage.setItem(key, ...args);
|
||||||
|
},
|
||||||
|
mergeItem: function () {
|
||||||
|
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
|
||||||
|
args[_key3] = arguments[_key3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return AsyncStorage.mergeItem(key, ...args);
|
||||||
|
},
|
||||||
|
removeItem: function () {
|
||||||
|
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
||||||
|
args[_key4] = arguments[_key4];
|
||||||
|
}
|
||||||
|
|
||||||
|
return AsyncStorage.removeItem(key, ...args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=hooks.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["AsyncStorage","useAsyncStorage","key","getItem","args","setItem","mergeItem","removeItem"],"sources":["hooks.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\nimport type { AsyncStorageHook } from './types';\n\nexport function useAsyncStorage(key: string): AsyncStorageHook {\n return {\n getItem: (...args) => AsyncStorage.getItem(key, ...args),\n setItem: (...args) => AsyncStorage.setItem(key, ...args),\n mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),\n removeItem: (...args) => AsyncStorage.removeItem(key, ...args),\n };\n}\n"],"mappings":"AAAA,OAAOA,YAAP,MAAyB,gBAAzB;AAGA,OAAO,SAASC,eAAT,CAAyBC,GAAzB,EAAwD;EAC7D,OAAO;IACLC,OAAO,EAAE;MAAA,kCAAIC,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACG,OAAb,CAAqBD,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CADJ;IAELC,OAAO,EAAE;MAAA,mCAAID,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACK,OAAb,CAAqBH,GAArB,EAA0B,GAAGE,IAA7B,CAAb;IAAA,CAFJ;IAGLE,SAAS,EAAE;MAAA,mCAAIF,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACM,SAAb,CAAuBJ,GAAvB,EAA4B,GAAGE,IAA/B,CAAb;IAAA,CAHN;IAILG,UAAU,EAAE;MAAA,mCAAIH,IAAJ;QAAIA,IAAJ;MAAA;;MAAA,OAAaJ,YAAY,CAACO,UAAb,CAAwBL,GAAxB,EAA6B,GAAGE,IAAhC,CAAb;IAAA;EAJP,CAAP;AAMD"}
|
@ -0,0 +1,4 @@
|
|||||||
|
import AsyncStorage from './AsyncStorage';
|
||||||
|
export { useAsyncStorage } from './hooks';
|
||||||
|
export default AsyncStorage;
|
||||||
|
//# sourceMappingURL=index.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["AsyncStorage","useAsyncStorage"],"sources":["index.ts"],"sourcesContent":["import AsyncStorage from './AsyncStorage';\n\nexport { useAsyncStorage } from './hooks';\n\nexport type { AsyncStorageStatic } from './types';\n\nexport default AsyncStorage;\n"],"mappings":"AAAA,OAAOA,YAAP,MAAyB,gBAAzB;AAEA,SAASC,eAAT,QAAgC,SAAhC;AAIA,eAAeD,YAAf"}
|
@ -0,0 +1,31 @@
|
|||||||
|
import { NativeModules } from 'react-native';
|
||||||
|
export function shouldFallbackToLegacyNativeModule() {
|
||||||
|
var _NativeModules$Native, _NativeModules$Native2;
|
||||||
|
|
||||||
|
const expoConstants = (_NativeModules$Native = NativeModules['NativeUnimoduleProxy']) === null || _NativeModules$Native === void 0 ? void 0 : (_NativeModules$Native2 = _NativeModules$Native.modulesConstants) === null || _NativeModules$Native2 === void 0 ? void 0 : _NativeModules$Native2.ExponentConstants;
|
||||||
|
|
||||||
|
if (expoConstants) {
|
||||||
|
/**
|
||||||
|
* In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.
|
||||||
|
* In bare React Native apps using expo-constants, appOwnership is never defined, so
|
||||||
|
* isLegacySdkVersion will be false in that context.
|
||||||
|
*/
|
||||||
|
const isLegacySdkVersion = expoConstants.appOwnership && !expoConstants.executionEnvironment;
|
||||||
|
/**
|
||||||
|
* Expo managed apps don't include the @react-native-async-storage/async-storage
|
||||||
|
* native modules yet, but the API interface is the same, so we can use the version
|
||||||
|
* exported from React Native still.
|
||||||
|
*
|
||||||
|
* If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this
|
||||||
|
* will likely not be valid anymore, and the package will need to be included in the Expo SDK
|
||||||
|
* to continue to work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (isLegacySdkVersion || ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=shouldFallbackToLegacyNativeModule.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["NativeModules","shouldFallbackToLegacyNativeModule","expoConstants","modulesConstants","ExponentConstants","isLegacySdkVersion","appOwnership","executionEnvironment","includes"],"sources":["shouldFallbackToLegacyNativeModule.ts"],"sourcesContent":["import { NativeModules } from 'react-native';\n\nexport function shouldFallbackToLegacyNativeModule(): boolean {\n const expoConstants =\n NativeModules['NativeUnimoduleProxy']?.modulesConstants?.ExponentConstants;\n\n if (expoConstants) {\n /**\n * In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.\n * In bare React Native apps using expo-constants, appOwnership is never defined, so\n * isLegacySdkVersion will be false in that context.\n */\n const isLegacySdkVersion =\n expoConstants.appOwnership && !expoConstants.executionEnvironment;\n\n /**\n * Expo managed apps don't include the @react-native-async-storage/async-storage\n * native modules yet, but the API interface is the same, so we can use the version\n * exported from React Native still.\n *\n * If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this\n * will likely not be valid anymore, and the package will need to be included in the Expo SDK\n * to continue to work.\n */\n if (\n isLegacySdkVersion ||\n ['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)\n ) {\n return true;\n }\n }\n\n return false;\n}\n"],"mappings":"AAAA,SAASA,aAAT,QAA8B,cAA9B;AAEA,OAAO,SAASC,kCAAT,GAAuD;EAAA;;EAC5D,MAAMC,aAAa,4BACjBF,aAAa,CAAC,sBAAD,CADI,oFACjB,sBAAuCG,gBADtB,2DACjB,uBAAyDC,iBAD3D;;EAGA,IAAIF,aAAJ,EAAmB;IACjB;AACJ;AACA;AACA;AACA;IACI,MAAMG,kBAAkB,GACtBH,aAAa,CAACI,YAAd,IAA8B,CAACJ,aAAa,CAACK,oBAD/C;IAGA;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;IACI,IACEF,kBAAkB,IAClB,CAAC,aAAD,EAAgB,YAAhB,EAA8BG,QAA9B,CAAuCN,aAAa,CAACK,oBAArD,CAFF,EAGE;MACA,OAAO,IAAP;IACD;EACF;;EAED,OAAO,KAAP;AACD"}
|
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
//# sourceMappingURL=types.js.map
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Nicolas Gallagher.
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
import type { AsyncStorageStatic } from './types';
|
||||||
|
declare const AsyncStorage: AsyncStorageStatic;
|
||||||
|
export default AsyncStorage;
|
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
import type { AsyncStorageStatic } from './types';
|
||||||
|
/**
|
||||||
|
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||||
|
* storage system that is global to the app. It should be used instead of
|
||||||
|
* LocalStorage.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||||
|
*/
|
||||||
|
declare const AsyncStorage: AsyncStorageStatic;
|
||||||
|
export default AsyncStorage;
|
@ -0,0 +1,2 @@
|
|||||||
|
declare let RCTAsyncStorage: any;
|
||||||
|
export default RCTAsyncStorage;
|
@ -0,0 +1,5 @@
|
|||||||
|
import type { ErrorLike } from './types';
|
||||||
|
export declare function checkValidArgs(keyValuePairs: unknown[], callback: unknown): void;
|
||||||
|
export declare function checkValidInput(...input: unknown[]): void;
|
||||||
|
export declare function convertError(error?: ErrorLike): Error | null;
|
||||||
|
export declare function convertErrors(errs?: ErrorLike[]): ReadonlyArray<Error | null> | null;
|
@ -0,0 +1,2 @@
|
|||||||
|
import type { AsyncStorageHook } from './types';
|
||||||
|
export declare function useAsyncStorage(key: string): AsyncStorageHook;
|
@ -0,0 +1,4 @@
|
|||||||
|
import AsyncStorage from './AsyncStorage';
|
||||||
|
export { useAsyncStorage } from './hooks';
|
||||||
|
export type { AsyncStorageStatic } from './types';
|
||||||
|
export default AsyncStorage;
|
@ -0,0 +1 @@
|
|||||||
|
export declare function shouldFallbackToLegacyNativeModule(): boolean;
|
@ -0,0 +1,113 @@
|
|||||||
|
export declare type ErrorLike = {
|
||||||
|
message: string;
|
||||||
|
key: string;
|
||||||
|
};
|
||||||
|
export declare type Callback = (error?: Error | null) => void;
|
||||||
|
export declare type CallbackWithResult<T> = (error?: Error | null, result?: T | null) => void;
|
||||||
|
export declare type KeyValuePair = [string, string | null];
|
||||||
|
export declare type MultiCallback = (errors?: readonly (Error | null)[] | null) => void;
|
||||||
|
export declare type MultiGetCallback = (errors?: readonly (Error | null)[] | null, result?: readonly KeyValuePair[]) => void;
|
||||||
|
export declare type MultiRequest = {
|
||||||
|
keys: readonly string[];
|
||||||
|
callback?: MultiGetCallback;
|
||||||
|
keyIndex: number;
|
||||||
|
resolve?: (result: readonly KeyValuePair[]) => void;
|
||||||
|
reject?: (error?: any) => void;
|
||||||
|
};
|
||||||
|
export declare type AsyncStorageHook = {
|
||||||
|
getItem: (callback?: CallbackWithResult<string>) => Promise<string | null>;
|
||||||
|
setItem: (value: string, callback?: Callback) => Promise<void>;
|
||||||
|
mergeItem: (value: string, callback?: Callback) => Promise<void>;
|
||||||
|
removeItem: (callback?: Callback) => Promise<void>;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||||
|
* storage system that is global to the app. It should be used instead of
|
||||||
|
* LocalStorage.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||||
|
*/
|
||||||
|
export declare type AsyncStorageStatic = {
|
||||||
|
/**
|
||||||
|
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||||
|
*/
|
||||||
|
getItem: (key: string, callback?: CallbackWithResult<string>) => Promise<string | null>;
|
||||||
|
/**
|
||||||
|
* Sets the value for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||||
|
*/
|
||||||
|
setItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Removes an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||||
|
*/
|
||||||
|
removeItem: (key: string, callback?: Callback) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Merges an existing `key` value with an input value, assuming both values
|
||||||
|
* are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||||
|
*/
|
||||||
|
mergeItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||||
|
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||||
|
* your app's keys.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||||
|
*/
|
||||||
|
clear: (callback?: Callback) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||||
|
*/
|
||||||
|
getAllKeys: (callback?: CallbackWithResult<readonly string[]>) => Promise<readonly string[]>;
|
||||||
|
/**
|
||||||
|
* The following batched functions are useful for executing a lot of
|
||||||
|
* operations at once, allowing for native optimizations and provide the
|
||||||
|
* convenience of a single callback after all operations are complete.
|
||||||
|
*
|
||||||
|
* These functions return arrays of errors, potentially one for every key.
|
||||||
|
* For key-specific errors, the Error object will have a key property to
|
||||||
|
* indicate which key caused the error.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||||
|
* */
|
||||||
|
flushGetRequests: () => void;
|
||||||
|
/**
|
||||||
|
* This allows you to batch the fetching of items given an array of `key`
|
||||||
|
* inputs. Your callback will be invoked with an array of corresponding
|
||||||
|
* key-value pairs found.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||||
|
*/
|
||||||
|
multiGet: (keys: readonly string[], callback?: MultiGetCallback) => Promise<readonly KeyValuePair[]>;
|
||||||
|
/**
|
||||||
|
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||||
|
* the operation completes you'll get a single callback with any errors.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||||
|
*/
|
||||||
|
multiSet: (keyValuePairs: [string, string][], callback?: MultiCallback) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Call this to batch the deletion of all keys in the `keys` array.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||||
|
*/
|
||||||
|
multiRemove: (keys: readonly string[], callback?: MultiCallback) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Batch operation to merge in existing and new values for a given set of
|
||||||
|
* keys. This assumes that the values are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||||
|
*/
|
||||||
|
multiMerge: (keyValuePairs: [string, string][], callback?: MultiCallback) => Promise<void>;
|
||||||
|
};
|
@ -0,0 +1,385 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||||
|
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||||
|
3893A2E123C509D1009200E3 /* RNCAsyncStorage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||||
|
3893A2E223C509D1009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||||
|
3893A2E523C50AFE009200E3 /* RNCAsyncStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||||
|
3893A2E623C50AFE009200E3 /* RNCAsyncStorageDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||||
|
3893A2E823C50AFE009200E3 /* RNCAsyncStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */; };
|
||||||
|
3893A2EB23C50AFE009200E3 /* RNCAsyncStorage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */; };
|
||||||
|
3893A2EC23C50AFE009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */; };
|
||||||
|
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
3893A2EA23C50AFE009200E3 /* CopyFiles */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = include/RNCAsyncStorage;
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
3893A2EB23C50AFE009200E3 /* RNCAsyncStorage.h in CopyFiles */,
|
||||||
|
3893A2EC23C50AFE009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "include/$(PRODUCT_NAME)";
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
3893A2E123C509D1009200E3 /* RNCAsyncStorage.h in CopyFiles */,
|
||||||
|
3893A2E223C509D1009200E3 /* RNCAsyncStorageDelegate.h in CopyFiles */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCAsyncStorage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNCAsyncStorageDelegate.h; path = ../ios/RNCAsyncStorageDelegate.h; sourceTree = "<group>"; };
|
||||||
|
3893A2F023C50AFE009200E3 /* libRNCAsyncStorage-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRNCAsyncStorage-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNCAsyncStorage.h; path = ../ios/RNCAsyncStorage.h; sourceTree = "<group>"; };
|
||||||
|
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNCAsyncStorage.m; path = ../ios/RNCAsyncStorage.m; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
3893A2E923C50AFE009200E3 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
58B511D21A9E6C8500147676 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
B3E7B5881CC2AC0600A0062D /* RNCAsyncStorage.h */,
|
||||||
|
B3E7B5891CC2AC0600A0062D /* RNCAsyncStorage.m */,
|
||||||
|
1990B9402233FE3A009E5EA1 /* RNCAsyncStorageDelegate.h */,
|
||||||
|
134814211AA4EA7D00B7C361 /* Products */,
|
||||||
|
3893A2F023C50AFE009200E3 /* libRNCAsyncStorage-macOS.a */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXHeadersBuildPhase section */
|
||||||
|
19F94B1D2239A948006921A9 /* Headers */ = {
|
||||||
|
isa = PBXHeadersBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
1990B97A223993B0009E5EA1 /* RNCAsyncStorage.h in Headers */,
|
||||||
|
1990B97B223993B0009E5EA1 /* RNCAsyncStorageDelegate.h in Headers */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
3893A2E423C50AFE009200E3 /* Headers */ = {
|
||||||
|
isa = PBXHeadersBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
3893A2E523C50AFE009200E3 /* RNCAsyncStorage.h in Headers */,
|
||||||
|
3893A2E623C50AFE009200E3 /* RNCAsyncStorageDelegate.h in Headers */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXHeadersBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
3893A2E323C50AFE009200E3 /* RNCAsyncStorage-macOS */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 3893A2ED23C50AFE009200E3 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage-macOS" */;
|
||||||
|
buildPhases = (
|
||||||
|
3893A2E423C50AFE009200E3 /* Headers */,
|
||||||
|
3893A2E723C50AFE009200E3 /* Sources */,
|
||||||
|
3893A2E923C50AFE009200E3 /* Frameworks */,
|
||||||
|
3893A2EA23C50AFE009200E3 /* CopyFiles */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "RNCAsyncStorage-macOS";
|
||||||
|
productName = RCTDataManager;
|
||||||
|
productReference = 3893A2F023C50AFE009200E3 /* libRNCAsyncStorage-macOS.a */;
|
||||||
|
productType = "com.apple.product-type.library.static";
|
||||||
|
};
|
||||||
|
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */;
|
||||||
|
buildPhases = (
|
||||||
|
19F94B1D2239A948006921A9 /* Headers */,
|
||||||
|
58B511D71A9E6C8500147676 /* Sources */,
|
||||||
|
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||||
|
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = RNCAsyncStorage;
|
||||||
|
productName = RCTDataManager;
|
||||||
|
productReference = 134814201AA4EA6300B7C361 /* libRNCAsyncStorage.a */;
|
||||||
|
productType = "com.apple.product-type.library.static";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
58B511D31A9E6C8500147676 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 0830;
|
||||||
|
ORGANIZATIONNAME = Facebook;
|
||||||
|
TargetAttributes = {
|
||||||
|
58B511DA1A9E6C8500147676 = {
|
||||||
|
CreatedOnToolsVersion = 6.1.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */;
|
||||||
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = English;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
English,
|
||||||
|
en,
|
||||||
|
);
|
||||||
|
mainGroup = 58B511D21A9E6C8500147676;
|
||||||
|
productRefGroup = 58B511D21A9E6C8500147676;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
58B511DA1A9E6C8500147676 /* RNCAsyncStorage */,
|
||||||
|
3893A2E323C50AFE009200E3 /* RNCAsyncStorage-macOS */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
3893A2E723C50AFE009200E3 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
3893A2E823C50AFE009200E3 /* RNCAsyncStorage.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
58B511D71A9E6C8500147676 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
B3E7B58A1CC2AC0600A0062D /* RNCAsyncStorage.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
3893A2EE23C50AFE009200E3 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
3893A2EF23C50AFE009200E3 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
58B511ED1A9E6C8500147676 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
58B511EE1A9E6C8500147676 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = YES;
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
58B511F01A9E6C8500147676 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/../../../React/**",
|
||||||
|
"$(SRCROOT)/../../react-native/React/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = RNCAsyncStorage;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
58B511F11A9E6C8500147676 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/../../../React/**",
|
||||||
|
"$(SRCROOT)/../../react-native/React/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = RNCAsyncStorage;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
3893A2ED23C50AFE009200E3 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage-macOS" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
3893A2EE23C50AFE009200E3 /* Debug */,
|
||||||
|
3893A2EF23C50AFE009200E3 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCAsyncStorage" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
58B511ED1A9E6C8500147676 /* Debug */,
|
||||||
|
58B511EE1A9E6C8500147676 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCAsyncStorage" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
58B511F01A9E6C8500147676 /* Debug */,
|
||||||
|
58B511F11A9E6C8500147676 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1120"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "3893A2E323C50AFE009200E3"
|
||||||
|
BuildableName = "libRNCAsyncStorage-macOS.a"
|
||||||
|
BlueprintName = "RNCAsyncStorage-macOS"
|
||||||
|
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "3893A2E323C50AFE009200E3"
|
||||||
|
BuildableName = "libRNCAsyncStorage-macOS.a"
|
||||||
|
BlueprintName = "RNCAsyncStorage-macOS"
|
||||||
|
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1120"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
|
||||||
|
BuildableName = "libRNCAsyncStorage.a"
|
||||||
|
BlueprintName = "RNCAsyncStorage"
|
||||||
|
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
|
||||||
|
BuildableName = "libRNCAsyncStorage.a"
|
||||||
|
BlueprintName = "RNCAsyncStorage"
|
||||||
|
ReferencedContainer = "container:RNCAsyncStorage.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -0,0 +1 @@
|
|||||||
|
../../../../react-native/cli.js
|
195
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/package.json
generated
vendored
195
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/package.json
generated
vendored
@ -0,0 +1,195 @@
|
|||||||
|
{
|
||||||
|
"name": "@react-native-async-storage/async-storage",
|
||||||
|
"version": "1.17.12",
|
||||||
|
"description": "Asynchronous, persistent, key-value storage system for React Native.",
|
||||||
|
"main": "lib/commonjs/index.js",
|
||||||
|
"module": "lib/module/index.js",
|
||||||
|
"react-native": "src/index.ts",
|
||||||
|
"types": "lib/typescript/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"RNCAsyncStorage.podspec",
|
||||||
|
"android/build.gradle",
|
||||||
|
"android/src",
|
||||||
|
"android/testresults.gradle",
|
||||||
|
"ios/",
|
||||||
|
"jest/",
|
||||||
|
"lib/",
|
||||||
|
"macos/",
|
||||||
|
"src/",
|
||||||
|
"windows/"
|
||||||
|
],
|
||||||
|
"author": "Krzysztof Borowy <hello@krizzu.dev>",
|
||||||
|
"contributors": [
|
||||||
|
"Evan Bacon <bacon@expo.io> (https://github.com/evanbacon)",
|
||||||
|
"Tommy Nguyen <4123478+tido64@users.noreply.github.com> (https://github.com/tido64)"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/react-native-async-storage/async-storage#readme",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"react-native",
|
||||||
|
"react native",
|
||||||
|
"async storage",
|
||||||
|
"asyncstorage",
|
||||||
|
"storage"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/react-native-async-storage/async-storage.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"format": "concurrently yarn:format:*",
|
||||||
|
"format:c": "clang-format -i $(git ls-files '*.cpp' '*.h' '*.m' '*.mm')",
|
||||||
|
"format:js": "prettier --write $(git ls-files '*.js' '*.json' '*.md' '*.ts' '*.tsx' '*.yml')",
|
||||||
|
"prepare": "bob build",
|
||||||
|
"start": "react-native start",
|
||||||
|
"start:android": "react-native run-android",
|
||||||
|
"start:ios": "react-native run-ios --project-path example/ios",
|
||||||
|
"start:macos": "react-native run-macos --project-path example/macos --scheme AsyncStorageExample",
|
||||||
|
"start:web": "expo start:web",
|
||||||
|
"start:windows": "install-windows-test-app -p example/windows && react-native run-windows --root example --logging --no-packager --no-telemetry",
|
||||||
|
"build:e2e:android": "scripts/android_e2e.sh 'build'",
|
||||||
|
"build:e2e:ios": "scripts/ios_e2e.sh 'build'",
|
||||||
|
"build:e2e:macos": "scripts/macos_e2e.sh 'build'",
|
||||||
|
"bundle:android": "scripts/android_e2e.sh 'bundle'",
|
||||||
|
"bundle:ios": "scripts/ios_e2e.sh 'bundle'",
|
||||||
|
"bundle:macos": "react-native bundle --entry-file index.ts --platform macos --bundle-output example/index.macos.jsbundle",
|
||||||
|
"test": "concurrently -n lint,ts yarn:test:lint yarn:test:ts",
|
||||||
|
"test:lint": "eslint src/**/*.ts example/**/*.ts jest/*.js",
|
||||||
|
"test:ts": "tsc --project tsconfig.all.json",
|
||||||
|
"test:e2e:android": "detox test -c android.emu.release --maxConcurrency 1",
|
||||||
|
"test:e2e:ios": "detox test -c ios.sim.release --maxConcurrency 1",
|
||||||
|
"test:e2e:macos": "scripts/macos_e2e.sh 'test'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"merge-options": "^3.0.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react-native": "^0.0.0-0 || 0.60 - 0.71 || 1000.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.0",
|
||||||
|
"@babel/preset-env": "^7.1.6",
|
||||||
|
"@react-native-community/eslint-config": "^3.0.0",
|
||||||
|
"@semantic-release/changelog": "^6.0.0",
|
||||||
|
"@semantic-release/git": "^10.0.0",
|
||||||
|
"@types/lodash": "^4.14.184",
|
||||||
|
"@types/react": "^17.0.0",
|
||||||
|
"@types/react-native": "^0.68.0",
|
||||||
|
"concurrently": "^6.4.0",
|
||||||
|
"detox": "^19.4.5",
|
||||||
|
"eslint": "^8.0.0",
|
||||||
|
"expo": "^45.0.0",
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"jest-circus": "^26.6.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"prettier": "^2.5.1",
|
||||||
|
"react": "17.0.2",
|
||||||
|
"react-dom": "17.0.2",
|
||||||
|
"react-native": "^0.68.0",
|
||||||
|
"react-native-builder-bob": "^0.18.0",
|
||||||
|
"react-native-macos": "^0.68.0",
|
||||||
|
"react-native-test-app": "^2.3.10",
|
||||||
|
"react-native-web": "^0.17.0",
|
||||||
|
"react-native-windows": "^0.68.0",
|
||||||
|
"react-test-renderer": "17.0.2",
|
||||||
|
"semantic-release": "^19.0.0",
|
||||||
|
"typescript": "^4.5.0"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@3.4.1",
|
||||||
|
"resolutions": {
|
||||||
|
"npm/chalk": "^4.1.2"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"preset": "react-native",
|
||||||
|
"setupFiles": [
|
||||||
|
"./example/jest.setup.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"detox": {
|
||||||
|
"test-runner": "jest",
|
||||||
|
"runner-config": "example/e2e/config.json",
|
||||||
|
"configurations": {
|
||||||
|
"ios.sim.release": {
|
||||||
|
"binaryPath": "example/ios/build/Build/Products/Release-iphonesimulator/ReactTestApp.app",
|
||||||
|
"type": "ios.simulator",
|
||||||
|
"device": {
|
||||||
|
"type": "iPhone 13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"android.emu.release": {
|
||||||
|
"binaryPath": "example/android/app/build/outputs/apk/release/app-release.apk",
|
||||||
|
"testBinaryPath": "example/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk",
|
||||||
|
"type": "android.emulator",
|
||||||
|
"device": {
|
||||||
|
"avdName": "E2E_API_30",
|
||||||
|
"utilBinaryPaths": [
|
||||||
|
"/var/tmp/test-butler.apk"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"android.emu.release.next": {
|
||||||
|
"binaryPath": "example/android/app/build/outputs/apk/next/app-next.apk",
|
||||||
|
"testBinaryPath": "example/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk",
|
||||||
|
"type": "android.emulator",
|
||||||
|
"device": {
|
||||||
|
"avdName": "E2E_API_30",
|
||||||
|
"utilBinaryPaths": [
|
||||||
|
"/var/tmp/test-butler.apk"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"@react-native-community",
|
||||||
|
"plugin:jest/recommended"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"dot-notation": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"endOfLine": "auto",
|
||||||
|
"singleQuote": true,
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.md",
|
||||||
|
"options": {
|
||||||
|
"proseWrap": "always"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"react-native-builder-bob": {
|
||||||
|
"source": "src",
|
||||||
|
"output": "lib",
|
||||||
|
"targets": [
|
||||||
|
"commonjs",
|
||||||
|
"module",
|
||||||
|
"typescript"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"release": {
|
||||||
|
"branches": [
|
||||||
|
"master"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@semantic-release/commit-analyzer",
|
||||||
|
"@semantic-release/release-notes-generator",
|
||||||
|
"@semantic-release/changelog",
|
||||||
|
"@semantic-release/npm",
|
||||||
|
"@semantic-release/github",
|
||||||
|
[
|
||||||
|
"@semantic-release/git",
|
||||||
|
{
|
||||||
|
"assets": [
|
||||||
|
"CHANGELOG.md",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"message": "chore(release): ${nextRelease.version} [skip ci]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,356 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
checkValidArgs,
|
||||||
|
checkValidInput,
|
||||||
|
convertError,
|
||||||
|
convertErrors,
|
||||||
|
} from './helpers';
|
||||||
|
import RCTAsyncStorage from './RCTAsyncStorage';
|
||||||
|
import type {
|
||||||
|
AsyncStorageStatic,
|
||||||
|
ErrorLike,
|
||||||
|
KeyValuePair,
|
||||||
|
MultiRequest,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
if (!RCTAsyncStorage) {
|
||||||
|
throw new Error(`[@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.
|
||||||
|
|
||||||
|
To fix this issue try these steps:
|
||||||
|
|
||||||
|
• Rebuild and restart the app.
|
||||||
|
|
||||||
|
• Run the packager with \`--reset-cache\` flag.
|
||||||
|
|
||||||
|
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.
|
||||||
|
|
||||||
|
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest
|
||||||
|
|
||||||
|
If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||||
|
* storage system that is global to the app. It should be used instead of
|
||||||
|
* LocalStorage.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||||
|
*/
|
||||||
|
const AsyncStorage = ((): AsyncStorageStatic => {
|
||||||
|
let _getRequests: MultiRequest[] = [];
|
||||||
|
let _getKeys: string[] = [];
|
||||||
|
let _immediate: ReturnType<typeof setImmediate> | null = null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||||
|
*/
|
||||||
|
getItem: (key, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key);
|
||||||
|
RCTAsyncStorage.multiGet(
|
||||||
|
[key],
|
||||||
|
(errors?: ErrorLike[], result?: string[][]) => {
|
||||||
|
// Unpack result to get value from [[key,value]]
|
||||||
|
const value = result?.[0]?.[1] ? result[0][1] : null;
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback?.(errs?.[0], value);
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||||
|
*/
|
||||||
|
setItem: (key, value, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key, value);
|
||||||
|
RCTAsyncStorage.multiSet([[key, value]], (errors?: ErrorLike[]) => {
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback?.(errs?.[0]);
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||||
|
*/
|
||||||
|
removeItem: (key, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key);
|
||||||
|
RCTAsyncStorage.multiRemove([key], (errors?: ErrorLike[]) => {
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback?.(errs?.[0]);
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges an existing `key` value with an input value, assuming both values
|
||||||
|
* are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||||
|
*/
|
||||||
|
mergeItem: (key, value, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkValidInput(key, value);
|
||||||
|
RCTAsyncStorage.multiMerge([[key, value]], (errors?: ErrorLike[]) => {
|
||||||
|
const errs = convertErrors(errors);
|
||||||
|
callback?.(errs?.[0]);
|
||||||
|
if (errs) {
|
||||||
|
reject(errs[0]);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||||
|
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||||
|
* your app's keys.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||||
|
*/
|
||||||
|
clear: (callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
RCTAsyncStorage.clear((error?: ErrorLike) => {
|
||||||
|
const err = convertError(error);
|
||||||
|
callback?.(err);
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||||
|
*/
|
||||||
|
getAllKeys: (callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
RCTAsyncStorage.getAllKeys((error?: ErrorLike, keys?: string[]) => {
|
||||||
|
const err = convertError(error);
|
||||||
|
callback?.(err, keys);
|
||||||
|
if (keys) {
|
||||||
|
resolve(keys);
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following batched functions are useful for executing a lot of
|
||||||
|
* operations at once, allowing for native optimizations and provide the
|
||||||
|
* convenience of a single callback after all operations are complete.
|
||||||
|
*
|
||||||
|
* These functions return arrays of errors, potentially one for every key.
|
||||||
|
* For key-specific errors, the Error object will have a key property to
|
||||||
|
* indicate which key caused the error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||||
|
* */
|
||||||
|
flushGetRequests: () => {
|
||||||
|
const getRequests = _getRequests;
|
||||||
|
const getKeys = _getKeys;
|
||||||
|
|
||||||
|
_getRequests = [];
|
||||||
|
_getKeys = [];
|
||||||
|
|
||||||
|
RCTAsyncStorage.multiGet(
|
||||||
|
getKeys,
|
||||||
|
(errors?: ErrorLike[], result?: string[][]) => {
|
||||||
|
// Even though the runtime complexity of this is theoretically worse vs if we used a map,
|
||||||
|
// it's much, much faster in practice for the data sets we deal with (we avoid
|
||||||
|
// allocating result pair arrays). This was heavily benchmarked.
|
||||||
|
//
|
||||||
|
// Is there a way to avoid using the map but fix the bug in this breaking test?
|
||||||
|
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
|
||||||
|
const map: Record<string, string> = {};
|
||||||
|
result?.forEach(([key, value]) => {
|
||||||
|
map[key] = value;
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
const reqLength = getRequests.length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As mentioned few lines above, this method could be called with the array of potential error,
|
||||||
|
* in case of anything goes wrong. The problem is, if any of the batched calls fails
|
||||||
|
* the rest of them would fail too, but the error would be consumed by just one. The rest
|
||||||
|
* would simply return `undefined` as their result, rendering false negatives.
|
||||||
|
*
|
||||||
|
* In order to avoid this situation, in case of any call failing,
|
||||||
|
* the rest of them will be rejected as well (with the same error).
|
||||||
|
*/
|
||||||
|
const errorList = convertErrors(errors);
|
||||||
|
const error = errorList?.length ? errorList[0] : null;
|
||||||
|
|
||||||
|
for (let i = 0; i < reqLength; i++) {
|
||||||
|
const request = getRequests[i];
|
||||||
|
if (error) {
|
||||||
|
request.callback?.(errorList);
|
||||||
|
request.reject?.(error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const requestResult = request.keys.map<KeyValuePair>((key) => [
|
||||||
|
key,
|
||||||
|
map[key],
|
||||||
|
]);
|
||||||
|
request.callback?.(null, requestResult);
|
||||||
|
request.resolve?.(requestResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This allows you to batch the fetching of items given an array of `key`
|
||||||
|
* inputs. Your callback will be invoked with an array of corresponding
|
||||||
|
* key-value pairs found.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||||
|
*/
|
||||||
|
multiGet: (keys, callback) => {
|
||||||
|
if (!_immediate) {
|
||||||
|
_immediate = setImmediate(() => {
|
||||||
|
_immediate = null;
|
||||||
|
AsyncStorage.flushGetRequests();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRequest: MultiRequest = {
|
||||||
|
keys: keys,
|
||||||
|
callback: callback,
|
||||||
|
// do we need this?
|
||||||
|
keyIndex: _getKeys.length,
|
||||||
|
resolve: null as any,
|
||||||
|
reject: null as any,
|
||||||
|
};
|
||||||
|
|
||||||
|
const promiseResult = new Promise<readonly KeyValuePair[]>(
|
||||||
|
(resolve, reject) => {
|
||||||
|
getRequest.resolve = resolve;
|
||||||
|
getRequest.reject = reject;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
_getRequests.push(getRequest);
|
||||||
|
// avoid fetching duplicates
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (_getKeys.indexOf(key) === -1) {
|
||||||
|
_getKeys.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return promiseResult;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||||
|
* the operation completes you'll get a single callback with any errors.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||||
|
*/
|
||||||
|
multiSet: (keyValuePairs, callback) => {
|
||||||
|
checkValidArgs(keyValuePairs, callback);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
keyValuePairs.forEach(([key, value]) => {
|
||||||
|
checkValidInput(key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
RCTAsyncStorage.multiSet(keyValuePairs, (errors?: ErrorLike[]) => {
|
||||||
|
const error = convertErrors(errors);
|
||||||
|
callback?.(error);
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this to batch the deletion of all keys in the `keys` array.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||||
|
*/
|
||||||
|
multiRemove: (keys, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
keys.forEach((key) => checkValidInput(key));
|
||||||
|
|
||||||
|
RCTAsyncStorage.multiRemove(keys, (errors?: ErrorLike[]) => {
|
||||||
|
const error = convertErrors(errors);
|
||||||
|
callback?.(error);
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch operation to merge in existing and new values for a given set of
|
||||||
|
* keys. This assumes that the values are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||||
|
*/
|
||||||
|
multiMerge: (keyValuePairs, callback) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
RCTAsyncStorage.multiMerge(keyValuePairs, (errors?: ErrorLike[]) => {
|
||||||
|
const error = convertErrors(errors);
|
||||||
|
callback?.(error);
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default AsyncStorage;
|
173
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts
generated
vendored
173
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts
generated
vendored
@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Nicolas Gallagher.
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-ignore Cannot find module 'merge-options' or its corresponding type declarations
|
||||||
|
import mergeOptions from 'merge-options';
|
||||||
|
import type {
|
||||||
|
AsyncStorageStatic,
|
||||||
|
MultiCallback,
|
||||||
|
MultiGetCallback,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
const merge = mergeOptions.bind({
|
||||||
|
concatArrays: true,
|
||||||
|
ignoreUndefined: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
function mergeLocalStorageItem(key: string, value: string) {
|
||||||
|
const oldValue = window.localStorage.getItem(key);
|
||||||
|
if (oldValue) {
|
||||||
|
const oldObject = JSON.parse(oldValue);
|
||||||
|
const newObject = JSON.parse(value);
|
||||||
|
const nextValue = JSON.stringify(merge(oldObject, newObject));
|
||||||
|
window.localStorage.setItem(key, nextValue);
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPromise<Result, Callback extends Function>(
|
||||||
|
getValue: () => Result,
|
||||||
|
callback?: Callback
|
||||||
|
): Promise<Result> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const value = getValue();
|
||||||
|
callback?.(null, value);
|
||||||
|
resolve(value);
|
||||||
|
} catch (err) {
|
||||||
|
callback?.(err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPromiseAll<ReturnType, Result, ResultProcessor extends Function>(
|
||||||
|
promises: Promise<Result>[],
|
||||||
|
callback?: MultiCallback | MultiGetCallback,
|
||||||
|
processResult?: ResultProcessor
|
||||||
|
): Promise<ReturnType> {
|
||||||
|
return Promise.all(promises).then(
|
||||||
|
(result) => {
|
||||||
|
const value = processResult?.(result) ?? null;
|
||||||
|
callback?.(null, value);
|
||||||
|
return Promise.resolve(value);
|
||||||
|
},
|
||||||
|
(errors) => {
|
||||||
|
callback?.(errors);
|
||||||
|
return Promise.reject(errors);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AsyncStorage: AsyncStorageStatic = {
|
||||||
|
/**
|
||||||
|
* Fetches `key` value.
|
||||||
|
*/
|
||||||
|
getItem: (key, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.getItem(key), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets `value` for `key`.
|
||||||
|
*/
|
||||||
|
setItem: (key, value, callback) => {
|
||||||
|
return createPromise(
|
||||||
|
() => window.localStorage.setItem(key, value),
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a `key`
|
||||||
|
*/
|
||||||
|
removeItem: (key, callback) => {
|
||||||
|
return createPromise(() => window.localStorage.removeItem(key), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges existing value with input value, assuming they are stringified JSON.
|
||||||
|
*/
|
||||||
|
mergeItem: (key, value, callback) => {
|
||||||
|
return createPromise(() => mergeLocalStorageItem(key, value), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erases *all* AsyncStorage for the domain.
|
||||||
|
*/
|
||||||
|
clear: (callback) => {
|
||||||
|
return createPromise(() => window.localStorage.clear(), callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to the app, for all callers, libraries, etc.
|
||||||
|
*/
|
||||||
|
getAllKeys: (callback) => {
|
||||||
|
return createPromise(() => {
|
||||||
|
const numberOfKeys = window.localStorage.length;
|
||||||
|
const keys: string[] = [];
|
||||||
|
for (let i = 0; i < numberOfKeys; i += 1) {
|
||||||
|
const key = window.localStorage.key(i) || '';
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (stub) Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*/
|
||||||
|
flushGetRequests: () => undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* multiGet resolves to an array of key-value pair arrays that matches the
|
||||||
|
* input format of multiSet.
|
||||||
|
*
|
||||||
|
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||||
|
*/
|
||||||
|
multiGet: (keys, callback) => {
|
||||||
|
const promises = keys.map((key) => AsyncStorage.getItem(key));
|
||||||
|
const processResult = (result: string[]) =>
|
||||||
|
result.map((value, i) => [keys[i], value]);
|
||||||
|
return createPromiseAll(promises, callback, processResult);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of key-value array pairs.
|
||||||
|
* multiSet([['k1', 'val1'], ['k2', 'val2']])
|
||||||
|
*/
|
||||||
|
multiSet: (keyValuePairs, callback) => {
|
||||||
|
const promises = keyValuePairs.map((item) =>
|
||||||
|
AsyncStorage.setItem(item[0], item[1])
|
||||||
|
);
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all the keys in the `keys` array.
|
||||||
|
*/
|
||||||
|
multiRemove: (keys, callback) => {
|
||||||
|
const promises = keys.map((key) => AsyncStorage.removeItem(key));
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of key-value array pairs and merges them with existing
|
||||||
|
* values, assuming they are stringified JSON.
|
||||||
|
*
|
||||||
|
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
|
||||||
|
*/
|
||||||
|
multiMerge: (keyValuePairs, callback) => {
|
||||||
|
const promises = keyValuePairs.map((item) =>
|
||||||
|
AsyncStorage.mergeItem(item[0], item[1])
|
||||||
|
);
|
||||||
|
return createPromiseAll(promises, callback);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AsyncStorage;
|
@ -0,0 +1,25 @@
|
|||||||
|
// @ts-ignore Module '"react-native"' has no exported member 'TurboModuleRegistry'.
|
||||||
|
import { NativeModules, TurboModuleRegistry } from 'react-native';
|
||||||
|
import { shouldFallbackToLegacyNativeModule } from './shouldFallbackToLegacyNativeModule';
|
||||||
|
|
||||||
|
let RCTAsyncStorage =
|
||||||
|
NativeModules['PlatformLocalStorage'] || // Support for external modules, like react-native-windows
|
||||||
|
NativeModules['RNC_AsyncSQLiteDBStorage'] ||
|
||||||
|
NativeModules['RNCAsyncStorage'];
|
||||||
|
|
||||||
|
if (!RCTAsyncStorage && shouldFallbackToLegacyNativeModule()) {
|
||||||
|
// TurboModuleRegistry falls back to NativeModules so we don't have to try go
|
||||||
|
// assign NativeModules' counterparts if TurboModuleRegistry would resolve
|
||||||
|
// with undefined.
|
||||||
|
if (TurboModuleRegistry) {
|
||||||
|
RCTAsyncStorage =
|
||||||
|
TurboModuleRegistry.get('AsyncSQLiteDBStorage') ||
|
||||||
|
TurboModuleRegistry.get('AsyncLocalStorage');
|
||||||
|
} else {
|
||||||
|
RCTAsyncStorage =
|
||||||
|
NativeModules['AsyncSQLiteDBStorage'] ||
|
||||||
|
NativeModules['AsyncLocalStorage'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RCTAsyncStorage;
|
74
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/helpers.ts
generated
vendored
74
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/helpers.ts
generated
vendored
@ -0,0 +1,74 @@
|
|||||||
|
import type { ErrorLike } from './types';
|
||||||
|
|
||||||
|
export function checkValidArgs(keyValuePairs: unknown[], callback: unknown) {
|
||||||
|
if (
|
||||||
|
!Array.isArray(keyValuePairs) ||
|
||||||
|
keyValuePairs.length === 0 ||
|
||||||
|
!Array.isArray(keyValuePairs[0])
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
'[AsyncStorage] Expected array of key-value pairs as first argument to multiSet'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback && typeof callback !== 'function') {
|
||||||
|
if (Array.isArray(callback)) {
|
||||||
|
throw new Error(
|
||||||
|
'[AsyncStorage] Expected function as second argument to multiSet. Did you forget to wrap key-value pairs in an array for the first argument?'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
'[AsyncStorage] Expected function as second argument to multiSet'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkValidInput(...input: unknown[]) {
|
||||||
|
const [key, value] = input;
|
||||||
|
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
console.warn(
|
||||||
|
`[AsyncStorage] Using ${typeof key} type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.\nKey passed: ${key}\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.length > 1 && typeof value !== 'string') {
|
||||||
|
if (value == null) {
|
||||||
|
throw new Error(
|
||||||
|
`[AsyncStorage] Passing null/undefined as value is not supported. If you want to remove value, Use .removeItem method instead.\nPassed value: ${value}\nPassed key: ${key}\n`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`[AsyncStorage] The value for key "${key}" is not a string. This can lead to unexpected behavior/errors. Consider stringifying it.\nPassed value: ${value}\nPassed key: ${key}\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertError(error?: ErrorLike): Error | null {
|
||||||
|
if (!error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const out = new Error(error.message);
|
||||||
|
(out as any).key = error.key;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertErrors(
|
||||||
|
errs?: ErrorLike[]
|
||||||
|
): ReadonlyArray<Error | null> | null {
|
||||||
|
const errors = ensureArray(errs);
|
||||||
|
return errors ? errors.map((e) => convertError(e)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureArray(e?: ErrorLike | ErrorLike[]): ErrorLike[] | null {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return e.length === 0 ? null : e;
|
||||||
|
} else if (e) {
|
||||||
|
return [e];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
11
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/hooks.ts
generated
vendored
11
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/hooks.ts
generated
vendored
@ -0,0 +1,11 @@
|
|||||||
|
import AsyncStorage from './AsyncStorage';
|
||||||
|
import type { AsyncStorageHook } from './types';
|
||||||
|
|
||||||
|
export function useAsyncStorage(key: string): AsyncStorageHook {
|
||||||
|
return {
|
||||||
|
getItem: (...args) => AsyncStorage.getItem(key, ...args),
|
||||||
|
setItem: (...args) => AsyncStorage.setItem(key, ...args),
|
||||||
|
mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),
|
||||||
|
removeItem: (...args) => AsyncStorage.removeItem(key, ...args),
|
||||||
|
};
|
||||||
|
}
|
7
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/index.ts
generated
vendored
7
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/index.ts
generated
vendored
@ -0,0 +1,7 @@
|
|||||||
|
import AsyncStorage from './AsyncStorage';
|
||||||
|
|
||||||
|
export { useAsyncStorage } from './hooks';
|
||||||
|
|
||||||
|
export type { AsyncStorageStatic } from './types';
|
||||||
|
|
||||||
|
export default AsyncStorage;
|
@ -0,0 +1,34 @@
|
|||||||
|
import { NativeModules } from 'react-native';
|
||||||
|
|
||||||
|
export function shouldFallbackToLegacyNativeModule(): boolean {
|
||||||
|
const expoConstants =
|
||||||
|
NativeModules['NativeUnimoduleProxy']?.modulesConstants?.ExponentConstants;
|
||||||
|
|
||||||
|
if (expoConstants) {
|
||||||
|
/**
|
||||||
|
* In SDK <= 39, appOwnership is defined in managed apps but executionEnvironment is not.
|
||||||
|
* In bare React Native apps using expo-constants, appOwnership is never defined, so
|
||||||
|
* isLegacySdkVersion will be false in that context.
|
||||||
|
*/
|
||||||
|
const isLegacySdkVersion =
|
||||||
|
expoConstants.appOwnership && !expoConstants.executionEnvironment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expo managed apps don't include the @react-native-async-storage/async-storage
|
||||||
|
* native modules yet, but the API interface is the same, so we can use the version
|
||||||
|
* exported from React Native still.
|
||||||
|
*
|
||||||
|
* If in future releases (eg: @react-native-async-storage/async-storage >= 2.0.0) this
|
||||||
|
* will likely not be valid anymore, and the package will need to be included in the Expo SDK
|
||||||
|
* to continue to work.
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
isLegacySdkVersion ||
|
||||||
|
['storeClient', 'standalone'].includes(expoConstants.executionEnvironment)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
155
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/types.ts
generated
vendored
155
iut-expo-starter/node_modules/@react-native-async-storage/async-storage/src/types.ts
generated
vendored
@ -0,0 +1,155 @@
|
|||||||
|
export type ErrorLike = {
|
||||||
|
message: string;
|
||||||
|
key: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Callback = (error?: Error | null) => void;
|
||||||
|
|
||||||
|
export type CallbackWithResult<T> = (
|
||||||
|
error?: Error | null,
|
||||||
|
result?: T | null
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export type KeyValuePair = [string, string | null];
|
||||||
|
|
||||||
|
export type MultiCallback = (errors?: readonly (Error | null)[] | null) => void;
|
||||||
|
|
||||||
|
export type MultiGetCallback = (
|
||||||
|
errors?: readonly (Error | null)[] | null,
|
||||||
|
result?: readonly KeyValuePair[]
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export type MultiRequest = {
|
||||||
|
keys: readonly string[];
|
||||||
|
callback?: MultiGetCallback;
|
||||||
|
keyIndex: number;
|
||||||
|
resolve?: (result: readonly KeyValuePair[]) => void;
|
||||||
|
reject?: (error?: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AsyncStorageHook = {
|
||||||
|
getItem: (callback?: CallbackWithResult<string>) => Promise<string | null>;
|
||||||
|
setItem: (value: string, callback?: Callback) => Promise<void>;
|
||||||
|
mergeItem: (value: string, callback?: Callback) => Promise<void>;
|
||||||
|
removeItem: (callback?: Callback) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `AsyncStorage` is a simple, unencrypted, asynchronous, persistent, key-value
|
||||||
|
* storage system that is global to the app. It should be used instead of
|
||||||
|
* LocalStorage.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api
|
||||||
|
*/
|
||||||
|
export type AsyncStorageStatic = {
|
||||||
|
/**
|
||||||
|
* Fetches an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getitem
|
||||||
|
*/
|
||||||
|
getItem: (
|
||||||
|
key: string,
|
||||||
|
callback?: CallbackWithResult<string>
|
||||||
|
) => Promise<string | null>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#setitem
|
||||||
|
*/
|
||||||
|
setItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item for a `key` and invokes a callback upon completion.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#removeitem
|
||||||
|
*/
|
||||||
|
removeItem: (key: string, callback?: Callback) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges an existing `key` value with an input value, assuming both values
|
||||||
|
* are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#mergeitem
|
||||||
|
*/
|
||||||
|
mergeItem: (key: string, value: string, callback?: Callback) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erases *all* `AsyncStorage` for all clients, libraries, etc. You probably
|
||||||
|
* don't want to call this; use `removeItem` or `multiRemove` to clear only
|
||||||
|
* your app's keys.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#clear
|
||||||
|
*/
|
||||||
|
clear: (callback?: Callback) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets *all* keys known to your app; for all callers, libraries, etc.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#getallkeys
|
||||||
|
*/
|
||||||
|
getAllKeys: (
|
||||||
|
callback?: CallbackWithResult<readonly string[]>
|
||||||
|
) => Promise<readonly string[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following batched functions are useful for executing a lot of
|
||||||
|
* operations at once, allowing for native optimizations and provide the
|
||||||
|
* convenience of a single callback after all operations are complete.
|
||||||
|
*
|
||||||
|
* These functions return arrays of errors, potentially one for every key.
|
||||||
|
* For key-specific errors, the Error object will have a key property to
|
||||||
|
* indicate which key caused the error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes any pending requests using a single batch call to get the data.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#flushgetrequests
|
||||||
|
* */
|
||||||
|
flushGetRequests: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This allows you to batch the fetching of items given an array of `key`
|
||||||
|
* inputs. Your callback will be invoked with an array of corresponding
|
||||||
|
* key-value pairs found.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiget
|
||||||
|
*/
|
||||||
|
multiGet: (
|
||||||
|
keys: readonly string[],
|
||||||
|
callback?: MultiGetCallback
|
||||||
|
) => Promise<readonly KeyValuePair[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this as a batch operation for storing multiple key-value pairs. When
|
||||||
|
* the operation completes you'll get a single callback with any errors.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiset
|
||||||
|
*/
|
||||||
|
multiSet: (
|
||||||
|
keyValuePairs: [string, string][],
|
||||||
|
callback?: MultiCallback
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this to batch the deletion of all keys in the `keys` array.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multiremove
|
||||||
|
*/
|
||||||
|
multiRemove: (
|
||||||
|
keys: readonly string[],
|
||||||
|
callback?: MultiCallback
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch operation to merge in existing and new values for a given set of
|
||||||
|
* keys. This assumes that the values are stringified JSON.
|
||||||
|
*
|
||||||
|
* See https://react-native-async-storage.github.io/async-storage/docs/api#multimerge
|
||||||
|
*/
|
||||||
|
multiMerge: (
|
||||||
|
keyValuePairs: [string, string][],
|
||||||
|
callback?: MultiCallback
|
||||||
|
) => Promise<void>;
|
||||||
|
};
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ImportGroup Label="PropertySheets" />
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<!--
|
||||||
|
To customize common C++/WinRT project properties:
|
||||||
|
* right-click the project node
|
||||||
|
* expand the Common Properties item
|
||||||
|
* select the C++/WinRT property page
|
||||||
|
|
||||||
|
For more advanced scenarios, and complete documentation, please see:
|
||||||
|
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||||
|
-->
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemDefinitionGroup />
|
||||||
|
</Project>
|
@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||||
|
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||||
|
<MinimalCoreWin>true</MinimalCoreWin>
|
||||||
|
<ProjectGuid>{4855D892-E16C-404D-8286-0089E0F7F9C4}</ProjectGuid>
|
||||||
|
<ProjectName>ReactNativeAsyncStorage</ProjectName>
|
||||||
|
<RootNamespace>ReactNativeAsyncStorage</RootNamespace>
|
||||||
|
<DefaultLanguage>en-US</DefaultLanguage>
|
||||||
|
<MinimumVisualStudioVersion>16.0</MinimumVisualStudioVersion>
|
||||||
|
<AppContainerApplication>true</AppContainerApplication>
|
||||||
|
<ApplicationType>Windows Store</ApplicationType>
|
||||||
|
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||||
|
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0</WindowsTargetPlatformVersion>
|
||||||
|
<WindowsTargetPlatformMinVersion>10.0.16299.0</WindowsTargetPlatformMinVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="ReactNativeWindowsProps">
|
||||||
|
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|ARM">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|ARM64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
<GenerateManifest>false</GenerateManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="PropertySheet.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="ReactNativeWindowsPropertySheets">
|
||||||
|
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
|
||||||
|
<!--Temporarily disable cppwinrt heap enforcement to work around xaml compiler generated std::shared_ptr use -->
|
||||||
|
<AdditionalOptions Condition="'$(CppWinRTHeapEnforcement)'==''">/DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)</AdditionalOptions>
|
||||||
|
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||||
|
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
|
||||||
|
<ModuleDefinitionFile>..\code\ReactNativeAsyncStorage.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalDependencies>winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<DelayLoadDLLs>winsqlite3.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<ClCompile>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<ClCompile>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\code\pch.h" />
|
||||||
|
<ClInclude Include="..\code\ReactPackageProvider.h">
|
||||||
|
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\code\DBStorage.h" />
|
||||||
|
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\code\pch.cpp">
|
||||||
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||||
|
<ClCompile Include="..\code\ReactPackageProvider.cpp">
|
||||||
|
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="PropertySheet.props" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="$(ReactNativeWindowsDir)\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj">
|
||||||
|
<Project>{f7d32bd0-2749-483e-9a0d-1635ef7e3136}</Project>
|
||||||
|
<Private>false</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ReactNativeWindowsTargets">
|
||||||
|
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureReactNativeWindowsTargets" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references targets in your node_modules\react-native-windows folder. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.props'))" />
|
||||||
|
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppLib.targets'))" />
|
||||||
|
</Target>
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="Deploy" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Resources">
|
||||||
|
<UniqueIdentifier>accd3aa8-1ba0-4223-9bbe-0c431709210b</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Generated Files">
|
||||||
|
<UniqueIdentifier>{926ab91d-31b4-48c3-b9a4-e681349f27f0}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\code\pch.h" />
|
||||||
|
<ClInclude Include="..\code\ReactPackageProvider.h" />
|
||||||
|
<ClInclude Include="..\code\DBStorage.h" />
|
||||||
|
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\code\pch.cpp" />
|
||||||
|
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||||
|
<ClCompile Include="..\code\ReactPackageProvider.cpp" />
|
||||||
|
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="PropertySheet.props" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||||
|
</packages>
|
@ -0,0 +1,195 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.29215.179
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Folly", "..\node_modules\react-native-windows\Folly\Folly.vcxproj", "{A990658C-CE31-4BCC-976F-0FC6B1AF693D}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "..\node_modules\react-native-windows\ReactCommon\ReactCommon.vcxproj", "{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactWindowsCore", "..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxproj", "{11C084A3-A57C-4296-A679-CAC17B603144}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "..\node_modules\react-native-windows\Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative", "..\node_modules\react-native-windows\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj", "{F7D32BD0-2749-483E-9A0D-1635EF7E3136}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Shared", "..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems", "{0CC28589-39E4-4288-B162-97B959F8B843}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Universal", "..\node_modules\react-native-windows\JSI\Universal\JSI.Universal.vcxproj", "{A62D504A-16B8-41D2-9F19-E2E86019E5E4}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}"
|
||||||
|
EndProject
|
||||||
|
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.ReactNative.SharedManaged", "..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.shproj", "{67A1076F-7790-4203-86EA-4402CCB5E782}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\node_modules\react-native-windows\Common\Common.vcxproj", "{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\node_modules\react-native-windows\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeAsyncStorage61", "ReactNativeAsyncStorage61\ReactNativeAsyncStorage61.vcxproj", "{4855D892-E16C-404D-8286-0089E0F7F9C4}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||||
|
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{4855d892-e16c-404d-8286-0089e0f7f9c4}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{67a1076f-7790-4203-86ea-4402ccb5e782}*SharedItemsImports = 13
|
||||||
|
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|ARM = Debug|ARM
|
||||||
|
Debug|ARM64 = Debug|ARM64
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|ARM = Release|ARM
|
||||||
|
Release|ARM64 = Release|ARM64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM64.ActiveCfg = Release|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.Build.0 = Release|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{0CC28589-39E4-4288-B162-97B959F8B843} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{67A1076F-7790-4203-86EA-4402CCB5E782} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {1F02BFA9-97C8-4ACF-A348-B3166C3BC7EA}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ImportGroup Label="PropertySheets" />
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<!--
|
||||||
|
To customize common C++/WinRT project properties:
|
||||||
|
* right-click the project node
|
||||||
|
* expand the Common Properties item
|
||||||
|
* select the C++/WinRT property page
|
||||||
|
|
||||||
|
For more advanced scenarios, and complete documentation, please see:
|
||||||
|
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||||
|
-->
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemDefinitionGroup />
|
||||||
|
</Project>
|
@ -0,0 +1,157 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||||
|
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||||
|
<MinimalCoreWin>true</MinimalCoreWin>
|
||||||
|
<ProjectGuid>{4855D892-E16C-404D-8286-0089E0F7F9C4}</ProjectGuid>
|
||||||
|
<ProjectName>ReactNativeAsyncStorage61</ProjectName>
|
||||||
|
<RootNamespace>ReactNativeAsyncStorage</RootNamespace>
|
||||||
|
<DefaultLanguage>en-US</DefaultLanguage>
|
||||||
|
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||||
|
<AppContainerApplication>true</AppContainerApplication>
|
||||||
|
<ApplicationType>Windows Store</ApplicationType>
|
||||||
|
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||||
|
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
|
||||||
|
<WindowsTargetPlatformMinVersion>10.0.15063.0</WindowsTargetPlatformMinVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="ReactNativeWindowsProps">
|
||||||
|
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|ARM">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
|
||||||
|
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
<GenerateManifest>false</GenerateManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
<Import Project="$(ReactNativeWindowsDir)\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems" Label="Shared" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="PropertySheet.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
|
||||||
|
<!--Temporarily disable cppwinrt heap enforcement to work around xaml compiler generated std::shared_ptr use -->
|
||||||
|
<AdditionalOptions Condition="'$(CppWinRTHeapEnforcement)'==''">/DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)</AdditionalOptions>
|
||||||
|
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||||
|
<PreprocessorDefinitions>_WINRT_DLL;RNW_61;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
|
||||||
|
<ModuleDefinitionFile>..\code\ReactNativeAsyncStorage.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalDependencies>winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<DelayLoadDLLs>winsqlite3.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<ClCompile>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<ClCompile>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\code\pch.h" />
|
||||||
|
<ClInclude Include="..\code\ReactPackageProvider.h">
|
||||||
|
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\code\DBStorage.h" />
|
||||||
|
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\code\pch.cpp">
|
||||||
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||||
|
<ClCompile Include="..\code\ReactPackageProvider.cpp">
|
||||||
|
<DependentUpon>..\code\ReactPackageProvider.idl</DependentUpon>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="PropertySheet.props" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="$(ReactNativeWindowsDir)\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj">
|
||||||
|
<Project>{f7d32bd0-2749-483e-9a0d-1635ef7e3136}</Project>
|
||||||
|
<Private>false</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="Deploy"/>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Resources">
|
||||||
|
<UniqueIdentifier>accd3aa8-1ba0-4223-9bbe-0c431709210b</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Generated Files">
|
||||||
|
<UniqueIdentifier>{926ab91d-31b4-48c3-b9a4-e681349f27f0}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\code\pch.h" />
|
||||||
|
<ClInclude Include="..\code\ReactPackageProvider.h" />
|
||||||
|
<ClInclude Include="..\code\RNCAsyncStorage.h" />
|
||||||
|
<ClInclude Include="..\code\DBStorage.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\code\pch.cpp" />
|
||||||
|
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||||
|
<ClCompile Include="..\code\ReactPackageProvider.cpp" />
|
||||||
|
<ClCompile Include="..\code\DBStorage.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\code\ReactNativeAsyncStorage.def" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="PropertySheet.props" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Midl Include="..\code\ReactPackageProvider.idl" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||||
|
</packages>
|
@ -0,0 +1,192 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30114.105
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeAsyncStorage", "ReactNativeAsyncStorage\ReactNativeAsyncStorage.vcxproj", "{4855D892-E16C-404D-8286-0089E0F7F9C4}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{4F6E56C3-12C5-4457-9239-0ACF0B7150A8}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\node_modules\react-native-windows\Common\Common.vcxproj", "{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Folly", "..\node_modules\react-native-windows\Folly\Folly.vcxproj", "{A990658C-CE31-4BCC-976F-0FC6B1AF693D}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Universal", "..\node_modules\react-native-windows\JSI\Universal\JSI.Universal.vcxproj", "{A62D504A-16B8-41D2-9F19-E2E86019E5E4}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Shared", "..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems", "{0CC28589-39E4-4288-B162-97B959F8B843}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "..\node_modules\react-native-windows\Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative", "..\node_modules\react-native-windows\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj", "{F7D32BD0-2749-483E-9A0D-1635EF7E3136}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\node_modules\react-native-windows\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "..\node_modules\react-native-windows\ReactCommon\ReactCommon.vcxproj", "{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactWindowsCore", "..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxproj", "{11C084A3-A57C-4296-A679-CAC17B603144}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\node_modules\react-native-windows\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||||
|
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxitems*{11c084a3-a57c-4296-a679-cac17b603144}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9
|
||||||
|
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|ARM = Debug|ARM
|
||||||
|
Debug|ARM64 = Debug|ARM64
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|ARM = Release|ARM
|
||||||
|
Release|ARM64 = Release|ARM64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|ARM64.ActiveCfg = Release|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x64.Build.0 = Release|x64
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{4855D892-E16C-404D-8286-0089E0F7F9C4}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.Build.0 = Debug|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.ActiveCfg = Release|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.Build.0 = Release|ARM
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{A62D504A-16B8-41D2-9F19-E2E86019E5E4} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{0CC28589-39E4-4288-B162-97B959F8B843} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{11C084A3-A57C-4296-A679-CAC17B603144} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
{EF074BA1-2D54-4D49-A28E-5E040B47CD2E} = {4F6E56C3-12C5-4457-9239-0ACF0B7150A8}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {1F02BFA9-97C8-4ACF-A348-B3166C3BC7EA}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
@ -0,0 +1,599 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "DBStorage.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace winrt
|
||||||
|
{
|
||||||
|
using namespace Microsoft::ReactNative;
|
||||||
|
using namespace Windows::ApplicationModel::Core;
|
||||||
|
using namespace Windows::Data::Json;
|
||||||
|
using namespace Windows::Foundation;
|
||||||
|
using namespace Windows::Storage;
|
||||||
|
} // namespace winrt
|
||||||
|
|
||||||
|
// All functions below return std::nullopt on error.
|
||||||
|
#define CHECK(expr) \
|
||||||
|
if (!(expr)) { \
|
||||||
|
return std::nullopt; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience macro to call CheckSQLiteResult.
|
||||||
|
#define CHECK_SQL_OK(expr) CHECK(CheckSQLiteResult(db, m_errorManager, (expr)))
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// To implement safe operator& for unique_ptr.
|
||||||
|
template <typename T, typename TDeleter>
|
||||||
|
struct UniquePtrSetter {
|
||||||
|
UniquePtrSetter(std::unique_ptr<T, TDeleter> &ptr) noexcept : m_ptr(ptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~UniquePtrSetter()
|
||||||
|
{
|
||||||
|
m_ptr = {m_rawPtr, m_ptr.get_deleter()};
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T **() noexcept
|
||||||
|
{
|
||||||
|
return &m_rawPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T *m_rawPtr{};
|
||||||
|
std::unique_ptr<T, TDeleter> &m_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename TDeleter>
|
||||||
|
UniquePtrSetter<T, TDeleter> operator&(std::unique_ptr<T, TDeleter> &ptr) noexcept
|
||||||
|
{
|
||||||
|
return UniquePtrSetter<T, TDeleter>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
using ExecCallback = int(SQLITE_CALLBACK *)(void *callbackData,
|
||||||
|
int columnCount,
|
||||||
|
char **columnTexts,
|
||||||
|
char **columnNames);
|
||||||
|
|
||||||
|
// Execute the provided SQLite statement (and optional execCallback & user data
|
||||||
|
// in callbackData). On error, report it to the errorManager and return std::nullopt.
|
||||||
|
std::optional<bool> Exec(sqlite3 *db,
|
||||||
|
DBStorage::ErrorManager &errorManager,
|
||||||
|
const char *statement,
|
||||||
|
ExecCallback execCallback = nullptr,
|
||||||
|
void *callbackData = nullptr) noexcept
|
||||||
|
{
|
||||||
|
auto errMsg = std::unique_ptr<char, decltype(&sqlite3_free)>{nullptr, &sqlite3_free};
|
||||||
|
int rc = sqlite3_exec(db, statement, execCallback, callbackData, &errMsg);
|
||||||
|
if (errMsg) {
|
||||||
|
return errorManager.AddError(errMsg.get());
|
||||||
|
}
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
return errorManager.AddError(sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience wrapper for using Exec with lambda expressions.
|
||||||
|
template <typename Fn>
|
||||||
|
std::optional<bool>
|
||||||
|
Exec(sqlite3 *db, DBStorage::ErrorManager &errorManager, const char *statement, Fn &fn) noexcept
|
||||||
|
{
|
||||||
|
return Exec(
|
||||||
|
db,
|
||||||
|
errorManager,
|
||||||
|
statement,
|
||||||
|
[](void *callbackData, int columnCount, char **columnTexts, char **columnNames) {
|
||||||
|
return (*static_cast<Fn *>(callbackData))(columnCount, columnTexts, columnNames);
|
||||||
|
},
|
||||||
|
&fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the args collection size is less than SQLITE_LIMIT_VARIABLE_NUMBER, and that
|
||||||
|
// every member of args is not an empty string.
|
||||||
|
// On error, report it to the errorManager and return std::nullopt.
|
||||||
|
std::optional<bool> CheckArgs(sqlite3 *db,
|
||||||
|
DBStorage::ErrorManager &errorManager,
|
||||||
|
const std::vector<std::string> &args) noexcept
|
||||||
|
{
|
||||||
|
int varLimit = sqlite3_limit(db, SQLITE_LIMIT_VARIABLE_NUMBER, -1);
|
||||||
|
auto argCount = args.size();
|
||||||
|
if (argCount > static_cast<size_t>(std::numeric_limits<int>::max()) ||
|
||||||
|
static_cast<int>(argCount) > varLimit) {
|
||||||
|
char errorMsg[60];
|
||||||
|
sprintf_s(errorMsg, "Too many keys. Maximum supported keys :%d.", varLimit);
|
||||||
|
return errorManager.AddError(errorMsg);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < static_cast<int>(argCount); i++) {
|
||||||
|
if (args[i].empty()) {
|
||||||
|
return errorManager.AddError("The key must be a non-empty string.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAII object to manage SQLite transaction. On destruction, if
|
||||||
|
// Commit() has not been called, rolls back the transactions.
|
||||||
|
// The provided SQLite connection handle & errorManager must outlive
|
||||||
|
// the Sqlite3Transaction object.
|
||||||
|
struct Sqlite3Transaction {
|
||||||
|
Sqlite3Transaction(sqlite3 *db, DBStorage::ErrorManager &errorManager) noexcept
|
||||||
|
: m_db(db), m_errorManager(errorManager)
|
||||||
|
{
|
||||||
|
if (!Exec(m_db, m_errorManager, "BEGIN TRANSACTION")) {
|
||||||
|
m_db = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Sqlite3Transaction(const Sqlite3Transaction &) = delete;
|
||||||
|
Sqlite3Transaction &operator=(const Sqlite3Transaction &) = delete;
|
||||||
|
|
||||||
|
~Sqlite3Transaction()
|
||||||
|
{
|
||||||
|
Rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept
|
||||||
|
{
|
||||||
|
return m_db != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> Commit() noexcept
|
||||||
|
{
|
||||||
|
if (m_db) {
|
||||||
|
return Exec(std::exchange(m_db, nullptr), m_errorManager, "COMMIT");
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> Rollback() noexcept
|
||||||
|
{
|
||||||
|
if (m_db) {
|
||||||
|
return Exec(std::exchange(m_db, nullptr), m_errorManager, "ROLLBACK");
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
sqlite3 *m_db{};
|
||||||
|
DBStorage::ErrorManager &m_errorManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Append argCount variables to prefix in a comma-separated list.
|
||||||
|
std::string MakeSQLiteParameterizedStatement(const char *prefix, int argCount) noexcept
|
||||||
|
{
|
||||||
|
assert(argCount != 0);
|
||||||
|
std::string result(prefix);
|
||||||
|
result.reserve(result.size() + (argCount * 2) + 1);
|
||||||
|
result += '(';
|
||||||
|
for (int x = 0; x < argCount - 1; x++) {
|
||||||
|
result += "?,";
|
||||||
|
}
|
||||||
|
result += "?)";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if sqliteResult is SQLITE_OK.
|
||||||
|
// If not, report the error to the errorManager and return std::nullopt.
|
||||||
|
std::optional<bool> CheckSQLiteResult(sqlite3 *db,
|
||||||
|
DBStorage::ErrorManager &errorManager,
|
||||||
|
int sqliteResult) noexcept
|
||||||
|
{
|
||||||
|
if (sqliteResult == SQLITE_OK) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return errorManager.AddError(sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using StatementPtr = std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)>;
|
||||||
|
|
||||||
|
// A convenience wrapper for sqlite3_prepare_v2 function.
|
||||||
|
int PrepareStatement(sqlite3 *db,
|
||||||
|
const std::string &statementText,
|
||||||
|
sqlite3_stmt **statement) noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_prepare_v2(db, statementText.c_str(), -1, statement, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A convenience wrapper for sqlite3_bind_text function.
|
||||||
|
int BindString(StatementPtr &statement, int index, const std::string &str) noexcept
|
||||||
|
{
|
||||||
|
return sqlite3_bind_text(statement.get(), index, str.c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge source into destination.
|
||||||
|
// It only merges objects - all other types are just clobbered (including arrays).
|
||||||
|
void MergeJsonObjects(winrt::JsonObject const &destination,
|
||||||
|
winrt::JsonObject const &source) noexcept
|
||||||
|
{
|
||||||
|
for (auto keyValue : source) {
|
||||||
|
auto key = keyValue.Key();
|
||||||
|
auto sourceValue = keyValue.Value();
|
||||||
|
if (destination.HasKey(key)) {
|
||||||
|
auto destinationValue = destination.GetNamedValue(key);
|
||||||
|
if (destinationValue.ValueType() == winrt::JsonValueType::Object &&
|
||||||
|
sourceValue.ValueType() == winrt::JsonValueType::Object) {
|
||||||
|
MergeJsonObjects(destinationValue.GetObject(), sourceValue.GetObject());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destination.SetNamedValue(key, sourceValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Initialize storage. On error, report it to the errorManager and return std::nullopt.
|
||||||
|
std::optional<sqlite3 *>
|
||||||
|
DBStorage::InitializeStorage(DBStorage::ErrorManager &errorManager) noexcept
|
||||||
|
{
|
||||||
|
winrt::slim_lock_guard guard{m_lock};
|
||||||
|
if (m_db) {
|
||||||
|
return m_db.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
try {
|
||||||
|
if (auto pathInspectable =
|
||||||
|
winrt::CoreApplication::Properties().TryLookup(s_dbPathProperty)) {
|
||||||
|
path = winrt::to_string(winrt::unbox_value<winrt::hstring>(pathInspectable));
|
||||||
|
} else {
|
||||||
|
auto const localAppDataPath = winrt::ApplicationData::Current().LocalFolder().Path();
|
||||||
|
path = winrt::to_string(localAppDataPath) + "\\AsyncStorage.db";
|
||||||
|
}
|
||||||
|
} catch (const winrt::hresult_error &error) {
|
||||||
|
errorManager.AddError(winrt::to_string(error.message()));
|
||||||
|
return errorManager.AddError(
|
||||||
|
"Please specify 'React-Native-Community-Async-Storage-Database-Path' in "
|
||||||
|
"CoreApplication::Properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto db = DatabasePtr{nullptr, &sqlite3_close};
|
||||||
|
if (sqlite3_open_v2(path.c_str(),
|
||||||
|
&db,
|
||||||
|
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
|
||||||
|
nullptr) != SQLITE_OK) {
|
||||||
|
if (db) {
|
||||||
|
return errorManager.AddError(sqlite3_errmsg(db.get()));
|
||||||
|
} else {
|
||||||
|
return errorManager.AddError("Storage database cannot be opened.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int userVersion = 0;
|
||||||
|
auto getUserVersionCallback =
|
||||||
|
[](void *callbackData, int colomnCount, char **columnTexts, char ** /*columnNames*/) {
|
||||||
|
if (colomnCount < 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
*static_cast<int *>(callbackData) = atoi(columnTexts[0]);
|
||||||
|
return SQLITE_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
Exec(db.get(), errorManager, "PRAGMA user_version", getUserVersionCallback, &userVersion));
|
||||||
|
|
||||||
|
if (userVersion == 0) {
|
||||||
|
CHECK(Exec(db.get(),
|
||||||
|
errorManager,
|
||||||
|
"CREATE TABLE IF NOT EXISTS AsyncLocalStorage(key TEXT PRIMARY KEY, value TEXT "
|
||||||
|
"NOT NULL); PRAGMA user_version=1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_db = std::move(db);
|
||||||
|
return m_db.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
DBStorage::~DBStorage()
|
||||||
|
{
|
||||||
|
decltype(m_tasks) tasks;
|
||||||
|
{
|
||||||
|
// If there is an in-progress async task, cancel it and wait on the condition_variable for
|
||||||
|
// the async task to acknowledge cancellation by nulling out m_action. Once m_action is
|
||||||
|
// null, it is safe to proceed with closing the DB connection. The DB connection is closed
|
||||||
|
// by the m_db destructor.
|
||||||
|
winrt::slim_lock_guard guard{m_lock};
|
||||||
|
swap(tasks, m_tasks);
|
||||||
|
if (m_action) {
|
||||||
|
m_action.Cancel();
|
||||||
|
m_cv.wait(m_lock, [this]() { return m_action == nullptr; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Under the lock, add a task to m_tasks and, if no async task is in progress schedule it.
|
||||||
|
void DBStorage::AddTask(ErrorManager &errorManager,
|
||||||
|
std::function<void(DBStorage::DBTask &task, sqlite3 *db)> &&onRun) noexcept
|
||||||
|
{
|
||||||
|
winrt::slim_lock_guard guard(m_lock);
|
||||||
|
m_tasks.push_back(std::make_unique<DBTask>(errorManager, std::move(onRun)));
|
||||||
|
if (!m_action) {
|
||||||
|
m_action = RunTasks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On a background thread, while the async task has not been canceled and
|
||||||
|
// there are more tasks to do, run the tasks. When there are either no more
|
||||||
|
// tasks or cancellation has been requested, set m_action to null to report
|
||||||
|
// that and complete the coroutine. N.B., it is important that detecting that
|
||||||
|
// m_tasks is empty and acknowledging completion is done atomically; otherwise
|
||||||
|
// there would be a race between the background task detecting m_tasks.empty()
|
||||||
|
// and AddTask checking the coroutine is running.
|
||||||
|
winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() noexcept
|
||||||
|
{
|
||||||
|
auto cancellationToken = co_await winrt::get_cancellation_token();
|
||||||
|
co_await winrt::resume_background();
|
||||||
|
for (;;) {
|
||||||
|
decltype(m_tasks) tasks;
|
||||||
|
sqlite3 *db{nullptr};
|
||||||
|
{
|
||||||
|
winrt::slim_lock_guard guard(m_lock);
|
||||||
|
if (m_tasks.empty()) {
|
||||||
|
m_action = nullptr;
|
||||||
|
m_cv.notify_all();
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
std::swap(tasks, m_tasks);
|
||||||
|
db = m_db.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &task : tasks) {
|
||||||
|
if (!cancellationToken()) {
|
||||||
|
task->Run(*this, db);
|
||||||
|
} else {
|
||||||
|
task->Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new Error to the error list.
|
||||||
|
// Return std::nullopt for convenience to other methods that use std::nullopt to indicate error
|
||||||
|
// result.
|
||||||
|
std::nullopt_t DBStorage::ErrorManager::AddError(std::string &&message) noexcept
|
||||||
|
{
|
||||||
|
m_errors.push_back(Error{std::move(message)});
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DBStorage::ErrorManager::HasErrors() const noexcept
|
||||||
|
{
|
||||||
|
return !m_errors.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<DBStorage::Error> &DBStorage::ErrorManager::GetErrorList() const noexcept
|
||||||
|
{
|
||||||
|
if (HasErrors()) {
|
||||||
|
return m_errors;
|
||||||
|
}
|
||||||
|
static std::vector<DBStorage::Error> s_unknownError{Error{"Unknown error."}};
|
||||||
|
return s_unknownError;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBStorage::Error DBStorage::ErrorManager::GetCombinedError() const noexcept
|
||||||
|
{
|
||||||
|
auto &errors = GetErrorList();
|
||||||
|
if (errors.size() == 1) {
|
||||||
|
return errors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string combinedMessage;
|
||||||
|
for (const auto &error : errors) {
|
||||||
|
combinedMessage += error.Message + '\n';
|
||||||
|
}
|
||||||
|
return Error{std::move(combinedMessage)};
|
||||||
|
}
|
||||||
|
|
||||||
|
DBStorage::DBTask::DBTask(DBStorage::ErrorManager &errorManager,
|
||||||
|
std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept
|
||||||
|
: m_errorManager(errorManager), m_onRun(std::move(onRun))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBStorage::DBTask::Run(DBStorage &storage, sqlite3 *db) noexcept
|
||||||
|
{
|
||||||
|
if (!db) {
|
||||||
|
// We initialize DB handler on demand to report errors in the task context.
|
||||||
|
if (auto res = storage.InitializeStorage(m_errorManager)) {
|
||||||
|
db = *res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db) {
|
||||||
|
m_onRun(*this, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBStorage::DBTask::Cancel() noexcept
|
||||||
|
{
|
||||||
|
m_errorManager.AddError("Task is canceled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<DBStorage::KeyValue>>
|
||||||
|
DBStorage::DBTask::MultiGet(sqlite3 *db, const std::vector<std::string> &keys) noexcept
|
||||||
|
{
|
||||||
|
CHECK(!m_errorManager.HasErrors());
|
||||||
|
CHECK(CheckArgs(db, m_errorManager, keys));
|
||||||
|
|
||||||
|
auto argCount = static_cast<int>(keys.size());
|
||||||
|
auto sql = MakeSQLiteParameterizedStatement(
|
||||||
|
"SELECT key, value FROM AsyncLocalStorage WHERE key IN ", argCount);
|
||||||
|
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
||||||
|
CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
|
||||||
|
for (int i = 0; i < argCount; i++) {
|
||||||
|
CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DBStorage::KeyValue> result;
|
||||||
|
for (;;) {
|
||||||
|
auto stepResult = sqlite3_step(statement.get());
|
||||||
|
if (stepResult == SQLITE_DONE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (stepResult != SQLITE_ROW) {
|
||||||
|
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto key = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 0));
|
||||||
|
if (!key) {
|
||||||
|
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
auto value = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 1));
|
||||||
|
if (!value) {
|
||||||
|
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
result.push_back(KeyValue{key, value});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> DBStorage::DBTask::MultiSet(sqlite3 *db,
|
||||||
|
const std::vector<KeyValue> &keyValues) noexcept
|
||||||
|
{
|
||||||
|
CHECK(!m_errorManager.HasErrors());
|
||||||
|
if (keyValues.empty()) {
|
||||||
|
return true; // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
Sqlite3Transaction transaction(db, m_errorManager);
|
||||||
|
CHECK(transaction);
|
||||||
|
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
||||||
|
CHECK_SQL_OK(
|
||||||
|
PrepareStatement(db, "INSERT OR REPLACE INTO AsyncLocalStorage VALUES(?, ?)", &statement));
|
||||||
|
for (const auto &keyValue : keyValues) {
|
||||||
|
CHECK_SQL_OK(BindString(statement, 1, keyValue.Key));
|
||||||
|
CHECK_SQL_OK(BindString(statement, 2, keyValue.Value));
|
||||||
|
auto rc = sqlite3_step(statement.get());
|
||||||
|
CHECK(rc == SQLITE_DONE || CheckSQLiteResult(db, m_errorManager, rc));
|
||||||
|
CHECK_SQL_OK(sqlite3_reset(statement.get()));
|
||||||
|
}
|
||||||
|
CHECK(transaction.Commit());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> DBStorage::DBTask::MultiMerge(sqlite3 *db,
|
||||||
|
const std::vector<KeyValue> &keyValues) noexcept
|
||||||
|
{
|
||||||
|
CHECK(!m_errorManager.HasErrors());
|
||||||
|
|
||||||
|
std::vector<std::string> keys;
|
||||||
|
std::unordered_map<std::string, std::string> newValues;
|
||||||
|
keys.reserve(keyValues.size());
|
||||||
|
for (const auto &keyValue : keyValues) {
|
||||||
|
keys.push_back(keyValue.Key);
|
||||||
|
newValues.try_emplace(keyValue.Key, keyValue.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto oldValues = MultiGet(db, keys);
|
||||||
|
CHECK(oldValues);
|
||||||
|
|
||||||
|
std::vector<KeyValue> mergedResults;
|
||||||
|
for (size_t i = 0; i < oldValues->size(); i++) {
|
||||||
|
auto &key = oldValues->at(i).Key;
|
||||||
|
auto &oldValue = oldValues->at(i).Value;
|
||||||
|
auto &newValue = newValues[key];
|
||||||
|
|
||||||
|
winrt::JsonObject oldJson;
|
||||||
|
winrt::JsonObject newJson;
|
||||||
|
if (winrt::JsonObject::TryParse(winrt::to_hstring(oldValue), oldJson) &&
|
||||||
|
winrt::JsonObject::TryParse(winrt::to_hstring(newValue), newJson)) {
|
||||||
|
MergeJsonObjects(oldJson, newJson);
|
||||||
|
mergedResults.push_back(KeyValue{key, winrt::to_string(oldJson.ToString())});
|
||||||
|
} else {
|
||||||
|
return m_errorManager.AddError("Values must be valid JSON object strings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MultiSet(db, mergedResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> DBStorage::DBTask::MultiRemove(sqlite3 *db,
|
||||||
|
const std::vector<std::string> &keys) noexcept
|
||||||
|
{
|
||||||
|
CHECK(!m_errorManager.HasErrors());
|
||||||
|
CHECK(CheckArgs(db, m_errorManager, keys));
|
||||||
|
auto argCount = static_cast<int>(keys.size());
|
||||||
|
auto sql =
|
||||||
|
MakeSQLiteParameterizedStatement("DELETE FROM AsyncLocalStorage WHERE key IN ", argCount);
|
||||||
|
auto statement = StatementPtr{nullptr, &sqlite3_finalize};
|
||||||
|
CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
|
||||||
|
for (int i = 0; i < argCount; i++) {
|
||||||
|
CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
auto stepResult = sqlite3_step(statement.get());
|
||||||
|
if (stepResult == SQLITE_DONE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (stepResult != SQLITE_ROW) {
|
||||||
|
return m_errorManager.AddError(sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<std::string>> DBStorage::DBTask::GetAllKeys(sqlite3 *db) noexcept
|
||||||
|
{
|
||||||
|
CHECK(!m_errorManager.HasErrors());
|
||||||
|
std::vector<std::string> result;
|
||||||
|
auto getAllKeysCallback = [&](int columnCount, char **columnTexts, char **) {
|
||||||
|
if (columnCount > 0) {
|
||||||
|
result.emplace_back(columnTexts[0]);
|
||||||
|
}
|
||||||
|
return SQLITE_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK(Exec(db, m_errorManager, "SELECT key FROM AsyncLocalStorage", getAllKeysCallback));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> DBStorage::DBTask::RemoveAll(sqlite3 *db) noexcept
|
||||||
|
{
|
||||||
|
CHECK(!m_errorManager.HasErrors());
|
||||||
|
CHECK(Exec(db, m_errorManager, "DELETE FROM AsyncLocalStorage"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read KeyValue from a JSON array.
|
||||||
|
void ReadValue(const winrt::IJSValueReader &reader,
|
||||||
|
/*out*/ DBStorage::KeyValue &value) noexcept
|
||||||
|
{
|
||||||
|
if (reader.ValueType() == winrt::JSValueType::Array) {
|
||||||
|
int index = 0;
|
||||||
|
while (reader.GetNextArrayItem()) {
|
||||||
|
if (index == 0) {
|
||||||
|
ReadValue(reader, value.Key);
|
||||||
|
} else if (index == 1) {
|
||||||
|
ReadValue(reader, value.Value);
|
||||||
|
} else {
|
||||||
|
// Read the array till the end to keep reader in a good state.
|
||||||
|
winrt::SkipValue<winrt::JSValue>(reader);
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// To keep reader in a good state.
|
||||||
|
winrt::SkipValue<winrt::JSValue>(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write KeyValue to a JSON array.
|
||||||
|
void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
|
||||||
|
const DBStorage::KeyValue &value) noexcept
|
||||||
|
{
|
||||||
|
writer.WriteArrayBegin();
|
||||||
|
WriteValue(writer, value.Key);
|
||||||
|
WriteValue(writer, value.Value);
|
||||||
|
writer.WriteArrayEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write Error object to JSON.
|
||||||
|
void WriteValue(const winrt::IJSValueWriter &writer, const DBStorage::Error &value) noexcept
|
||||||
|
{
|
||||||
|
writer.WriteObjectBegin();
|
||||||
|
winrt::WriteProperty(writer, L"message", value.Message);
|
||||||
|
writer.WriteObjectEnd();
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <winsqlite/winsqlite3.h>
|
||||||
|
|
||||||
|
#include "NativeModules.h"
|
||||||
|
|
||||||
|
struct DBStorage {
|
||||||
|
// To pass KeyValue pairs in the native module API.
|
||||||
|
// It has custom ReadValue and WriteValue to read/write to/from JSON.
|
||||||
|
struct KeyValue {
|
||||||
|
std::string Key;
|
||||||
|
std::string Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An Error object for the native module API.
|
||||||
|
// It has a custom WriteValue to write to JSON.
|
||||||
|
struct Error {
|
||||||
|
std::string Message;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An error list shared between Promise and DBTask.
|
||||||
|
struct ErrorManager {
|
||||||
|
std::nullopt_t AddError(std::string &&message) noexcept;
|
||||||
|
bool HasErrors() const noexcept;
|
||||||
|
const std::vector<Error> &GetErrorList() const noexcept;
|
||||||
|
Error GetCombinedError() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Error> m_errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure that only one result onResolve or onReject callback is called once.
|
||||||
|
template <typename TOnResolve, typename TOnReject>
|
||||||
|
struct Promise {
|
||||||
|
|
||||||
|
Promise(TOnResolve &&onResolve, TOnReject &&onReject) noexcept
|
||||||
|
: m_onResolve(std::move(onResolve)), m_onReject(std::move(onReject))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Promise()
|
||||||
|
{
|
||||||
|
Reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise(const Promise &other) = delete;
|
||||||
|
Promise &operator=(const Promise &other) = delete;
|
||||||
|
|
||||||
|
ErrorManager &GetErrorManager() noexcept
|
||||||
|
{
|
||||||
|
return m_errorManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TValue>
|
||||||
|
void Resolve(const TValue &value) noexcept
|
||||||
|
{
|
||||||
|
Complete([&] { m_onResolve(value); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reject() noexcept
|
||||||
|
{
|
||||||
|
Complete([&] {
|
||||||
|
// Ensure that we have at least one error on rejection.
|
||||||
|
if (!m_errorManager.HasErrors()) {
|
||||||
|
m_errorManager.AddError("Promise is rejected.");
|
||||||
|
}
|
||||||
|
m_onReject(m_errorManager);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TValue>
|
||||||
|
void ResolveOrReject(const std::optional<TValue> &value) noexcept
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
Resolve(*value);
|
||||||
|
} else {
|
||||||
|
Reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Fn>
|
||||||
|
void Complete(Fn &&fn)
|
||||||
|
{
|
||||||
|
if (m_isCompleted.test_and_set() == false) {
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorManager m_errorManager;
|
||||||
|
std::atomic_flag m_isCompleted = ATOMIC_FLAG_INIT;
|
||||||
|
TOnResolve m_onResolve;
|
||||||
|
TOnReject m_onReject;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An asynchronous task that run in a background thread.
|
||||||
|
struct DBTask {
|
||||||
|
DBTask(ErrorManager &errorManager,
|
||||||
|
std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept;
|
||||||
|
|
||||||
|
DBTask() = default;
|
||||||
|
DBTask(const DBTask &) = delete;
|
||||||
|
DBTask &operator=(const DBTask &) = delete;
|
||||||
|
|
||||||
|
void Run(DBStorage &storage, sqlite3 *db) noexcept;
|
||||||
|
void Cancel() noexcept;
|
||||||
|
|
||||||
|
std::optional<std::vector<KeyValue>>
|
||||||
|
MultiGet(sqlite3 *db, const std::vector<std::string> &keys) noexcept;
|
||||||
|
std::optional<bool> MultiSet(sqlite3 *db, const std::vector<KeyValue> &keyValues) noexcept;
|
||||||
|
std::optional<bool> MultiMerge(sqlite3 *db,
|
||||||
|
const std::vector<KeyValue> &keyValues) noexcept;
|
||||||
|
std::optional<bool> MultiRemove(sqlite3 *db, const std::vector<std::string> &keys) noexcept;
|
||||||
|
std::optional<std::vector<std::string>> GetAllKeys(sqlite3 *db) noexcept;
|
||||||
|
std::optional<bool> RemoveAll(sqlite3 *db) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<void(DBTask &task, sqlite3 *db)> m_onRun;
|
||||||
|
ErrorManager &m_errorManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
using DatabasePtr = std::unique_ptr<sqlite3, decltype(&sqlite3_close)>;
|
||||||
|
|
||||||
|
std::optional<sqlite3 *> InitializeStorage(ErrorManager &errorManager) noexcept;
|
||||||
|
~DBStorage();
|
||||||
|
|
||||||
|
template <typename TOnResolve, typename TOnReject>
|
||||||
|
static auto CreatePromise(TOnResolve &&onResolve, TOnReject &&onReject) noexcept
|
||||||
|
{
|
||||||
|
using PromiseType = Promise<std::decay_t<TOnResolve>, std::decay_t<TOnReject>>;
|
||||||
|
return std::make_shared<PromiseType>(std::forward<TOnResolve>(onResolve),
|
||||||
|
std::forward<TOnReject>(onReject));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddTask(ErrorManager &errorManager,
|
||||||
|
std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept;
|
||||||
|
|
||||||
|
winrt::Windows::Foundation::IAsyncAction RunTasks() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr auto s_dbPathProperty = L"React-Native-Community-Async-Storage-Database-Path";
|
||||||
|
|
||||||
|
DatabasePtr m_db{nullptr, &sqlite3_close};
|
||||||
|
winrt::slim_mutex m_lock;
|
||||||
|
winrt::slim_condition_variable m_cv;
|
||||||
|
winrt::Windows::Foundation::IAsyncAction m_action{nullptr};
|
||||||
|
std::vector<std::unique_ptr<DBTask>> m_tasks;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ReadValue(const winrt::Microsoft::ReactNative::IJSValueReader &reader,
|
||||||
|
/*out*/ DBStorage::KeyValue &value) noexcept;
|
||||||
|
|
||||||
|
void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
|
||||||
|
const DBStorage::KeyValue &value) noexcept;
|
||||||
|
|
||||||
|
void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
|
||||||
|
const DBStorage::Error &value) noexcept;
|
@ -0,0 +1,118 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DBStorage.h"
|
||||||
|
#include "NativeModules.h"
|
||||||
|
|
||||||
|
namespace winrt::ReactNativeAsyncStorage::implementation
|
||||||
|
{
|
||||||
|
REACT_MODULE(RNCAsyncStorage)
|
||||||
|
struct RNCAsyncStorage {
|
||||||
|
|
||||||
|
REACT_METHOD(multiGet)
|
||||||
|
void multiGet(
|
||||||
|
std::vector<std::string> &&keys,
|
||||||
|
std::function<void(const std::vector<DBStorage::Error> &errors,
|
||||||
|
const std::vector<DBStorage::KeyValue> &result)> &&callback) noexcept
|
||||||
|
{
|
||||||
|
auto promise = DBStorage::CreatePromise(
|
||||||
|
[callback](const std::vector<DBStorage::KeyValue> &result) {
|
||||||
|
callback({}, result);
|
||||||
|
},
|
||||||
|
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||||
|
callback(errorManager.GetErrorList(), {});
|
||||||
|
});
|
||||||
|
m_dbStorage.AddTask(
|
||||||
|
promise->GetErrorManager(),
|
||||||
|
[promise, keys = std::move(keys)](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||||
|
promise->ResolveOrReject(task.MultiGet(db, keys));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
REACT_METHOD(multiSet)
|
||||||
|
void multiSet(
|
||||||
|
std::vector<DBStorage::KeyValue> &&keyValues,
|
||||||
|
std::function<void(const std::vector<DBStorage::Error> &errors)> &&callback) noexcept
|
||||||
|
{
|
||||||
|
auto promise =
|
||||||
|
DBStorage::CreatePromise([callback](bool /*value*/) { callback({}); },
|
||||||
|
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||||
|
callback(errorManager.GetErrorList());
|
||||||
|
});
|
||||||
|
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||||
|
[promise, keyValues = std::move(keyValues)](DBStorage::DBTask &task,
|
||||||
|
sqlite3 *db) noexcept {
|
||||||
|
promise->ResolveOrReject(task.MultiSet(db, keyValues));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
REACT_METHOD(multiMerge)
|
||||||
|
void multiMerge(
|
||||||
|
std::vector<DBStorage::KeyValue> &&keyValues,
|
||||||
|
std::function<void(const std::vector<DBStorage::Error> &errors)> &&callback) noexcept
|
||||||
|
{
|
||||||
|
auto promise =
|
||||||
|
DBStorage::CreatePromise([callback](bool /*value*/) { callback({}); },
|
||||||
|
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||||
|
callback(errorManager.GetErrorList());
|
||||||
|
});
|
||||||
|
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||||
|
[promise, keyValues = std::move(keyValues)](DBStorage::DBTask &task,
|
||||||
|
sqlite3 *db) noexcept {
|
||||||
|
promise->ResolveOrReject(task.MultiMerge(db, keyValues));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
REACT_METHOD(multiRemove)
|
||||||
|
void multiRemove(
|
||||||
|
std::vector<std::string> &&keys,
|
||||||
|
std::function<void(const std::vector<DBStorage::Error> &errors)> &&callback) noexcept
|
||||||
|
{
|
||||||
|
auto promise =
|
||||||
|
DBStorage::CreatePromise([callback](bool /*value*/) { callback({}); },
|
||||||
|
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||||
|
callback(errorManager.GetErrorList());
|
||||||
|
});
|
||||||
|
m_dbStorage.AddTask(
|
||||||
|
promise->GetErrorManager(),
|
||||||
|
[promise, keys = std::move(keys)](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||||
|
promise->ResolveOrReject(task.MultiRemove(db, keys));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
REACT_METHOD(getAllKeys)
|
||||||
|
void
|
||||||
|
getAllKeys(std::function<void(const std::optional<DBStorage::Error> &error,
|
||||||
|
const std::vector<std::string> &keys)> &&callback) noexcept
|
||||||
|
{
|
||||||
|
auto promise = DBStorage::CreatePromise(
|
||||||
|
[callback](const std::vector<std::string> &keys) { callback(std::nullopt, keys); },
|
||||||
|
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||||
|
callback(errorManager.GetCombinedError(), {});
|
||||||
|
});
|
||||||
|
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||||
|
[promise](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||||
|
promise->ResolveOrReject(task.GetAllKeys(db));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
REACT_METHOD(clear)
|
||||||
|
void
|
||||||
|
clear(std::function<void(const std::optional<DBStorage::Error> &error)> &&callback) noexcept
|
||||||
|
{
|
||||||
|
auto promise =
|
||||||
|
DBStorage::CreatePromise([callback](bool /*value*/) { callback(std::nullopt); },
|
||||||
|
[callback](const DBStorage::ErrorManager &errorManager) {
|
||||||
|
callback(errorManager.GetCombinedError());
|
||||||
|
});
|
||||||
|
m_dbStorage.AddTask(promise->GetErrorManager(),
|
||||||
|
[promise](DBStorage::DBTask &task, sqlite3 *db) noexcept {
|
||||||
|
promise->ResolveOrReject(task.RemoveAll(db));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DBStorage m_dbStorage;
|
||||||
|
};
|
||||||
|
} // namespace winrt::ReactNativeAsyncStorage::implementation
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue