Skip to content

Commit cf0cc8c

Browse files
authored
feat: gluestack v2 (#129)
* feat: gluestack v2 ui wip * fix: tests pass things work * fix: secure input layout * fix: respect system theme * fix: properly respect app theme * feat: standard ui in gluestack * fix: bug bot bug Redundant theme loading on system color scheme change The ThemeContext has two useEffect hooks that both trigger loadTheme() when the system color scheme changes. The first effect at line 38-41 runs when loadTheme changes (which happens when systemColorScheme changes due to the useCallback dependency). The third effect at line 55-58 also runs when systemColorScheme changes. Since loadTheme depends on systemColorScheme in its useCallback dependency array, whenever the system theme changes, both effects run causing loadTheme() to be called twice. While functionally correct, this results in unnecessary duplicate async calls to the native ThemeModule.getColorScheme(). * chore: code cleanup * test: fix break * fix: bug bot * fix: cruft cleanup * fix: alert icon * fix: un wanted plugin drop * fix: turns out we only need reanimated it pulls in worklets * ci: ccache react-native-worklets * fix: action button * fix: keep powersync specific stuff in metro.config * fix: action buttons again * test: fix
1 parent 7c21aca commit cf0cc8c

49 files changed

Lines changed: 5026 additions & 812 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

android/app/src/main/java/com/github/quarck/calnotify/GlobalState.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import com.facebook.react.defaults.DefaultReactNativeHost
3333
import com.facebook.soloader.SoLoader
3434
import com.facebook.react.soloader.OpenSourceMergedSoMapping
3535
import com.github.quarck.calnotify.notification.NotificationChannels
36+
import com.github.quarck.calnotify.react.ThemePackage
3637

3738
// This storage is wiped every time app is restarted. Only keep variables
3839
// that are instance-specific here
@@ -43,7 +44,7 @@ class GlobalState : Application(), ReactApplication {
4344
override val reactNativeHost: ReactNativeHost =
4445
object : DefaultReactNativeHost(this) {
4546
override fun getPackages(): List<ReactPackage> =
46-
PackageList(this@GlobalState).packages
47+
PackageList(this@GlobalState).packages + listOf(ThemePackage())
4748

4849
override fun getJSMainModuleName(): String = "index"
4950

@@ -67,8 +68,9 @@ class GlobalState : Application(), ReactApplication {
6768
load()
6869
}
6970
}
70-
// Always follow system theme
71-
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
71+
// Apply saved theme preference (defaults to follow system if not set)
72+
val settings = Settings(this)
73+
AppCompatDelegate.setDefaultNightMode(settings.themeMode)
7274
// Create notification channels (required for Android 8+, no-op on older versions)
7375
NotificationChannels.createChannels(this)
7476
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (C) 2025 William Harris (wharris+cnplus@upscalews.com)
2+
package com.github.quarck.calnotify.react
3+
4+
import android.content.res.Configuration
5+
import androidx.appcompat.app.AppCompatDelegate
6+
import com.facebook.react.bridge.ReactApplicationContext
7+
import com.facebook.react.bridge.ReactContextBaseJavaModule
8+
import com.facebook.react.bridge.ReactMethod
9+
import com.facebook.react.bridge.Promise
10+
import com.facebook.react.modules.core.DeviceEventManagerModule
11+
import com.github.quarck.calnotify.Settings
12+
13+
/**
14+
* Native module to expose the app's theme setting to React Native.
15+
* This allows React Native to respect the in-app theme choice (system/light/dark)
16+
* rather than just reading the system theme.
17+
*/
18+
class ThemeModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
19+
20+
override fun getName(): String = "ThemeModule"
21+
22+
/**
23+
* Get the current effective color scheme based on the app's theme setting.
24+
* Returns "light" or "dark".
25+
*/
26+
@ReactMethod
27+
fun getColorScheme(promise: Promise) {
28+
try {
29+
val context = reactApplicationContext
30+
val settings = Settings(context)
31+
val themeMode = settings.themeMode
32+
33+
val colorScheme = when (themeMode) {
34+
AppCompatDelegate.MODE_NIGHT_NO -> "light"
35+
AppCompatDelegate.MODE_NIGHT_YES -> "dark"
36+
else -> {
37+
// Follow system - check the actual current configuration
38+
val nightModeFlags = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
39+
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) "dark" else "light"
40+
}
41+
}
42+
43+
promise.resolve(colorScheme)
44+
} catch (e: Exception) {
45+
promise.reject("THEME_ERROR", e.message)
46+
}
47+
}
48+
49+
/**
50+
* Get the raw theme mode setting.
51+
* Returns: -1 = follow system, 1 = light, 2 = dark
52+
*/
53+
@ReactMethod
54+
fun getThemeMode(promise: Promise) {
55+
try {
56+
val settings = Settings(reactApplicationContext)
57+
promise.resolve(settings.themeMode)
58+
} catch (e: Exception) {
59+
promise.reject("THEME_ERROR", e.message)
60+
}
61+
}
62+
}
63+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (C) 2025 William Harris (wharris+cnplus@upscalews.com)
2+
package com.github.quarck.calnotify.react
3+
4+
import com.facebook.react.ReactPackage
5+
import com.facebook.react.bridge.NativeModule
6+
import com.facebook.react.bridge.ReactApplicationContext
7+
import com.facebook.react.uimanager.ViewManager
8+
9+
/**
10+
* React Native package that provides the ThemeModule.
11+
*/
12+
class ThemePackage : ReactPackage {
13+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
14+
return listOf(ThemeModule(reactContext))
15+
}
16+
17+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
18+
return emptyList()
19+
}
20+
}
21+

0 commit comments

Comments
 (0)