mirror of
https://github.com/avinal/nikki.git
synced 2026-07-03 21:40:09 +05:30
Add dependency wiring, token storage, and app entry points
- AppDependencies: manual DI container with lazy singletons - TokenStore: DataStore-backed persistence for server URL, access token, theme preference, and accent color selection - DataStoreFactory: multiplatform DataStore creation - LocalAppDependencies: CompositionLocal for DI access in composables - App.kt: root composable with theme + Coil ImageLoader (Ktor engine) - MainActivity: creates AppDependencies, provides via CompositionLocal - Coil configured with authenticated Ktor HttpClient for private images Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.avinal.memos
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import coil3.ImageLoader
|
||||
import coil3.compose.setSingletonImageLoaderFactory
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.network.ktor3.KtorNetworkFetcherFactory
|
||||
import com.avinal.memos.ui.navigation.AppNavHost
|
||||
import com.avinal.memos.ui.theme.MemosAppTheme
|
||||
import com.avinal.memos.util.LocalAppDependencies
|
||||
|
||||
@Composable
|
||||
fun App() {
|
||||
val deps = LocalAppDependencies.current
|
||||
|
||||
setSingletonImageLoaderFactory { context ->
|
||||
ImageLoader.Builder(context)
|
||||
.components {
|
||||
add(KtorNetworkFetcherFactory(httpClient = deps.httpClient))
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
MemosAppTheme {
|
||||
AppNavHost(deps)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.avinal.memos
|
||||
|
||||
import com.avinal.memos.api.HttpClientFactory
|
||||
import com.avinal.memos.api.MemosApiClient
|
||||
import com.avinal.memos.db.MemosDatabase
|
||||
import com.avinal.memos.db.createPlatformDatabase
|
||||
import com.avinal.memos.domain.AuthRepository
|
||||
import com.avinal.memos.domain.MemoRepository
|
||||
import com.avinal.memos.util.TokenStore
|
||||
import com.avinal.memos.util.createDataStore
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AppDependencies(
|
||||
dataStorePath: String,
|
||||
platformContext: Any,
|
||||
) {
|
||||
@Volatile private var cachedToken: String? = null
|
||||
@Volatile private var cachedServerUrl: String? = null
|
||||
|
||||
private val dataStore = createDataStore(dataStorePath)
|
||||
val tokenStore = TokenStore(dataStore)
|
||||
|
||||
val database: MemosDatabase by lazy { createPlatformDatabase(platformContext) }
|
||||
|
||||
val httpClient by lazy {
|
||||
HttpClientFactory.create { cachedToken }
|
||||
}
|
||||
|
||||
val apiClient: MemosApiClient by lazy {
|
||||
MemosApiClient(
|
||||
httpClient = httpClient,
|
||||
baseUrlProvider = { cachedServerUrl ?: "" },
|
||||
)
|
||||
}
|
||||
|
||||
val authRepository: AuthRepository by lazy { AuthRepository(apiClient, tokenStore) }
|
||||
val memoRepository: MemoRepository by lazy { MemoRepository(apiClient, database.memoDao()) }
|
||||
|
||||
fun initialize() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
launch { tokenStore.accessToken.collect { cachedToken = it } }
|
||||
launch { tokenStore.serverUrl.collect { cachedServerUrl = it } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.avinal.memos.util
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import okio.Path.Companion.toPath
|
||||
|
||||
fun createDataStore(path: String): DataStore<Preferences> =
|
||||
PreferenceDataStoreFactory.createWithPath(produceFile = { path.toPath() })
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.avinal.memos.util
|
||||
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import com.avinal.memos.AppDependencies
|
||||
|
||||
val LocalAppDependencies = staticCompositionLocalOf<AppDependencies> {
|
||||
error("AppDependencies not provided")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.avinal.memos.util
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class TokenStore(private val dataStore: DataStore<Preferences>) {
|
||||
|
||||
val serverUrl: Flow<String?> = dataStore.data.map { it[KEY_SERVER_URL] }
|
||||
val accessToken: Flow<String?> = dataStore.data.map { it[KEY_ACCESS_TOKEN] }
|
||||
val theme: Flow<String> = dataStore.data.map { it[KEY_THEME] ?: "DARK" }
|
||||
val accentColor: Flow<String> = dataStore.data.map { it[KEY_ACCENT] ?: "Cobalt" }
|
||||
|
||||
suspend fun saveCredentials(serverUrl: String, token: String) {
|
||||
dataStore.edit { prefs ->
|
||||
prefs[KEY_SERVER_URL] = serverUrl
|
||||
prefs[KEY_ACCESS_TOKEN] = token
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveTheme(theme: String) {
|
||||
dataStore.edit { it[KEY_THEME] = theme }
|
||||
}
|
||||
|
||||
suspend fun saveAccentColor(name: String) {
|
||||
dataStore.edit { it[KEY_ACCENT] = name }
|
||||
}
|
||||
|
||||
suspend fun clear() {
|
||||
dataStore.edit {
|
||||
val theme = it[KEY_THEME]
|
||||
val accent = it[KEY_ACCENT]
|
||||
it.clear()
|
||||
if (theme != null) it[KEY_THEME] = theme
|
||||
if (accent != null) it[KEY_ACCENT] = accent
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val KEY_SERVER_URL = stringPreferencesKey("server_url")
|
||||
private val KEY_ACCESS_TOKEN = stringPreferencesKey("access_token")
|
||||
private val KEY_THEME = stringPreferencesKey("app_theme")
|
||||
private val KEY_ACCENT = stringPreferencesKey("accent_color")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user