Skip to content

Commit

Permalink
shared preferences api
Browse files Browse the repository at this point in the history
  • Loading branch information
haileyok committed Jun 26, 2024
1 parent b23f112 commit 37ff335
Show file tree
Hide file tree
Showing 19 changed files with 637 additions and 77 deletions.
31 changes: 31 additions & 0 deletions __e2e__/flows/shared-prefs.yml
Original file line number Diff line number Diff line change
@@ -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: "setBoolBtn"
- assertVisible: "true"
- tapOn:
id: "setNumberBtn"
- assertVisible: "123"
- tapOn:
id: "addToSetBtn"
- assertVisible: "true"
- tapOn:
id: "removeFromSetBtn"
- assertVisible: "false"

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
4 changes: 2 additions & 2 deletions modules/expo-bluesky-swiss-army/expo-module.config.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"platforms": ["ios", "tvos", "android", "web"],
"ios": {
"modules": ["ExpoBlueskyDevicePrefsModule", "ExpoBlueskyReferrerModule"]
"modules": ["ExpoBlueskySharedPrefsModule", "ExpoBlueskyReferrerModule"]
},
"android": {
"modules": [
"expo.modules.blueskyswissarmy.deviceprefs.ExpoBlueskyDevicePrefsModule",
"expo.modules.blueskyswissarmy.sharedprefs.ExpoBlueskySharedPrefsModule",
"expo.modules.blueskyswissarmy.referrer.ExpoBlueskyReferrerModule"
]
}
Expand Down
4 changes: 2 additions & 2 deletions modules/expo-bluesky-swiss-army/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as DevicePrefs from './src/DevicePrefs'
import * as Referrer from './src/Referrer'
import * as SharedPrefs from './src/SharedPrefs'

export {DevicePrefs, Referrer}
export {Referrer, SharedPrefs}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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
if value.isString() {
SharedPrefs.shared.setValue(key, value.getString())
} else if value.isNumber() {
SharedPrefs.shared.setValue(key, value.getDouble())
} else if value.isBool() {
SharedPrefs.shared.setValue(key, value.getBool())
} else if value.isNull() || value.isUndefined() {
SharedPrefs.shared.removeValue(key)
} else {
promise.reject("UNSUPPORTED_TYPE_ERROR", "Attempted to set an unsupported type")
}
}

AsyncFunction("removeValueAsync") { (key: String) in
SharedPrefs.shared.removeValue(key)
}

AsyncFunction("getStringAsync") { (key: String) in
return SharedPrefs.shared.getString(key)
}

AsyncFunction("getBoolAsync") { (key: String) in
return SharedPrefs.shared.getBool(key)
}

AsyncFunction("getNumberAsync") { (key: String) in
return SharedPrefs.shared.getNumber(key)
}

AsyncFunction("addToSetAsync") { (key: String, value: String) in
SharedPrefs.shared.addToSet(key, value)
}

AsyncFunction("removeFromSetAsync") { (key: String, value: String) in
SharedPrefs.shared.removeFromSet(key, value)
}

AsyncFunction("setContainsAsync") { (key: String, value: String) in
return SharedPrefs.shared.setContains(key, value)
}
}
}
Loading

0 comments on commit 37ff335

Please sign in to comment.