Skip to content

Commit

Permalink
Implemented dark mode setting (#96)
Browse files Browse the repository at this point in the history
* Migrated to Recoil state management library

* Upgraded to Capacitor 4

* Store & retrieve animation settings from preferences

* Store & retrieve background settings from preferences

* Migrated to @capacitor/preferences (from cordova preferences)

* Implemented dark mode setting

* Fade when changing app theme
  • Loading branch information
matthew-merkas authored Aug 17, 2022
1 parent 4d40b83 commit 9385d16
Show file tree
Hide file tree
Showing 16 changed files with 191 additions and 46 deletions.
1 change: 1 addition & 0 deletions android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation project(':capacitor-filesystem')
implementation project(':capacitor-haptics')
implementation project(':capacitor-keyboard')
implementation project(':capacitor-preferences')
implementation project(':capacitor-share')
implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar')
Expand Down
4 changes: 4 additions & 0 deletions android/app/src/main/assets/capacitor.plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"pkg": "@capacitor/keyboard",
"classpath": "com.capacitorjs.plugins.keyboard.KeyboardPlugin"
},
{
"pkg": "@capacitor/preferences",
"classpath": "com.capacitorjs.plugins.preferences.PreferencesPlugin"
},
{
"pkg": "@capacitor/share",
"classpath": "com.capacitorjs.plugins.share.SharePlugin"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ private void updateDarkMode(Configuration configuration) {
// setStyle("DARK");
break;
}
// We want status & navigation bar to remain dark (because it looks better)
setBackgroundColor();
setStyle("DARK");
}

// Update webview theme
Expand Down
2 changes: 2 additions & 0 deletions android/app/src/main/res/values-v30/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
<item name="android:statusBarColor">@color/navigationBarColor</item>
<item name="android:navigationBarColor">@color/navigationBarColor</item>
</style>

<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
Expand Down
2 changes: 2 additions & 0 deletions android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
<item name="android:statusBarColor">@color/navigationBarColor</item>
<item name="android:navigationBarColor">@color/navigationBarColor</item>
</style>

<style name="AppTheme.NoActionBarLaunch" parent="AppTheme.NoActionBar">
Expand Down
4 changes: 0 additions & 4 deletions android/app/src/main/res/xml/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,5 @@
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<access origin="*" />

<feature name="AppPreferences">
<param name="android-package" value="me.apla.cordova.AppPreferences"/>
</feature>


</widget>
3 changes: 3 additions & 0 deletions android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/
include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')

include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')

include ':capacitor-share'
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android')

Expand Down
1 change: 1 addition & 0 deletions ios/App/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def capacitor_pods
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
Expand Down
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import '@ionic/react/css/flex-utils.css'
import '@ionic/react/css/display.css'

/* Theme variables */
import './theme/variables.css'
import './theme/variables.scss'
import { RecoilRoot } from 'recoil'

// Singleton SQLite Hook
Expand Down
39 changes: 29 additions & 10 deletions src/components/AgeSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,27 @@ import {
playForwardOutline,
timeOutline,
} from 'ionicons/icons'
import React, { useState } from 'react'
import React, { useEffect } from 'react'
import { setNumber } from '../functions/input'
import { AnimationService } from '../functions/animation'
import { LIMIT_LOWER, LIMIT_UPPER } from '../functions/atoms'
import {
appDarkMode,
isAgeSliderShown,
LIMIT_LOWER,
LIMIT_UPPER,
} from '../functions/atoms'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import {
age,
animatePlaying,
isSettingsMenuShow,
settingsPath,
} from '../functions/atoms'
import {
matchDarkMode,
setStatusBarTheme,
statusBarListener,
} from '../functions/darkMode'

