Skip to content
Merged
1 change: 1 addition & 0 deletions packages/features/src/Domain/Feature/FeatureDescription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export type ThemeFeatureDescription = ComponentFeatureDescription & {
/** Some themes can be layered on top of other themes */
layerable?: boolean
dock_icon?: ThemeDockIcon
isDark?: boolean
}

export type FeatureDescription = BaseFeatureDescription &
Expand Down
4 changes: 4 additions & 0 deletions packages/features/src/Domain/Lists/Themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function themes(): ThemeFeatureDescription[] {
permission_name: PermissionName.MidnightTheme,
description: 'Elegant utilitarianism.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/midnight-with-mobile.jpg',
isDark: true,
dock_icon: {
type: 'circle',
background_color: '#086DD6',
Expand All @@ -27,6 +28,7 @@ export function themes(): ThemeFeatureDescription[] {
permission_name: PermissionName.FuturaTheme,
description: 'Calm and relaxed. Take some time off.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/futura-with-mobile.jpg',
isDark: true,
dock_icon: {
type: 'circle',
background_color: '#fca429',
Expand All @@ -42,6 +44,7 @@ export function themes(): ThemeFeatureDescription[] {
permission_name: PermissionName.SolarizedDarkTheme,
description: 'The perfect theme for any time.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/solarized-dark.jpg',
isDark: true,
dock_icon: {
type: 'circle',
background_color: '#2AA198',
Expand Down Expand Up @@ -72,6 +75,7 @@ export function themes(): ThemeFeatureDescription[] {
permission_name: PermissionName.FocusedTheme,
description: 'For when you need to go in.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/focus-with-mobile.jpg',
isDark: true,
dock_icon: {
type: 'circle',
background_color: '#a464c2',
Expand Down
13 changes: 12 additions & 1 deletion packages/mobile/src/Lib/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
removeFromArray,
TransferPayload,
} from '@standardnotes/snjs'
import { Alert, Linking, Platform } from 'react-native'
import { Alert, Linking, Platform, StatusBar } from 'react-native'
import FingerprintScanner from 'react-native-fingerprint-scanner'
import FlagSecure from 'react-native-flag-secure-android'
import { hide, show } from 'react-native-privacy-snapshot'
Expand Down Expand Up @@ -65,6 +65,7 @@ const showLoadFailForItemIds = (failedItemIds: string[]) => {
export class MobileDevice implements MobileDeviceInterface {
environment: Environment.Mobile = Environment.Mobile
private eventObservers: MobileDeviceEventHandler[] = []
public isDarkMode = false

constructor(private stateObserverService?: AppStateObserverService) {}

Expand Down Expand Up @@ -432,6 +433,16 @@ export class MobileDevice implements MobileDeviceInterface {
}
}

handleThemeSchemeChange(isDark: boolean): void {
this.isDarkMode = isDark

this.reloadStatusBarStyle()
}

reloadStatusBarStyle(animated = true) {
StatusBar.setBarStyle(this.isDarkMode ? 'light-content' : 'dark-content', animated)
}

private notifyEvent(event: MobileDeviceEvent): void {
for (const handler of this.eventObservers) {
handler(event)
Expand Down
13 changes: 12 additions & 1 deletion packages/mobile/src/MobileWebAppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MobileDevice, MobileDeviceEvent } from '@Lib/Interface'
import { IsDev } from '@Lib/Utils'
import { ReactNativeToWebEvent } from '@standardnotes/snjs'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Platform } from 'react-native'
import { Keyboard, Platform } from 'react-native'
import { WebView, WebViewMessageEvent } from 'react-native-webview'
import { AppStateObserverService } from './AppStateObserverService'

Expand All @@ -29,8 +29,18 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
webViewRef.current?.postMessage(JSON.stringify({ reactNativeEvent: event, messageType: 'event' }))
})

const keyboardShowListener = Keyboard.addListener('keyboardWillShow', () => {
device.reloadStatusBarStyle(false)
})

const keyboardHideListener = Keyboard.addListener('keyboardDidHide', () => {
device.reloadStatusBarStyle(false)
})

return () => {
removeListener()
keyboardShowListener.remove()
keyboardHideListener.remove()
}
}, [webViewRef, stateService])

Expand Down Expand Up @@ -183,6 +193,7 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
allowFileAccess={true}
allowUniversalAccessFromFileURLs={true}
injectedJavaScriptBeforeContentLoaded={injectedJS}
bounces={false}
/>
)
/* eslint-enable @typescript-eslint/no-empty-function */
Expand Down
3 changes: 2 additions & 1 deletion packages/models/src/Domain/Syncable/Component/PackageInfo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { FeatureDescription } from '@standardnotes/features'
import { FeatureDescription, ThemeFeatureDescription } from '@standardnotes/features'

type ThirdPartyPackageInfo = {
version: string
download_url?: string
}

export type ComponentPackageInfo = FeatureDescription & Partial<ThirdPartyPackageInfo>
export type ThemePackageInfo = FeatureDescription & Partial<ThirdPartyPackageInfo> & ThemeFeatureDescription
2 changes: 2 additions & 0 deletions packages/models/src/Domain/Syncable/Theme/Theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { HistoryEntryInterface } from '../../Runtime/History'
import { DecryptedItemInterface, ItemInterface } from '../../Abstract/Item'
import { ContentType } from '@standardnotes/common'
import { useBoolean } from '@standardnotes/utils'
import { ThemePackageInfo } from '../Component/PackageInfo'

export const isTheme = (x: ItemInterface): x is SNTheme => x.content_type === ContentType.Theme

export class SNTheme extends SNComponent {
public override area: ComponentArea = ComponentArea.Themes
public override readonly package_info!: ThemePackageInfo

isLayerable(): boolean {
return useBoolean(this.package_info && this.package_info.layerable, false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MobileDeviceInterface } from './../Device/MobileDeviceInterface'
import { DesktopManagerInterface } from '../Device/DesktopManagerInterface'
import { WebAppEvent } from '../Event/WebAppEvent'
import { ApplicationInterface } from './ApplicationInterface'
Expand All @@ -9,4 +10,6 @@ export interface WebApplicationInterface extends ApplicationInterface {
handleMobileGainingFocusEvent(): Promise<void>
handleMobileLosingFocusEvent(): Promise<void>
handleMobileResumingFromBackgroundEvent(): Promise<void>
isNativeMobileWeb(): boolean
get mobileDevice(): MobileDeviceInterface
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export interface MobileDeviceInterface extends DeviceInterface {
hideMobileInterfaceFromScreenshots(): void
stopHidingMobileInterfaceFromScreenshots(): void
consoleLog(...args: any[]): void
handleThemeSchemeChange(isDark: boolean): void
}
3 changes: 2 additions & 1 deletion packages/ui-services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@typescript-eslint/parser": "^5.12.1",
"eslint-plugin-prettier": "*",
"jest": "^28.1.2",
"ts-jest": "^28.0.5"
"ts-jest": "^28.0.5",
"typescript": "*"
}
}
15 changes: 14 additions & 1 deletion packages/ui-services/src/Theme/ThemeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,14 @@ export class ThemeManager extends AbstractService {
this.deactivateTheme(theme.uuid)
}
}

if (source !== PayloadEmitSource.LocalRetrieved) {
this.cacheThemeState().catch(console.error)
}
})
}

