about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
Diffstat (limited to 'src/view')
-rw-r--r--src/view/lib/styles.ts8
-rw-r--r--src/view/routes.ts4
-rw-r--r--src/view/screens/Login.tsx294
-rw-r--r--src/view/screens/Signup.tsx30
-rw-r--r--src/view/shell/mobile/index.tsx54
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}>