interface AgeSliderProps {
buttons: any
Expand All @@ -36,19 +46,30 @@ interface AgeSliderProps {

const AgeSlider: React.FC<AgeSliderProps> = ({ buttons, animationService }) => {
const [_age, setAge] = useRecoilState(age)
const darkMode = useRecoilValue(appDarkMode)
const playing = useRecoilValue(animatePlaying)
const setMenuPath = useSetRecoilState(settingsPath)
const setMenuState = useSetRecoilState(isSettingsMenuShow)
const [hidden, setHidden] = useState(true)
const [shown, setShown] = useRecoilState(isAgeSliderShown)

const openMenu = () => {
setMenuPath('animation')
setMenuState(true)
}

useEffect(() => {
if (shown) {
setStatusBarTheme(darkMode)
} else {
setStatusBarTheme('dark')
// Remove listener so status bar stays dark (Android) or light (iOS)
matchDarkMode.removeEventListener('change', statusBarListener)
}
}, [shown])

return (
<div>
<div className={hidden ? 'container hidden' : 'container'}>
<div className={shown ? 'container' : 'container hidden'}>
<IonItem className="time-input" lines="none">
<IonLabel>Time:</IonLabel>
<IonInput
Expand Down Expand Up @@ -115,14 +136,12 @@ const AgeSlider: React.FC<AgeSliderProps> = ({ buttons, animationService }) => {
/>
</IonItem>
</div>
<div
className={hidden ? 'buttons container hidden' : 'buttons container'}
>
<div className={shown ? 'buttons container' : 'buttons container hidden'}>
<div
className="time"
id={'timeStamp'} // screenshot need time information, using id to locate element
onClick={() => {
setHidden(!hidden)
setShown(!shown)
}}
>
{_age} Ma
Expand All @@ -132,11 +151,11 @@ const AgeSlider: React.FC<AgeSliderProps> = ({ buttons, animationService }) => {
<IonButton
className="round-button show-button"
onClick={() => {
setHidden(!hidden)
setShown(!shown)
}}
size="default"
>
<IonIcon icon={hidden ? timeOutline : chevronUpCircleOutline} />
<IonIcon icon={shown ? chevronUpCircleOutline : timeOutline} />
</IonButton>
</div>
</div>
Expand Down
6 changes: 6 additions & 0 deletions src/functions/atoms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ export const backgroundIsCustom = atom({
default: false,
})

export const appDarkMode = atom({ key: 'appDarkMode', default: 'auto' })

export const isAboutPageShow = atom({ key: 'isAboutPageShow', default: false })
export const isAgeSliderShown = atom({
key: 'isAgeSliderShown',
default: false,
})
export const isRasterMenuShow = atom({
key: 'isRasterMenuShow',
default: false,
Expand Down
40 changes: 40 additions & 0 deletions src/functions/darkMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { StatusBar, Style } from '@capacitor/status-bar'
import { Capacitor } from '@capacitor/core'

export const matchDarkMode = window.matchMedia('(prefers-color-scheme: dark)')
const darkModeListener = () => {
setDarkMode.apply(matchDarkMode.matches)
}

export const setDarkMode = (darkMode = 'auto') => {
const apply = (isDark: boolean) => {
document.body.classList.toggle('dark', isDark)
}

if (darkMode === 'auto') {
apply(matchDarkMode.matches)
matchDarkMode.addEventListener('change', darkModeListener)
} else {
apply(darkMode === 'dark')
}
}

export const statusBarListener = () => {
setStatusBarTheme.apply(matchDarkMode.matches)
}

export const setStatusBarTheme = (darkMode = 'auto') => {
const apply = (isDark: boolean) => {
StatusBar.setBackgroundColor({ color: isDark ? '#000000' : '#FFFFFF' })
StatusBar.setStyle({ style: isDark ? Style.Dark : Style.Light })
}

if (Capacitor.getPlatform() !== 'web') {
if (darkMode === 'auto') {
apply(matchDarkMode.matches)
matchDarkMode.addEventListener('change', statusBarListener)
} else {
apply(darkMode === 'dark')
}
}
}
12 changes: 12 additions & 0 deletions src/pages/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ import {
backgroundIsEnabled,
backgroundIsCustom,
backgroundColor,
appDarkMode,
} from '../functions/atoms'
import { initCesiumViewer } from '../functions/cesiumViewer'
import { gplates_coastlines } from '../functions/DataLoader'
import rasterMaps, { loadRasterMaps } from '../functions/rasterMaps'
import { BackgroundService } from '../functions/background'
import { Preferences } from '@capacitor/preferences'
import { setDarkMode } from '../functions/darkMode'

Ion.defaultAccessToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlMGFjYTVjNC04OTJjLTQ0Y2EtYTExOS1mYzAzOWFmYmM1OWQiLCJpZCI6MjA4OTksInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1Nzg1MzEyNjF9.KyUbfBd_2aCHlvBlrBgdM3c3uDEfYyKoEmWzAHSGSsk'
Expand All @@ -88,6 +90,7 @@ const Main: React.FC = () => {

// Animation
const setAge = useSetRecoilState(age)
const _setDarkMode = useSetRecoilState(appDarkMode)
const [exact, setExact] = useRecoilState(animateExact)
const [fps, setFps] = useRecoilState(animateFps)
const [increment, setIncrement] = useRecoilState(animateIncrement)
Expand Down Expand Up @@ -160,6 +163,15 @@ const Main: React.FC = () => {
setRange(settings.range)
}
})
Preferences.get({ key: 'appSettings' }).then((res) => {
if (res?.value) {
const settings = JSON.parse(res.value)
_setDarkMode(settings.darkMode)
setDarkMode(settings.darkMode)
} else {
setDarkMode()
}
})
Preferences.get({ key: 'backgroundSettings' }).then((res) => {
if (res?.value) {
const settings = JSON.parse(res.value)
Expand Down
14 changes: 14 additions & 0 deletions src/pages/SettingMenuPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ ion-checkbox.single-setting-option {
margin-right: 0.75rem;
}

ion-list.md {
padding-top: 0;
margin-top: 8px;
}

ion-toolbar {
--background: none;
}

ion-modal::part(content), ion-list, ion-item-divider, ion-item::part(native) {
transition: background 150ms;
}

.fade-enter {
opacity: 0;
}
Expand Down Expand Up @@ -67,6 +80,7 @@ ion-checkbox.single-setting-option {
text-transform: revert;
}
.description {
color: var(--ion-text-color);
padding: 8px;
}
align-items: center;
Expand Down
Loading

0 comments on commit 9385d16

Please sign in to comment.