shared preferences api
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
appId: xyz.blueskyweb.app
|
||||||
|
---
|
||||||
|
- runScript:
|
||||||
|
file: ../setupServer.js
|
||||||
|
env:
|
||||||
|
SERVER_PATH: "?users&posts&feeds"
|
||||||
|
- runFlow:
|
||||||
|
file: ../setupApp.yml
|
||||||
|
- tapOn:
|
||||||
|
id: "e2eSignInAlice"
|
||||||
|
- tapOn: "/sys/debug"
|
||||||
|
- tapOn:
|
||||||
|
id: "sharedPrefsTestOpenBtn"
|
||||||
|
- tapOn:
|
||||||
|
id: "setStringBtn"
|
||||||
|
- assertVisible: "Hello"
|
||||||
|
- tapOn:
|
||||||
|
id: "removeStringBtn"
|
||||||
|
- assertVisible: "null"
|
||||||
|
- tapOn:
|
||||||
|
id: "setNumberBtn"
|
||||||
|
- assertVisible: "123"
|
||||||
|
- tapOn:
|
||||||
|
id: "setBoolBtn"
|
||||||
|
- assertVisible: "true"
|
||||||
|
- tapOn:
|
||||||
|
id: "addToSetBtn"
|
||||||
|
- assertVisible: "true"
|
||||||
|
- tapOn:
|
||||||
|
id: "removeFromSetBtn"
|
||||||
|
- assertVisible: "false"
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
package expo.modules.blueskyswissarmy.deviceprefs
|
|
||||||
|
|
||||||
import expo.modules.kotlin.modules.Module
|
|
||||||
import expo.modules.kotlin.modules.ModuleDefinition
|
|
||||||
|
|
||||||
class ExpoBlueskyDevicePrefsModule : Module() {
|
|
||||||
override fun definition() = ModuleDefinition {
|
|
||||||
Name("ExpoBlueskyDevicePrefs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
package expo.modules.blueskyswissarmy.sharedprefs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import expo.modules.kotlin.Promise
|
||||||
|
import expo.modules.kotlin.jni.JavaScriptValue
|
||||||
|
import expo.modules.kotlin.modules.Module
|
||||||
|
import expo.modules.kotlin.modules.ModuleDefinition
|
||||||
|
|
||||||
|
class ExpoBlueskySharedPrefsModule : Module() {
|
||||||
|
private fun getContext(): Context {
|
||||||
|
val context = appContext.reactContext ?: throw Error("Context is null")
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun definition() = ModuleDefinition {
|
||||||
|
Name("ExpoBlueskySharedPrefs")
|
||||||
|
|
||||||
|
AsyncFunction("setStringAsync") { key: String, value: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).setValue(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("setValueAsync") { key: String, value: JavaScriptValue, promise: Promise ->
|
||||||
|
val context = getContext()
|
||||||
|
try {
|
||||||
|
if (value.isNumber()) {
|
||||||
|
Preferences(context).setValue(key, value.getFloat())
|
||||||
|
promise.resolve()
|
||||||
|
} else if (value.isBool()) {
|
||||||
|
Preferences(context).setValue(key, value.getBool())
|
||||||
|
promise.resolve()
|
||||||
|
} else if (value.isNull() || value.isUndefined()) {
|
||||||
|
Preferences(context).removeValue(key)
|
||||||
|
promise.resolve()
|
||||||
|
} else {
|
||||||
|
Log.d(NAME, "Unsupported type: ${value.kind()}")
|
||||||
|
promise.reject("UNSUPPORTED_TYPE_ERROR", "Attempted to set an unsupported type", null)
|
||||||
|
}
|
||||||
|
} catch (e: Error) {
|
||||||
|
Log.d(NAME, "Error setting value: $e")
|
||||||
|
promise.reject("SET_VALUE_ERROR", "Error setting value", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("removeValueAsync") { key: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).removeValue(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("getStringAsync") { key: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).getString(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("getNumberAsync") { key: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).getFloat(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("getBoolAsync") { key: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).getBoolean(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("addToSetAsync") { key: String, value: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).addToSet(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("removeFromSetAsync") { key: String, value: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).removeFromSet(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("setContainsAsync") { key: String, value: String ->
|
||||||
|
return@AsyncFunction Preferences(getContext()).setContains(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
package expo.modules.blueskyswissarmy.sharedprefs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
val DEFAULTS = mapOf<String, Any>(
|
||||||
|
"playSoundChat" to true,
|
||||||
|
"playSoundFollow" to false,
|
||||||
|
"playSoundLike" to false,
|
||||||
|
"playSoundMention" to false,
|
||||||
|
"playSoundQuote" to false,
|
||||||
|
"playSoundReply" to false,
|
||||||
|
"playSoundRepost" to false,
|
||||||
|
"badgeCount" to 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
const val NAME = "SharedPrefs"
|
||||||
|
|
||||||
|
class Preferences(private val context: Context) {
|
||||||
|
companion object {
|
||||||
|
private var hasInitialized = false
|
||||||
|
|
||||||
|
private var instance: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun getInstance(context: Context, info: String? = "(no info)"): SharedPreferences {
|
||||||
|
if (instance == null) {
|
||||||
|
Log.d(NAME, "No preferences instance found, creating one.")
|
||||||
|
instance = context.getSharedPreferences("xyz.blueskyweb.app", Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
val safeInstance = instance ?: throw Error("Preferences is null: $info")
|
||||||
|
|
||||||
|
if (!hasInitialized) {
|
||||||
|
Log.d(NAME, "Preferences instance has not been initialized yet.")
|
||||||
|
initialize(safeInstance)
|
||||||
|
hasInitialized = true
|
||||||
|
Log.d(NAME, "Preferences instance has been initialized.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initialize(instance: SharedPreferences) {
|
||||||
|
instance
|
||||||
|
.edit()
|
||||||
|
.apply {
|
||||||
|
DEFAULTS.forEach { (key, value) ->
|
||||||
|
if (instance.contains(key)) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
when (value) {
|
||||||
|
is Boolean -> {
|
||||||
|
putBoolean(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
is String -> {
|
||||||
|
putString(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Array<*> -> {
|
||||||
|
putStringSet(key, value.map { it.toString() }.toSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
is Map<*, *> -> {
|
||||||
|
putStringSet(key, value.map { it.toString() }.toSet())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(key: String, value: String) {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
safeInstance.edit().apply {
|
||||||
|
putString(key, value)
|
||||||
|
}.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(key: String, value: Float) {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
safeInstance.edit().apply {
|
||||||
|
putFloat(key, value)
|
||||||
|
}.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(key: String, value: Boolean) {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
safeInstance.edit().apply {
|
||||||
|
putBoolean(key, value)
|
||||||
|
}.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(key: String, value: Set<String>) {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
safeInstance.edit().apply {
|
||||||
|
putStringSet(key, value)
|
||||||
|
}.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeValue(key: String) {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
safeInstance.edit().apply {
|
||||||
|
remove(key)
|
||||||
|
}.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getString(key: String): String? {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
return safeInstance.getString(key, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFloat(key: String): Float? {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
if (!safeInstance.contains(key)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return safeInstance.getFloat(key, 0.0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBoolean(key: String): Boolean? {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
if (!safeInstance.contains(key)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
Log.d(NAME, "Getting boolean for key: $key")
|
||||||
|
val res = safeInstance.getBoolean(key, false)
|
||||||
|
Log.d(NAME, "Got boolean for key: $key, value: $res")
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToSet(key: String, value: String) {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
val set = safeInstance.getStringSet(key, setOf()) ?: setOf()
|
||||||
|
val newSet = set.toMutableSet().apply {
|
||||||
|
add(value)
|
||||||
|
}
|
||||||
|
safeInstance.edit().apply {
|
||||||
|
putStringSet(key, newSet)
|
||||||
|
}.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromSet(key: String, value: String) {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
val set = safeInstance.getStringSet(key, setOf()) ?: setOf()
|
||||||
|
val newSet = set.toMutableSet().apply {
|
||||||
|
remove(value)
|
||||||
|
}
|
||||||
|
safeInstance.edit().apply {
|
||||||
|
putStringSet(key, newSet)
|
||||||
|
}.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setContains(key: String, value: String): Boolean {
|
||||||
|
val safeInstance = getInstance(context)
|
||||||
|
val set = safeInstance.getStringSet(key, setOf()) ?: setOf()
|
||||||
|
return set.contains(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"platforms": ["ios", "tvos", "android", "web"],
|
"platforms": ["ios", "tvos", "android", "web"],
|
||||||
"ios": {
|
"ios": {
|
||||||
"modules": ["ExpoBlueskyDevicePrefsModule", "ExpoBlueskyReferrerModule"]
|
"modules": ["ExpoBlueskySharedPrefsModule", "ExpoBlueskyReferrerModule"]
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"modules": [
|
"modules": [
|
||||||
"expo.modules.blueskyswissarmy.deviceprefs.ExpoBlueskyDevicePrefsModule",
|
"expo.modules.blueskyswissarmy.sharedprefs.ExpoBlueskySharedPrefsModule",
|
||||||
"expo.modules.blueskyswissarmy.referrer.ExpoBlueskyReferrerModule"
|
"expo.modules.blueskyswissarmy.referrer.ExpoBlueskyReferrerModule"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as DevicePrefs from './src/DevicePrefs'
|
|
||||||
import * as Referrer from './src/Referrer'
|
import * as Referrer from './src/Referrer'
|
||||||
|
import * as SharedPrefs from './src/SharedPrefs'
|
||||||
|
|
||||||
export {DevicePrefs, Referrer}
|
export {Referrer, SharedPrefs}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import ExpoModulesCore
|
|
||||||
|
|
||||||
public class ExpoBlueskyDevicePrefsModule: Module {
|
|
||||||
func getDefaults(_ useAppGroup: Bool) -> UserDefaults? {
|
|
||||||
if useAppGroup {
|
|
||||||
return UserDefaults(suiteName: "group.app.bsky")
|
|
||||||
} else {
|
|
||||||
return UserDefaults.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func definition() -> ModuleDefinition {
|
|
||||||
Name("ExpoBlueskyDevicePrefs")
|
|
||||||
|
|
||||||
AsyncFunction("getStringValueAsync") { (key: String, useAppGroup: Bool) in
|
|
||||||
return self.getDefaults(useAppGroup)?.string(forKey: key)
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncFunction("setStringValueAsync") { (key: String, value: String?, useAppGroup: Bool) in
|
|
||||||
self.getDefaults(useAppGroup)?.setValue(value, forKey: key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import Foundation
|
||||||
|
import ExpoModulesCore
|
||||||
|
|
||||||
|
public class ExpoBlueskySharedPrefsModule: Module {
|
||||||
|
let defaults = UserDefaults(suiteName: "group.app.bsky")
|
||||||
|
|
||||||
|
func getDefaults(_ info: String = "(no info)") -> UserDefaults? {
|
||||||
|
guard let defaults = self.defaults else {
|
||||||
|
NSLog("Failed to get defaults for app group: \(info)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
public func definition() -> ModuleDefinition {
|
||||||
|
Name("ExpoBlueskySharedPrefs")
|
||||||
|
|
||||||
|
AsyncFunction("setValueAsync") { (key: String, value: JavaScriptValue, promise: Promise) in
|
||||||
|
guard value.isString() || value.isNumber() || value.isBool() || value.isNull() || value.isUndefined() else {
|
||||||
|
promise.reject("UNSUPPORTED_TYPE_ERROR", "Attempted to set an unsupported type")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let defaults = self.getDefaults() else {
|
||||||
|
promise.reject("PREFS_ERROR", "Was unable to get shared preferences")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.isNumber() {
|
||||||
|
defaults.set(value.getDouble(), forKey: key)
|
||||||
|
} else {
|
||||||
|
defaults.set(value.getRaw(), forKey: key)
|
||||||
|
}
|
||||||
|
promise.resolve()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("removeValueAsync") { (key: String) in
|
||||||
|
self.getDefaults(key)?.removeObject(forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("getStringAsync") { (key: String) in
|
||||||
|
return self.getDefaults(key)?.string(forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("getBoolAsync") { (key: String) in
|
||||||
|
return self.getDefaults(key)?.bool(forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("getNumberAsync") { (key: String) in
|
||||||
|
return self.getDefaults(key)?.double(forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("addToSetAsync") { (key: String, value: String) in
|
||||||
|
var dict: [String:Bool]?
|
||||||
|
if var currDict = self.getDefaults(key)?.dictionary(forKey: key) as? [String:Bool] {
|
||||||
|
currDict[value] = true
|
||||||
|
dict = currDict
|
||||||
|
} else {
|
||||||
|
dict = [
|
||||||
|
value : true
|
||||||
|
]
|
||||||
|
}
|
||||||
|
self.getDefaults(key)?.setValue(dict, forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("removeFromSetAsync") { (key: String, value: String) in
|
||||||
|
guard var dict = self.getDefaults(key)?.dictionary(forKey: key) as? [String:Bool] else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dict.removeValue(forKey: value)
|
||||||
|
self.getDefaults(key)?.setValue(dict, forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFunction("setContainsAsync") { (key: String, value: String) in
|
||||||
|
guard let dict = self.getDefaults(key)?.dictionary(forKey: key) as? [String:Bool] else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return dict[value] == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import {requireNativeModule} from 'expo-modules-core'
|
|
||||||
|
|
||||||
const NativeModule = requireNativeModule('ExpoBlueskyDevicePrefs')
|
|
||||||
|
|
||||||
export function getStringValueAsync(
|
|
||||||
key: string,
|
|
||||||
useAppGroup?: boolean,
|
|
||||||
): Promise<string | null> {
|
|
||||||
return NativeModule.getStringValueAsync(key, useAppGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setStringValueAsync(
|
|
||||||
key: string,
|
|
||||||
value: string | null,
|
|
||||||
useAppGroup?: boolean,
|
|
||||||
): Promise<void> {
|
|
||||||
return NativeModule.setStringValueAsync(key, value, useAppGroup)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import {NotImplementedError} from '../NotImplemented'
|
|
||||||
|
|
||||||
export function getStringValueAsync(
|
|
||||||
key: string,
|
|
||||||
useAppGroup?: boolean,
|
|
||||||
): Promise<string | null> {
|
|
||||||
throw new NotImplementedError({key, useAppGroup})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setStringValueAsync(
|
|
||||||
key: string,
|
|
||||||
value: string | null,
|
|
||||||
useAppGroup?: boolean,
|
|
||||||
): Promise<string | null> {
|
|
||||||
throw new NotImplementedError({key, value, useAppGroup})
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import {Platform} from 'react-native'
|
||||||
|
import {requireNativeModule} from 'expo-modules-core'
|
||||||
|
|
||||||
|
const NativeModule = requireNativeModule('ExpoBlueskySharedPrefs')
|
||||||
|
|
||||||
|
export function setValueAsync(
|
||||||
|
key: string,
|
||||||
|
value: string | number | boolean | null | undefined,
|
||||||
|
): Promise<void> {
|
||||||
|
// A bug on Android causes `JavaScripValue.isString()` to cause a crash on some occasions, seemingly because of a
|
||||||
|
// memory violation. Instead, we will use a specific function to set strings on this platform.
|
||||||
|
if (Platform.OS === 'android' && typeof value === 'string') {
|
||||||
|
return NativeModule.setStringAsync(key, value)
|
||||||
|
}
|
||||||
|
return NativeModule.setValueAsync(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeValueAsync(key: string): Promise<void> {
|
||||||
|
return NativeModule.removeValueAsync(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStringAsync(key: string): Promise<string | null> {
|
||||||
|
return NativeModule.getStringAsync(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNumberAsync(key: string): Promise<number | null> {
|
||||||
|
return NativeModule.getNumberAsync(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBoolAsync(key: string): Promise<boolean | null> {
|
||||||
|
return NativeModule.getBoolAsync(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addToSetAsync(key: string, value: string): Promise<void> {
|
||||||
|
return NativeModule.addToSetAsync(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeFromSetAsync(key: string, value: string): Promise<void> {
|
||||||
|
return NativeModule.removeFromSetAsync(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setContainsAsync(key: string, value: string): Promise<boolean> {
|
||||||
|
return NativeModule.setContainsAsync(key, value)
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import {NotImplementedError} from '../NotImplemented'
|
||||||
|
|
||||||
|
export function setValueAsync(
|
||||||
|
key: string,
|
||||||
|
value: string | number | boolean | null | undefined,
|
||||||
|
): Promise<void> {
|
||||||
|
throw new NotImplementedError({key, value})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeValueAsync(key: string): Promise<void> {
|
||||||
|
throw new NotImplementedError({key})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStringAsync(key: string): Promise<string | null> {
|
||||||
|
console.log('call')
|
||||||
|
throw new NotImplementedError({key})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNumberAsync(key: string): Promise<number | null> {
|
||||||
|
throw new NotImplementedError({key})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBoolAsync(key: string): Promise<boolean | null> {
|
||||||
|
throw new NotImplementedError({key})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addToSetAsync(key: string, value: string): Promise<void> {
|
||||||
|
throw new NotImplementedError({key, value})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeFromSetAsync(key: string, value: string): Promise<void> {
|
||||||
|
throw new NotImplementedError({key, value})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setContainsAsync(key: string, value: string): Promise<boolean> {
|
||||||
|
throw new NotImplementedError({key, value})
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
"typecheck": "tsc --project ./tsconfig.check.json",
|
"typecheck": "tsc --project ./tsconfig.check.json",
|
||||||
"e2e:mock-server": "./jest/dev-infra/with-test-redis-and-db.sh ts-node --project tsconfig.e2e.json __e2e__/mock-server.ts",
|
"e2e:mock-server": "./jest/dev-infra/with-test-redis-and-db.sh ts-node --project tsconfig.e2e.json __e2e__/mock-server.ts",
|
||||||
"e2e:metro": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios",
|
"e2e:metro": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios",
|
||||||
|
"e2e:metro-android": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:android",
|
||||||
"e2e:run": "maestro test __e2e__",
|
"e2e:run": "maestro test __e2e__",
|
||||||
"perf:test": "NODE_ENV=test maestro test",
|
"perf:test": "NODE_ENV=test maestro test",
|
||||||
"perf:test:run": "NODE_ENV=test maestro test __e2e__/perf-test.yml",
|
"perf:test:run": "NODE_ENV=test maestro test __e2e__/perf-test.yml",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts'
|
|||||||
import {PreferencesFollowingFeed} from 'view/screens/PreferencesFollowingFeed'
|
import {PreferencesFollowingFeed} from 'view/screens/PreferencesFollowingFeed'
|
||||||
import {PreferencesThreads} from 'view/screens/PreferencesThreads'
|
import {PreferencesThreads} from 'view/screens/PreferencesThreads'
|
||||||
import {SavedFeeds} from 'view/screens/SavedFeeds'
|
import {SavedFeeds} from 'view/screens/SavedFeeds'
|
||||||
|
import {SharedPreferencesTesterScreen} from '#/screens/E2E/SharedPreferencesTesterScreen'
|
||||||
import HashtagScreen from '#/screens/Hashtag'
|
import HashtagScreen from '#/screens/Hashtag'
|
||||||
import {ModerationScreen} from '#/screens/Moderation'
|
import {ModerationScreen} from '#/screens/Moderation'
|
||||||
import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers'
|
import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers'
|
||||||
@@ -230,6 +231,11 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
|||||||
getComponent={() => DebugModScreen}
|
getComponent={() => DebugModScreen}
|
||||||
options={{title: title(msg`Moderation states`), requireAuth: true}}
|
options={{title: title(msg`Moderation states`), requireAuth: true}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="SharedPreferencesTester"
|
||||||
|
getComponent={() => SharedPreferencesTesterScreen}
|
||||||
|
options={{title: title(msg`Shared Preferences Tester`)}}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="Log"
|
name="Log"
|
||||||
getComponent={() => LogScreen}
|
getComponent={() => LogScreen}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
import {isAndroid} from 'platform/detection'
|
import {isAndroid} from 'platform/detection'
|
||||||
import {useHasCheckedForStarterPack} from 'state/preferences/used-starter-packs'
|
import {useHasCheckedForStarterPack} from 'state/preferences/used-starter-packs'
|
||||||
import {useSetActiveStarterPack} from 'state/shell/starter-pack'
|
import {useSetActiveStarterPack} from 'state/shell/starter-pack'
|
||||||
import {DevicePrefs, Referrer} from '../../../modules/expo-bluesky-swiss-army'
|
import {Referrer, SharedPrefs} from '../../../modules/expo-bluesky-swiss-army'
|
||||||
|
|
||||||
export function useStarterPackEntry() {
|
export function useStarterPackEntry() {
|
||||||
const [ready, setReady] = React.useState(false)
|
const [ready, setReady] = React.useState(false)
|
||||||
@@ -39,14 +39,11 @@ export function useStarterPackEntry() {
|
|||||||
uri = createStarterPackLinkFromAndroidReferrer(res.installReferrer)
|
uri = createStarterPackLinkFromAndroidReferrer(res.installReferrer)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const res = await DevicePrefs.getStringValueAsync(
|
const res = await SharedPrefs.getStringAsync('starterPackUri')
|
||||||
'starterPackUri',
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
uri = httpStarterPackUriToAtUri(res)
|
uri = httpStarterPackUriToAtUri(res)
|
||||||
DevicePrefs.setStringValueAsync('starterPackUri', null, true)
|
SharedPrefs.setValueAsync('starterPackUri', null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export type CommonNavigatorParams = {
|
|||||||
ProfileLabelerLikedBy: {name: string}
|
ProfileLabelerLikedBy: {name: string}
|
||||||
Debug: undefined
|
Debug: undefined
|
||||||
DebugMod: undefined
|
DebugMod: undefined
|
||||||
|
SharedPreferencesTester: undefined
|
||||||
Log: undefined
|
Log: undefined
|
||||||
Support: undefined
|
Support: undefined
|
||||||
PrivacyPolicy: undefined
|
PrivacyPolicy: undefined
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
|
||||||
|
import {ScrollView} from 'view/com/util/Views'
|
||||||
|
import {atoms as a} from '#/alf'
|
||||||
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
import {SharedPrefs} from '../../../modules/expo-bluesky-swiss-army'
|
||||||
|
|
||||||
|
export function SharedPreferencesTesterScreen() {
|
||||||
|
const [currentTestOutput, setCurrentTestOutput] = React.useState<string>('')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView contentContainerStyle={{backgroundColor: 'red'}}>
|
||||||
|
<View style={[a.flex_1]}>
|
||||||
|
<View>
|
||||||
|
<Text testID="testOutput">{currentTestOutput}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[a.flex_wrap]}>
|
||||||
|
<Button
|
||||||
|
label="btn"
|
||||||
|
testID="setStringBtn"
|
||||||
|
style={[a.self_center]}
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="xsmall"
|
||||||
|
onPress={async () => {
|
||||||
|
await SharedPrefs.removeValueAsync('testerString')
|
||||||
|
await SharedPrefs.setValueAsync('testerString', 'Hello')
|
||||||
|
const res = await SharedPrefs.getStringAsync('testerString')
|
||||||
|
setCurrentTestOutput(`${res}`)
|
||||||
|
}}>
|
||||||
|
<ButtonText>Set String</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
label="btn"
|
||||||
|
testID="removeStringBtn"
|
||||||
|
style={[a.self_center]}
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="xsmall"
|
||||||
|
onPress={async () => {
|
||||||
|
await SharedPrefs.removeValueAsync('testerString')
|
||||||
|
const res = await SharedPrefs.getStringAsync('testerString')
|
||||||
|
setCurrentTestOutput(`${res}`)
|
||||||
|
}}>
|
||||||
|
<ButtonText>Remove String</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
label="btn"
|
||||||
|
testID="setNumberBtn"
|
||||||
|
style={[a.self_center]}
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="xsmall"
|
||||||
|
onPress={async () => {
|
||||||
|
await SharedPrefs.removeValueAsync('testerNumber')
|
||||||
|
await SharedPrefs.setValueAsync('testerNumber', 123)
|
||||||
|
const res = await SharedPrefs.getNumberAsync('testerNumber')
|
||||||
|
setCurrentTestOutput(`${res}`)
|
||||||
|
}}>
|
||||||
|
<ButtonText>Set Number</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
label="btn"
|
||||||
|
testID="setBoolBtn"
|
||||||
|
style={[a.self_center]}
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="xsmall"
|
||||||
|
onPress={async () => {
|
||||||
|
await SharedPrefs.removeValueAsync('testerBool')
|
||||||
|
await SharedPrefs.setValueAsync('testerBool', true)
|
||||||
|
const res = await SharedPrefs.getBoolAsync('testerBool')
|
||||||
|
setCurrentTestOutput(`${res}`)
|
||||||
|
}}>
|
||||||
|
<ButtonText>Set Bool</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
label="btn"
|
||||||
|
testID="addToSetBtn"
|
||||||
|
style={[a.self_center]}
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="xsmall"
|
||||||
|
onPress={async () => {
|
||||||
|
await SharedPrefs.removeFromSetAsync('testerSet', 'Hello!')
|
||||||
|
await SharedPrefs.addToSetAsync('testerSet', 'Hello!')
|
||||||
|
const res = await SharedPrefs.setContainsAsync(
|
||||||
|
'testerSet',
|
||||||
|
'Hello!',
|
||||||
|
)
|
||||||
|
setCurrentTestOutput(`${res}`)
|
||||||
|
}}>
|
||||||
|
<ButtonText>Add to Set</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
label="btn"
|
||||||
|
testID="removeFromSetBtn"
|
||||||
|
style={[a.self_center]}
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="xsmall"
|
||||||
|
onPress={async () => {
|
||||||
|
await SharedPrefs.removeFromSetAsync('testerSet', 'Hello!')
|
||||||
|
const res = await SharedPrefs.setContainsAsync(
|
||||||
|
'testerSet',
|
||||||
|
'Hello!',
|
||||||
|
)
|
||||||
|
setCurrentTestOutput(`${res}`)
|
||||||
|
}}>
|
||||||
|
<ButtonText>Remove from Set</ButtonText>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||||
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
@@ -18,6 +20,7 @@ export function Dialogs() {
|
|||||||
const [shouldRenderUnmountTest, setShouldRenderUnmountTest] =
|
const [shouldRenderUnmountTest, setShouldRenderUnmountTest] =
|
||||||
React.useState(false)
|
React.useState(false)
|
||||||
const unmountTestInterval = React.useRef<number>()
|
const unmountTestInterval = React.useRef<number>()
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
const onUnmountTestStartPressWithClose = () => {
|
const onUnmountTestStartPressWithClose = () => {
|
||||||
setShouldRenderUnmountTest(true)
|
setShouldRenderUnmountTest(true)
|
||||||
@@ -134,6 +137,16 @@ export function Dialogs() {
|
|||||||
<ButtonText>End Unmount Test</ButtonText>
|
<ButtonText>End Unmount Test</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="solid"
|
||||||
|
color="primary"
|
||||||
|
size="small"
|
||||||
|
onPress={() => navigation.navigate('SharedPreferencesTester')}
|
||||||
|
label="two"
|
||||||
|
testID="sharedPrefsTestOpenBtn">
|
||||||
|
<ButtonText>Open Shared Prefs Tester</ButtonText>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Prompt.Outer control={prompt}>
|
<Prompt.Outer control={prompt}>
|
||||||
<Prompt.TitleText>This is a prompt</Prompt.TitleText>
|
<Prompt.TitleText>This is a prompt</Prompt.TitleText>
|
||||||
<Prompt.DescriptionText>
|
<Prompt.DescriptionText>
|
||||||
|
|||||||
Reference in New Issue
Block a user