feat: mobile workspaces (#1093)

This commit is contained in:
Vardan Hakobyan
2022-06-21 15:42:43 +04:00
committed by GitHub
parent 1f903f17d1
commit 7d60dfee73
71 changed files with 599 additions and 317 deletions

View File

@@ -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}

View File

@@ -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) {

View File

@@ -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"

View File

@@ -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()

View File

@@ -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) {

View File

@@ -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>
)
}

View File

@@ -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}

View File

@@ -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>

View File

@@ -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)
}}
/>

View File

@@ -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}

View File

@@ -43,7 +43,7 @@ export const NoteHistoryPreview = ({
} else {
await application?.mutator.changeAndSaveItem(
originalNote,
mutator => {
(mutator) => {
mutator.setCustomContent(revision.payload.content)
},
true,

View File

@@ -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}

View File

@@ -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}

View File

@@ -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;

View File

@@ -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)
},

View File

@@ -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>

View File

@@ -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;
`

View File

@@ -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}

View File

@@ -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)

View File

@@ -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 }

View File

@@ -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%'

View File

@@ -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}
/>

View File

@@ -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 (

View File

@@ -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>
)
}

View File

@@ -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

View File

@@ -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 (

View File

@@ -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)

View File

@@ -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,

View File

@@ -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.'}
/>

View File

@@ -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 }) =>

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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};

View File

@@ -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}

View File

@@ -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>}

View File

@@ -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}
/>
) : (

View File

@@ -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