diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/lib/styles.ts | 8 | ||||
-rw-r--r-- | src/view/routes.ts | 4 | ||||
-rw-r--r-- | src/view/screens/Login.tsx | 294 | ||||
-rw-r--r-- | src/view/screens/Signup.tsx | 30 | ||||
-rw-r--r-- | src/view/shell/mobile/index.tsx | 54 |
5 files changed, 337 insertions, 53 deletions
diff --git a/src/view/lib/styles.ts b/src/view/lib/styles.ts index ba6dc0de4..9cc776020 100644 --- a/src/view/lib/styles.ts +++ b/src/view/lib/styles.ts @@ -102,24 +102,32 @@ export const s = StyleSheet.create({ p2: {padding: 2}, p5: {padding: 5}, p10: {padding: 10}, + p20: {padding: 20}, pr2: {paddingRight: 2}, pr5: {paddingRight: 5}, pr10: {paddingRight: 10}, + pr20: {paddingRight: 20}, pl2: {paddingLeft: 2}, pl5: {paddingLeft: 5}, pl10: {paddingLeft: 10}, + pl20: {paddingLeft: 20}, pt2: {paddingTop: 2}, pt5: {paddingTop: 5}, pt10: {paddingTop: 10}, + pt20: {paddingTop: 20}, pb2: {paddingBottom: 2}, pb5: {paddingBottom: 5}, pb10: {paddingBottom: 10}, + pb20: {paddingBottom: 20}, // flex flexRow: {flexDirection: 'row'}, flexCol: {flexDirection: 'column'}, flex1: {flex: 1}, + // position + absolute: {position: 'absolute'}, + // dimensions w100pct: {width: '100%'}, h100pct: {height: '100%'}, diff --git a/src/view/routes.ts b/src/view/routes.ts index d31dbae35..d89dafff8 100644 --- a/src/view/routes.ts +++ b/src/view/routes.ts @@ -3,8 +3,6 @@ import {IconProp} from '@fortawesome/fontawesome-svg-core' import {Home} from './screens/Home' import {Search} from './screens/Search' import {Notifications} from './screens/Notifications' -import {Login} from './screens/Login' -import {Signup} from './screens/Signup' import {NotFound} from './screens/NotFound' import {PostThread} from './screens/PostThread' import {PostLikedBy} from './screens/PostLikedBy' @@ -47,8 +45,6 @@ export const routes: Route[] = [ 'retweet', r('/profile/(?<name>[^/]+)/post/(?<recordKey>[^/]+)/reposted-by'), ], - [Login, ['far', 'user'], r('/login')], - [Signup, ['far', 'user'], r('/signup')], ] export function match(url: string): MatchResult { diff --git a/src/view/screens/Login.tsx b/src/view/screens/Login.tsx index 0857687ab..110e23ff2 100644 --- a/src/view/screens/Login.tsx +++ b/src/view/screens/Login.tsx @@ -1,27 +1,287 @@ -import React from 'react' -import {Text, View} from 'react-native' +import React, {useState} from 'react' +import { + ActivityIndicator, + KeyboardAvoidingView, + StyleSheet, + Text, + TextInput, + TouchableOpacity, + View, + useWindowDimensions, +} from 'react-native' +import Svg, {Line} from 'react-native-svg' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {observer} from 'mobx-react-lite' -// import {useStores} from '../../state' +import {s, colors} from '../lib/styles' +import {useStores} from '../../state' + +enum ScreenState { + SigninOrCreateAccount, + Signin, +} + +const SigninOrCreateAccount = ({ + onPressSignin, +}: { + onPressSignin: () => void +}) => { + const winDim = useWindowDimensions() + const halfWidth = winDim.width / 2 + return ( + <> + <View style={styles.hero}> + <Text style={styles.title}>Bluesky</Text> + <Text style={styles.subtitle}>[ private beta ]</Text> + </View> + <View style={s.flex1}> + <TouchableOpacity style={styles.btn}> + <Text style={styles.btnLabel}>Create a new account</Text> + </TouchableOpacity> + <View style={styles.or}> + <Svg height="1" width={winDim.width} style={styles.orLine}> + <Line + x1="30" + y1="0" + x2={halfWidth - 20} + y2="0" + stroke="white" + strokeWidth="1" + /> + <Line + x1={halfWidth + 20} + y1="0" + x2={winDim.width - 30} + y2="0" + stroke="white" + strokeWidth="1" + /> + </Svg> + <Text style={styles.orLabel}>or</Text> + </View> + <TouchableOpacity style={styles.btn} onPress={onPressSignin}> + <Text style={styles.btnLabel}>Sign in</Text> + </TouchableOpacity> + </View> + </> + ) +} + +const Signin = ({onPressBack}: {onPressBack: () => void}) => { + const store = useStores() + const [isProcessing, setIsProcessing] = useState<boolean>(false) + const [error, setError] = useState<string>('') + const [username, setUsername] = useState<string>('') + const [password, setPassword] = useState<string>('') + + const onPressNext = async () => { + setError('') + setIsProcessing(true) + try { + await store.session.login({ + service: 'http://localhost:2583/', + username, + password, + }) + } catch (e: any) { + const errMsg = e.toString() + console.log(e) + if (errMsg.includes('Authentication Required')) { + setError('Invalid username or password') + } else if (errMsg.includes('Network request failed')) { + setError( + 'Unable to contact your service. Please check your Internet connection.', + ) + } else { + setError(errMsg.replace(/^Error:/, '')) + } + } finally { + setIsProcessing(false) + } + } + + return ( + <KeyboardAvoidingView behavior="padding" style={{flex: 1}}> + <View style={styles.hero}> + <Text style={styles.title}>Bluesky</Text> + <Text style={styles.subtitle}>[ private beta ]</Text> + </View> + <View style={s.flex1}> + <View style={styles.group}> + <View style={styles.groupTitle}> + <Text style={[s.white, s.f18]}>Sign in</Text> + </View> + <View style={styles.groupContent}> + <View style={[s.mb5]}> + <TextInput + style={styles.textInput} + placeholder="Email or username" + autoCapitalize="none" + autoFocus + value={username} + onChangeText={setUsername} + editable={!isProcessing} + /> + </View> + <View style={[s.mb5]}> + <TextInput + style={styles.textInput} + placeholder="Password" + autoCapitalize="none" + secureTextEntry + value={password} + onChangeText={setPassword} + editable={!isProcessing} + /> + </View> + {error ? ( + <View style={styles.error}> + <View style={styles.errorIcon}> + <FontAwesomeIcon + icon="exclamation" + style={s.white} + size={10} + /> + </View> + <View style={s.flex1}> + <Text style={[s.white, s.bold]}>{error}</Text> + </View> + </View> + ) : undefined} + </View> + </View> + <View style={[s.flexRow, s.pl20, s.pr20]}> + <TouchableOpacity onPress={onPressBack}> + <Text style={[s.white, s.f18, s.bold, s.pl5]}>Back</Text> + </TouchableOpacity> + <View style={s.flex1} /> + <TouchableOpacity onPress={onPressNext}> + {isProcessing ? ( + <ActivityIndicator color="#fff" /> + ) : ( + <Text style={[s.white, s.f18, s.bold, s.pr5]}>Next</Text> + )} + </TouchableOpacity> + </View> + </View> + </KeyboardAvoidingView> + ) +} export const Login = observer( (/*{navigation}: RootTabsScreenProps<'Login'>*/) => { // const store = useStores() + const [screenState, setScreenState] = useState<ScreenState>( + ScreenState.SigninOrCreateAccount, + ) + const onPressSignin = () => { + setScreenState(ScreenState.Signin) + } + return ( - <View style={{justifyContent: 'center', alignItems: 'center'}}> - <Text style={{fontSize: 20, fontWeight: 'bold'}}>Sign In</Text> - {/*store.session.uiError && <Text>{store.session.uiError}</Text>} - {!store.session.uiIsProcessing ? ( - <> - <Button title="Login" onPress={() => store.session.login()} /> - <Button - title="Sign Up" - onPress={() => navigation.navigate('Signup')} - /> - </> - ) : ( - <ActivityIndicator /> - )*/} + <View style={styles.outer}> + {screenState === ScreenState.SigninOrCreateAccount ? ( + <SigninOrCreateAccount onPressSignin={onPressSignin} /> + ) : undefined} + {screenState === ScreenState.Signin ? ( + <Signin + onPressBack={() => + setScreenState(ScreenState.SigninOrCreateAccount) + } + /> + ) : undefined} </View> ) }, ) + +const styles = StyleSheet.create({ + outer: { + flex: 1, + }, + hero: { + flex: 1, + justifyContent: 'center', + }, + title: { + textAlign: 'center', + color: colors.white, + fontSize: 68, + fontWeight: 'bold', + }, + subtitle: { + textAlign: 'center', + color: colors.white, + fontSize: 18, + }, + btn: { + borderWidth: 1, + borderColor: colors.white, + borderRadius: 10, + paddingVertical: 16, + marginBottom: 20, + marginHorizontal: 20, + }, + btnLabel: { + textAlign: 'center', + color: colors.white, + fontSize: 18, + fontWeight: 'bold', + }, + or: { + marginBottom: 20, + }, + orLine: { + position: 'absolute', + top: 10, + }, + orLabel: { + textAlign: 'center', + color: colors.white, + fontSize: 16, + }, + group: { + borderWidth: 1, + borderColor: colors.white, + borderRadius: 10, + marginBottom: 20, + marginHorizontal: 20, + }, + groupTitle: { + paddingVertical: 8, + paddingHorizontal: 12, + borderBottomWidth: 1, + borderBottomColor: colors.blue1, + }, + groupContent: { + paddingVertical: 8, + paddingHorizontal: 12, + }, + textInput: { + width: '100%', + backgroundColor: colors.white, + paddingHorizontal: 8, + paddingVertical: 8, + borderRadius: 4, + fontSize: 18, + }, + error: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 5, + backgroundColor: colors.purple3, + paddingHorizontal: 8, + paddingVertical: 5, + borderRadius: 4, + }, + errorIcon: { + borderWidth: 1, + borderColor: colors.white, + color: colors.white, + borderRadius: 30, + width: 16, + height: 16, + alignItems: 'center', + justifyContent: 'center', + marginRight: 5, + }, +}) diff --git a/src/view/screens/Signup.tsx b/src/view/screens/Signup.tsx deleted file mode 100644 index a34cd5727..000000000 --- a/src/view/screens/Signup.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import {Text, View} from 'react-native' -import {observer} from 'mobx-react-lite' -// import {useStores} from '../../state' - -export const Signup = observer( - (/*{navigation}: RootTabsScreenProps<'Signup'>*/) => { - // const store = useStores() - return ( - <View style={{justifyContent: 'center', alignItems: 'center'}}> - <Text style={{fontSize: 20, fontWeight: 'bold'}}>Create Account</Text> - {/*store.session.uiError ?? <Text>{store.session.uiError}</Text>} - {!store.session.uiIsProcessing ? ( - <> - <Button - title="Create new account" - onPress={() => store.session.login()} - /> - <Button - title="Log in to an existing account" - onPress={() => navigation.navigate('Login')} - /> - </> - ) : ( - <ActivityIndicator /> - )*/} - </View> - ) - }, -) diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx index 5896d7008..2a0a96a22 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/mobile/index.tsx @@ -1,4 +1,4 @@ -import React, {useState, useRef, useEffect} from 'react' +import React, {useState, useEffect} from 'react' import {observer} from 'mobx-react-lite' import { useWindowDimensions, @@ -11,6 +11,8 @@ import { View, } from 'react-native' import {ScreenContainer, Screen} from 'react-native-screens' +import LinearGradient from 'react-native-linear-gradient' +// import Svg, {Polygon} from 'react-native-svg' import {GestureDetector, Gesture} from 'react-native-gesture-handler' import Animated, { useSharedValue, @@ -25,12 +27,13 @@ import {useStores} from '../../../state' import {NavigationModel} from '../../../state/models/navigation' import {TabsSelectorModel} from '../../../state/models/shell' import {match, MatchResult} from '../../routes' +import {Login} from '../../screens/Login' import {Modal} from '../../com/modals/Modal' import {LocationNavigator} from './location-navigator' import {createBackMenu, createForwardMenu} from './history-menu' import {createAccountsMenu} from './accounts-menu' import {createLocationMenu} from './location-menu' -import {s, colors} from '../../lib/styles' +import {s, colors, gradients} from '../../lib/styles' import {AVIS} from '../../lib/assets' const locationIconNeedsNudgeUp = (icon: IconProp) => icon === 'house' @@ -164,6 +167,53 @@ export const MobileShell: React.FC = observer(() => { opacity: interpolate(swipeGestureInterp.value, [0, 1.0], [0.6, 0.0]), })) + console.log('authed?', store.session.isAuthed) + if (!store.session.isAuthed) { + return ( + <LinearGradient + colors={['#007CFF', '#00BCFF']} + start={{x: 0, y: 0.8}} + end={{x: 1, y: 1}} + style={styles.outerContainer}> + { + undefined /* TODO want this? <Svg height={winDim.height} width={winDim.width} style={s.absolute}> + <Polygon + points={` + ${winDim.width},0 + ${winDim.width - 250},0 + 0,${winDim.height - 140} + 0,${winDim.height} + ${winDim.width},${winDim.height}`} + fill="#fff" + fillOpacity="0.04" + /> + <Polygon + points={` + ${winDim.width},0 + ${winDim.width - 100},0 + 0,${winDim.height - 60} + 0,${winDim.height} + ${winDim.width},${winDim.height}`} + fill="#fff" + fillOpacity="0.04" + /> + <Polygon + points={` + ${winDim.width},100 + 0,${winDim.height} + ${winDim.width},${winDim.height}`} + fill="#fff" + fillOpacity="0.04" + /> + </Svg>*/ + } + <SafeAreaView style={styles.innerContainer}> + <Login /> + </SafeAreaView> + </LinearGradient> + ) + } + return ( <View style={styles.outerContainer}> <View style={styles.topBar}> |