Compare commits

...
42 changed files with 470 additions and 171 deletions
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.23.260](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.259](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.258](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.257](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.256](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.23.256",
"version": "3.23.260",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.45.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.45.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.45.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.45.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
### Bug Fixes
* color scheme handling on mobile ([#1902](https://github.com/standardnotes/app/issues/1902)) ([6071ebf](https://github.com/standardnotes/app/commit/6071ebffeb55c78eac79f39aecca07f6ebdbed93))
## [3.45.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.45.3",
"version": "3.45.7",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
@@ -0,0 +1,19 @@
import { AbstractService, InternalEventBus, ReactNativeToWebEvent } from '@standardnotes/snjs'
import { Appearance, NativeEventSubscription } from 'react-native'
export class ColorSchemeObserverService extends AbstractService<ReactNativeToWebEvent> {
private removeListener: NativeEventSubscription
constructor() {
const internalEventBus = new InternalEventBus()
super(internalEventBus)
this.removeListener = Appearance.addChangeListener(() => {
void this.notifyEvent(ReactNativeToWebEvent.ColorSchemeChanged)
})
}
deinit() {
this.removeListener.remove()
}
}
+19 -1
View File
@@ -13,7 +13,18 @@ import {
TransferPayload,
UuidString,
} from '@standardnotes/snjs'
import { Alert, AppState, AppStateStatus, Linking, PermissionsAndroid, Platform, StatusBar } from 'react-native'
import { ColorSchemeObserverService } from 'ColorSchemeObserverService'
import {
Alert,
Appearance,
AppState,
AppStateStatus,
ColorSchemeName,
Linking,
PermissionsAndroid,
Platform,
StatusBar,
} from 'react-native'
import FileViewer from 'react-native-file-viewer'
import FingerprintScanner from 'react-native-fingerprint-scanner'
import FlagSecure from 'react-native-flag-secure-android'
@@ -85,6 +96,7 @@ export class MobileDevice implements MobileDeviceInterface {
constructor(
private stateObserverService?: AppStateObserverService,
private androidBackHandlerService?: AndroidBackHandlerService,
private colorSchemeService?: ColorSchemeObserverService,
) {}
deinit() {
@@ -92,6 +104,8 @@ export class MobileDevice implements MobileDeviceInterface {
;(this.stateObserverService as unknown) = undefined
this.androidBackHandlerService?.deinit()
;(this.androidBackHandlerService as unknown) = undefined
this.colorSchemeService?.deinit()
;(this.colorSchemeService as unknown) = undefined
}
consoleLog(...args: any[]): void {
@@ -616,4 +630,8 @@ export class MobileDevice implements MobileDeviceInterface {
async getAppState(): Promise<AppStateStatus> {
return AppState.currentState
}
async getColorScheme(): Promise<ColorSchemeName> {
return Appearance.getColorScheme()
}
}
+10 -3
View File
@@ -1,4 +1,5 @@
import { ReactNativeToWebEvent } from '@standardnotes/snjs'
import { ColorSchemeObserverService } from './ColorSchemeObserverService'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Keyboard, Platform } from 'react-native'
import VersionInfo from 'react-native-version-info'
@@ -28,9 +29,10 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
const sourceUri = (Platform.OS === 'android' ? 'file:///android_asset/' : '') + 'Web.bundle/src/index.html'
const stateService = useMemo(() => new AppStateObserverService(), [])
const androidBackHandlerService = useMemo(() => new AndroidBackHandlerService(), [])
const colorSchemeService = useMemo(() => new ColorSchemeObserverService(), [])
const device = useMemo(
() => new MobileDevice(stateService, androidBackHandlerService),
[androidBackHandlerService, stateService],
() => new MobileDevice(stateService, androidBackHandlerService, colorSchemeService),
[androidBackHandlerService, colorSchemeService, stateService],
)
useEffect(() => {
@@ -44,6 +46,10 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
},
)
const removeColorSchemeServiceListener = colorSchemeService.addEventObserver((event: ReactNativeToWebEvent) => {
webViewRef.current?.postMessage(JSON.stringify({ reactNativeEvent: event, messageType: 'event' }))
})
const keyboardShowListener = Keyboard.addListener('keyboardWillShow', () => {
device.reloadStatusBarStyle(false)
})
@@ -55,10 +61,11 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
return () => {
removeStateServiceListener()
removeBackHandlerServiceListener()
removeColorSchemeServiceListener()
keyboardShowListener.remove()
keyboardHideListener.remove()
}
}, [webViewRef, stateService, device, androidBackHandlerService])
}, [webViewRef, stateService, device, androidBackHandlerService, colorSchemeService])
useEffect(() => {
const observer = device.addMobileWebEventReceiver((event) => {
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.189](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.188](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.187](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.186](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.185](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.3.185",
"version": "1.3.189",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
### Bug Fixes
* color scheme handling on mobile ([#1902](https://github.com/standardnotes/app/issues/1902)) ([6071ebf](https://github.com/standardnotes/app/commit/6071ebffeb55c78eac79f39aecca07f6ebdbed93))
# [1.39.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-27)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.39.0",
"version": "1.39.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -10,6 +10,7 @@ export interface WebApplicationInterface extends ApplicationInterface {
handleMobileGainingFocusEvent(): Promise<void>
handleMobileLosingFocusEvent(): Promise<void>
handleMobileResumingFromBackgroundEvent(): Promise<void>
handleMobileColorSchemeChangeEvent(): void
isNativeMobileWeb(): boolean
mobileDevice(): MobileDeviceInterface
handleAndroidBackButtonPressed(): void
@@ -21,4 +21,5 @@ export interface MobileDeviceInterface extends DeviceInterface {
removeComponentUrl(componentUuid: string): void
isUrlComponentUrl(url: string): boolean
getAppState(): Promise<'active' | 'background' | 'inactive' | 'unknown' | 'extension'>
getColorScheme(): Promise<'light' | 'dark' | null | undefined>
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.145.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
### Bug Fixes
* **mobile:** increase save debounce timeout ([35e2fa9](https://github.com/standardnotes/app/commit/35e2fa9eb6df44beb7717ce38209ee4944286355))
## [2.145.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
### Bug Fixes
* color scheme handling on mobile ([#1902](https://github.com/standardnotes/app/issues/1902)) ([6071ebf](https://github.com/standardnotes/app/commit/6071ebffeb55c78eac79f39aecca07f6ebdbed93))
# [2.145.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-27)
### Features
+22 -14
View File
@@ -1,3 +1,4 @@
import { InfoStrings } from '../Strings/Info'
import { NoteType } from '@standardnotes/features'
import {
NoteMutator,
@@ -11,14 +12,6 @@ import { removeFromArray } from '@standardnotes/utils'
import { ContentType } from '@standardnotes/common'
import { UuidString } from '@Lib/Types/UuidString'
import { SNApplication } from '../Application/Application'
import {
STRING_SAVING_WHILE_DOCUMENT_HIDDEN,
STRING_INVALID_NOTE,
NOTE_PREVIEW_CHAR_LIMIT,
STRING_ELLIPSES,
SAVE_TIMEOUT_NO_DEBOUNCE,
SAVE_TIMEOUT_DEBOUNCE,
} from './Types'
import { ItemViewControllerInterface } from './ItemViewControllerInterface'
import { TemplateNoteViewControllerOptions } from './TemplateNoteViewControllerOptions'
@@ -27,6 +20,15 @@ export type EditorValues = {
text: string
}
const StringEllipses = '...'
const NotePreviewCharLimit = 160
const SaveTimeoutDebounc = {
Desktop: 350,
ImmediateChange: 100,
NativeMobileWeb: 700,
}
export class NoteViewController implements ItemViewControllerInterface {
public item!: SNNote
public dealloced = false
@@ -182,7 +184,7 @@ export class NoteViewController implements ItemViewControllerInterface {
const isTemplate = this.isTemplateNote
if (typeof document !== 'undefined' && document.hidden) {
void this.application.alertService.alert(STRING_SAVING_WHILE_DOCUMENT_HIDDEN)
void this.application.alertService.alert(InfoStrings.SavingWhileDocumentHidden)
return
}
@@ -191,7 +193,7 @@ export class NoteViewController implements ItemViewControllerInterface {
}
if (!this.application.items.findItem(this.item.uuid)) {
void this.application.alertService.alert(STRING_INVALID_NOTE)
void this.application.alertService.alert(InfoStrings.InvalidNote)
return
}
@@ -207,9 +209,9 @@ export class NoteViewController implements ItemViewControllerInterface {
if (!dto.dontUpdatePreviews) {
const noteText = text || ''
const truncate = noteText.length > NOTE_PREVIEW_CHAR_LIMIT
const substring = noteText.substring(0, NOTE_PREVIEW_CHAR_LIMIT)
const previewPlain = substring + (truncate ? STRING_ELLIPSES : '')
const truncate = noteText.length > NotePreviewCharLimit
const substring = noteText.substring(0, NotePreviewCharLimit)
const previewPlain = substring + (truncate ? StringEllipses : '')
// eslint-disable-next-line camelcase
noteMutator.preview_plain = previewPlain
@@ -225,7 +227,13 @@ export class NoteViewController implements ItemViewControllerInterface {
}
const noDebounce = dto.bypassDebouncer || this.application.noAccount()
const syncDebouceMs = noDebounce ? SAVE_TIMEOUT_NO_DEBOUNCE : SAVE_TIMEOUT_DEBOUNCE
const syncDebouceMs = noDebounce
? SaveTimeoutDebounc.ImmediateChange
: this.application.isNativeMobileWeb()
? SaveTimeoutDebounc.NativeMobileWeb
: SaveTimeoutDebounc.Desktop
this.saveTimeout = setTimeout(() => {
void this.application.sync.sync()
}, syncDebouceMs)
@@ -4,4 +4,5 @@ export enum ReactNativeToWebEvent {
GainingFocus = 'GainingFocus',
LosingFocus = 'LosingFocus',
AndroidBackButtonPressed = 'AndroidBackButtonPressed',
ColorSchemeChanged = 'ColorSchemeChanged',
}
-8
View File
@@ -1,8 +0,0 @@
export const STRING_SAVING_WHILE_DOCUMENT_HIDDEN =
'Attempting to save an item while the application is hidden. To protect data integrity, please refresh the application window and try again.'
export const STRING_INVALID_NOTE =
"The note you are attempting to save can not be found or has been deleted. Changes you make will not be synced. Please copy this note's text and start a new note."
export const STRING_ELLIPSES = '...'
export const NOTE_PREVIEW_CHAR_LIMIT = 160
export const SAVE_TIMEOUT_DEBOUNCE = 350
export const SAVE_TIMEOUT_NO_DEBOUNCE = 100
+4
View File
@@ -4,4 +4,8 @@ export const InfoStrings = {
'This backup file was created using a newer version of the application and cannot be imported here. Please update your application and try again.',
BackupFileMoreRecentThanAccount:
"This backup file was created using a newer encryption version than your account's. Please run the available encryption upgrade and try again.",
SavingWhileDocumentHidden:
'Attempting to save an item while the application is hidden. To protect data integrity, please refresh the application window and try again.',
InvalidNote:
"The note you are attempting to save can not be found or has been deleted. Changes you make will not be synced. Please copy this note's text and start a new note.",
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.145.0",
"version": "2.145.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
### Bug Fixes
* color scheme handling on mobile ([#1902](https://github.com/standardnotes/app/issues/1902)) ([6071ebf](https://github.com/standardnotes/app/commit/6071ebffeb55c78eac79f39aecca07f6ebdbed93))
## [1.10.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-27)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.10.5",
"version": "1.10.6",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+20 -5
View File
@@ -59,11 +59,13 @@ export class ThemeManager extends AbstractService {
break
}
case ApplicationEvent.Launched: {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', this.colorSchemeEventHandler)
if (!this.application.isNativeMobileWeb()) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', this.colorSchemeEventHandler)
}
break
}
case ApplicationEvent.PreferencesChanged: {
this.handlePreferencesChangeEvent()
void this.handlePreferencesChangeEvent()
break
}
}
@@ -88,7 +90,16 @@ export class ThemeManager extends AbstractService {
})
}
private handlePreferencesChangeEvent(): void {
async handleMobileColorSchemeChangeEvent() {
const useDeviceThemeSettings = this.application.getPreference(PrefKey.UseSystemColorScheme, false)
if (useDeviceThemeSettings) {
const prefersDarkColorScheme = (await this.application.mobileDevice().getColorScheme()) === 'dark'
this.setThemeAsPerColorScheme(prefersDarkColorScheme)
}
}
private async handlePreferencesChangeEvent() {
const useDeviceThemeSettings = this.application.getPreference(PrefKey.UseSystemColorScheme, false)
const hasPreferenceChanged = useDeviceThemeSettings !== this.lastUseDeviceThemeSettings
@@ -98,9 +109,13 @@ export class ThemeManager extends AbstractService {
}
if (hasPreferenceChanged && useDeviceThemeSettings) {
const prefersDarkColorScheme = window.matchMedia('(prefers-color-scheme: dark)')
let prefersDarkColorScheme = window.matchMedia('(prefers-color-scheme: dark)').matches
this.setThemeAsPerColorScheme(prefersDarkColorScheme.matches)
if (this.application.isNativeMobileWeb()) {
prefersDarkColorScheme = (await this.application.mobileDevice().getColorScheme()) === 'dark'
}
this.setThemeAsPerColorScheme(prefersDarkColorScheme)
}
}
+22
View File
@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.90.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
**Note:** Version bump only for package @standardnotes/web
## [3.90.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-30)
### Performance Improvements
* avoid uneccessary notes list item rerenders ([#1904](https://github.com/standardnotes/app/issues/1904)) ([89927a3](https://github.com/standardnotes/app/commit/89927a37908fc9e84b981f1cba7c9abd37039c8b))
## [3.90.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-29)
### Bug Fixes
* **mobile:** note view hide bar options on editor focus ([#1903](https://github.com/standardnotes/app/issues/1903)) ([88017ce](https://github.com/standardnotes/app/commit/88017ce3d9c84f0383e147179d88c0fcb6538331))
## [3.90.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
### Bug Fixes
* color scheme handling on mobile ([#1902](https://github.com/standardnotes/app/issues/1902)) ([6071ebf](https://github.com/standardnotes/app/commit/6071ebffeb55c78eac79f39aecca07f6ebdbed93))
## [3.90.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-28)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.90.2",
"version": "3.90.6",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -289,6 +289,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
setViewportHeightWithFallback()
}
handleMobileColorSchemeChangeEvent() {
void this.getThemeService().handleMobileColorSchemeChangeEvent()
}
private async lockApplicationAfterMobileEventIfApplicable(): Promise<void> {
const isLocked = await this.isLocked()
if (isLocked) {
@@ -7,7 +7,7 @@ import { Component } from 'react'
export type PureComponentState = Partial<Record<string, any>>
export type PureComponentProps = Partial<Record<string, any>>
export abstract class PureComponent<P = PureComponentProps, S = PureComponentState> extends Component<P, S> {
export abstract class AbstractComponent<P = PureComponentProps, S = PureComponentState> extends Component<P, S> {
private unsubApp!: () => void
private reactionDisposers: IReactionDisposer[] = []
@@ -4,7 +4,7 @@ import { ApplicationEvent, Challenge, removeFromArray, WebAppEvent } from '@stan
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants'
import { alertDialog, RouteType } from '@standardnotes/ui-services'
import { WebApplication } from '@/Application/Application'
import Navigation from '@/Components/Navigation/Navigation'
import Navigation from '@/Components/Tags/Navigation'
import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView'
import Footer from '@/Components/Footer/Footer'
import SessionsModal from '@/Components/SessionsModal/SessionsModal'
@@ -174,28 +174,22 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
}, [needsUnlock, launched])
const renderChallenges = useCallback(() => {
return (
<AndroidBackHandlerProvider application={application}>
{challenges.map((challenge) => {
return (
<div className="sk-modal" key={`${challenge.id}${application.ephemeralIdentifier}`}>
<ChallengeModal
key={`${challenge.id}${application.ephemeralIdentifier}`}
application={application}
viewControllerManager={viewControllerManager}
mainApplicationGroup={mainApplicationGroup}
challenge={challenge}
onDismiss={removeChallenge}
/>
</div>
)
})}
</AndroidBackHandlerProvider>
)
return challenges.map((challenge) => (
<div className="sk-modal" key={`${challenge.id}${application.ephemeralIdentifier}`}>
<ChallengeModal
key={`${challenge.id}${application.ephemeralIdentifier}`}
application={application}
viewControllerManager={viewControllerManager}
mainApplicationGroup={mainApplicationGroup}
challenge={challenge}
onDismiss={removeChallenge}
/>
</div>
))
}, [viewControllerManager, challenges, mainApplicationGroup, removeChallenge, application])
if (!renderAppContents) {
return renderChallenges()
return <AndroidBackHandlerProvider application={application}>{renderChallenges()}</AndroidBackHandlerProvider>
}
return (
@@ -40,6 +40,7 @@ const ContentList: FunctionComponent<Props> = ({
const { selectPreviousItem, selectNextItem } = selectionController
const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = itemListController.webDisplayOptions
const { sortBy } = itemListController.displayOptions
const selectedTag = navigationController.selected
const onScroll: UIEventHandler = useCallback(
(e) => {
@@ -72,25 +73,27 @@ const ContentList: FunctionComponent<Props> = ({
[selectionController],
)
const getTagsForItem = (item: ListableContentItem) => {
if (hideTags) {
return []
}
const getTagsForItem = useCallback(
(item: ListableContentItem) => {
if (hideTags) {
return []
}
const selectedTag = navigationController.selected
if (!selectedTag) {
return []
}
if (!selectedTag) {
return []
}
const tags = application.getItemTags(item)
const tags = application.getItemTags(item)
const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1
if (isNavigatingOnlyTag) {
return []
}
const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1
if (isNavigatingOnlyTag) {
return []
}
return tags
}
return tags
},
[hideTags, selectedTag, application],
)
return (
<div
@@ -1,8 +1,8 @@
import { ContentType } from '@standardnotes/snjs'
import { FunctionComponent } from 'react'
import React, { FunctionComponent } from 'react'
import FileListItem from './FileListItem'
import NoteListItem from './NoteListItem'
import { AbstractListItemProps } from './Types/AbstractListItemProps'
import { AbstractListItemProps, doListItemPropsMeritRerender } from './Types/AbstractListItemProps'
const ContentListItem: FunctionComponent<AbstractListItemProps> = (props) => {
switch (props.item.content_type) {
@@ -15,4 +15,4 @@ const ContentListItem: FunctionComponent<AbstractListItemProps> = (props) => {
}
}
export default ContentListItem
export default React.memo(ContentListItem, (a, b) => !doListItemPropsMeritRerender(a, b))
@@ -13,6 +13,7 @@ import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
import ListItemNotePreviewText from './ListItemNotePreviewText'
import { ListItemTitle } from './ListItemTitle'
import { log, LoggingDomain } from '@/Logging'
const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
application,
@@ -70,6 +71,8 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
useContextMenuEvent(listItemRef, openContextMenu)
log(LoggingDomain.ItemsList, 'Rendering note list item', item.title)
return (
<div
ref={listItemRef}
@@ -1,7 +1,7 @@
import { WebApplication } from '@/Application/Application'
import { FilesController } from '@/Controllers/FilesController'
import { NotesController } from '@/Controllers/NotesController'
import { SortableItem, SNTag } from '@standardnotes/snjs'
import { SortableItem, SNTag, Uuids } from '@standardnotes/snjs'
import { ListableContentItem } from './ListableContentItem'
export type AbstractListItemProps = {
@@ -18,3 +18,89 @@ export type AbstractListItemProps = {
sortBy: keyof SortableItem | undefined
tags: SNTag[]
}
export function doListItemPropsMeritRerender(previous: AbstractListItemProps, next: AbstractListItemProps): boolean {
const simpleComparison: (keyof AbstractListItemProps)[] = [
'onSelect',
'hideDate',
'hideIcon',
'hideTags',
'hidePreview',
'selected',
'sortBy',
]
for (const key of simpleComparison) {
if (previous[key] !== next[key]) {
return true
}
}
if (previous['item'] !== next['item']) {
if (doesItemChangeMeritRerender(previous['item'], next['item'])) {
return true
}
}
return doesTagsChangeMeritRerender(previous['tags'], next['tags'])
}
function doesTagsChangeMeritRerender(previous: SNTag[], next: SNTag[]): boolean {
if (previous === next) {
return false
}
if (previous.length !== next.length) {
return true
}
if (previous.length === 0 && next.length === 0) {
return false
}
if (Uuids(previous).sort().join() !== Uuids(next).sort().join()) {
return true
}
if (
previous
.map((t) => t.title)
.sort()
.join() !==
next
.map((t) => t.title)
.sort()
.join()
) {
return true
}
return false
}
function doesItemChangeMeritRerender(previous: ListableContentItem, next: ListableContentItem): boolean {
if (previous.uuid !== next.uuid) {
return true
}
const propertiesMeritingRerender: (keyof ListableContentItem)[] = [
'title',
'protected',
'updatedAtString',
'createdAtString',
'hidePreview',
'preview_html',
'preview_plain',
'archived',
'starred',
'pinned',
]
for (const key of propertiesMeritingRerender) {
if (previous[key] !== next[key]) {
return true
}
}
return false
}
@@ -1,6 +1,6 @@
import { WebApplication } from '@/Application/Application'
import { ApplicationGroup } from '@/Application/ApplicationGroup'
import { PureComponent } from '@/Components/Abstract/PureComponent'
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
import { destroyAllObjectProperties, preventRefreshing } from '@/Utils'
import { ApplicationEvent, ApplicationDescriptor, WebAppEvent } from '@standardnotes/snjs'
import {
@@ -41,7 +41,7 @@ type State = {
arbitraryStatusMessage?: string
}
class Footer extends PureComponent<Props, State> {
class Footer extends AbstractComponent<Props, State> {
public user?: unknown
private didCheckForOffline = false
private completedInitialSync = false
@@ -1,5 +1,5 @@
import { FileItem, FileViewController, NoteViewController } from '@standardnotes/snjs'
import { PureComponent } from '@/Components/Abstract/PureComponent'
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
import { WebApplication } from '@/Application/Application'
import MultipleSelectedNotes from '@/Components/MultipleSelectedNotes/MultipleSelectedNotes'
import MultipleSelectedFiles from '../MultipleSelectedFiles/MultipleSelectedFiles'
@@ -22,7 +22,7 @@ type Props = {
application: WebApplication
}
class NoteGroupView extends PureComponent<Props, State> {
class NoteGroupView extends AbstractComponent<Props, State> {
private removeChangeObserver!: () => void
constructor(props: Props) {
@@ -1,49 +1,50 @@
import { ChangeEventHandler, createRef, KeyboardEventHandler, RefObject } from 'react'
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
import ChangeEditorButton from '@/Components/ChangeEditor/ChangeEditorButton'
import ComponentView from '@/Components/ComponentView/ComponentView'
import NotesOptionsPanel from '@/Components/NotesOptions/NotesOptionsPanel'
import PanelResizer, { PanelResizeType, PanelSide } from '@/Components/PanelResizer/PanelResizer'
import PinNoteButton from '@/Components/PinNoteButton/PinNoteButton'
import ProtectedItemOverlay from '@/Components/ProtectedItemOverlay/ProtectedItemOverlay'
import { ElementIds } from '@/Constants/ElementIDs'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { StringDeleteNote, STRING_DELETE_LOCKED_ATTEMPT, STRING_DELETE_PLACEHOLDER_ATTEMPT } from '@/Constants/Strings'
import { log, LoggingDomain } from '@/Logging'
import { debounce, isDesktopApplication, isMobileScreen } from '@/Utils'
import { classNames } from '@/Utils/ConcatenateClassNames'
import {
ApplicationEvent,
isPayloadSourceRetrieved,
isPayloadSourceInternalChange,
ContentType,
SNComponent,
SNNote,
ComponentArea,
PrefKey,
ComponentViewerInterface,
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
ContentType,
EditorFontSize,
EditorLineHeight,
isPayloadSourceInternalChange,
isPayloadSourceRetrieved,
NoteType,
NoteViewController,
PayloadEmitSource,
PrefKey,
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
SNComponent,
SNNote,
WebAppEvent,
EditorLineHeight,
EditorFontSize,
NoteType,
} from '@standardnotes/snjs'
import { debounce, isDesktopApplication } from '@/Utils'
import { confirmDialog, KeyboardKey, KeyboardModifier } from '@standardnotes/ui-services'
import { ChangeEventHandler, createRef, KeyboardEventHandler, RefObject } from 'react'
import { EditorEventSource } from '../../Types/EditorEventSource'
import { confirmDialog, KeyboardModifier, KeyboardKey } from '@standardnotes/ui-services'
import { STRING_DELETE_PLACEHOLDER_ATTEMPT, STRING_DELETE_LOCKED_ATTEMPT, StringDeleteNote } from '@/Constants/Strings'
import { PureComponent } from '@/Components/Abstract/PureComponent'
import ProtectedItemOverlay from '@/Components/ProtectedItemOverlay/ProtectedItemOverlay'
import PinNoteButton from '@/Components/PinNoteButton/PinNoteButton'
import NotesOptionsPanel from '@/Components/NotesOptions/NotesOptionsPanel'
import ComponentView from '@/Components/ComponentView/ComponentView'
import PanelResizer, { PanelSide, PanelResizeType } from '@/Components/PanelResizer/PanelResizer'
import { ElementIds } from '@/Constants/ElementIDs'
import ChangeEditorButton from '@/Components/ChangeEditor/ChangeEditorButton'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
import LinkedItemsButton from '../LinkedItems/LinkedItemsButton'
import MobileItemsListButton from '../NoteGroupView/MobileItemsListButton'
import EditingDisabledBanner from './EditingDisabledBanner'
import { reloadFont } from './FontFunctions'
import NoteStatusIndicator, { NoteStatus } from './NoteStatusIndicator'
import NoteViewFileDropTarget from './NoteViewFileDropTarget'
import { NoteViewProps } from './NoteViewProps'
import {
transactionForAssociateComponentWithCurrentNote,
transactionForDisassociateComponentWithCurrentNote,
} from './TransactionFunctions'
import { reloadFont } from './FontFunctions'
import { NoteViewProps } from './NoteViewProps'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
import { classNames } from '@/Utils/ConcatenateClassNames'
import MobileItemsListButton from '../NoteGroupView/MobileItemsListButton'
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
import NoteStatusIndicator, { NoteStatus } from './NoteStatusIndicator'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import LinkedItemsButton from '../LinkedItems/LinkedItemsButton'
import NoteViewFileDropTarget from './NoteViewFileDropTarget'
const MinimumStatusDuration = 400
const TextareaDebounce = 100
@@ -74,6 +75,7 @@ type State = {
/** Setting to true then false will allow the main content textarea to be destroyed
* then re-initialized. Used when reloading spellcheck status. */
textareaUnloading: boolean
plaintextEditorFocused?: boolean
leftResizerWidth: number
leftResizerOffset: number
@@ -97,14 +99,13 @@ const PlaintextFontSizeMapping: Record<EditorFontSize, string> = {
Large: 'text-xl',
}
class NoteView extends PureComponent<NoteViewProps, State> {
class NoteView extends AbstractComponent<NoteViewProps, State> {
readonly controller!: NoteViewController
private statusTimeout?: NodeJS.Timeout
private lastEditorFocusEventSource?: EditorEventSource
onEditorComponentLoad?: () => void
private scrollPosition = 0
private removeTrashKeyObserver?: () => void
private removeTabObserver?: () => void
private removeComponentStreamObserver?: () => void
@@ -193,9 +194,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
;(this.onPanelResizeFinish as unknown) = undefined
;(this.stackComponentExpanded as unknown) = undefined
;(this.toggleStackComponent as unknown) = undefined
;(this.setScrollPosition as unknown) = undefined
;(this.resetScrollPosition as unknown) = undefined
;(this.onSystemEditorLoad as unknown) = undefined
;(this.onSystemEditorRef as unknown) = undefined
;(this.debounceReloadEditorComponent as unknown) = undefined
;(this.textAreaChangeDebounceSave as unknown) = undefined
;(this.editorContentRef as unknown) = undefined
@@ -209,6 +208,24 @@ class NoteView extends PureComponent<NoteViewProps, State> {
return this.controller.item
}
override shouldComponentUpdate(_nextProps: Readonly<NoteViewProps>, nextState: Readonly<State>): boolean {
const complexObjects: (keyof State)[] = ['availableStackComponents', 'stackComponentViewers']
for (const key of Object.keys(nextState) as (keyof State)[]) {
if (complexObjects.includes(key)) {
continue
}
const prevValue = this.state[key]
const nextValue = nextState[key]
if (prevValue !== nextValue) {
log(LoggingDomain.NoteView, 'Rendering due to state change', key, prevValue, nextValue)
return true
}
}
return false
}
override componentDidMount(): void {
super.componentDidMount()
@@ -255,6 +272,8 @@ class NoteView extends PureComponent<NoteViewProps, State> {
}
onNoteInnerChange(note: SNNote, source: PayloadEmitSource): void {
log(LoggingDomain.NoteView, 'On inner note change', PayloadEmitSource[source])
if (note.uuid !== this.note.uuid) {
throw Error('Editor received changes for non-current note')
}
@@ -433,12 +452,15 @@ class NoteView extends PureComponent<NoteViewProps, State> {
streamItems() {
this.removeComponentStreamObserver = this.application.streamItems(ContentType.Component, async ({ source }) => {
log(LoggingDomain.NoteView, 'On component stream observer', PayloadEmitSource[source])
if (isPayloadSourceInternalChange(source) || source === PayloadEmitSource.InitialObserverRegistrationPush) {
return
}
if (!this.note) {
return
}
await this.reloadStackComponents()
this.debounceReloadEditorComponent()
})
@@ -456,6 +478,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
if (this.state.editorComponentViewerDidAlreadyReload && !force) {
return
}
const component = viewer.component
this.application.componentManager.destroyComponentViewer(viewer)
this.setState(
@@ -491,6 +514,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
}
async reloadEditorComponent() {
log(LoggingDomain.NoteView, 'Reload editor component')
if (this.state.showProtectedWarning) {
this.destroyCurrentEditorComponent()
return
@@ -583,13 +607,16 @@ class NoteView extends PureComponent<NoteViewProps, State> {
onTextAreaChange: ChangeEventHandler<HTMLTextAreaElement> = ({ currentTarget }) => {
const text = currentTarget.value
this.setState({
editorText: text,
})
this.textAreaChangeDebounceSave()
}
textAreaChangeDebounceSave = () => {
log(LoggingDomain.NoteView, 'Performing save after debounce')
this.controller
.save({
editorValues: {
@@ -611,10 +638,14 @@ class NoteView extends PureComponent<NoteViewProps, State> {
}
onTitleChange: ChangeEventHandler<HTMLInputElement> = ({ currentTarget }) => {
log(LoggingDomain.NoteView, 'Performing save after title change')
const title = currentTarget.value
this.setState({
editorTitle: title,
})
this.controller
.save({
editorValues: {
@@ -644,6 +675,15 @@ class NoteView extends PureComponent<NoteViewProps, State> {
this.application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: this.lastEditorFocusEventSource })
}
this.lastEditorFocusEventSource = undefined
this.setState({ plaintextEditorFocused: true })
}
onContentBlur = () => {
if (this.lastEditorFocusEventSource) {
this.application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: this.lastEditorFocusEventSource })
}
this.lastEditorFocusEventSource = undefined
this.setState({ plaintextEditorFocused: false })
}
setShowProtectedOverlay(show: boolean) {
@@ -655,10 +695,12 @@ class NoteView extends PureComponent<NoteViewProps, State> {
this.application.alertService.alert(STRING_DELETE_PLACEHOLDER_ATTEMPT).catch(console.error)
return
}
if (this.note.locked) {
this.application.alertService.alert(STRING_DELETE_LOCKED_ATTEMPT).catch(console.error)
return
}
const title = this.note.title.length ? `'${this.note.title}'` : 'this note'
const text = StringDeleteNote(title, permanently)
if (
@@ -720,6 +762,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
}
async reloadPreferences() {
log(LoggingDomain.NoteView, 'Reload preferences')
const monospaceFont = this.application.getPreference(
PrefKey.EditorMonospaceEnabled,
PrefDefaults[PrefKey.EditorMonospaceEnabled],
@@ -769,9 +812,8 @@ class NoteView extends PureComponent<NoteViewProps, State> {
}
}
/** @components */
async reloadStackComponents() {
log(LoggingDomain.NoteView, 'Reload stack components')
const stackComponents = sortAlphabetically(
this.application.componentManager
.componentsForArea(ComponentArea.EditorStack)
@@ -837,20 +879,13 @@ class NoteView extends PureComponent<NoteViewProps, State> {
})
}
setScrollPosition = () => {
const editor = document.getElementById(ElementIds.NoteTextEditor) as HTMLInputElement
this.scrollPosition = editor.scrollTop
}
resetScrollPosition = () => {
const editor = document.getElementById(ElementIds.NoteTextEditor) as HTMLInputElement
editor.scrollTop = this.scrollPosition
}
onSystemEditorLoad = (ref: HTMLTextAreaElement | null) => {
onSystemEditorRef = (ref: HTMLTextAreaElement | null) => {
if (this.removeTabObserver || !ref) {
return
}
log(LoggingDomain.NoteView, 'On system editor ref')
/**
* Insert 4 spaces when a tab key is pressed,
* only used when inside of the text editor.
@@ -900,18 +935,12 @@ class NoteView extends PureComponent<NoteViewProps, State> {
},
})
editor.addEventListener('scroll', this.setScrollPosition)
editor.addEventListener('input', this.resetScrollPosition)
const observer = new MutationObserver((records) => {
for (const record of records) {
record.removedNodes.forEach((node) => {
if (node === editor) {
this.removeTabObserver?.()
this.removeTabObserver = undefined
editor.removeEventListener('scroll', this.setScrollPosition)
editor.removeEventListener('scroll', this.resetScrollPosition)
this.scrollPosition = 0
}
})
}
@@ -942,6 +971,8 @@ class NoteView extends PureComponent<NoteViewProps, State> {
)
}
const renderHeaderOptions = isMobileScreen() ? !this.state.plaintextEditorFocused : true
return (
<div
aria-label="Note"
@@ -1005,31 +1036,33 @@ class NoteView extends PureComponent<NoteViewProps, State> {
updateSavingIndicator={this.state.updateSavingIndicator}
/>
</div>
<div className="flex items-center gap-3">
<LinkedItemsButton
filesController={this.viewControllerManager.filesController}
linkingController={this.viewControllerManager.linkingController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
featuresController={this.viewControllerManager.featuresController}
/>
<ChangeEditorButton
application={this.application}
viewControllerManager={this.viewControllerManager}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<PinNoteButton
notesController={this.viewControllerManager.notesController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<NotesOptionsPanel
application={this.application}
navigationController={this.viewControllerManager.navigationController}
notesController={this.viewControllerManager.notesController}
linkingController={this.viewControllerManager.linkingController}
historyModalController={this.viewControllerManager.historyModalController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
</div>
{renderHeaderOptions && (
<div className="flex items-center gap-3">
<LinkedItemsButton
filesController={this.viewControllerManager.filesController}
linkingController={this.viewControllerManager.linkingController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
featuresController={this.viewControllerManager.featuresController}
/>
<ChangeEditorButton
application={this.application}
viewControllerManager={this.viewControllerManager}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<PinNoteButton
notesController={this.viewControllerManager.notesController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<NotesOptionsPanel
application={this.application}
navigationController={this.viewControllerManager.navigationController}
notesController={this.viewControllerManager.notesController}
linkingController={this.viewControllerManager.linkingController}
historyModalController={this.viewControllerManager.historyModalController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
</div>
)}
</div>
<LinkedItemBubblesContainer linkingController={this.viewControllerManager.linkingController} />
</div>
@@ -1073,8 +1106,9 @@ class NoteView extends PureComponent<NoteViewProps, State> {
id={ElementIds.NoteTextEditor}
onChange={this.onTextAreaChange}
onFocus={this.onContentFocus}
onBlur={this.onContentBlur}
readOnly={this.state.noteLocked}
ref={(ref) => ref && this.onSystemEditorLoad(ref)}
ref={(ref) => ref && this.onSystemEditorRef(ref)}
spellCheck={this.state.spellcheck}
value={this.state.editorText}
className={classNames(
@@ -1,6 +1,6 @@
import { WebApplication } from '@/Application/Application'
import { createRef } from 'react'
import { PureComponent } from '@/Components/Abstract/PureComponent'
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
import Button from '@/Components/Button/Button'
import DecoratedPasswordInput from '../Input/DecoratedPasswordInput'
import ModalDialog from '../Shared/ModalDialog'
@@ -38,7 +38,7 @@ type FormData = {
status?: string
}
class PasswordWizard extends PureComponent<Props, State> {
class PasswordWizard extends AbstractComponent<Props, State> {
private currentPasswordInput = createRef<HTMLInputElement>()
constructor(props: Props) {
@@ -54,7 +54,7 @@ function useStateRef<State>(state: State): MutableRefObject<State> {
return ref
}
const MemoizedChildren = memo(({ children }: ChildrenProps) => <div>{children}</div>)
const MemoizedChildren = memo(({ children }: ChildrenProps) => <>{children}</>)
const ResponsivePaneProvider = ({ paneController, children }: ProviderProps) => {
const currentSelectedPane = paneController.currentPane
@@ -1,12 +1,12 @@
import { WebApplication } from '@/Application/Application'
import { PureComponent } from '@/Components/Abstract/PureComponent'
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
type Props = {
application: WebApplication
close: () => void
}
class SyncResolutionMenu extends PureComponent<Props> {
class SyncResolutionMenu extends AbstractComponent<Props> {
constructor(props: Props) {
super(props, props.application)
}
@@ -28,6 +28,7 @@ import { mergeRefs } from '@/Hooks/mergeRefs'
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
import { LinkingController } from '@/Controllers/LinkingController'
import { TagListSectionType } from './TagListSection'
import { log, LoggingDomain } from '@/Logging'
type Props = {
tag: SNTag
@@ -237,6 +238,8 @@ export const TagsListItem: FunctionComponent<Props> = observer(
}
}, [addDragTarget, linkingController, removeDragTarget, tag])
log(LoggingDomain.NavigationList, 'Rendering TagsListItem')
return (
<>
<div
+8 -1
View File
@@ -1,15 +1,22 @@
import { log as utilsLog } from '@standardnotes/utils'
import { isDev } from './Utils'
export enum LoggingDomain {
DailyNotes,
NoteView,
ItemsList,
NavigationList,
}
const LoggingStatus: Record<LoggingDomain, boolean> = {
[LoggingDomain.DailyNotes]: false,
[LoggingDomain.NoteView]: false,
[LoggingDomain.ItemsList]: false,
[LoggingDomain.NavigationList]: false,
}
export function log(domain: LoggingDomain, ...args: any[]): void {
if (!LoggingStatus[domain]) {
if (!isDev || !LoggingStatus[domain]) {
return
}
@@ -68,6 +68,9 @@ export class MobileWebReceiver {
case ReactNativeToWebEvent.AndroidBackButtonPressed:
void this.application.handleAndroidBackButtonPressed()
break
case ReactNativeToWebEvent.ColorSchemeChanged:
void this.application.handleMobileColorSchemeChangeEvent()
break
default:
break
@@ -25,7 +25,7 @@ type ProviderProps = {
application: WebApplication
} & ChildrenProps
const MemoizedChildren = memo(({ children }: ChildrenProps) => <div>{children}</div>)
const MemoizedChildren = memo(({ children }: ChildrenProps) => <>{children}</>)
const AndroidBackHandlerProvider = ({ application, children }: ProviderProps) => {
const addAndroidBackHandler = useCallback(