mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-24 22:45:20 +00:00
allow user to select a theme preference over light/dark/system (#318)
This commit is contained in:
@@ -2,6 +2,7 @@ package com.bitchat.android
|
||||
|
||||
import android.app.Application
|
||||
import com.bitchat.android.nostr.RelayDirectory
|
||||
import com.bitchat.android.ui.theme.ThemePreferenceManager
|
||||
|
||||
/**
|
||||
* Main application class for bitchat Android
|
||||
@@ -23,5 +24,8 @@ class BitchatApplication : Application() {
|
||||
try {
|
||||
com.bitchat.android.nostr.NostrIdentityBridge.getCurrentNostrIdentity(this)
|
||||
} catch (_: Exception) { }
|
||||
|
||||
// Initialize theme preference
|
||||
ThemePreferenceManager.init(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,11 @@ import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -42,6 +40,8 @@ import com.bitchat.android.onboarding.PermissionManager
|
||||
import com.bitchat.android.ui.ChatScreen
|
||||
import com.bitchat.android.ui.ChatViewModel
|
||||
import com.bitchat.android.ui.theme.BitchatTheme
|
||||
import com.bitchat.android.ui.theme.ThemePreference
|
||||
import com.bitchat.android.ui.theme.ThemePreferenceManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@@ -160,6 +160,40 @@ fun AboutSheet(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Appearance section (theme toggle)
|
||||
item {
|
||||
val themePref by com.bitchat.android.ui.theme.ThemePreferenceManager.themeFlow.collectAsState()
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "appearance",
|
||||
fontSize = 12.sp,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = colorScheme.onSurface.copy(alpha = 0.8f)
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
FilterChip(
|
||||
selected = themePref == com.bitchat.android.ui.theme.ThemePreference.System,
|
||||
onClick = { com.bitchat.android.ui.theme.ThemePreferenceManager.set(context, com.bitchat.android.ui.theme.ThemePreference.System) },
|
||||
label = { Text("system", fontFamily = FontFamily.Monospace) }
|
||||
)
|
||||
FilterChip(
|
||||
selected = themePref == com.bitchat.android.ui.theme.ThemePreference.Light,
|
||||
onClick = { com.bitchat.android.ui.theme.ThemePreferenceManager.set(context, com.bitchat.android.ui.theme.ThemePreference.Light) },
|
||||
label = { Text("light", fontFamily = FontFamily.Monospace) }
|
||||
)
|
||||
FilterChip(
|
||||
selected = themePref == com.bitchat.android.ui.theme.ThemePreference.Dark,
|
||||
onClick = { com.bitchat.android.ui.theme.ThemePreferenceManager.set(context, com.bitchat.android.ui.theme.ThemePreference.Dark) },
|
||||
label = { Text("dark", fontFamily = FontFamily.Monospace) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emergency warning
|
||||
item {
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.bitchat.android.R
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import com.bitchat.android.ui.theme.BASE_FONT_SIZE
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
|
||||
/**
|
||||
* Input components for ChatScreen
|
||||
|
||||
@@ -5,6 +5,8 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
// Colors that match the iOS bitchat theme
|
||||
@@ -36,14 +38,23 @@ private val LightColorScheme = lightColorScheme(
|
||||
|
||||
@Composable
|
||||
fun BitchatTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
darkTheme: Boolean? = null,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
// App-level override from ThemePreferenceManager
|
||||
val themePref by ThemePreferenceManager.themeFlow.collectAsState(initial = ThemePreference.System)
|
||||
val shouldUseDark = when (darkTheme) {
|
||||
true -> true
|
||||
false -> false
|
||||
null -> when (themePref) {
|
||||
ThemePreference.Dark -> true
|
||||
ThemePreference.Light -> false
|
||||
ThemePreference.System -> isSystemInDarkTheme()
|
||||
}
|
||||
}
|
||||
|
||||
val colorScheme = if (shouldUseDark) DarkColorScheme else LightColorScheme
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.bitchat.android.ui.theme
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* App theme preference: System default, Light, or Dark.
|
||||
*/
|
||||
enum class ThemePreference {
|
||||
System, Light, Dark
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple SharedPreferences-backed manager for theme preference with a StateFlow.
|
||||
* Avoids adding DataStore dependency for now.
|
||||
*/
|
||||
object ThemePreferenceManager {
|
||||
private const val PREFS_NAME = "bitchat_settings"
|
||||
private const val KEY_THEME = "theme_preference"
|
||||
|
||||
private val _themeFlow = MutableStateFlow(ThemePreference.System)
|
||||
val themeFlow: StateFlow<ThemePreference> = _themeFlow
|
||||
|
||||
fun init(context: Context) {
|
||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val saved = prefs.getString(KEY_THEME, ThemePreference.System.name)
|
||||
_themeFlow.value = runCatching { ThemePreference.valueOf(saved!!) }.getOrDefault(ThemePreference.System)
|
||||
}
|
||||
|
||||
fun set(context: Context, preference: ThemePreference) {
|
||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit().putString(KEY_THEME, preference.name).apply()
|
||||
_themeFlow.value = preference
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user