feat: mobile workspaces (#1093)
This commit is contained in:
14
packages/mobile/.eslintrc
Normal file
14
packages/mobile/.eslintrc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"commonjs": true
|
||||
},
|
||||
"extends": ["@react-native-community", "plugin:react-hooks/recommended", "../../.eslintrc.json"],
|
||||
"rules": {
|
||||
"no-console": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"eqeqeq": ["off"]
|
||||
},
|
||||
"ignorePatterns": ["metro.config.js"]
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@react-native-community', 'prettier', '../../node_modules/@standardnotes/config/src/.eslintrc'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'prettier'],
|
||||
ignorePatterns: ['.eslintrc.js', 'metro.config.js'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-shadow': ['error'],
|
||||
'no-shadow': 'off',
|
||||
'no-undef': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'no-invalid-this': 'warn',
|
||||
'no-console': 'warn',
|
||||
eqeqeq: ['warn', 'smart'],
|
||||
'no-void': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
'prettier/prettier': 'warn',
|
||||
},
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
module.exports = {
|
||||
arrowParens: 'avoid',
|
||||
bracketSpacing: true,
|
||||
jsxSingleQuote: false,
|
||||
quoteProps: 'as-needed',
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
printWidth: 120,
|
||||
trailingComma: 'all',
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const faker = require('faker')
|
||||
import { by, device, element, expect, waitFor } from 'detox'
|
||||
|
||||
@@ -13,14 +12,10 @@ export const expectToBeVisible = async (testedElement: Detox.IndexableNativeElem
|
||||
|
||||
const checkAfterReinstall = async () => {
|
||||
if (device.getPlatform() === 'ios') {
|
||||
const alertElement = element(
|
||||
by.label('Delete Local Data').and(by.type('_UIAlertControllerActionView'))
|
||||
)
|
||||
const alertElement = element(by.label('Delete Local Data').and(by.type('_UIAlertControllerActionView')))
|
||||
const alertVisible = await expectToBeVisible(alertElement)
|
||||
if (alertVisible) {
|
||||
await element(
|
||||
by.label('Delete Local Data').and(by.type('_UIAlertControllerActionView'))
|
||||
).tap()
|
||||
await element(by.label('Delete Local Data').and(by.type('_UIAlertControllerActionView'))).tap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const helpers = require('../../Helpers')
|
||||
import { by, device, element, expect, waitFor } from 'detox'
|
||||
import { openSettingsScreen } from '../../Helpers'
|
||||
@@ -45,9 +44,7 @@ fdescribe('Account section', () => {
|
||||
await element(by.id('passwordField')).typeText(helpers.randomCredentials.password)
|
||||
await element(by.id('otherOptionsButton')).tap()
|
||||
await element(by.id('syncServerField')).clearText()
|
||||
await element(by.id('syncServerField')).typeText(
|
||||
helpers.randomCredentials.syncServerUrl + '\n'
|
||||
)
|
||||
await element(by.id('syncServerField')).typeText(helpers.randomCredentials.syncServerUrl + '\n')
|
||||
// wait for buttons to be visible after closing keyboard on smaller devices
|
||||
await waitFor(element(by.id('registerButton')))
|
||||
.toBeVisible()
|
||||
@@ -90,9 +87,7 @@ fdescribe('Account section', () => {
|
||||
await element(by.id('passwordField')).typeText(helpers.randomCredentials.password)
|
||||
await element(by.id('otherOptionsButton')).tap()
|
||||
await element(by.id('syncServerField')).clearText()
|
||||
await element(by.id('syncServerField')).typeText(
|
||||
helpers.randomCredentials.syncServerUrl + '\n'
|
||||
)
|
||||
await element(by.id('syncServerField')).typeText(helpers.randomCredentials.syncServerUrl + '\n')
|
||||
// wait for button to be visible after keyboard close
|
||||
await waitFor(element(by.id('signInButton')))
|
||||
.toBeVisible()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const helpers = require('../../Helpers')
|
||||
import { by, device, element, expect } from 'detox'
|
||||
|
||||
|
||||
@@ -21,6 +21,18 @@ if (__DEV__ === false) {
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
|
||||
const originalWarn = console.warn
|
||||
|
||||
console.warn = function filterWarnings(msg) {
|
||||
const supressedWarnings = [
|
||||
"[react-native-gesture-handler] Seems like you're using an old API with gesture components",
|
||||
]
|
||||
|
||||
if (!supressedWarnings.some(entry => msg.includes(entry))) {
|
||||
originalWarn.apply(console, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
enableAndroidFontFix()
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App)
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
"ios-dev": "react-native run-ios --scheme StandardNotesDev",
|
||||
"ios-prod": "react-native run-ios --scheme StandardNotes",
|
||||
"clear-cache": "watchman watch-del-all && rm -rf $TMPDIR/react-native-packager-cache-* && rm -rf $TMPDIR/metro-bundler-cache-*",
|
||||
"lint": "yarn lint:eslint && yarn lint:prettier",
|
||||
"lint:eslint": "yarn eslint . --ext .ts,.tsx --fix --quiet",
|
||||
"lint:prettier": "prettier ./src --write",
|
||||
"lint": "yarn eslint . --ext .ts,.tsx",
|
||||
"lint:fix": "yarn lint --fix",
|
||||
"format": "prettier ./src",
|
||||
"format:fix": "yarn format --write",
|
||||
"build": "yarn android:bundle && yarn install:pods",
|
||||
"tsc": "tsc --noEmit",
|
||||
"start": "react-native start",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { MobileApplication } from '@Lib/Application'
|
||||
import { ApplicationGroup } from '@Lib/ApplicationGroup'
|
||||
import { navigationRef } from '@Lib/NavigationService'
|
||||
import { DefaultTheme, NavigationContainer } from '@react-navigation/native'
|
||||
import { ApplicationGroupContext } from '@Root/ApplicationGroupContext'
|
||||
import { MobileThemeVariables } from '@Root/Style/Themes/styled-components'
|
||||
import { ApplicationGroupEvent, DeinitMode, DeinitSource } from '@standardnotes/snjs'
|
||||
import { ThemeService, ThemeServiceContext } from '@Style/ThemeService'
|
||||
@@ -68,7 +69,7 @@ const AppComponent: React.FC<{
|
||||
setThemeServiceRef(themeServiceInstance)
|
||||
|
||||
await application.prepareForLaunch({
|
||||
receiveChallenge: async challenge => {
|
||||
receiveChallenge: async (challenge) => {
|
||||
application.promptForChallenge(challenge)
|
||||
},
|
||||
})
|
||||
@@ -135,7 +136,7 @@ export const App = (props: { env: TEnvironment }) => {
|
||||
const [appGroup, setAppGroup] = useState<ApplicationGroup>(() => createNewAppGroup())
|
||||
|
||||
useEffect(() => {
|
||||
const removeAppChangeObserver = appGroup.addEventObserver(event => {
|
||||
const removeAppChangeObserver = appGroup.addEventObserver((event) => {
|
||||
if (event === ApplicationGroupEvent.PrimaryApplicationSet) {
|
||||
const mobileApplication = appGroup.primaryApplication as MobileApplication
|
||||
setApplication(mobileApplication)
|
||||
@@ -145,15 +146,17 @@ export const App = (props: { env: TEnvironment }) => {
|
||||
}
|
||||
})
|
||||
return removeAppChangeObserver
|
||||
}, [appGroup, appGroup.primaryApplication, setAppGroup, createNewAppGroup])
|
||||
}, [appGroup, appGroup.primaryApplication, createNewAppGroup])
|
||||
|
||||
if (!application) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<ApplicationContext.Provider value={application}>
|
||||
<AppComponent env={props.env} key={application.Uuid} application={application} />
|
||||
</ApplicationContext.Provider>
|
||||
<ApplicationGroupContext.Provider value={appGroup}>
|
||||
<ApplicationContext.Provider value={application}>
|
||||
<AppComponent env={props.env} key={application.Uuid} application={application} />
|
||||
</ApplicationContext.Provider>
|
||||
</ApplicationGroupContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) =
|
||||
const noteDrawerRef = useRef<DrawerLayout>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const removeObserver = application?.getAppState().addStateChangeObserver(event => {
|
||||
const removeObserver = application?.getAppState().addStateChangeObserver((event) => {
|
||||
if (event === AppStateType.EditorClosed) {
|
||||
noteDrawerRef.current?.closeDrawer()
|
||||
if (!isInTabletMode && props.navigation.canGoBack()) {
|
||||
@@ -76,7 +76,7 @@ export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) =
|
||||
}, [application, props.navigation, isInTabletMode])
|
||||
|
||||
useEffect(() => {
|
||||
const removeObserver = application?.getStatusManager().addHeaderStatusObserver(messages => {
|
||||
const removeObserver = application?.getStatusManager().addHeaderStatusObserver((messages) => {
|
||||
setNotesStatus(messages[SCREEN_NOTES])
|
||||
setComposeStatus(messages[SCREEN_COMPOSE])
|
||||
})
|
||||
@@ -161,7 +161,7 @@ export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) =
|
||||
const screenStatus = isInTabletMode ? composeStatus || notesStatus : notesStatus
|
||||
|
||||
const title = route.params?.title ?? (children || '')
|
||||
const subtitle = [screenStatus?.status, route.params?.subTitle].filter(x => !!x).join(' • ')
|
||||
const subtitle = [screenStatus?.status, route.params?.subTitle].filter((x) => !!x).join(' • ')
|
||||
|
||||
return <HeaderTitleView title={title} subtitle={subtitle} subtitleColor={screenStatus?.color} />
|
||||
},
|
||||
|
||||
4
packages/mobile/src/ApplicationGroupContext.tsx
Normal file
4
packages/mobile/src/ApplicationGroupContext.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import { ApplicationGroup } from '@Lib/ApplicationGroup'
|
||||
import React from 'react'
|
||||
|
||||
export const ApplicationGroupContext = React.createContext<ApplicationGroup | undefined>(undefined)
|
||||
@@ -55,7 +55,7 @@ const ButtonLabel = styled.Text<{ primary?: boolean }>`
|
||||
color: ${({ theme, primary }) => {
|
||||
return primary ? theme.stylekitInfoContrastColor : theme.stylekitForegroundColor
|
||||
}};
|
||||
font-size: ${props => props.theme.mainTextFontSize}px;
|
||||
font-size: ${(props) => props.theme.mainTextFontSize}px;
|
||||
`
|
||||
|
||||
export const Button: React.FC<Props> = ({ onPress, label, primary, fullWidth, last }: Props) => {
|
||||
|
||||
@@ -17,7 +17,7 @@ type Props = {
|
||||
}
|
||||
|
||||
type ContainerProps = Pick<Props, 'maxHeight'> & TableCellProps
|
||||
const Container = styled(SectionedTableCellTouchableHighlight).attrs(props => ({
|
||||
const Container = styled(SectionedTableCellTouchableHighlight).attrs((props) => ({
|
||||
underlayColor: props.theme.stylekitBorderColor,
|
||||
}))<ContainerProps>`
|
||||
padding-top: ${12}px;
|
||||
@@ -32,9 +32,9 @@ const ButtonContainer = styled.View``
|
||||
|
||||
type ButtonLabelProps = Pick<Props, 'leftAligned' | 'bold' | 'disabled' | 'important'>
|
||||
const ButtonLabel = styled.Text<ButtonLabelProps>`
|
||||
text-align: ${props => (props.leftAligned ? 'left' : 'center')};
|
||||
text-align: ${(props) => (props.leftAligned ? 'left' : 'center')};
|
||||
text-align-vertical: center;
|
||||
color: ${props => {
|
||||
color: ${(props) => {
|
||||
let color = Platform.OS === 'android' ? props.theme.stylekitForegroundColor : props.theme.stylekitInfoColor
|
||||
if (props.disabled) {
|
||||
color = 'gray'
|
||||
@@ -43,7 +43,7 @@ const ButtonLabel = styled.Text<ButtonLabelProps>`
|
||||
}
|
||||
return color
|
||||
}};
|
||||
font-size: ${props => props.theme.mainTextFontSize}px;
|
||||
font-size: ${(props) => props.theme.mainTextFontSize}px;
|
||||
${({ bold }) =>
|
||||
bold &&
|
||||
css`
|
||||
@@ -56,7 +56,7 @@ const ButtonLabel = styled.Text<ButtonLabelProps>`
|
||||
`}
|
||||
`
|
||||
|
||||
export const ButtonCell: React.FC<Props> = props => (
|
||||
export const ButtonCell: React.FC<Props> = (props) => (
|
||||
<Container
|
||||
first={props.first}
|
||||
last={props.last}
|
||||
|
||||
@@ -7,10 +7,10 @@ type Props = {
|
||||
}
|
||||
|
||||
export const Circle = styled.View<Props>`
|
||||
width: ${props => props.size ?? 12}px;
|
||||
height: ${props => props.size ?? 12}px;
|
||||
border-radius: ${props => (props.size ?? 12) / 2}px;
|
||||
background-color: ${props => props.backgroundColor};
|
||||
border-color: ${props => props.borderColor};
|
||||
width: ${(props) => props.size ?? 12}px;
|
||||
height: ${(props) => props.size ?? 12}px;
|
||||
border-radius: ${(props) => (props.size ?? 12) / 2}px;
|
||||
background-color: ${(props) => props.backgroundColor};
|
||||
border-color: ${(props) => props.borderColor};
|
||||
border-width: 1px;
|
||||
`
|
||||
|
||||
@@ -9,14 +9,14 @@ type Props = {
|
||||
}
|
||||
|
||||
const Container = styled.View`
|
||||
/* background-color: ${props => props.theme.stylekitContrastBackgroundColor}; */
|
||||
/* background-color: ${(props) => props.theme.stylekitContrastBackgroundColor}; */
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
|
||||
${Platform.OS === 'android' && 'align-items: flex-start; min-width: 100px;'}
|
||||
`
|
||||
const Title = styled.Text`
|
||||
color: ${props => props.theme.stylekitForegroundColor};
|
||||
color: ${(props) => props.theme.stylekitForegroundColor};
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
@@ -25,13 +25,13 @@ const SubTitle = styled.Text.attrs(() => ({
|
||||
adjustsFontSizeToFit: true,
|
||||
numberOfLines: 1,
|
||||
}))<{ color?: string }>`
|
||||
color: ${props => props.color ?? props.theme.stylekitForegroundColor};
|
||||
opacity: ${props => (props.color ? 1 : 0.6)};
|
||||
color: ${(props) => props.color ?? props.theme.stylekitForegroundColor};
|
||||
opacity: ${(props) => (props.color ? 1 : 0.6)};
|
||||
font-size: ${Platform.OS === 'android' ? 13 : 12}px;
|
||||
${Platform.OS === 'ios' && 'text-align: center'}
|
||||
`
|
||||
|
||||
export const HeaderTitleView: React.FC<Props> = props => (
|
||||
export const HeaderTitleView: React.FC<Props> = (props) => (
|
||||
<Container>
|
||||
<Title>{props.title}</Title>
|
||||
{props.subtitle && props.subtitle.length > 0 ? (
|
||||
|
||||
@@ -17,19 +17,19 @@ const Container = styled.View<Pick<Props, 'backgroundColor'>>`
|
||||
/* flex-grow: 0; */
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
padding-right: ${props => props.theme.paddingLeft}px;
|
||||
padding-right: ${(props) => props.theme.paddingLeft}px;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
background-color: ${props => props.backgroundColor ?? props.theme.stylekitBackgroundColor};
|
||||
background-color: ${(props) => props.backgroundColor ?? props.theme.stylekitBackgroundColor};
|
||||
`
|
||||
const TitleContainer = styled.View``
|
||||
const Title = styled.Text<Pick<Props, 'tinted'>>`
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
font-size: ${props => {
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
font-size: ${(props) => {
|
||||
return Platform.OS === 'android' ? props.theme.mainTextFontSize - 2 : props.theme.mainTextFontSize - 4
|
||||
}}px;
|
||||
padding-left: ${props => props.theme.paddingLeft}px;
|
||||
color: ${props => {
|
||||
padding-left: ${(props) => props.theme.paddingLeft}px;
|
||||
color: ${(props) => {
|
||||
if (props.tinted) {
|
||||
return props.theme.stylekitInfoColor
|
||||
}
|
||||
@@ -39,11 +39,11 @@ const Title = styled.Text<Pick<Props, 'tinted'>>`
|
||||
font-weight: ${Platform.OS === 'android' ? 'bold' : 'normal'};
|
||||
`
|
||||
const SubTitle = styled.Text`
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
font-size: ${props => props.theme.mainTextFontSize - 5}px;
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
font-size: ${(props) => props.theme.mainTextFontSize - 5}px;
|
||||
margin-top: 4px;
|
||||
padding-left: ${props => props.theme.paddingLeft}px;
|
||||
color: ${props => props.theme.stylekitNeutralColor};
|
||||
padding-left: ${(props) => props.theme.paddingLeft}px;
|
||||
color: ${(props) => props.theme.stylekitNeutralColor};
|
||||
`
|
||||
const ButtonContainer = styled.TouchableOpacity`
|
||||
flex: 1;
|
||||
@@ -51,10 +51,10 @@ const ButtonContainer = styled.TouchableOpacity`
|
||||
justify-content: center;
|
||||
`
|
||||
const Button = styled.Text`
|
||||
color: ${props => props.theme.stylekitInfoColor};
|
||||
color: ${(props) => props.theme.stylekitInfoColor};
|
||||
`
|
||||
|
||||
export const SectionHeader: React.FC<Props> = props => (
|
||||
export const SectionHeader: React.FC<Props> = (props) => (
|
||||
<Container>
|
||||
<TitleContainer>
|
||||
{!!props.title && (
|
||||
|
||||
@@ -22,7 +22,7 @@ type Props = {
|
||||
last?: boolean
|
||||
}
|
||||
|
||||
const TouchableContainer = styled(SectionedTableCellTouchableHighlight).attrs(props => ({
|
||||
const TouchableContainer = styled(SectionedTableCellTouchableHighlight).attrs((props) => ({
|
||||
underlayColor: props.theme.stylekitBorderColor,
|
||||
}))`
|
||||
flex-direction: column;
|
||||
@@ -33,7 +33,7 @@ const TouchableContainer = styled(SectionedTableCellTouchableHighlight).attrs(pr
|
||||
`
|
||||
const ContentContainer = styled.View<Pick<Props, 'leftAlignIcon'>>`
|
||||
flex: 1;
|
||||
justify-content: ${props => {
|
||||
justify-content: ${(props) => {
|
||||
return props.leftAlignIcon ? 'flex-start' : 'space-between'
|
||||
}};
|
||||
flex-direction: row;
|
||||
@@ -46,7 +46,7 @@ const IconContainer = styled.View`
|
||||
type LabelProps = Pick<Props, 'bold' | 'tinted' | 'dimmed' | 'selected' | 'color'>
|
||||
const Label = styled.Text<LabelProps>`
|
||||
min-width: 80%;
|
||||
color: ${props => {
|
||||
color: ${(props) => {
|
||||
let color = props.theme.stylekitForegroundColor
|
||||
if (props.tinted) {
|
||||
color = props.theme.stylekitInfoColor
|
||||
@@ -59,7 +59,7 @@ const Label = styled.Text<LabelProps>`
|
||||
}
|
||||
return color
|
||||
}};
|
||||
font-size: ${props => props.theme.mainTextFontSize}px;
|
||||
font-size: ${(props) => props.theme.mainTextFontSize}px;
|
||||
${({ bold, selected }) =>
|
||||
((selected && selected() === true) || bold) &&
|
||||
css`
|
||||
@@ -67,7 +67,7 @@ const Label = styled.Text<LabelProps>`
|
||||
`};
|
||||
`
|
||||
|
||||
export const SectionedAccessoryTableCell: React.FC<Props> = props => {
|
||||
export const SectionedAccessoryTableCell: React.FC<Props> = (props) => {
|
||||
const themeContext = useContext(ThemeContext)
|
||||
const onPress = () => {
|
||||
if (props.disabled) {
|
||||
|
||||
@@ -15,11 +15,11 @@ type Props = {
|
||||
type ContainerProps = Omit<Props, 'title' | 'onPress' | 'options'>
|
||||
|
||||
export const Container = styled.View<ContainerProps>`
|
||||
border-bottom-color: ${props => props.theme.stylekitBorderColor};
|
||||
border-bottom-color: ${(props) => props.theme.stylekitBorderColor};
|
||||
border-bottom-width: 1px;
|
||||
padding-left: ${props => props.theme.paddingLeft}px;
|
||||
padding-right: ${props => props.theme.paddingLeft}px;
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
padding-left: ${(props) => props.theme.paddingLeft}px;
|
||||
padding-right: ${(props) => props.theme.paddingLeft}px;
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
${({ first, theme }) =>
|
||||
first &&
|
||||
css`
|
||||
@@ -38,9 +38,9 @@ export const Container = styled.View<ContainerProps>`
|
||||
`
|
||||
|
||||
const Title = styled.Text<{ leftAligned?: boolean }>`
|
||||
font-size: ${props => props.theme.mainTextFontSize}px;
|
||||
color: ${props => props.theme.stylekitForegroundColor};
|
||||
text-align: ${props => (props.leftAligned ? 'left' : 'center')};
|
||||
font-size: ${(props) => props.theme.mainTextFontSize}px;
|
||||
color: ${(props) => props.theme.stylekitForegroundColor};
|
||||
text-align: ${(props) => (props.leftAligned ? 'left' : 'center')};
|
||||
width: 42%;
|
||||
min-width: 0px;
|
||||
`
|
||||
@@ -50,13 +50,13 @@ const OptionsContainer = styled.View`
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
`
|
||||
|
||||
const ButtonTouchable = styled.TouchableHighlight.attrs(props => ({
|
||||
const ButtonTouchable = styled.TouchableHighlight.attrs((props) => ({
|
||||
underlayColor: props.theme.stylekitBorderColor,
|
||||
}))`
|
||||
border-left-color: ${props => props.theme.stylekitBorderColor};
|
||||
border-left-color: ${(props) => props.theme.stylekitBorderColor};
|
||||
border-left-width: 1px;
|
||||
flex-grow: 1;
|
||||
padding: 10px;
|
||||
@@ -64,7 +64,7 @@ const ButtonTouchable = styled.TouchableHighlight.attrs(props => ({
|
||||
`
|
||||
|
||||
const ButtonTitle = styled.Text<{ selected: boolean }>`
|
||||
color: ${props => {
|
||||
color: ${(props) => {
|
||||
return props.selected ? props.theme.stylekitInfoColor : props.theme.stylekitNeutralColor
|
||||
}};
|
||||
font-size: 16px;
|
||||
@@ -72,11 +72,11 @@ const ButtonTitle = styled.Text<{ selected: boolean }>`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
export const SectionedOptionsTableCell: React.FC<Props> = props => (
|
||||
export const SectionedOptionsTableCell: React.FC<Props> = (props) => (
|
||||
<Container first={props.first}>
|
||||
<Title leftAligned={props.leftAligned}>{props.title}</Title>
|
||||
<OptionsContainer>
|
||||
{props.options.map(option => {
|
||||
{props.options.map((option) => {
|
||||
return (
|
||||
<ButtonTouchable key={option.title} onPress={() => props.onPress(option)}>
|
||||
<ButtonTitle selected={option.selected}>{option.title}</ButtonTitle>
|
||||
|
||||
@@ -9,12 +9,12 @@ export type Props = {
|
||||
}
|
||||
|
||||
export const SectionedTableCell = styled.View<Props>`
|
||||
border-bottom-color: ${props => props.theme.stylekitBorderColor};
|
||||
border-bottom-color: ${(props) => props.theme.stylekitBorderColor};
|
||||
border-bottom-width: 1px;
|
||||
padding-left: ${props => props.theme.paddingLeft}px;
|
||||
padding-right: ${props => props.theme.paddingLeft}px;
|
||||
padding-bottom: ${props => (props.textInputCell ? 0 : 12)}px;
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
padding-left: ${(props) => props.theme.paddingLeft}px;
|
||||
padding-right: ${(props) => props.theme.paddingLeft}px;
|
||||
padding-bottom: ${(props) => (props.textInputCell ? 0 : 12)}px;
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
${({ first, theme }) =>
|
||||
first &&
|
||||
css`
|
||||
@@ -34,12 +34,12 @@ export const SectionedTableCell = styled.View<Props>`
|
||||
`
|
||||
|
||||
export const SectionedTableCellTouchableHighlight = styled.TouchableHighlight<Props>`
|
||||
border-bottom-color: ${props => props.theme.stylekitBorderColor};
|
||||
border-bottom-color: ${(props) => props.theme.stylekitBorderColor};
|
||||
border-bottom-width: 1px;
|
||||
padding-left: ${props => props.theme.paddingLeft}px;
|
||||
padding-right: ${props => props.theme.paddingLeft}px;
|
||||
padding-bottom: ${props => (props.textInputCell ? 0 : 12)}px;
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
padding-left: ${(props) => props.theme.paddingLeft}px;
|
||||
padding-right: ${(props) => props.theme.paddingLeft}px;
|
||||
padding-bottom: ${(props) => (props.textInputCell ? 0 : 12)}px;
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
${({ first, theme }) =>
|
||||
first &&
|
||||
css`
|
||||
|
||||
@@ -3,5 +3,5 @@ import styled from 'styled-components/native'
|
||||
export const TableSection = styled.View`
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
`
|
||||
|
||||
@@ -12,7 +12,7 @@ export const ToastWrapper: FC = () => {
|
||||
const { updateProgressBar, progressBarWidth } = useProgressBar()
|
||||
|
||||
const toastStyles: ToastConfig = {
|
||||
info: props => {
|
||||
info: (props) => {
|
||||
const percentComplete = props.props?.percentComplete || 0
|
||||
updateProgressBar(percentComplete)
|
||||
|
||||
@@ -35,13 +35,13 @@ export const ToastWrapper: FC = () => {
|
||||
</View>
|
||||
)
|
||||
},
|
||||
success: props => {
|
||||
success: (props) => {
|
||||
const percentComplete = props.props?.percentComplete || 0
|
||||
updateProgressBar(percentComplete)
|
||||
|
||||
return <SuccessToast {...props} style={styles(props.props).success} />
|
||||
},
|
||||
error: props => {
|
||||
error: (props) => {
|
||||
const percentComplete = props.props?.percentComplete || 0
|
||||
updateProgressBar(percentComplete)
|
||||
|
||||
|
||||
@@ -560,7 +560,7 @@ export const useFiles = ({ note }: Props) => {
|
||||
return
|
||||
}
|
||||
if (shouldAttachToNote(currentTab)) {
|
||||
uploadedFiles.forEach(file => attachFileToNote(file, false))
|
||||
uploadedFiles.forEach((file) => attachFileToNote(file, false))
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -587,7 +587,7 @@ export const useFiles = ({ note }: Props) => {
|
||||
},
|
||||
},
|
||||
]
|
||||
const osSpecificOptions = Platform.OS === 'android' ? options.filter(option => option.key !== 'library') : options
|
||||
const osSpecificOptions = Platform.OS === 'android' ? options.filter((option) => option.key !== 'library') : options
|
||||
showActionSheet({
|
||||
title: 'Choose action',
|
||||
options: osSpecificOptions,
|
||||
@@ -783,7 +783,7 @@ export const useFiles = ({ note }: Props) => {
|
||||
},
|
||||
]
|
||||
const osDependentActions =
|
||||
Platform.OS === 'ios' ? actions.filter(action => action.text !== 'Download') : [...actions]
|
||||
Platform.OS === 'ios' ? actions.filter((action) => action.text !== 'Download') : [...actions]
|
||||
showActionSheet({
|
||||
title: file.name,
|
||||
options: osDependentActions,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApplicationGroup } from '@Lib/ApplicationGroup'
|
||||
import { ApplicationGroupContext } from '@Root/ApplicationGroupContext'
|
||||
import { useContext } from 'react'
|
||||
|
||||
export const useSafeApplicationGroupContext = () => {
|
||||
const applicationGroupContext = useContext(ApplicationGroupContext) as ApplicationGroup
|
||||
return applicationGroupContext
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export class MobileAlertService extends AlertService {
|
||||
return goBack
|
||||
}
|
||||
alert(text: string, title: string, closeButtonText?: string) {
|
||||
return new Promise<void>(resolve => {
|
||||
return new Promise<void>((resolve) => {
|
||||
// On iOS, confirm should go first. On Android, cancel should go first.
|
||||
const buttons = [
|
||||
{
|
||||
|
||||
@@ -155,7 +155,7 @@ export class ApplicationState extends ApplicationService {
|
||||
|
||||
const savedTag =
|
||||
(this.application.items.findItem(savedTagUuid) as SNTag) ||
|
||||
this.application.items.getSmartViews().find(tag => tag.uuid === savedTagUuid)
|
||||
this.application.items.getSmartViews().find((tag) => tag.uuid === savedTagUuid)
|
||||
if (savedTag) {
|
||||
this.setSelectedTag(savedTag, false)
|
||||
this.selectedTagRestored = true
|
||||
@@ -288,7 +288,7 @@ export class ApplicationState extends ApplicationService {
|
||||
|
||||
if (note && note.conflictOf) {
|
||||
void InteractionManager.runAfterInteractions(() => {
|
||||
void this.application?.mutator.changeAndSaveItem(note, mutator => {
|
||||
void this.application?.mutator.changeAndSaveItem(note, (mutator) => {
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
})
|
||||
@@ -328,7 +328,7 @@ export class ApplicationState extends ApplicationService {
|
||||
}
|
||||
}
|
||||
|
||||
private keyboardDidShow: KeyboardEventListener = e => {
|
||||
private keyboardDidShow: KeyboardEventListener = (e) => {
|
||||
this.keyboardHeight = e.endCoordinates.height
|
||||
this.notifyEventObservers(AppStateEventType.KeyboardChangeEvent)
|
||||
}
|
||||
@@ -353,7 +353,7 @@ export class ApplicationState extends ApplicationService {
|
||||
[ContentType.Note, ContentType.Tag],
|
||||
async ({ changed, inserted, removed, source }) => {
|
||||
if (source === PayloadEmitSource.PreSyncSave || source === PayloadEmitSource.RemoteRetrieved) {
|
||||
const removedNotes = removed.filter(i => i.content_type === ContentType.Note)
|
||||
const removedNotes = removed.filter((i) => i.content_type === ContentType.Note)
|
||||
for (const removedNote of removedNotes) {
|
||||
const editor = this.editorForNote(removedNote.uuid)
|
||||
if (editor) {
|
||||
@@ -361,7 +361,7 @@ export class ApplicationState extends ApplicationService {
|
||||
}
|
||||
}
|
||||
|
||||
const notes = [...changed, ...inserted].filter(candidate => candidate.content_type === ContentType.Note)
|
||||
const notes = [...changed, ...inserted].filter((candidate) => candidate.content_type === ContentType.Note)
|
||||
|
||||
const isBrowswingTrashedNotes =
|
||||
this.selectedTag instanceof SmartView && this.selectedTag.uuid === SystemViewId.TrashedNotes
|
||||
@@ -384,7 +384,7 @@ export class ApplicationState extends ApplicationService {
|
||||
}
|
||||
|
||||
if (this.selectedTag) {
|
||||
const matchingTag = [...changed, ...inserted].find(candidate => candidate.uuid === this.selectedTag.uuid)
|
||||
const matchingTag = [...changed, ...inserted].find((candidate) => candidate.uuid === this.selectedTag.uuid)
|
||||
if (matchingTag) {
|
||||
this.selectedTag = matchingTag as SNTag
|
||||
}
|
||||
@@ -397,7 +397,7 @@ export class ApplicationState extends ApplicationService {
|
||||
* Registers for MobileApplication events
|
||||
*/
|
||||
private handleApplicationEvents() {
|
||||
this.removeAppEventObserver = this.application.addEventObserver(async eventName => {
|
||||
this.removeAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
||||
switch (eventName) {
|
||||
case ApplicationEvent.LocalDataIncrementalLoad:
|
||||
case ApplicationEvent.LocalDataLoaded: {
|
||||
@@ -441,7 +441,7 @@ export class ApplicationState extends ApplicationService {
|
||||
* @returns tags that are referencing note
|
||||
*/
|
||||
public getNoteTags(note: SNNote) {
|
||||
return this.application.items.itemsReferencingItem(note).filter(ref => {
|
||||
return this.application.items.itemsReferencingItem(note).filter((ref) => {
|
||||
return ref.content_type === ContentType.Tag
|
||||
}) as SNTag[]
|
||||
}
|
||||
@@ -453,7 +453,7 @@ export class ApplicationState extends ApplicationService {
|
||||
if (tag instanceof SmartView) {
|
||||
return this.application.items.notesMatchingSmartView(tag)
|
||||
} else {
|
||||
return this.application.items.referencesForItem(tag).filter(ref => {
|
||||
return this.application.items.referencesForItem(tag).filter((ref) => {
|
||||
return ref.content_type === ContentType.Note
|
||||
}) as SNNote[]
|
||||
}
|
||||
|
||||
@@ -61,13 +61,13 @@ export class BackupsService extends ApplicationService {
|
||||
}
|
||||
|
||||
private async exportIOS(filename: string, data: string) {
|
||||
return new Promise<boolean>(resolve => {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
void (this.application! as MobileApplication).getAppState().performActionWithoutStateChangeImpact(async () => {
|
||||
Share.share({
|
||||
title: filename,
|
||||
message: data,
|
||||
})
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
resolve(result.action !== Share.dismissedAction)
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -98,7 +98,7 @@ export class BackupsService extends ApplicationService {
|
||||
// success
|
||||
return true
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error('Error opening file', error)
|
||||
return false
|
||||
})
|
||||
@@ -119,7 +119,7 @@ export class BackupsService extends ApplicationService {
|
||||
}
|
||||
|
||||
private async exportViaEmailAndroid(data: string, filename: string) {
|
||||
return new Promise<boolean>(resolve => {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
const fileType = '.json' // Android creates a tmp file and expects dot with extension
|
||||
|
||||
let resolved = false
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Base64 } from 'js-base64'
|
||||
import RNFS, { DocumentDirectoryPath } from 'react-native-fs'
|
||||
import StaticServer from 'react-native-static-server'
|
||||
import { unzip } from 'react-native-zip-archive'
|
||||
import { componentsCdn, version, name } from '../../package.json'
|
||||
import { componentsCdn, name, version } from '../../package.json'
|
||||
import { MobileThemeContent } from '../Style/MobileTheme'
|
||||
import { IsDev } from './Utils'
|
||||
|
||||
@@ -356,7 +356,7 @@ export class ComponentManager extends SNComponentManager {
|
||||
}
|
||||
|
||||
export async function associateComponentWithNote(application: SNApplication, component: SNComponent, note: SNNote) {
|
||||
return application.mutator.changeItem<ComponentMutator>(component, mutator => {
|
||||
return application.mutator.changeItem<ComponentMutator>(component, (mutator) => {
|
||||
mutator.removeDisassociatedItemId(note.uuid)
|
||||
mutator.associateWithItem(note.uuid)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SNReactNative from '@standardnotes/react-native-utils'
|
||||
import { ApplicationService, ButtonType, isNullOrUndefined, StorageValueModes } from '@standardnotes/snjs'
|
||||
import { ApplicationService, ButtonType, StorageValueModes } from '@standardnotes/snjs'
|
||||
import { MobileDeviceInterface } from './Interface'
|
||||
|
||||
const FIRST_RUN_KEY = 'first_run'
|
||||
@@ -14,7 +14,7 @@ export class InstallationService extends ApplicationService {
|
||||
}
|
||||
|
||||
async markApplicationAsRan() {
|
||||
return this.application?.setValue(FIRST_RUN_KEY, false, StorageValueModes.Nonwrapped)
|
||||
return this.application.deviceInterface.setRawStorageValue(FIRST_RUN_KEY, 'false')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,24 +22,20 @@ export class InstallationService extends ApplicationService {
|
||||
* AsyncStorage failures, we want to confirm with the user before deleting anything.
|
||||
*/
|
||||
async needsWipe() {
|
||||
const hasNormalKeys = this.application?.hasAccount() || this.application?.hasPasscode()
|
||||
const deviceInterface = this.application?.deviceInterface as MobileDeviceInterface
|
||||
const keychainKey = await deviceInterface?.getRawKeychainValue()
|
||||
const hasKeychainValue = !(
|
||||
isNullOrUndefined(keychainKey) ||
|
||||
(typeof keychainKey === 'object' && Object.keys(keychainKey).length === 0)
|
||||
)
|
||||
const hasAccountOrPasscode = this.application.hasAccount() || this.application?.hasPasscode()
|
||||
const deviceInterface = this.application.deviceInterface as MobileDeviceInterface
|
||||
const keychainKey = await deviceInterface.getNamespacedKeychainValue(this.application.identifier)
|
||||
|
||||
const firstRunKey = await this.application?.getValue(FIRST_RUN_KEY, StorageValueModes.Nonwrapped)
|
||||
let firstRunKeyMissing = isNullOrUndefined(firstRunKey)
|
||||
/*
|
||||
* Because of migration failure first run key might not be in non wrapped storage
|
||||
*/
|
||||
const hasKeychainValue = keychainKey != undefined
|
||||
|
||||
const firstRunKey = await this.application.deviceInterface.getRawStorageValue(FIRST_RUN_KEY)
|
||||
let firstRunKeyMissing = firstRunKey == undefined
|
||||
if (firstRunKeyMissing) {
|
||||
const fallbackFirstRunValue = await this.application?.deviceInterface?.getRawStorageValue(FIRST_RUN_KEY)
|
||||
firstRunKeyMissing = isNullOrUndefined(fallbackFirstRunValue)
|
||||
const fallbackFirstRunValue = await this.application.getValue(FIRST_RUN_KEY, StorageValueModes.Nonwrapped)
|
||||
firstRunKeyMissing = fallbackFirstRunValue == undefined
|
||||
}
|
||||
return !hasNormalKeys && hasKeychainValue && firstRunKeyMissing
|
||||
|
||||
return !hasAccountOrPasscode && hasKeychainValue && firstRunKeyMissing
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AsyncStorage from '@react-native-community/async-storage'
|
||||
import SNReactNative from '@standardnotes/react-native-utils'
|
||||
import {
|
||||
ApplicationIdentifier,
|
||||
DeviceInterface,
|
||||
@@ -32,7 +33,7 @@ const showLoadFailForItemIds = (failedItemIds: string[]) => {
|
||||
let text =
|
||||
'The following items could not be loaded. This may happen if you are in low-memory conditions, or if the note is very large in size. We recommend breaking up large notes into smaller chunks using the desktop or web app.\n\nItems:\n'
|
||||
let index = 0
|
||||
text += failedItemIds.map(id => {
|
||||
text += failedItemIds.map((id) => {
|
||||
let result = id
|
||||
if (index !== failedItemIds.length - 1) {
|
||||
result += '\n'
|
||||
@@ -79,8 +80,8 @@ export class MobileDeviceInterface implements DeviceInterface {
|
||||
|
||||
private async getAllDatabaseKeys(identifier: ApplicationIdentifier) {
|
||||
const keys = await AsyncStorage.getAllKeys()
|
||||
const filtered = keys.filter(key => {
|
||||
return key.includes(this.getDatabaseKeyPrefix(identifier))
|
||||
const filtered = keys.filter((key) => {
|
||||
return key.startsWith(this.getDatabaseKeyPrefix(identifier))
|
||||
})
|
||||
return filtered
|
||||
}
|
||||
@@ -205,7 +206,7 @@ export class MobileDeviceInterface implements DeviceInterface {
|
||||
return
|
||||
}
|
||||
await Promise.all(
|
||||
payloads.map(item => {
|
||||
payloads.map((item) => {
|
||||
return AsyncStorage.setItem(this.keyForPayloadId(item.uuid, identifier), JSON.stringify(item))
|
||||
}),
|
||||
)
|
||||
@@ -294,7 +295,7 @@ export class MobileDeviceInterface implements DeviceInterface {
|
||||
}
|
||||
|
||||
Linking.canOpenURL(url)
|
||||
.then(supported => {
|
||||
.then((supported) => {
|
||||
if (!supported) {
|
||||
showAlert()
|
||||
return
|
||||
@@ -306,15 +307,16 @@ export class MobileDeviceInterface implements DeviceInterface {
|
||||
}
|
||||
|
||||
async clearAllDataFromDevice(_workspaceIdentifiers: string[]): Promise<{ killsApplication: boolean }> {
|
||||
await this.clearRawKeychainValue()
|
||||
|
||||
await this.removeAllRawStorageValues()
|
||||
|
||||
await this.clearRawKeychainValue()
|
||||
|
||||
return { killsApplication: false }
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
performSoftReset() {}
|
||||
performSoftReset() {
|
||||
SNReactNative.exitApp()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
performHardReset() {}
|
||||
|
||||
@@ -30,7 +30,7 @@ export const useSignedIn = (signedInCallback?: () => void, signedOutCallback?: (
|
||||
}
|
||||
}
|
||||
void getSignedIn()
|
||||
const removeSignedInObserver = application.addEventObserver(async event => {
|
||||
const removeSignedInObserver = application.addEventObserver(async (event) => {
|
||||
if (event === ApplicationEvent.Launched) {
|
||||
void getSignedIn()
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export const useOutOfSync = () => {
|
||||
}, [application])
|
||||
|
||||
React.useEffect(() => {
|
||||
const removeSignedInObserver = application.addEventObserver(async event => {
|
||||
const removeSignedInObserver = application.addEventObserver(async (event) => {
|
||||
if (event === ApplicationEvent.EnteredOutOfSync) {
|
||||
setOutOfSync(true)
|
||||
} else if (event === ApplicationEvent.ExitedOutOfSync) {
|
||||
@@ -103,7 +103,7 @@ export const useIsLocked = () => {
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true
|
||||
const removeSignedInObserver = application?.getAppState().addLockStateChangeObserver(event => {
|
||||
const removeSignedInObserver = application?.getAppState().addLockStateChangeObserver((event) => {
|
||||
if (isMounted) {
|
||||
if (event === LockStateType.Locked) {
|
||||
setIsLocked(true)
|
||||
@@ -131,7 +131,7 @@ export const useHasEditor = () => {
|
||||
const [hasEditor, setHasEditor] = React.useState<boolean>(false)
|
||||
|
||||
useEffect(() => {
|
||||
const removeEditorObserver = application?.editorGroup.addActiveControllerChangeObserver(newEditor => {
|
||||
const removeEditorObserver = application?.editorGroup.addActiveControllerChangeObserver((newEditor) => {
|
||||
setHasEditor(Boolean(newEditor))
|
||||
})
|
||||
return removeEditorObserver
|
||||
@@ -207,7 +207,7 @@ export const useSyncStatus = () => {
|
||||
}, [application, completedInitialSync, setStatus])
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeAppEvents = application?.addEventObserver(async eventName => {
|
||||
const unsubscribeAppEvents = application?.addEventObserver(async (eventName) => {
|
||||
if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
|
||||
updateLocalDataStatus()
|
||||
} else if (eventName === ApplicationEvent.SyncStatusChanged || eventName === ApplicationEvent.FailedSync) {
|
||||
@@ -340,7 +340,7 @@ export const useProtectionSessionExpiry = () => {
|
||||
const [protectionsDisabledUntil, setProtectionsDisabledUntil] = React.useState(getProtectionsDisabledUntil())
|
||||
|
||||
useEffect(() => {
|
||||
const removeProtectionLengthSubscriber = application?.addEventObserver(async event => {
|
||||
const removeProtectionLengthSubscriber = application?.addEventObserver(async (event) => {
|
||||
if ([ApplicationEvent.UnprotectedSessionBegan, ApplicationEvent.UnprotectedSessionExpired].includes(event)) {
|
||||
setProtectionsDisabledUntil(getProtectionsDisabledUntil())
|
||||
}
|
||||
@@ -389,7 +389,7 @@ export const useChangeNote = (note: SNNote | undefined, editor: NoteViewControll
|
||||
if (await canChangeNote()) {
|
||||
await application?.mutator.changeAndSaveItem(
|
||||
note!,
|
||||
mutator => {
|
||||
(mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
mutate(noteMutator)
|
||||
},
|
||||
|
||||
@@ -14,13 +14,15 @@ import {
|
||||
SCREEN_INPUT_MODAL_FILE_NAME,
|
||||
SCREEN_INPUT_MODAL_PASSCODE,
|
||||
SCREEN_INPUT_MODAL_TAG,
|
||||
SCREEN_INPUT_MODAL_WORKSPACE_NAME,
|
||||
SCREEN_MANAGE_SESSIONS,
|
||||
SCREEN_SETTINGS,
|
||||
SCREEN_UPLOADED_FILES_LIST,
|
||||
} from '@Root/Screens/screens'
|
||||
import { Settings } from '@Root/Screens/Settings/Settings'
|
||||
import { UploadedFilesList } from '@Root/Screens/UploadedFilesList/UploadedFilesList'
|
||||
import { Challenge, DeinitMode, DeinitSource, FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import { WorkspaceInputModal } from '@Screens/InputModal/WorkspaceInputModal'
|
||||
import { ApplicationDescriptor, Challenge, DeinitMode, DeinitSource, FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import { ICON_CHECKMARK, ICON_CLOSE } from '@Style/Icons'
|
||||
import { ThemeService } from '@Style/ThemeService'
|
||||
import React, { memo, useContext } from 'react'
|
||||
@@ -48,6 +50,10 @@ export type ModalStackNavigatorParamList = {
|
||||
[SCREEN_UPLOADED_FILES_LIST]: HeaderTitleParams & {
|
||||
note: SNNote
|
||||
}
|
||||
[SCREEN_INPUT_MODAL_WORKSPACE_NAME]: HeaderTitleParams & {
|
||||
descriptor: ApplicationDescriptor
|
||||
renameWorkspace: (descriptor: ApplicationDescriptor, workspaceName: string) => Promise<void>
|
||||
}
|
||||
[SCREEN_INPUT_MODAL_PASSCODE]: undefined
|
||||
[SCREEN_AUTHENTICATE]: {
|
||||
challenge: Challenge
|
||||
@@ -275,6 +281,28 @@ export const MainStackComponent = ({ env }: { env: TEnvironment }) => {
|
||||
})}
|
||||
component={BlockingModal}
|
||||
/>
|
||||
<MainStack.Screen
|
||||
name={SCREEN_INPUT_MODAL_WORKSPACE_NAME}
|
||||
options={({ route }) => ({
|
||||
title: 'Workspace',
|
||||
gestureEnabled: false,
|
||||
headerTitle: ({ children }) => {
|
||||
return <HeaderTitleView title={route.params?.title ?? (children || '')} />
|
||||
},
|
||||
headerLeft: ({ disabled, onPress }) => (
|
||||
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
|
||||
<Item
|
||||
testID="headerButton"
|
||||
disabled={disabled}
|
||||
title={Platform.OS === 'ios' ? 'Cancel' : ''}
|
||||
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CLOSE)}
|
||||
onPress={onPress}
|
||||
/>
|
||||
</HeaderButtons>
|
||||
),
|
||||
})}
|
||||
component={WorkspaceInputModal}
|
||||
/>
|
||||
</MainStack.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export const Authenticate = ({
|
||||
const [passcodeKeyboardType, setPasscodeKeyboardType] = useState<PasscodeKeyboardType | undefined>(
|
||||
PasscodeKeyboardType.Default,
|
||||
)
|
||||
const [singleValidation] = useState(() => !(challenge.prompts.filter(prompt => prompt.validates).length > 0))
|
||||
const [singleValidation] = useState(() => !(challenge.prompts.filter((prompt) => prompt.validates).length > 0))
|
||||
const [showSwitchKeyboard, setShowSwitchKeyboard] = useState<boolean>(false)
|
||||
|
||||
const [{ challengeValues, challengeValueStates }, dispatch] = useReducer(
|
||||
@@ -217,7 +217,7 @@ export const Authenticate = ({
|
||||
onValueChange(newChallengeValue)
|
||||
return validateChallengeValue(newChallengeValue)
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
FingerprintScanner.release()
|
||||
if (error.name === 'DeviceLocked') {
|
||||
onValueLocked(challengeValue)
|
||||
@@ -250,7 +250,7 @@ export const Authenticate = ({
|
||||
onValueChange(newChallengeValue)
|
||||
return validateChallengeValue(newChallengeValue)
|
||||
})
|
||||
.catch(error_1 => {
|
||||
.catch((error_1) => {
|
||||
onValueChange({ ...challengeValue, value: false })
|
||||
FingerprintScanner.release()
|
||||
if (error_1.name !== 'SystemCancel') {
|
||||
@@ -340,7 +340,7 @@ export const Authenticate = ({
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const remove = application?.getAppState().addStateChangeObserver(state => {
|
||||
const remove = application?.getAppState().addStateChangeObserver((state) => {
|
||||
if (state === AppStateType.ResumingFromBackground) {
|
||||
if (!isAuthenticating.current) {
|
||||
beginAuthenticatingForNextChallengeReason()
|
||||
@@ -440,7 +440,7 @@ export const Authenticate = ({
|
||||
Keyboard.dismiss()
|
||||
|
||||
const biometricChallengeValue = Object.values(challengeValues).find(
|
||||
value => value.prompt.validation === ChallengeValidation.Biometric,
|
||||
(value) => value.prompt.validation === ChallengeValidation.Biometric,
|
||||
)
|
||||
const state = challengeValueStates[biometricChallengeValue?.prompt.id as number]
|
||||
if (state === AuthenticationValueStateType.Locked || state === AuthenticationValueStateType.Success) {
|
||||
@@ -506,8 +506,8 @@ export const Authenticate = ({
|
||||
const readyToSubmit = useMemo(
|
||||
() =>
|
||||
Object.values(challengeValues)
|
||||
.map(challengeValue => challengeValue.value)
|
||||
.filter(value => !value).length === 0,
|
||||
.map((challengeValue) => challengeValue.value)
|
||||
.filter((value) => !value).length === 0,
|
||||
[challengeValues],
|
||||
)
|
||||
|
||||
@@ -556,7 +556,7 @@ export const Authenticate = ({
|
||||
key={Platform.OS === 'android' ? keyboardType : undefined}
|
||||
ref={Array.of(firstInputRef, secondInputRef, thirdInputRef, fourthInputRef)[index] as any}
|
||||
placeholder={challengeValue.prompt.placeholder}
|
||||
onChangeText={text => {
|
||||
onChangeText={(text) => {
|
||||
onValueChange({ ...challengeValue, value: text })
|
||||
}}
|
||||
value={(challengeValue.value || '') as string}
|
||||
@@ -621,7 +621,7 @@ export const Authenticate = ({
|
||||
}
|
||||
|
||||
const isPending = useMemo(
|
||||
() => Object.values(challengeValueStates).findIndex(state => state === AuthenticationValueStateType.Pending) >= 0,
|
||||
() => Object.values(challengeValueStates).findIndex((state) => state === AuthenticationValueStateType.Pending) >= 0,
|
||||
[challengeValueStates],
|
||||
)
|
||||
|
||||
@@ -644,7 +644,7 @@ export const Authenticate = ({
|
||||
|
||||
return (
|
||||
<HeaderHeightContext.Consumer>
|
||||
{headerHeight => (
|
||||
{(headerHeight) => (
|
||||
<StyledKeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={headerHeight}
|
||||
|
||||
@@ -220,7 +220,7 @@ export const ComponentView = ({
|
||||
onLoadStart()
|
||||
}
|
||||
|
||||
const onShouldStartLoadWithRequest: OnShouldStartLoadWithRequest = request => {
|
||||
const onShouldStartLoadWithRequest: OnShouldStartLoadWithRequest = (request) => {
|
||||
log('Setting last iframe URL to', request.url)
|
||||
/** The first request can typically be 'about:blank', which we want to ignore */
|
||||
if (!didLoadRootUrl.current) {
|
||||
|
||||
@@ -88,7 +88,7 @@ export class Compose extends React.Component<PropsWhenNavigating | PropsWhenRend
|
||||
this.context = context
|
||||
|
||||
const noteUuid = 'noteUuid' in props ? props.noteUuid : props.route.params.noteUuid
|
||||
const editor = this.context.editorGroup.itemControllers.find(c => c.item.uuid === noteUuid) as NoteViewController
|
||||
const editor = this.context.editorGroup.itemControllers.find((c) => c.item.uuid === noteUuid) as NoteViewController
|
||||
if (!editor) {
|
||||
throw 'Unable to to find note controller'
|
||||
}
|
||||
@@ -149,7 +149,7 @@ export class Compose extends React.Component<PropsWhenNavigating | PropsWhenRend
|
||||
void this.reloadComponentEditorState()
|
||||
})
|
||||
|
||||
this.removeAppEventObserver = this.context?.addEventObserver(async eventName => {
|
||||
this.removeAppEventObserver = this.context?.addEventObserver(async (eventName) => {
|
||||
if (eventName === ApplicationEvent.CompletedFullSync) {
|
||||
/** if we're still dirty, don't change status, a sync is likely upcoming. */
|
||||
if (!this.note.dirty && this.state.saveError) {
|
||||
@@ -169,7 +169,7 @@ export class Compose extends React.Component<PropsWhenNavigating | PropsWhenRend
|
||||
}
|
||||
})
|
||||
|
||||
this.removeStateEventObserver = this.context?.getAppState().addStateEventObserver(state => {
|
||||
this.removeStateEventObserver = this.context?.getAppState().addStateEventObserver((state) => {
|
||||
if (state === AppStateEventType.DrawerOpen) {
|
||||
this.dismissKeyboard()
|
||||
/**
|
||||
@@ -352,7 +352,7 @@ export class Compose extends React.Component<PropsWhenNavigating | PropsWhenRend
|
||||
|
||||
await this.context.mutator.changeItem(
|
||||
this.note,
|
||||
mutator => {
|
||||
(mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
|
||||
if (newTitle != null) {
|
||||
@@ -483,7 +483,7 @@ export class Compose extends React.Component<PropsWhenNavigating | PropsWhenRend
|
||||
return (
|
||||
<Container>
|
||||
<ThemeContext.Consumer>
|
||||
{theme => (
|
||||
{(theme) => (
|
||||
<>
|
||||
{this.noteLocked && (
|
||||
<LockedContainer>
|
||||
@@ -507,7 +507,7 @@ export class Compose extends React.Component<PropsWhenNavigating | PropsWhenRend
|
||||
</LockedContainer>
|
||||
)}
|
||||
<ThemeServiceContext.Consumer>
|
||||
{themeService => (
|
||||
{(themeService) => (
|
||||
<>
|
||||
<NoteTitleInput
|
||||
testID="noteTitleField"
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Container, Input } from './InputModal.styled'
|
||||
|
||||
type Props = ModalStackNavigationProp<typeof SCREEN_INPUT_MODAL_FILE_NAME>
|
||||
|
||||
export const FileInputModal: FC<Props> = props => {
|
||||
export const FileInputModal: FC<Props> = (props) => {
|
||||
const { file, renameFile } = props.route.params
|
||||
const themeService = useContext(ThemeServiceContext)
|
||||
const application = useSafeApplicationContext()
|
||||
|
||||
@@ -41,7 +41,7 @@ export const TagInputModal = (props: Props) => {
|
||||
const onSubmit = useCallback(async () => {
|
||||
if (props.route.params.tagUuid) {
|
||||
const tag = application?.items.findItem(props.route.params.tagUuid) as SNTag
|
||||
await application?.mutator.changeItem(tag, mutator => {
|
||||
await application?.mutator.changeItem(tag, (mutator) => {
|
||||
const tagMutator = mutator as TagMutator
|
||||
tagMutator.title = text
|
||||
if (props.route.params.noteUuid) {
|
||||
@@ -54,7 +54,7 @@ export const TagInputModal = (props: Props) => {
|
||||
} else {
|
||||
const tag = await application!.mutator.findOrCreateTag(text)
|
||||
if (props.route.params.noteUuid) {
|
||||
await application?.mutator.changeItem(tag, mutator => {
|
||||
await application?.mutator.changeItem(tag, (mutator) => {
|
||||
const tagMutator = mutator as TagMutator
|
||||
const note = application.items.findItem(props.route.params.noteUuid!)
|
||||
if (note) {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { ButtonCell } from '@Root/Components/ButtonCell'
|
||||
import { SectionedTableCell } from '@Root/Components/SectionedTableCell'
|
||||
import { TableSection } from '@Root/Components/TableSection'
|
||||
import { useSafeApplicationContext } from '@Root/Hooks/useSafeApplicationContext'
|
||||
import { ModalStackNavigationProp } from '@Root/ModalStack'
|
||||
import { SCREEN_INPUT_MODAL_WORKSPACE_NAME } from '@Root/Screens/screens'
|
||||
import { ThemeServiceContext } from '@Style/ThemeService'
|
||||
import React, { FC, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { TextInput } from 'react-native'
|
||||
import { Container, Input } from './InputModal.styled'
|
||||
|
||||
type Props = ModalStackNavigationProp<typeof SCREEN_INPUT_MODAL_WORKSPACE_NAME>
|
||||
|
||||
export const WorkspaceInputModal: FC<Props> = (props) => {
|
||||
const { descriptor, renameWorkspace } = props.route.params
|
||||
const themeService = useContext(ThemeServiceContext)
|
||||
const application = useSafeApplicationContext()
|
||||
|
||||
const workspaceNameInputRef = useRef<TextInput>(null)
|
||||
|
||||
const [workspaceName, setWorkspaceName] = useState(descriptor.label)
|
||||
|
||||
const onSubmit = async () => {
|
||||
const trimmedWorkspaceName = workspaceName.trim()
|
||||
if (trimmedWorkspaceName === '') {
|
||||
setWorkspaceName(descriptor.label)
|
||||
await application?.alertService.alert('Workspace name cannot be empty')
|
||||
workspaceNameInputRef.current?.focus()
|
||||
return
|
||||
}
|
||||
await renameWorkspace(descriptor, trimmedWorkspaceName)
|
||||
void application.sync.sync()
|
||||
props.navigation.goBack()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
workspaceNameInputRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<TableSection>
|
||||
<SectionedTableCell textInputCell first={true}>
|
||||
<Input
|
||||
ref={workspaceNameInputRef as any}
|
||||
placeholder={'Workspace name'}
|
||||
onChangeText={setWorkspaceName}
|
||||
value={workspaceName}
|
||||
autoCorrect={false}
|
||||
autoCapitalize={'none'}
|
||||
keyboardAppearance={themeService?.keyboardColorForActiveTheme()}
|
||||
underlineColorAndroid={'transparent'}
|
||||
onSubmitEditing={onSubmit}
|
||||
/>
|
||||
</SectionedTableCell>
|
||||
|
||||
<ButtonCell maxHeight={45} disabled={workspaceName.length === 0} title={'Save'} bold onPress={onSubmit} />
|
||||
</TableSection>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
@@ -64,7 +64,7 @@ const useSessions = (): [
|
||||
setErrorMessage('An unknown error occurred while revoking the session.')
|
||||
}
|
||||
} else {
|
||||
setSessions(sessions.filter(session => session.uuid !== uuid))
|
||||
setSessions(sessions.filter((session) => session.uuid !== uuid))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export const ManageSessions: React.FC = () => {
|
||||
|
||||
return (
|
||||
<FlatList<RemoteSession>
|
||||
keyExtractor={item => item.uuid}
|
||||
keyExtractor={(item) => item.uuid}
|
||||
contentContainerStyle={{ paddingBottom: insets.bottom }}
|
||||
initialNumToRender={7}
|
||||
windowSize={7}
|
||||
|
||||
@@ -11,7 +11,7 @@ type Props = {
|
||||
currentSession: boolean
|
||||
}
|
||||
|
||||
const Container = styled(SectionedTableCellTouchableHighlight).attrs(props => ({
|
||||
const Container = styled(SectionedTableCellTouchableHighlight).attrs((props) => ({
|
||||
underlayColor: props.theme.stylekitBorderColor,
|
||||
}))<TableCellProps>`
|
||||
padding-top: ${12}px;
|
||||
@@ -21,7 +21,7 @@ const ButtonContainer = styled.View``
|
||||
|
||||
type ButtonLabelProps = Pick<Props, 'disabled'>
|
||||
const ButtonLabel = styled.Text<ButtonLabelProps>`
|
||||
color: ${props => {
|
||||
color: ${(props) => {
|
||||
let color = props.theme.stylekitForegroundColor
|
||||
if (props.disabled) {
|
||||
color = 'gray'
|
||||
@@ -29,7 +29,7 @@ const ButtonLabel = styled.Text<ButtonLabelProps>`
|
||||
return color
|
||||
}};
|
||||
font-weight: bold;
|
||||
font-size: ${props => props.theme.mainTextFontSize}px;
|
||||
font-size: ${(props) => props.theme.mainTextFontSize}px;
|
||||
${({ disabled }) =>
|
||||
disabled &&
|
||||
css`
|
||||
@@ -46,7 +46,7 @@ export const SubTitleText = styled.Text<{ current: boolean }>`
|
||||
line-height: 21px;
|
||||
`
|
||||
|
||||
export const SessionCell: React.FC<Props> = props => (
|
||||
export const SessionCell: React.FC<Props> = (props) => (
|
||||
<Container testID={props.testID} disabled={props.disabled} onPress={props.onPress}>
|
||||
<ButtonContainer>
|
||||
<ButtonLabel disabled={props.disabled}>{props.title}</ButtonLabel>
|
||||
|
||||
@@ -61,9 +61,9 @@ export const NoteHistory = (props: Props) => {
|
||||
fontStyle={{
|
||||
color: theme.stylekitForegroundColor,
|
||||
}}
|
||||
values={routes.map(route => route.title)}
|
||||
values={routes.map((route) => route.title)}
|
||||
selectedIndex={tabBarProps.navigationState.index}
|
||||
onChange={event => {
|
||||
onChange={(event) => {
|
||||
setIndex(event.nativeEvent.selectedSegmentIndex)
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -12,7 +12,7 @@ type Props = {
|
||||
subTitle?: string
|
||||
}
|
||||
|
||||
const Container = styled(SectionedTableCellTouchableHighlight).attrs(props => ({
|
||||
const Container = styled(SectionedTableCellTouchableHighlight).attrs((props) => ({
|
||||
underlayColor: props.theme.stylekitBorderColor,
|
||||
}))<TableCellProps>`
|
||||
padding-top: ${12}px;
|
||||
@@ -22,7 +22,7 @@ const ButtonContainer = styled.View``
|
||||
|
||||
type ButtonLabelProps = Pick<Props, 'disabled'>
|
||||
const ButtonLabel = styled.Text<ButtonLabelProps>`
|
||||
color: ${props => {
|
||||
color: ${(props) => {
|
||||
let color = props.theme.stylekitForegroundColor
|
||||
if (props.disabled) {
|
||||
color = 'gray'
|
||||
@@ -30,7 +30,7 @@ const ButtonLabel = styled.Text<ButtonLabelProps>`
|
||||
return color
|
||||
}};
|
||||
font-weight: bold;
|
||||
font-size: ${props => props.theme.mainTextFontSize}px;
|
||||
font-size: ${(props) => props.theme.mainTextFontSize}px;
|
||||
${({ disabled }) =>
|
||||
disabled &&
|
||||
css`
|
||||
@@ -45,7 +45,7 @@ export const SubTitleText = styled.Text`
|
||||
line-height: 21px;
|
||||
`
|
||||
|
||||
export const NoteHistoryCell: React.FC<Props> = props => (
|
||||
export const NoteHistoryCell: React.FC<Props> = (props) => (
|
||||
<Container
|
||||
first={props.first}
|
||||
last={props.last}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const NoteHistoryPreview = ({
|
||||
} else {
|
||||
await application?.mutator.changeAndSaveItem(
|
||||
originalNote,
|
||||
mutator => {
|
||||
(mutator) => {
|
||||
mutator.setCustomContent(revision.payload.content)
|
||||
},
|
||||
true,
|
||||
|
||||
@@ -70,7 +70,7 @@ export const RemoteHistory: React.FC<Props> = ({ note, onPress }) => {
|
||||
|
||||
return (
|
||||
<FlatList<RevisionListEntry>
|
||||
keyExtractor={item => item.uuid}
|
||||
keyExtractor={(item) => item.uuid}
|
||||
contentContainerStyle={{ paddingBottom: insets.bottom }}
|
||||
initialNumToRender={10}
|
||||
windowSize={10}
|
||||
|
||||
@@ -42,7 +42,7 @@ export const SessionHistory: React.FC<Props> = ({ note, onPress }) => {
|
||||
|
||||
return (
|
||||
<FlatList<NoteHistoryEntry>
|
||||
keyExtractor={item => item.previewTitle()}
|
||||
keyExtractor={(item) => item.previewTitle()}
|
||||
contentContainerStyle={{ paddingBottom: insets.bottom }}
|
||||
initialNumToRender={10}
|
||||
windowSize={10}
|
||||
|
||||
@@ -8,7 +8,7 @@ export const Container = styled.View<{ selected: boolean; distance: number }>`
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: ${props => props.distance}px 0 0 ${props => props.distance}px;
|
||||
padding: ${(props) => props.distance}px 0 0 ${(props) => props.distance}px;
|
||||
background-color: ${({ theme, selected }) => {
|
||||
return selected ? theme.stylekitInfoColor : theme.stylekitBackgroundColor
|
||||
}};
|
||||
@@ -16,10 +16,10 @@ export const Container = styled.View<{ selected: boolean; distance: number }>`
|
||||
export const NoteDataContainer = styled.View<{ distance: number }>`
|
||||
border-bottom-color: ${({ theme }) => hexToRGBA(theme.stylekitBorderColor, 0.75)};
|
||||
border-bottom-width: 1px;
|
||||
padding-bottom: ${props => props.distance}px;
|
||||
padding-bottom: ${(props) => props.distance}px;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
padding-right: ${props => props.distance}px;
|
||||
padding-right: ${(props) => props.distance}px;
|
||||
`
|
||||
export const DeletedText = styled.Text`
|
||||
color: ${({ theme }) => theme.stylekitInfoColor};
|
||||
@@ -54,7 +54,7 @@ export const TagText = styled.Text<{ selected: boolean }>`
|
||||
color: ${({ theme, selected }) => {
|
||||
return selected ? theme.stylekitInfoContrastColor : theme.stylekitForegroundColor
|
||||
}};
|
||||
opacity: ${props => (props.selected ? 0.8 : 0.5)};
|
||||
opacity: ${(props) => (props.selected ? 0.8 : 0.5)};
|
||||
`
|
||||
export const DetailsText = styled(TagText)`
|
||||
margin-right: 0;
|
||||
|
||||
@@ -62,7 +62,7 @@ export const NoteCell = ({
|
||||
await application?.mutator.deleteItem(note)
|
||||
},
|
||||
() => {
|
||||
void changeNote(mutator => {
|
||||
void changeNote((mutator) => {
|
||||
mutator.trashed = true
|
||||
}, false)
|
||||
},
|
||||
@@ -105,7 +105,7 @@ export const NoteCell = ({
|
||||
text: note.pinned ? 'Unpin' : 'Pin',
|
||||
key: 'pin',
|
||||
callback: () =>
|
||||
changeNote(mutator => {
|
||||
changeNote((mutator) => {
|
||||
mutator.pinned = !note.pinned
|
||||
}, false),
|
||||
})
|
||||
@@ -123,7 +123,7 @@ export const NoteCell = ({
|
||||
return
|
||||
}
|
||||
|
||||
void changeNote(mutator => {
|
||||
void changeNote((mutator) => {
|
||||
mutator.archived = !note.archived
|
||||
}, false)
|
||||
},
|
||||
@@ -133,7 +133,7 @@ export const NoteCell = ({
|
||||
text: note.locked ? 'Enable editing' : 'Prevent editing',
|
||||
key: 'lock',
|
||||
callback: () =>
|
||||
changeNote(mutator => {
|
||||
changeNote((mutator) => {
|
||||
mutator.locked = !note.locked
|
||||
}, false),
|
||||
})
|
||||
@@ -157,7 +157,7 @@ export const NoteCell = ({
|
||||
text: 'Restore',
|
||||
key: 'restore-note',
|
||||
callback: () => {
|
||||
void changeNote(mutator => {
|
||||
void changeNote((mutator) => {
|
||||
mutator.trashed = false
|
||||
}, false)
|
||||
},
|
||||
|
||||
@@ -44,7 +44,7 @@ export const NoteCellFlags = ({ note, highlight }: { note: SNNote; highlight: bo
|
||||
|
||||
return flags.length > 0 ? (
|
||||
<FlagsContainer>
|
||||
{flags.map(flag => (
|
||||
{flags.map((flag) => (
|
||||
<FlagContainer key={flag.text.concat(flag.color)} color={flag.color} selected={highlight}>
|
||||
<FlagLabel selected={highlight}>{flag.text}</FlagLabel>
|
||||
</FlagContainer>
|
||||
|
||||
@@ -15,7 +15,7 @@ export const styles = StyleSheet.create({
|
||||
})
|
||||
|
||||
export const Container = styled.View`
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
flex: 1;
|
||||
`
|
||||
|
||||
@@ -32,8 +32,8 @@ interface LoadingTextProps {
|
||||
export const LoadingText = styled.Text<LoadingTextProps>`
|
||||
position: absolute;
|
||||
opacity: 0.5;
|
||||
color: ${props => props.theme.stylekitForegroundColor};
|
||||
text-align: ${props => props.textAlign ?? 'left'};
|
||||
color: ${(props) => props.theme.stylekitForegroundColor};
|
||||
text-align: ${(props) => props.textAlign ?? 'left'};
|
||||
`
|
||||
|
||||
export const HeaderContainer = styled.View`
|
||||
@@ -43,7 +43,7 @@ export const HeaderContainer = styled.View`
|
||||
`
|
||||
|
||||
export const SearchBarContainer = styled.View`
|
||||
background-color: ${props => props.theme.stylekitBackgroundColor};
|
||||
background-color: ${(props) => props.theme.stylekitBackgroundColor};
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export const NoteList = (props: Props) => {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeStateEventObserver = application?.getAppState().addStateEventObserver(state => {
|
||||
const unsubscribeStateEventObserver = application?.getAppState().addStateEventObserver((state) => {
|
||||
if (state === AppStateEventType.DrawerOpen) {
|
||||
dismissKeyboard()
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export const NoteList = (props: Props) => {
|
||||
}, [noteListScrolled, props.notes])
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeTagChangedEventObserver = application?.getAppState().addStateChangeObserver(event => {
|
||||
const unsubscribeTagChangedEventObserver = application?.getAppState().addStateChangeObserver((event) => {
|
||||
if (event === AppStateType.TagChanged) {
|
||||
scrollListToTop()
|
||||
}
|
||||
@@ -223,7 +223,7 @@ export const NoteList = (props: Props) => {
|
||||
<FlatList
|
||||
ref={noteListRef}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item?.uuid}
|
||||
keyExtractor={(item) => item?.uuid}
|
||||
contentContainerStyle={[{ paddingBottom: insets.bottom }, props.notes.length > 0 ? {} : { height: '100%' }]}
|
||||
initialNumToRender={6}
|
||||
windowSize={6}
|
||||
|
||||
@@ -83,7 +83,7 @@ export const Notes = React.memo(
|
||||
title = selectedTag.title
|
||||
if (selectedTag instanceof SNTag && selectedTag.parentId) {
|
||||
const parents = application.items.getTagParentChain(selectedTag)
|
||||
const hierarchy = parents.map(tag => tag.title).join(' ⫽ ')
|
||||
const hierarchy = parents.map((tag) => tag.title).join(' ⫽ ')
|
||||
subTitle = hierarchy.length > 0 ? `in ${hierarchy}` : undefined
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ export const Notes = React.memo(
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
const removeEditorObserver = application.editorGroup.addActiveControllerChangeObserver(activeEditor => {
|
||||
const removeEditorObserver = application.editorGroup.addActiveControllerChangeObserver((activeEditor) => {
|
||||
if (mounted) {
|
||||
setSelectedNoteId(activeEditor?.item?.uuid)
|
||||
}
|
||||
@@ -305,7 +305,7 @@ export const Notes = React.memo(
|
||||
toggleIncludeTrashed,
|
||||
])
|
||||
|
||||
const getFirstSelectableNote = useCallback((newNotes: SNNote[]) => newNotes.find(note => !note.protected), [])
|
||||
const getFirstSelectableNote = useCallback((newNotes: SNNote[]) => newNotes.find((note) => !note.protected), [])
|
||||
|
||||
const selectFirstNote = useCallback(
|
||||
(newNotes: SNNote[]) => {
|
||||
@@ -475,7 +475,7 @@ export const Notes = React.memo(
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const removeAppStateChangeHandler = application.getAppState().addStateChangeObserver(state => {
|
||||
const removeAppStateChangeHandler = application.getAppState().addStateChangeObserver((state) => {
|
||||
if (state === AppStateType.TagChanged) {
|
||||
reloadNotesDisplayOptions()
|
||||
reloadNotes(true, true)
|
||||
|
||||
@@ -11,17 +11,17 @@ const Container = styled.View`
|
||||
padding: ${PADDING}px;
|
||||
border-width: 1px;
|
||||
border-radius: 4px;
|
||||
border-color: ${props => props.theme.stylekitBorderColor};
|
||||
border-color: ${(props) => props.theme.stylekitBorderColor};
|
||||
`
|
||||
const CenterContainer = styled.View`
|
||||
justify-content: center;
|
||||
`
|
||||
const UserIcon = styled(Icon)`
|
||||
font-size: 24px;
|
||||
color: ${props => props.theme.stylekitInfoColor};
|
||||
color: ${(props) => props.theme.stylekitInfoColor};
|
||||
`
|
||||
const ForwardIcon = styled(UserIcon)`
|
||||
color: ${props => props.theme.stylekitNeutralColor};
|
||||
color: ${(props) => props.theme.stylekitNeutralColor};
|
||||
`
|
||||
const TextContainer = styled.View`
|
||||
flex: 1;
|
||||
@@ -30,12 +30,12 @@ const TextContainer = styled.View`
|
||||
const BoldText = styled.Text`
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: ${props => props.theme.stylekitForegroundColor};
|
||||
color: ${(props) => props.theme.stylekitForegroundColor};
|
||||
`
|
||||
const SubText = styled.Text`
|
||||
margin-top: 2px;
|
||||
font-size: 11px;
|
||||
color: ${props => props.theme.stylekitNeutralColor};
|
||||
color: ${(props) => props.theme.stylekitNeutralColor};
|
||||
`
|
||||
|
||||
export { Touchable, Container, CenterContainer, UserIcon, ForwardIcon, TextContainer, BoldText, SubText }
|
||||
|
||||
@@ -26,7 +26,7 @@ export const Root = () => {
|
||||
const [keyboardHeight, setKeyboardHeight] = useState<number | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
const removeStateObserver = application?.getAppState().addStateChangeObserver(state => {
|
||||
const removeStateObserver = application?.getAppState().addStateChangeObserver((state) => {
|
||||
if (state === AppStateType.GainingFocus) {
|
||||
void application.sync.sync()
|
||||
}
|
||||
@@ -50,7 +50,7 @@ export const Root = () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const removeNoteObserver = application?.editorGroup.addActiveControllerChangeObserver(activeController => {
|
||||
const removeNoteObserver = application?.editorGroup.addActiveControllerChangeObserver((activeController) => {
|
||||
if (activeController instanceof NoteViewController) {
|
||||
setActiveNoteView(activeController)
|
||||
} else {
|
||||
@@ -100,7 +100,7 @@ export const Root = () => {
|
||||
}
|
||||
|
||||
const toggleNoteList = () => {
|
||||
setNoteListCollapsed(value => !value)
|
||||
setNoteListCollapsed((value) => !value)
|
||||
}
|
||||
|
||||
const collapseIconBottomPosition = (keyboardHeight ?? 0) > (height ?? 0) / 2 ? (keyboardHeight ?? 0) + 40 : '50%'
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useSignedIn } from '@Lib/SnjsHelperHooks'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { ApplicationContext } from '@Root/ApplicationContext'
|
||||
import { ButtonCell } from '@Root/Components/ButtonCell'
|
||||
import { SectionedAccessoryTableCell } from '@Root/Components/SectionedAccessoryTableCell'
|
||||
import { SectionedOptionsTableCell } from '@Root/Components/SectionedOptionsTableCell'
|
||||
import { SectionHeader } from '@Root/Components/SectionHeader'
|
||||
import { TableSection } from '@Root/Components/TableSection'
|
||||
import { useSafeApplicationContext } from '@Root/Hooks/useSafeApplicationContext'
|
||||
import { ModalStackNavigationProp } from '@Root/ModalStack'
|
||||
import { SCREEN_MANAGE_SESSIONS, SCREEN_SETTINGS } from '@Root/Screens/screens'
|
||||
import { ButtonType, PrefKey } from '@standardnotes/snjs'
|
||||
import moment from 'moment'
|
||||
import React, { useCallback, useContext, useMemo, useState } from 'react'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Platform } from 'react-native'
|
||||
import DocumentPicker from 'react-native-document-picker'
|
||||
import RNFS from 'react-native-fs'
|
||||
@@ -22,7 +22,8 @@ type Props = {
|
||||
|
||||
export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
// Context
|
||||
const application = useContext(ApplicationContext)
|
||||
const application = useSafeApplicationContext()
|
||||
|
||||
const [signedIn] = useSignedIn()
|
||||
const navigation = useNavigation<ModalStackNavigationProp<typeof SCREEN_SETTINGS>['navigation']>()
|
||||
|
||||
@@ -57,7 +58,7 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
|
||||
const email = useMemo(() => {
|
||||
if (signedIn) {
|
||||
const user = application?.getUser()
|
||||
const user = application.getUser()
|
||||
return user?.email
|
||||
}
|
||||
return
|
||||
@@ -76,25 +77,25 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
|
||||
const destroyLocalData = async () => {
|
||||
if (
|
||||
await application?.alertService?.confirm(
|
||||
await application.alertService.confirm(
|
||||
'Signing out will remove all data from this device, including notes and tags. Make sure your data is synced before proceeding.',
|
||||
'Sign Out?',
|
||||
'Sign Out',
|
||||
ButtonType.Danger,
|
||||
)
|
||||
) {
|
||||
await application!.user.signOut()
|
||||
await application.user.signOut()
|
||||
}
|
||||
}
|
||||
|
||||
const exportData = useCallback(
|
||||
async (encrypted: boolean) => {
|
||||
setExporting(true)
|
||||
const result = await application?.getBackupsService().export(encrypted)
|
||||
const result = await application.getBackupsService().export(encrypted)
|
||||
if (result) {
|
||||
const exportDate = new Date()
|
||||
setLastExportDate(exportDate)
|
||||
void application?.getLocalPreferences().setUserPrefValue(PrefKey.MobileLastExportDate, exportDate)
|
||||
void application.getLocalPreferences().setUserPrefValue(PrefKey.MobileLastExportDate, exportDate)
|
||||
}
|
||||
setExporting(false)
|
||||
},
|
||||
@@ -103,25 +104,25 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
|
||||
const readImportFile = async (fileUri: string): Promise<any> => {
|
||||
return RNFS.readFile(fileUri)
|
||||
.then(result => JSON.parse(result))
|
||||
.then((result) => JSON.parse(result))
|
||||
.catch(() => {
|
||||
void application!.alertService!.alert('Unable to open file. Ensure it is a proper JSON file and try again.')
|
||||
void application.alertService.alert('Unable to open file. Ensure it is a proper JSON file and try again.')
|
||||
})
|
||||
}
|
||||
|
||||
const performImport = async (data: any) => {
|
||||
const result = await application!.mutator.importData(data)
|
||||
const result = await application.mutator.importData(data)
|
||||
if (!result) {
|
||||
return
|
||||
} else if ('error' in result) {
|
||||
void application!.alertService!.alert(result.error.text)
|
||||
void application.alertService.alert(result.error.text)
|
||||
} else if (result.errorCount) {
|
||||
void application!.alertService!.alert(
|
||||
void application.alertService.alert(
|
||||
`Import complete. ${result.errorCount} items were not imported because ` +
|
||||
'there was an error decrypting them. Make sure the password is correct and try again.',
|
||||
)
|
||||
} else {
|
||||
void application!.alertService!.alert('Your data has been successfully imported.')
|
||||
void application.alertService.alert('Your data has been successfully imported.')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,10 +140,10 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
setImporting(true)
|
||||
if (data.version || data.auth_params || data.keyParams) {
|
||||
const version = data.version || data.keyParams?.version || data.auth_params?.version
|
||||
if (application!.protocolService.supportedVersions().includes(version)) {
|
||||
if (application.protocolService.supportedVersions().includes(version)) {
|
||||
await performImport(data)
|
||||
} else {
|
||||
void application!.alertService.alert(
|
||||
void application.alertService.alert(
|
||||
'This backup file was created using an unsupported version of the application ' +
|
||||
'and cannot be imported here. Please update your application and try again.',
|
||||
)
|
||||
@@ -159,7 +160,7 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
async (option: { key: string }) => {
|
||||
const encrypted = option.key === 'encrypted'
|
||||
if (encrypted && !encryptionAvailable) {
|
||||
void application?.alertService!.alert(
|
||||
void application.alertService.alert(
|
||||
'You must be signed in, or have a local passcode set, to generate an encrypted export file.',
|
||||
'Not Available',
|
||||
'OK',
|
||||
@@ -176,12 +177,12 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
}, [navigation])
|
||||
|
||||
const showDataBackupAlert = useCallback(() => {
|
||||
void application?.alertService.alert(
|
||||
void application.alertService.alert(
|
||||
'Because you are using the app offline without a sync account, it is your responsibility to keep your data safe and backed up. It is recommended you export a backup of your data at least once a week, or, to sign up for a sync account so that your data is backed up automatically.',
|
||||
'No Backups Created',
|
||||
'OK',
|
||||
)
|
||||
}, [application?.alertService])
|
||||
}, [application.alertService])
|
||||
|
||||
return (
|
||||
<TableSection>
|
||||
@@ -192,7 +193,7 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
<ButtonCell
|
||||
testID="manageSessionsButton"
|
||||
leftAligned={true}
|
||||
first={true}
|
||||
first={false}
|
||||
title={'Manage Sessions'}
|
||||
onPress={openManageSessions}
|
||||
/>
|
||||
|
||||
@@ -36,7 +36,7 @@ export const PreferencesSection = () => {
|
||||
|
||||
const toggleReverseSort = () => {
|
||||
void application.getLocalPreferences().setUserPrefValue(PrefKey.MobileSortNotesReverse, !sortReverse)
|
||||
setSortReverse(value => !value)
|
||||
setSortReverse((value) => !value)
|
||||
}
|
||||
|
||||
const changeSortOption = (key: CollectionSortProperty) => {
|
||||
@@ -45,15 +45,15 @@ export const PreferencesSection = () => {
|
||||
}
|
||||
const toggleNotesPreviewHidden = () => {
|
||||
void application.getLocalPreferences().setUserPrefValue(PrefKey.MobileNotesHideNotePreview, !hidePreviews)
|
||||
setHidePreviews(value => !value)
|
||||
setHidePreviews((value) => !value)
|
||||
}
|
||||
const toggleNotesDateHidden = () => {
|
||||
void application.getLocalPreferences().setUserPrefValue(PrefKey.MobileNotesHideDate, !hideDates)
|
||||
setHideDates(value => !value)
|
||||
setHideDates((value) => !value)
|
||||
}
|
||||
const toggleNotesEditorIconHidden = () => {
|
||||
void application.getLocalPreferences().setUserPrefValue(PrefKey.MobileNotesHideEditorIcon, !hideEditorIcon)
|
||||
setHideEditorIcon(value => !value)
|
||||
setHideEditorIcon((value) => !value)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
import { SectionedAccessoryTableCell } from '@Components/SectionedAccessoryTableCell'
|
||||
import { SectionHeader } from '@Components/SectionHeader'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { TableSection } from '@Root/Components/TableSection'
|
||||
import { useSafeApplicationContext } from '@Root/Hooks/useSafeApplicationContext'
|
||||
import { useSafeApplicationGroupContext } from '@Root/Hooks/useSafeApplicationGroupContext'
|
||||
import { ModalStackNavigationProp } from '@Root/ModalStack'
|
||||
import { SCREEN_INPUT_MODAL_WORKSPACE_NAME, SCREEN_SETTINGS } from '@Screens/screens'
|
||||
import { ApplicationDescriptor, ApplicationGroupEvent, ButtonType } from '@standardnotes/snjs'
|
||||
import { CustomActionSheetOption, useCustomActionSheet } from '@Style/CustomActionSheet'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
export const WorkspacesSection = () => {
|
||||
const application = useSafeApplicationContext()
|
||||
const appGroup = useSafeApplicationGroupContext()
|
||||
const navigation = useNavigation<ModalStackNavigationProp<typeof SCREEN_SETTINGS>['navigation']>()
|
||||
|
||||
const [applicationDescriptors, setApplicationDescriptors] = useState<ApplicationDescriptor[]>([])
|
||||
|
||||
enum WorkspaceAction {
|
||||
AddAnother = 'Add another workspace',
|
||||
Activate = 'Activate',
|
||||
Rename = 'Rename',
|
||||
Remove = 'Remove',
|
||||
SignOutAll = 'Sign out all workspaces',
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let descriptors = appGroup.getDescriptors()
|
||||
setApplicationDescriptors(descriptors)
|
||||
|
||||
const removeAppGroupObserver = appGroup.addEventObserver((event) => {
|
||||
if (event === ApplicationGroupEvent.DescriptorsDataChanged) {
|
||||
descriptors = appGroup.getDescriptors()
|
||||
setApplicationDescriptors(descriptors)
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
removeAppGroupObserver()
|
||||
}
|
||||
}, [appGroup])
|
||||
|
||||
const { showActionSheet } = useCustomActionSheet()
|
||||
|
||||
const getWorkspaceActionConfirmation = useCallback(
|
||||
async (action: WorkspaceAction): Promise<boolean> => {
|
||||
const { Info, Danger } = ButtonType
|
||||
const { AddAnother, Activate, Remove, SignOutAll } = WorkspaceAction
|
||||
let message = ''
|
||||
let buttonText = ''
|
||||
let buttonType = Info
|
||||
|
||||
switch (action) {
|
||||
case Activate:
|
||||
message = 'Your workspace will be ready for you when you come back.'
|
||||
buttonText = 'Quit App'
|
||||
break
|
||||
case AddAnother:
|
||||
message = 'Your new workspace will be ready for you when you come back.'
|
||||
buttonText = 'Quit App'
|
||||
break
|
||||
case SignOutAll:
|
||||
message =
|
||||
'Are you sure you want to sign out of all workspaces on this device? This action will restart the application.'
|
||||
buttonText = 'Sign Out All'
|
||||
break
|
||||
case Remove:
|
||||
message =
|
||||
'This action will remove this workspace and its related data from this device. Your synced data will not be affected.'
|
||||
buttonText = 'Delete Workspace'
|
||||
buttonType = Danger
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
return application.alertService.confirm(message, undefined, buttonText, buttonType)
|
||||
},
|
||||
[WorkspaceAction, application.alertService],
|
||||
)
|
||||
|
||||
const renameWorkspace = useCallback(
|
||||
async (descriptor: ApplicationDescriptor, newName: string) => {
|
||||
appGroup.renameDescriptor(descriptor, newName)
|
||||
},
|
||||
[appGroup],
|
||||
)
|
||||
|
||||
const signOutWorkspace = useCallback(async () => {
|
||||
const confirmed = await getWorkspaceActionConfirmation(WorkspaceAction.Remove)
|
||||
|
||||
if (!confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await application.user.signOut()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}, [WorkspaceAction.Remove, application.user, getWorkspaceActionConfirmation])
|
||||
|
||||
const openWorkspace = useCallback(
|
||||
async (descriptor: ApplicationDescriptor) => {
|
||||
const confirmed = await getWorkspaceActionConfirmation(WorkspaceAction.Activate)
|
||||
if (!confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
await appGroup.unloadCurrentAndActivateDescriptor(descriptor)
|
||||
},
|
||||
[WorkspaceAction.Activate, appGroup, getWorkspaceActionConfirmation],
|
||||
)
|
||||
|
||||
const getSingleWorkspaceItemOptions = useCallback(
|
||||
(descriptor: ApplicationDescriptor) => {
|
||||
const { Activate, Rename, Remove } = WorkspaceAction
|
||||
const worskspaceItemOptions: CustomActionSheetOption[] = []
|
||||
|
||||
if (descriptor.primary) {
|
||||
worskspaceItemOptions.push(
|
||||
{
|
||||
text: Rename,
|
||||
callback: () => {
|
||||
navigation.navigate(SCREEN_INPUT_MODAL_WORKSPACE_NAME, {
|
||||
descriptor,
|
||||
renameWorkspace,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
text: Remove,
|
||||
destructive: true,
|
||||
callback: signOutWorkspace,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
worskspaceItemOptions.push({
|
||||
text: Activate,
|
||||
callback: () => openWorkspace(descriptor),
|
||||
})
|
||||
}
|
||||
|
||||
return worskspaceItemOptions
|
||||
},
|
||||
[WorkspaceAction, navigation, openWorkspace, renameWorkspace, signOutWorkspace],
|
||||
)
|
||||
|
||||
const addAnotherWorkspace = useCallback(async () => {
|
||||
const confirmed = await getWorkspaceActionConfirmation(WorkspaceAction.AddAnother)
|
||||
if (!confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
await appGroup.unloadCurrentAndCreateNewDescriptor()
|
||||
}, [WorkspaceAction.AddAnother, appGroup, applicationDescriptors, getWorkspaceActionConfirmation])
|
||||
|
||||
const signOutAllWorkspaces = useCallback(async () => {
|
||||
try {
|
||||
const confirmed = await getWorkspaceActionConfirmation(WorkspaceAction.SignOutAll)
|
||||
if (!confirmed) {
|
||||
return
|
||||
}
|
||||
await appGroup.signOutAllWorkspaces()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}, [WorkspaceAction.SignOutAll, appGroup, getWorkspaceActionConfirmation])
|
||||
|
||||
return (
|
||||
<TableSection>
|
||||
<SectionHeader title={'Workspaces'} />
|
||||
{applicationDescriptors.map((descriptor, index) => {
|
||||
return (
|
||||
<SectionedAccessoryTableCell
|
||||
onPress={() => {
|
||||
const singleItemOptions = getSingleWorkspaceItemOptions(descriptor)
|
||||
|
||||
showActionSheet({
|
||||
title: '',
|
||||
options: singleItemOptions,
|
||||
})
|
||||
}}
|
||||
key={descriptor.identifier}
|
||||
text={descriptor.label}
|
||||
first={index === 0}
|
||||
selected={() => descriptor.primary}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<SectionedAccessoryTableCell onPress={addAnotherWorkspace} text={WorkspaceAction.AddAnother} key={'add-new'} />
|
||||
<SectionedAccessoryTableCell
|
||||
onPress={signOutAllWorkspaces}
|
||||
text={WorkspaceAction.SignOutAll}
|
||||
key={'sign-out-all'}
|
||||
/>
|
||||
</TableSection>
|
||||
)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { useSafeApplicationContext } from '@Root/Hooks/useSafeApplicationContext
|
||||
import { ModalStackNavigationProp } from '@Root/ModalStack'
|
||||
import { SCREEN_SETTINGS } from '@Root/Screens/screens'
|
||||
import { FilesSection } from '@Screens/Settings/Sections/FilesSection'
|
||||
import { WorkspacesSection } from '@Screens/Settings/Sections/WorkspacesSection'
|
||||
import { FeatureIdentifier } from '@standardnotes/features'
|
||||
import { ApplicationEvent, FeatureStatus } from '@standardnotes/snjs'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
@@ -30,7 +31,7 @@ export const Settings = (props: Props) => {
|
||||
}, [application])
|
||||
|
||||
useEffect(() => {
|
||||
const removeApplicationEventSubscriber = application.addEventObserver(async event => {
|
||||
const removeApplicationEventSubscriber = application.addEventObserver(async (event) => {
|
||||
if (event === ApplicationEvent.KeyStatusChanged) {
|
||||
setHasPasscode(Boolean(application.hasPasscode()))
|
||||
updateProtectionsAvailable()
|
||||
@@ -54,6 +55,7 @@ export const Settings = (props: Props) => {
|
||||
<Container keyboardShouldPersistTaps={'always'} keyboardDismissMode={'interactive'}>
|
||||
<AuthSection title="Account" signedIn={signedIn} />
|
||||
<OptionsSection encryptionAvailable={!!encryptionAvailable} title="Options" />
|
||||
<WorkspacesSection />
|
||||
<PreferencesSection />
|
||||
{application.hasAccount() && isEntitledToFiles && <FilesSection />}
|
||||
<SecuritySection
|
||||
|
||||
@@ -54,7 +54,7 @@ export const Files: FC<Props> = ({ note }) => {
|
||||
|
||||
return (
|
||||
<FilesContainer>
|
||||
{attachedFiles.sort(filesService.sortByName).map(file => {
|
||||
{attachedFiles.sort(filesService.sortByName).map((file) => {
|
||||
const iconType = application.iconsController.getIconForFileType(file.mimeType)
|
||||
|
||||
return (
|
||||
|
||||
@@ -102,7 +102,7 @@ export const Listed: FC<TProps> = ({ note }) => {
|
||||
|
||||
showActionSheet({
|
||||
title: item.display_name,
|
||||
options: item.actions.map(action => ({
|
||||
options: item.actions.map((action) => ({
|
||||
text: (action as Action).label,
|
||||
callback: async () => {
|
||||
setIsActionInProgress(true)
|
||||
|
||||
@@ -36,7 +36,7 @@ export const MainSideMenu = React.memo(({ drawerRef }: Props) => {
|
||||
const styles = useStyles(theme)
|
||||
|
||||
useEffect(() => {
|
||||
const removeTagChangeObserver = application!.getAppState().addStateChangeObserver(state => {
|
||||
const removeTagChangeObserver = application!.getAppState().addStateChangeObserver((state) => {
|
||||
if (state === AppStateType.TagChanged) {
|
||||
setSelectedTag(application!.getAppState().getSelectedTag())
|
||||
}
|
||||
@@ -160,7 +160,7 @@ export const MainSideMenu = React.memo(({ drawerRef }: Props) => {
|
||||
const themeOptions = useMemo(() => {
|
||||
const options: SideMenuOption[] = themeService!
|
||||
.systemThemes()
|
||||
.map(systemTheme => ({
|
||||
.map((systemTheme) => ({
|
||||
text: systemTheme?.name,
|
||||
key: systemTheme?.uuid,
|
||||
iconDesc: iconDescriptorForTheme(systemTheme),
|
||||
@@ -172,7 +172,7 @@ export const MainSideMenu = React.memo(({ drawerRef }: Props) => {
|
||||
.concat(
|
||||
themes
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map(mapTheme => ({
|
||||
.map((mapTheme) => ({
|
||||
text: mapTheme.name,
|
||||
key: mapTheme.uuid,
|
||||
iconDesc: iconDescriptorForTheme(mapTheme),
|
||||
@@ -207,7 +207,7 @@ export const MainSideMenu = React.memo(({ drawerRef }: Props) => {
|
||||
const onTagSelect = useCallback(
|
||||
async (tag: SNTag | SmartView) => {
|
||||
if (tag.conflictOf) {
|
||||
void application!.mutator.changeAndSaveItem(tag, mutator => {
|
||||
void application!.mutator.changeAndSaveItem(tag, (mutator) => {
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
}
|
||||
@@ -251,7 +251,7 @@ export const MainSideMenu = React.memo(({ drawerRef }: Props) => {
|
||||
<SideMenuHero testID="settingsButton" onPress={openSettings} onOutOfSyncPress={outOfSyncPressed} />
|
||||
<FlatList
|
||||
style={styles.sections}
|
||||
data={['themes-section', 'views-section', 'tags-section'].map(key => ({
|
||||
data={['themes-section', 'views-section', 'tags-section'].map((key) => ({
|
||||
key,
|
||||
themeOptions,
|
||||
onTagSelect,
|
||||
|
||||
@@ -122,7 +122,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
}
|
||||
},
|
||||
() => {
|
||||
void changeNote(mutator => {
|
||||
void changeNote((mutator) => {
|
||||
mutator.trashed = true
|
||||
}, false)
|
||||
props.drawerRef?.closeDrawer()
|
||||
@@ -225,7 +225,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
const disassociateComponentWithCurrentNote = useCallback(
|
||||
async (component: SNComponent) => {
|
||||
if (note) {
|
||||
return application.mutator.changeItem(component, m => {
|
||||
return application.mutator.changeItem(component, (m) => {
|
||||
const mutator = m as ComponentMutator
|
||||
mutator.removeAssociatedItemId(note.uuid)
|
||||
mutator.disassociateWithItem(note.uuid)
|
||||
@@ -256,7 +256,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
if (!note?.prefersPlainEditor) {
|
||||
await application.mutator.changeItem(
|
||||
note,
|
||||
mutator => {
|
||||
(mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
noteMutator.prefersPlainEditor = true
|
||||
},
|
||||
@@ -275,7 +275,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
if (prefersPlain) {
|
||||
await application.mutator.changeItem(
|
||||
note,
|
||||
mutator => {
|
||||
(mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
noteMutator.prefersPlainEditor = false
|
||||
},
|
||||
@@ -294,7 +294,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
async (component?: SNComponent) => {
|
||||
const currentDefault = application.componentManager
|
||||
.componentsForArea(ComponentArea.Editor)
|
||||
.filter(e => e.isMobileDefault)[0]
|
||||
.filter((e) => e.isMobileDefault)[0]
|
||||
|
||||
let isDefault = false
|
||||
if (!component) {
|
||||
@@ -314,14 +314,14 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
|
||||
const setAsDefault = () => {
|
||||
if (currentDefault) {
|
||||
void application.mutator.changeItem(currentDefault, m => {
|
||||
void application.mutator.changeItem(currentDefault, (m) => {
|
||||
const mutator = m as ComponentMutator
|
||||
mutator.isMobileDefault = false
|
||||
})
|
||||
}
|
||||
|
||||
if (component) {
|
||||
void application.mutator.changeAndSaveItem(component, m => {
|
||||
void application.mutator.changeAndSaveItem(component, (m) => {
|
||||
const mutator = m as ComponentMutator
|
||||
mutator.isMobileDefault = true
|
||||
})
|
||||
@@ -329,7 +329,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
}
|
||||
|
||||
const removeAsDefault = () => {
|
||||
void application.mutator.changeItem(currentDefault, m => {
|
||||
void application.mutator.changeItem(currentDefault, (m) => {
|
||||
const mutator = m as ComponentMutator
|
||||
mutator.isMobileDefault = false
|
||||
})
|
||||
@@ -376,7 +376,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
},
|
||||
},
|
||||
]
|
||||
components.map(component => {
|
||||
components.map((component) => {
|
||||
options.push({
|
||||
text: FindNativeFeature(component.identifier)?.name || component.name,
|
||||
subtext: component.isMobileDefault ? 'Mobile Default' : undefined,
|
||||
@@ -435,7 +435,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
|
||||
const pinOption = note.pinned ? 'Unpin' : 'Pin'
|
||||
const pinEvent = () =>
|
||||
changeNote(mutator => {
|
||||
changeNote((mutator) => {
|
||||
mutator.pinned = !note.pinned
|
||||
}, false)
|
||||
|
||||
@@ -447,7 +447,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
)
|
||||
return
|
||||
}
|
||||
void changeNote(mutator => {
|
||||
void changeNote((mutator) => {
|
||||
mutator.archived = !note.archived
|
||||
}, false)
|
||||
leaveEditor()
|
||||
@@ -455,7 +455,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
|
||||
const lockOption = note.locked ? 'Enable editing' : 'Prevent editing'
|
||||
const lockEvent = () =>
|
||||
changeNote(mutator => {
|
||||
changeNote((mutator) => {
|
||||
mutator.locked = !note.locked
|
||||
}, false)
|
||||
|
||||
@@ -506,7 +506,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
})
|
||||
}
|
||||
|
||||
let options: SideMenuOption[] = rawOptions.map(rawOption => ({
|
||||
let options: SideMenuOption[] = rawOptions.map((rawOption) => ({
|
||||
text: rawOption.text,
|
||||
key: rawOption.icon,
|
||||
iconDesc: {
|
||||
@@ -523,7 +523,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
text: 'Restore',
|
||||
key: 'restore-note',
|
||||
onSelect: () => {
|
||||
void changeNote(mutator => {
|
||||
void changeNote((mutator) => {
|
||||
mutator.trashed = false
|
||||
}, false)
|
||||
},
|
||||
@@ -574,11 +574,11 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
|
||||
const onTagSelect = useCallback(
|
||||
async (tag: SNTag | SmartView, addTagHierachy: boolean) => {
|
||||
const isSelected = selectedTags.findIndex(selectedTag => selectedTag.uuid === tag.uuid) > -1
|
||||
const isSelected = selectedTags.findIndex((selectedTag) => selectedTag.uuid === tag.uuid) > -1
|
||||
|
||||
if (note) {
|
||||
if (isSelected) {
|
||||
await application.mutator.changeItem(tag, mutator => {
|
||||
await application.mutator.changeItem(tag, (mutator) => {
|
||||
mutator.removeItemAsRelationship(note)
|
||||
})
|
||||
} else {
|
||||
@@ -607,7 +607,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
<SafeAreaContainer edges={['top', 'bottom', 'right']}>
|
||||
<FlatList
|
||||
style={styles.sections}
|
||||
data={Object.values(MenuSections).map(key => ({
|
||||
data={Object.values(MenuSections).map((key) => ({
|
||||
key,
|
||||
noteOptions,
|
||||
editorComponents: editors,
|
||||
@@ -650,7 +650,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
<TagSelectionList
|
||||
hasBottomPadding={Platform.OS === 'android'}
|
||||
contentType={ContentType.Tag}
|
||||
onTagSelect={tag => item.onTagSelect(tag, shouldAddTagHierarchy)}
|
||||
onTagSelect={(tag) => item.onTagSelect(tag, shouldAddTagHierarchy)}
|
||||
selectedTags={item.selectedTags}
|
||||
emptyPlaceholder={'Create a new tag using the tag button in the bottom right corner.'}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Platform } from 'react-native'
|
||||
import styled, { css } from 'styled-components/native'
|
||||
|
||||
export const Touchable = styled.TouchableOpacity<{ isSubtext: boolean }>`
|
||||
min-height: ${props => (props.isSubtext ? 52 : 42)}px;
|
||||
min-height: ${(props) => (props.isSubtext ? 52 : 42)}px;
|
||||
`
|
||||
export const CellContent = styled.View<{
|
||||
iconSide: 'right' | 'left' | null
|
||||
@@ -32,7 +32,7 @@ export const TextContainer = styled.View<{
|
||||
isSubtext: boolean
|
||||
selected?: boolean
|
||||
}>`
|
||||
min-height: ${props => (props.isSubtext ? 38 : 24)}px;
|
||||
min-height: ${(props) => (props.isSubtext ? 38 : 24)}px;
|
||||
margin-left: 6px;
|
||||
flex-shrink: 1;
|
||||
${({ selected, theme }) =>
|
||||
|
||||
@@ -46,7 +46,7 @@ const renderIcon = (desc: SideMenuOption['iconDesc'], color: string) => {
|
||||
return <RegularText>*</RegularText>
|
||||
}
|
||||
|
||||
export const SideMenuCell: React.FC<SideMenuOption> = props => {
|
||||
export const SideMenuCell: React.FC<SideMenuOption> = (props) => {
|
||||
const theme = useContext(ThemeContext)
|
||||
const colorForTextClass = (textClass: SideMenuOption['textClass']) => {
|
||||
if (!textClass) {
|
||||
|
||||
@@ -13,7 +13,7 @@ type Props = {
|
||||
testID: ViewProps['testID']
|
||||
}
|
||||
|
||||
export const SideMenuHero: React.FC<Props> = props => {
|
||||
export const SideMenuHero: React.FC<Props> = (props) => {
|
||||
// Context
|
||||
const application = useContext(ApplicationContext)
|
||||
const theme = useContext(ThemeContext)
|
||||
@@ -26,7 +26,7 @@ export const SideMenuHero: React.FC<Props> = props => {
|
||||
|
||||
useEffect(() => {
|
||||
const observedContentTypes = [ContentType.Note, ContentType.Tag]
|
||||
const removeStreamItems = application?.streamItems(observedContentTypes, _items => {
|
||||
const removeStreamItems = application?.streamItems(observedContentTypes, (_items) => {
|
||||
const notesAndTagsCount = application?.items.getItems(observedContentTypes).length ?? 0
|
||||
|
||||
if (notesAndTagsCount !== itemsCount) {
|
||||
|
||||
@@ -4,7 +4,7 @@ export const Root = styled.View`
|
||||
padding-bottom: 6px;
|
||||
`
|
||||
export const Header = styled.TouchableOpacity<{ collapsed: boolean }>`
|
||||
height: ${props => (props.collapsed ? 50 : 22)}px;
|
||||
height: ${(props) => (props.collapsed ? 50 : 22)}px;
|
||||
`
|
||||
export const Title = styled.Text`
|
||||
color: ${({ theme }) => theme.stylekitInfoColor};
|
||||
|
||||
@@ -39,7 +39,7 @@ type Props = {
|
||||
options?: SideMenuOption[]
|
||||
}
|
||||
|
||||
export const SideMenuSection: React.FC<Props> = React.memo(props => {
|
||||
export const SideMenuSection: React.FC<Props> = React.memo((props) => {
|
||||
const [collapsed, setCollapsed] = useState(Boolean(props.collapsed))
|
||||
const options = useMemo(() => {
|
||||
return props.options || []
|
||||
@@ -58,7 +58,7 @@ export const SideMenuSection: React.FC<Props> = React.memo(props => {
|
||||
|
||||
{!collapsed && (
|
||||
<>
|
||||
{options.map(option => {
|
||||
{options.map((option) => {
|
||||
return (
|
||||
<SideMenuCell
|
||||
text={option.text}
|
||||
|
||||
@@ -100,7 +100,7 @@ export const TagSelectionList = React.memo(
|
||||
let children: SNTag[] = []
|
||||
|
||||
if (showFolders && item instanceof SNTag) {
|
||||
const rawChildren = application.items.getTagChildren(item).map(tag => tag.uuid)
|
||||
const rawChildren = application.items.getTagChildren(item).map((tag) => tag.uuid)
|
||||
children = (tags as SNTag[]).filter((tag: SNTag) => rawChildren.includes(tag.uuid))
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ export const TagSelectionList = React.memo(
|
||||
windowSize={10}
|
||||
maxToRenderPerBatch={10}
|
||||
data={children}
|
||||
keyExtractor={childTag => childTag.uuid}
|
||||
keyExtractor={(childTag) => childTag.uuid}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
)}
|
||||
@@ -147,7 +147,7 @@ export const TagSelectionList = React.memo(
|
||||
windowSize={10}
|
||||
maxToRenderPerBatch={10}
|
||||
data={renderedTags as SNTag[]}
|
||||
keyExtractor={item => item.uuid}
|
||||
keyExtractor={(item) => item.uuid}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
{tags.length === 0 && <EmptyPlaceholder>{emptyPlaceholder}</EmptyPlaceholder>}
|
||||
|
||||
@@ -29,7 +29,7 @@ export enum Tabs {
|
||||
|
||||
type Props = ModalStackNavigationProp<typeof SCREEN_UPLOADED_FILES_LIST>
|
||||
|
||||
export const UploadedFilesList: FC<Props> = props => {
|
||||
export const UploadedFilesList: FC<Props> = (props) => {
|
||||
const { AttachedFiles, AllFiles } = Tabs
|
||||
const { note } = props.route.params
|
||||
|
||||
@@ -54,7 +54,7 @@ export const UploadedFilesList: FC<Props> = props => {
|
||||
|
||||
const filteredList = useMemo(() => {
|
||||
return searchString
|
||||
? filesList.filter(file => file.name.toLowerCase().includes(searchString.toLowerCase()))
|
||||
? filesList.filter((file) => file.name.toLowerCase().includes(searchString.toLowerCase()))
|
||||
: filesList
|
||||
}, [filesList, searchString])
|
||||
|
||||
@@ -128,7 +128,7 @@ export const UploadedFilesList: FC<Props> = props => {
|
||||
ref={filesListRef}
|
||||
data={filteredList}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={item => item.uuid}
|
||||
keyExtractor={(item) => item.uuid}
|
||||
onScroll={onScroll}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -8,6 +8,7 @@ export const SCREEN_INPUT_MODAL_FILE_NAME = 'InputModalFileName'
|
||||
export const SCREEN_NOTE_HISTORY = 'NoteSessionHistory' as const
|
||||
export const SCREEN_NOTE_HISTORY_PREVIEW = 'NoteSessionHistoryPreview' as const
|
||||
export const SCREEN_UPLOADED_FILES_LIST = 'UploadedFilesList' as const
|
||||
export const SCREEN_INPUT_MODAL_WORKSPACE_NAME = 'InputModalWorkspaceName' as const
|
||||
|
||||
export const SCREEN_SETTINGS = 'Settings'
|
||||
export const SCREEN_MANAGE_SESSIONS = 'ManageSessions' as const
|
||||
|
||||
@@ -3,7 +3,7 @@ const PREFIX_STANDARD_NOTES = '--sn-stylekit'
|
||||
const PREFIX_STANDARD_NOTES_BURN = '--sn-'
|
||||
|
||||
function camelCaseToDashed(camel: string) {
|
||||
return camel.replace(/[A-Z]/g, m => '-' + m.toLowerCase())
|
||||
return camel.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())
|
||||
}
|
||||
|
||||
export function objectToCss(object: any) {
|
||||
|
||||
@@ -49,12 +49,12 @@ export const useCustomActionSheet = () => {
|
||||
},
|
||||
]
|
||||
const tempOptions = options.concat(cancelOption)
|
||||
const destructiveIndex = tempOptions.findIndex(item => item.destructive)
|
||||
const destructiveIndex = tempOptions.findIndex((item) => item.destructive)
|
||||
const cancelIndex = tempOptions.length - 1
|
||||
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: tempOptions.map(option => option.text),
|
||||
options: tempOptions.map((option) => option.text),
|
||||
destructiveButtonIndex: destructiveIndex,
|
||||
cancelButtonIndex: cancelIndex,
|
||||
title,
|
||||
@@ -72,7 +72,7 @@ export const useCustomActionSheet = () => {
|
||||
},
|
||||
anchor: anchor ? findNodeHandle(anchor) ?? undefined : undefined,
|
||||
},
|
||||
buttonIndex => {
|
||||
(buttonIndex) => {
|
||||
const option = tempOptions[buttonIndex!]
|
||||
option.callback && option.callback(option)
|
||||
},
|
||||
|
||||
@@ -78,7 +78,7 @@ export class ThemeService {
|
||||
}
|
||||
|
||||
private registerObservers() {
|
||||
this.unsubsribeAppEventObserver = this.application?.addEventObserver(async event => {
|
||||
this.unsubsribeAppEventObserver = this.application?.addEventObserver(async (event) => {
|
||||
/**
|
||||
* If there are any migrations we need to set default theme to start UI
|
||||
*/
|
||||
@@ -259,7 +259,7 @@ export class ThemeService {
|
||||
private async resolveInitialThemeForMode() {
|
||||
try {
|
||||
const savedThemeId = await this.getThemeForMode(this.getColorScheme())
|
||||
const matchingThemeId = Object.keys(this.themes).find(themeId => themeId === savedThemeId)
|
||||
const matchingThemeId = Object.keys(this.themes).find((themeId) => themeId === savedThemeId)
|
||||
if (matchingThemeId) {
|
||||
this.setActiveTheme(matchingThemeId)
|
||||
void this.application?.mobileComponentManager.preloadThirdPartyThemeIndexPath()
|
||||
@@ -278,7 +278,7 @@ export class ThemeService {
|
||||
|
||||
systemThemes() {
|
||||
return Object.values(this.themes)
|
||||
.filter(theme => theme.mobileContent.isSystemTheme)
|
||||
.filter((theme) => theme.mobileContent.isSystemTheme)
|
||||
.sort((a, b) => {
|
||||
if (a.name < b.name) {
|
||||
return -1
|
||||
@@ -292,7 +292,7 @@ export class ThemeService {
|
||||
|
||||
nonSystemThemes() {
|
||||
return Object.values(this.themes)
|
||||
.filter(theme => !theme.mobileContent.isSystemTheme)
|
||||
.filter((theme) => !theme.mobileContent.isSystemTheme)
|
||||
.sort((a, b) => {
|
||||
if (a.name < b.name) {
|
||||
return -1
|
||||
@@ -465,7 +465,7 @@ export class ThemeService {
|
||||
private async loadCachedThemes() {
|
||||
const rawValue = (await this.application!.getValue(CACHED_THEMES_KEY, StorageValueModes.Nonwrapped)) || []
|
||||
|
||||
const themes = (rawValue as DecryptedTransferPayload<ComponentContent>[]).map(rawPayload => {
|
||||
const themes = (rawValue as DecryptedTransferPayload<ComponentContent>[]).map((rawPayload) => {
|
||||
const payload = new DecryptedPayload<ComponentContent>(rawPayload)
|
||||
|
||||
return new MobileTheme(payload)
|
||||
@@ -480,7 +480,7 @@ export class ThemeService {
|
||||
const themes = this.nonSystemThemes()
|
||||
return this.application!.setValue(
|
||||
CACHED_THEMES_KEY,
|
||||
themes.map(t => t.payloadRepresentation()),
|
||||
themes.map((t) => t.payloadRepresentation()),
|
||||
StorageValueModes.Nonwrapped,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user