feat: mobile app package (#1075)

This commit is contained in:
Mo
2022-06-09 09:45:15 -05:00
committed by GitHub
parent 58b63898de
commit 8248a38280
336 changed files with 47696 additions and 22563 deletions

View File

@@ -0,0 +1,154 @@
import { ApplicationContext } from '@Root/ApplicationContext'
import { LoadingContainer, LoadingText } from '@Root/Screens/Notes/NoteList.styled'
import { ButtonType, RemoteSession, SessionStrings, UuidString } from '@standardnotes/snjs'
import { useCustomActionSheet } from '@Style/CustomActionSheet'
import React, { useCallback, useContext, useEffect, useState } from 'react'
import { FlatList, ListRenderItem, RefreshControl } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { ThemeContext } from 'styled-components'
import { SessionCell } from './SessionCell'
const useSessions = (): [
RemoteSession[],
() => void,
() => void,
boolean,
(uuid: UuidString) => Promise<void>,
string,
] => {
// Context
const application = useContext(ApplicationContext)
// State
const [sessions, setSessions] = useState<RemoteSession[]>([])
const [refreshing, setRefreshing] = useState(false)
const [errorMessage, setErrorMessage] = useState('')
const getSessions = useCallback(async () => {
const response = await application?.getSessions()
if (!response) {
setErrorMessage('An unknown error occurred while loading sessions.')
return
}
if ('error' in response || !response.data) {
if (response.error?.message) {
setErrorMessage(response.error.message)
} else {
setErrorMessage('An unknown error occurred while loading sessions.')
}
} else {
const newSessions = response.data as RemoteSession[]
setSessions(newSessions)
setErrorMessage('')
}
}, [application])
const refreshSessions = useCallback(async () => {
setRefreshing(true)
await getSessions()
setRefreshing(false)
}, [getSessions])
useEffect(() => {
void refreshSessions()
}, [application, refreshSessions])
async function revokeSession(uuid: UuidString) {
const response = await application?.revokeSession(uuid)
if (response && 'error' in response) {
if (response.error?.message) {
setErrorMessage(response.error?.message)
} else {
setErrorMessage('An unknown error occurred while revoking the session.')
}
} else {
setSessions(sessions.filter(session => session.uuid !== uuid))
}
}
return [sessions, getSessions, refreshSessions, refreshing, revokeSession, errorMessage]
}
export const ManageSessions: React.FC = () => {
// Context
const application = useContext(ApplicationContext)
const { showActionSheet } = useCustomActionSheet()
const theme = useContext(ThemeContext)
const insets = useSafeAreaInsets()
const [sessions, getSessions, refreshSessions, refreshing, revokeSession, errorMessage] = useSessions()
const onItemPress = (item: RemoteSession) => {
showActionSheet({
title: item.device_info,
options: [
{
text: 'Revoke',
destructive: true,
callback: () => showRevokeSessionAlert(item),
},
],
})
}
const showRevokeSessionAlert = useCallback(
async (item: RemoteSession) => {
const confirmed = await application?.alertService.confirm(
SessionStrings.RevokeText,
SessionStrings.RevokeTitle,
SessionStrings.RevokeConfirmButton,
ButtonType.Danger,
SessionStrings.RevokeCancelButton,
)
if (confirmed) {
try {
await revokeSession(item.uuid)
getSessions()
} catch (e) {
void application?.alertService.alert('Action failed. Please try again.')
}
}
},
[application?.alertService, getSessions, revokeSession],
)
const RenderItem: ListRenderItem<RemoteSession> | null | undefined = ({ item }) => {
return (
<SessionCell
onPress={() => onItemPress(item)}
title={item.device_info}
subTitle={item.updated_at.toLocaleDateString()}
currentSession={item.current}
disabled={item.current}
/>
)
}
if (errorMessage) {
return (
<LoadingContainer>
<LoadingText>{errorMessage}</LoadingText>
</LoadingContainer>
)
}
return (
<FlatList<RemoteSession>
keyExtractor={item => item.uuid}
contentContainerStyle={{ paddingBottom: insets.bottom }}
initialNumToRender={7}
windowSize={7}
data={sessions}
refreshControl={
<RefreshControl
tintColor={theme.stylekitContrastForegroundColor}
refreshing={refreshing}
onRefresh={refreshSessions}
/>
}
renderItem={RenderItem}
/>
)
}

View File

@@ -0,0 +1,58 @@
import { Props as TableCellProps, SectionedTableCellTouchableHighlight } from '@Root/Components/SectionedTableCell'
import React from 'react'
import styled, { css } from 'styled-components/native'
type Props = {
testID?: string
disabled?: boolean
onPress: () => void
title: string
subTitle: string
currentSession: boolean
}
const Container = styled(SectionedTableCellTouchableHighlight).attrs(props => ({
underlayColor: props.theme.stylekitBorderColor,
}))<TableCellProps>`
padding-top: ${12}px;
justify-content: center;
`
const ButtonContainer = styled.View``
type ButtonLabelProps = Pick<Props, 'disabled'>
const ButtonLabel = styled.Text<ButtonLabelProps>`
color: ${props => {
let color = props.theme.stylekitForegroundColor
if (props.disabled) {
color = 'gray'
}
return color
}};
font-weight: bold;
font-size: ${props => props.theme.mainTextFontSize}px;
${({ disabled }) =>
disabled &&
css`
opacity: 0.6;
`}
`
export const SubTitleText = styled.Text<{ current: boolean }>`
font-size: 14px;
margin-top: 4px;
color: ${({ theme, current }) => {
return current ? theme.stylekitInfoColor : theme.stylekitForegroundColor
}};
opacity: 0.8;
line-height: 21px;
`
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>
<SubTitleText current={props.currentSession}>
{props.currentSession ? 'Current session' : 'Signed in on ' + props.subTitle}
</SubTitleText>
</ButtonContainer>
</Container>
)