Get MVP of web app running
parent
751dfb20fd
commit
a3d2db9645
|
@ -1,7 +1,8 @@
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useState, useEffect} from 'react'
|
||||||
|
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
||||||
import * as view from './view/index'
|
import * as view from './view/index'
|
||||||
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
||||||
import {DesktopWebShell} from './view/shell/desktop-web'
|
import {WebShell} from './view/shell/web'
|
||||||
// import Toast from 'react-native-root-toast' TODO
|
// import Toast from 'react-native-root-toast' TODO
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
@ -22,7 +23,9 @@ function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RootStoreProvider value={rootStore}>
|
<RootStoreProvider value={rootStore}>
|
||||||
<DesktopWebShell />
|
<SafeAreaProvider>
|
||||||
|
<WebShell />
|
||||||
|
</SafeAreaProvider>
|
||||||
</RootStoreProvider>
|
</RootStoreProvider>
|
||||||
)
|
)
|
||||||
// <Toast.ToastContainer /> TODO
|
// <Toast.ToastContainer /> TODO
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import {ComAtprotoAccountCreate} from '@atproto/api'
|
import {ComAtprotoAccountCreate} from '@atproto/api'
|
||||||
import * as EmailValidator from 'email-validator'
|
import * as EmailValidator from 'email-validator'
|
||||||
import {useAnalytics} from '@segment/analytics-react-native'
|
// import {useAnalytics} from '@segment/analytics-react-native' TODO
|
||||||
import {LogoTextHero} from './Logo'
|
import {LogoTextHero} from './Logo'
|
||||||
import {Picker} from '../util/Picker'
|
import {Picker} from '../util/Picker'
|
||||||
import {TextLink} from '../util/Link'
|
import {TextLink} from '../util/Link'
|
||||||
|
@ -32,7 +32,7 @@ import {ServerInputModal} from '../../../state/models/shell-ui'
|
||||||
import {usePalette} from '../../lib/hooks/usePalette'
|
import {usePalette} from '../../lib/hooks/usePalette'
|
||||||
|
|
||||||
export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
|
export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
|
||||||
const {track} = useAnalytics()
|
// const {track} = useAnalytics() TODO
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||||
|
@ -109,7 +109,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
|
||||||
password,
|
password,
|
||||||
inviteCode,
|
inviteCode,
|
||||||
})
|
})
|
||||||
track('Create Account')
|
// track('Create Account') TODO
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
let errMsg = e.toString()
|
let errMsg = e.toString()
|
||||||
if (e instanceof ComAtprotoAccountCreate.InvalidInviteCodeError) {
|
if (e instanceof ComAtprotoAccountCreate.InvalidInviteCodeError) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import * as EmailValidator from 'email-validator'
|
import * as EmailValidator from 'email-validator'
|
||||||
import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
|
import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
|
||||||
import {useAnalytics} from '@segment/analytics-react-native'
|
// import {useAnalytics} from '@segment/analytics-react-native' TODO
|
||||||
import {LogoTextHero} from './Logo'
|
import {LogoTextHero} from './Logo'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
|
@ -153,7 +153,7 @@ const ChooseAccountForm = ({
|
||||||
onSelectAccount: (account?: AccountData) => void
|
onSelectAccount: (account?: AccountData) => void
|
||||||
onPressBack: () => void
|
onPressBack: () => void
|
||||||
}) => {
|
}) => {
|
||||||
const {track} = useAnalytics()
|
// const {track} = useAnalytics() TODO
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const [isProcessing, setIsProcessing] = React.useState(false)
|
const [isProcessing, setIsProcessing] = React.useState(false)
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ const ChooseAccountForm = ({
|
||||||
if (account.accessJwt && account.refreshJwt) {
|
if (account.accessJwt && account.refreshJwt) {
|
||||||
setIsProcessing(true)
|
setIsProcessing(true)
|
||||||
if (await store.session.resumeSession(account)) {
|
if (await store.session.resumeSession(account)) {
|
||||||
track('Sign In', {resumedSession: true})
|
// track('Sign In', {resumedSession: true}) TODO
|
||||||
setIsProcessing(false)
|
setIsProcessing(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ const LoginForm = ({
|
||||||
onPressBack: () => void
|
onPressBack: () => void
|
||||||
onPressForgotPassword: () => void
|
onPressForgotPassword: () => void
|
||||||
}) => {
|
}) => {
|
||||||
const {track} = useAnalytics()
|
// const {track} = useAnalytics() TODO
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||||
const [handle, setHandle] = useState<string>(initialHandle)
|
const [handle, setHandle] = useState<string>(initialHandle)
|
||||||
|
@ -302,7 +302,7 @@ const LoginForm = ({
|
||||||
handle: fullHandle,
|
handle: fullHandle,
|
||||||
password,
|
password,
|
||||||
})
|
})
|
||||||
track('Sign In', {resumedSession: false})
|
// track('Sign In', {resumedSession: false}) TODO
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
const errMsg = e.toString()
|
const errMsg = e.toString()
|
||||||
store.log.warn('Failed to login', e)
|
store.log.warn('Failed to login', e)
|
||||||
|
|
|
@ -76,7 +76,11 @@ export function DropdownButton({
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP}
|
||||||
// Fix an issue where specific references cause runtime error in jest environment
|
// Fix an issue where specific references cause runtime error in jest environment
|
||||||
ref={process.env.JEST_WORKER_ID != null ? null : ref}>
|
ref={
|
||||||
|
typeof process !== 'undefined' && process.env.JEST_WORKER_ID != null
|
||||||
|
? null
|
||||||
|
: ref
|
||||||
|
}>
|
||||||
{children}
|
{children}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import {observer} from 'mobx-react-lite'
|
|
||||||
import {View, StyleSheet} from 'react-native'
|
|
||||||
import {DesktopLeftColumn} from './left-column'
|
|
||||||
import {DesktopRightColumn} from './right-column'
|
|
||||||
import {useStores} from '../../../state'
|
|
||||||
|
|
||||||
export const DesktopWebShell: React.FC = observer(({children}) => {
|
|
||||||
const store = useStores()
|
|
||||||
return (
|
|
||||||
<View style={styles.outerContainer}>
|
|
||||||
{store.session.hasSession ? (
|
|
||||||
<>
|
|
||||||
<DesktopLeftColumn />
|
|
||||||
<View style={styles.innerContainer}>{children}</View>
|
|
||||||
<DesktopRightColumn />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<View style={styles.innerContainer}>{children}</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
outerContainer: {
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
innerContainer: {
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
width: '600px',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {observer} from 'mobx-react-lite'
|
||||||
|
import {View, StyleSheet, Text} from 'react-native'
|
||||||
|
import {useStores} from '../../../state'
|
||||||
|
import {match, MatchResult} from '../../routes'
|
||||||
|
// import {DesktopLeftColumn} from './left-column'
|
||||||
|
// import {DesktopRightColumn} from './right-column'
|
||||||
|
import {Login} from '../../screens/Login'
|
||||||
|
import {ErrorBoundary} from '../../com/util/ErrorBoundary'
|
||||||
|
import {usePalette} from '../../lib/hooks/usePalette'
|
||||||
|
import {s} from '../../lib/styles'
|
||||||
|
|
||||||
|
export const WebShell: React.FC = observer(() => {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const store = useStores()
|
||||||
|
const screenRenderDesc = constructScreenRenderDesc(store.nav)
|
||||||
|
|
||||||
|
if (!store.session.hasSession) {
|
||||||
|
return (
|
||||||
|
<View style={styles.outerContainer}>
|
||||||
|
<View style={styles.innerContainer}>
|
||||||
|
<Login />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.outerContainer, pal.view]}>
|
||||||
|
<View style={styles.innerContainer}>
|
||||||
|
{screenRenderDesc.screens.map(({Com, navIdx, params, key, current}) => (
|
||||||
|
<View
|
||||||
|
key={key}
|
||||||
|
style={[s.h100pct, current ? styles.visible : styles.hidden]}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Com params={params} navIdx={navIdx} visible={current} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
// TODO
|
||||||
|
// <Modal />
|
||||||
|
// <Lightbox />
|
||||||
|
// <Composer
|
||||||
|
// active={store.shell.isComposerActive}
|
||||||
|
// onClose={() => store.shell.closeComposer()}
|
||||||
|
// winHeight={winDim.height}
|
||||||
|
// replyTo={store.shell.composerOpts?.replyTo}
|
||||||
|
// imagesOpen={store.shell.composerOpts?.imagesOpen}
|
||||||
|
// onPost={store.shell.composerOpts?.onPost}
|
||||||
|
// />
|
||||||
|
// return (
|
||||||
|
// <View style={styles.outerContainer}>
|
||||||
|
// {store.session.hasSession ? (
|
||||||
|
// <>
|
||||||
|
// <DesktopLeftColumn />
|
||||||
|
// <View style={styles.innerContainer}>
|
||||||
|
// <Text>Hello, world! (Logged in)</Text>
|
||||||
|
// {children}
|
||||||
|
// </View>
|
||||||
|
// <DesktopRightColumn />
|
||||||
|
// </>
|
||||||
|
// ) : (
|
||||||
|
// <View style={styles.innerContainer}>
|
||||||
|
// <Text>Hello, world! (Logged out)</Text>
|
||||||
|
// {children}
|
||||||
|
// </View>
|
||||||
|
// )}
|
||||||
|
// </View>
|
||||||
|
// )
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method produces the information needed by the shell to
|
||||||
|
* render the current screens with screen-caching behaviors.
|
||||||
|
*/
|
||||||
|
type ScreenRenderDesc = MatchResult & {
|
||||||
|
key: string
|
||||||
|
navIdx: string
|
||||||
|
current: boolean
|
||||||
|
previous: boolean
|
||||||
|
isNewTab: boolean
|
||||||
|
}
|
||||||
|
function constructScreenRenderDesc(nav: NavigationModel): {
|
||||||
|
icon: IconProp
|
||||||
|
hasNewTab: boolean
|
||||||
|
screens: ScreenRenderDesc[]
|
||||||
|
} {
|
||||||
|
let hasNewTab = false
|
||||||
|
let icon: IconProp = 'magnifying-glass'
|
||||||
|
let screens: ScreenRenderDesc[] = []
|
||||||
|
for (const tab of nav.tabs) {
|
||||||
|
const tabScreens = [
|
||||||
|
...tab.getBackList(5),
|
||||||
|
Object.assign({}, tab.current, {index: tab.index}),
|
||||||
|
]
|
||||||
|
const parsedTabScreens = tabScreens.map(screen => {
|
||||||
|
const isCurrent = nav.isCurrentScreen(tab.id, screen.index)
|
||||||
|
const isPrevious = nav.isCurrentScreen(tab.id, screen.index + 1)
|
||||||
|
const matchRes = match(screen.url)
|
||||||
|
if (isCurrent) {
|
||||||
|
icon = matchRes.icon
|
||||||
|
}
|
||||||
|
hasNewTab = hasNewTab || tab.isNewTab
|
||||||
|
return Object.assign(matchRes, {
|
||||||
|
key: `t${tab.id}-s${screen.index}`,
|
||||||
|
navIdx: `${tab.id}-${screen.id}`,
|
||||||
|
current: isCurrent,
|
||||||
|
previous: isPrevious,
|
||||||
|
isNewTab: tab.isNewTab,
|
||||||
|
}) as ScreenRenderDesc
|
||||||
|
})
|
||||||
|
screens = screens.concat(parsedTabScreens)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
icon,
|
||||||
|
hasNewTab,
|
||||||
|
screens,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
outerContainer: {
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
innerContainer: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
width: '600px',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
hidden: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
})
|
Loading…
Reference in New Issue