Skip to content

Commit 37db93e

Browse files
author
Krzysztof Borowy
committed
initial implementation
1 parent 7fb5376 commit 37db93e

File tree

5 files changed

+165
-7
lines changed

5 files changed

+165
-7
lines changed

android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77

88
package com.reactnativecommunity.asyncstorage;
99

10+
import android.util.Log;
1011
import com.facebook.react.ReactPackage;
1112
import com.facebook.react.bridge.JavaScriptModule;
1213
import com.facebook.react.bridge.NativeModule;
1314
import com.facebook.react.bridge.ReactApplicationContext;
15+
import com.facebook.react.bridge.ReactContext;
1416
import com.facebook.react.uimanager.ViewManager;
15-
import com.reactnativecommunity.asyncstorage.next.StorageModule;
1617
import java.util.ArrayList;
1718
import java.util.Collections;
1819
import java.util.List;
@@ -23,23 +24,35 @@ public List<NativeModule> createNativeModules(ReactApplicationContext reactConte
2324

2425
List<NativeModule> moduleList = new ArrayList<>(1);
2526

26-
if(BuildConfig.AsyncStorage_useRoomLibrary) {
27-
moduleList.add(new StorageModule(reactContext));
27+
if (BuildConfig.AsyncStorage_useRoomLibrary) {
28+
try {
29+
Class storageClass = Class.forName("com.reactnativecommunity.asyncstorage.next.StorageModule");
30+
NativeModule inst = (NativeModule) storageClass.getDeclaredConstructor(new Class[]{ReactContext.class}).newInstance(reactContext);
31+
moduleList.add(inst);
32+
} catch (Exception e) {
33+
// todo notify about potential issues
34+
String message = "Something went wrong when initializing module:"
35+
+ "\n"
36+
+ e.getCause().getClass()
37+
+ "\n"
38+
+ "Cause:" + e.getCause().getLocalizedMessage();
39+
Log.e("AsyncStorage_Next", message);
40+
}
2841
} else {
2942
moduleList.add(new AsyncStorageModule(reactContext));
3043
}
3144

32-
return moduleList;
45+
return moduleList;
3346
}
3447

3548
// Deprecated in RN 0.47
3649
public List<Class<? extends JavaScriptModule>> createJSModules() {
37-
return Collections.emptyList();
50+
return Collections.emptyList();
3851
}
3952

4053
@Override
4154
@SuppressWarnings("rawtypes")
4255
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
43-
return Collections.emptyList();
56+
return Collections.emptyList();
4457
}
4558
}
Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
11
package com.reactnativecommunity.asyncstorage.next
22

3+
import com.facebook.react.bridge.Arguments
4+
import com.facebook.react.bridge.Callback
35
import com.facebook.react.bridge.ReactContext
46
import com.facebook.react.bridge.ReactContextBaseJavaModule
7+
import com.facebook.react.bridge.ReactMethod
8+
import com.facebook.react.bridge.ReadableArray
9+
import kotlinx.coroutines.CoroutineName
10+
import kotlinx.coroutines.CoroutineScope
11+
import kotlinx.coroutines.Dispatchers
12+
import kotlinx.coroutines.SupervisorJob
13+
import kotlinx.coroutines.launch
514