public deactivateAllThemes() {
private deactivateAllThemes() {
const activeThemes = this.activeThemes.slice()

for (const uuid of activeThemes) {
Expand Down Expand Up @@ -289,6 +290,10 @@ export class ThemeManager extends AbstractService {
link.id = theme.uuid
link.onload = this.syncThemeColorMetadata
document.getElementsByTagName('head')[0].appendChild(link)

if (this.application.isNativeMobileWeb()) {
this.application.mobileDevice.handleThemeSchemeChange(theme.package_info.isDark ?? false)
}
}

/**
Expand All @@ -306,13 +311,21 @@ export class ThemeManager extends AbstractService {
}

private deactivateTheme(uuid: string) {
if (!this.activeThemes.includes(uuid)) {
return
}

const element = document.getElementById(uuid) as HTMLLinkElement
if (element) {
element.disabled = true
element.parentNode?.removeChild(element)
}

removeFromArray(this.activeThemes, uuid)

if (this.activeThemes.length === 0) {
this.application.mobileDevice.handleThemeSchemeChange(false)
}
}

private async cacheThemeState() {
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7650,6 +7650,7 @@ __metadata:
eslint-plugin-prettier: "*"
jest: ^28.1.2
ts-jest: ^28.0.5
typescript: "*"
languageName: unknown
linkType: soft

Expand Down