6-
class StorageModule(private val reactContext: ReactContext) : ReactContextBaseJavaModule() {
15+
class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule() {
716
override fun getName() = "RNC_AsyncSQLiteDBStorage"
17+
18+
private val storage: AsyncStorageAccess = StorageSupplier.getInstance(reactContext)
19+
private val scope = CoroutineScope(
20+
Dispatchers.IO + CoroutineName("AsyncStorageCoroutine") + SupervisorJob()
21+
)
22+
23+
@ReactMethod
24+
fun getAllKeys(cb: Callback) {
25+
scope.launch {
26+
val keys = storage.getKeys()
27+
val result = Arguments.createArray()
28+
keys.forEach { result.pushString(it) }
29+
cb.invoke(result)
30+
}
31+
}
832
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.reactnativecommunity.asyncstorage.next
2+
3+
import android.content.Context
4+
import androidx.room.ColumnInfo
5+
import androidx.room.Dao
6+
import androidx.room.Database
7+
import androidx.room.Entity
8+
import androidx.room.Insert
9+
import androidx.room.OnConflictStrategy
10+
import androidx.room.PrimaryKey
11+
import androidx.room.Query
12+
import androidx.room.Room
13+
import androidx.room.RoomDatabase
14+
import androidx.room.Transaction
15+
import androidx.room.Update
16+
17+
private const val DATABASE_NAME = "RKStorage"
18+
private const val DATABASE_VERSION = 1
19+
private const val TABLE_NAME = "catalystLocalStorage"
20+
private const val COLUMN_KEY = "key"
21+
private const val COLUMN_VALUE = "value"
22+
23+
24+
@Entity(tableName = TABLE_NAME, primaryKeys = ["key"])
25+
data class Entry(
26+
@PrimaryKey @ColumnInfo(name = COLUMN_KEY) val key: String,
27+
@ColumnInfo(name = COLUMN_VALUE) val value: String
28+
)
29+
30+
@Dao
31+
private interface StorageDao {
32+
33+
// fun mergeValues() // todo
34+
35+
@Transaction
36+
@Query("SELECT * FROM $TABLE_NAME WHERE `$COLUMN_KEY` IN (:keys)")
37+
fun getValues(keys: List<String>): List<Entry>
38+
39+
@Transaction
40+
fun setValues(entries: List<Entry>) {
41+
val insertResult = insert(entries)
42+
with(entries.filterIndexed { i, _ -> insertResult[i] == -1L }) {
43+
update(this)
44+
}
45+
}
46+
47+
@Transaction
48+
@Query("DELETE FROM $TABLE_NAME WHERE `$COLUMN_KEY` in (:keys)")
49+
fun removeValues(keys: List<String>)
50+
51+
@Transaction
52+
@Query("SELECT `$COLUMN_KEY` FROM $TABLE_NAME")
53+
fun getKeys(): List<String>
54+
55+
@Transaction
56+
@Query("DELETE FROM $TABLE_NAME")
57+
fun clear()
58+
59+
60+
// insert and update are components of setValues - not to be used separately
61+
@Insert(onConflict = OnConflictStrategy.IGNORE)
62+
fun insert(entries: List<Entry>): List<Long>
63+
@Update
64+
fun update(entries: List<Entry>)
65+
}
66+
67+
@Database(entities = [Entry::class], version = DATABASE_VERSION, exportSchema = false)
68+
private abstract class StorageDb : RoomDatabase() {
69+
abstract fun storage(): StorageDao
70+
71+
companion object {
72+
private var instance: StorageDb? = null
73+
74+
fun getDatabase(context: Context): StorageDb {
75+
var inst = instance
76+
if (inst != null) {
77+
return inst
78+
}
79+
80+
synchronized(this) {
81+
inst = Room.databaseBuilder(
82+
context, StorageDb::class.java, DATABASE_NAME
83+
).build()
84+
85+
instance = inst
86+
return instance!!
87+
}
88+
}
89+
}
90+
}
91+
92+
interface AsyncStorageAccess {
93+
suspend fun getValue(keys: List<String>): List<Entry>
94+
suspend fun setValues(entries: List<Entry>)
95+
suspend fun removeValues(keys: List<String>)
96+
suspend fun getKeys(): List<String>
97+
suspend fun clear()
98+
// suspend fun mergeValues() // todo
99+
}
100+
101+
class StorageSupplier private constructor(db: StorageDb) : AsyncStorageAccess {
102+
companion object {
103+
fun getInstance(ctx: Context): AsyncStorageAccess {
104+
return StorageSupplier(StorageDb.getDatabase(ctx))
105+
}
106+
}
107+
108+
private val access = db.storage()
109+
110+
override suspend fun getValue(keys: List<String>) = access.getValues(keys)
111+
override suspend fun setValues(entries: List<Entry>) = access.setValues(entries)
112+
override suspend fun removeValues(keys: List<String>) = access.removeValues(keys)
113+
override suspend fun getKeys() = access.getKeys()
114+
override suspend fun clear() = access.clear()
115+
}

example/android/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ buildscript {
4040
dependencies {
4141
// Needs to match `react-native-test-app/android/app/build.gradle`
4242
classpath "com.android.tools.build:gradle:4.0.2"
43+
44+
// todo: remember to document this as well
4345
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
4446
}
4547
}

example/examples/GetSetClear.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
1616
export default function GetSet() {
1717
const [storedNumber, setStoredNumber] = React.useState('');
1818
const [needsRestart, setNeedsRestart] = React.useState(false);
19+
const [keys, setKeys] = React.useState([]);
1920

2021
React.useEffect(() => {
2122
AsyncStorage.getItem(STORAGE_KEY).then((value) => {
2223
if (value) {
2324
setStoredNumber(value);
2425
}
2526
});
27+
AsyncStorage.getAllKeys().then(setKeys);
2628
}, []);
2729

2830
const increaseByTen = React.useCallback(async () => {
@@ -54,6 +56,8 @@ export default function GetSet() {
5456

5557
<Button testID="clear_button" title="Clear item" onPress={clearItem} />
5658

59+
<Text>Keys: {keys.join(', ')}</Text>
60+
5761
{needsRestart ? <Text>Hit restart to see effect</Text> : null}
5862
</View>
5963
);

0 commit comments

Comments
 (0)