diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/view/com/onboard/FeatureExplainer.web.tsx | 203 | ||||
-rw-r--r-- | src/view/com/onboard/Follows.web.tsx | 47 | ||||
-rw-r--r-- | src/view/screens/Login.web.tsx | 163 | ||||
-rw-r--r-- | src/view/shell/web/index.tsx | 16 |
4 files changed, 423 insertions, 6 deletions
diff --git a/src/view/com/onboard/FeatureExplainer.web.tsx b/src/view/com/onboard/FeatureExplainer.web.tsx new file mode 100644 index 000000000..83d460808 --- /dev/null +++ b/src/view/com/onboard/FeatureExplainer.web.tsx @@ -0,0 +1,203 @@ +import React, {useState} from 'react' +import { + Animated, + Image, + SafeAreaView, + StyleSheet, + TouchableOpacity, + useWindowDimensions, + View, +} from 'react-native' +import {TabView, SceneMap, Route, TabBarProps} from 'react-native-tab-view' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import {CenteredView} from '../util/Views.web' +import {Text} from '../util/text/Text' +import {useStores} from '../../../state' +import {s, colors} from '../../lib/styles' +import {TABS_EXPLAINER} from '../../lib/assets' +import {TABS_ENABLED} from '../../../build-flags' + +const ROUTES = TABS_ENABLED + ? [ + {key: 'intro', title: 'Intro'}, + {key: 'tabs', title: 'Tabs'}, + ] + : [{key: 'intro', title: 'Intro'}] + +const Intro = () => ( + <View style={styles.explainer}> + <Text + style={[styles.explainerHeading, s.normal, styles.explainerHeadingIntro]}> + Welcome to{' '} + <Text style={[s.bold, s.blue3, styles.explainerHeadingBrand]}> + Bluesky + </Text> + </Text> + <Text style={[styles.explainerDesc, styles.explainerDescIntro]}> + This is an early beta. Your feedback is appreciated! + </Text> + </View> +) + +const Tabs = () => ( + <View style={styles.explainer}> + <View style={styles.explainerIcon}> + <View style={s.flex1} /> + <FontAwesomeIcon + icon={['far', 'clone']} + style={[s.black as FontAwesomeIconStyle, s.mb5]} + size={36} + /> + <View style={s.flex1} /> + </View> + <Text style={styles.explainerHeading}>Tabs</Text> + <Text style={styles.explainerDesc}> + Never lose your place! Long-press to open posts and profiles in a new tab. + </Text> + <Text style={styles.explainerDesc}> + <Image source={TABS_EXPLAINER} style={styles.explainerImg} /> + </Text> + </View> +) + +const SCENE_MAP = { + intro: Intro, + tabs: Tabs, +} +const renderScene = SceneMap(SCENE_MAP) + +export const FeatureExplainer = () => { + const layout = useWindowDimensions() + const store = useStores() + const [index, setIndex] = useState(0) + + const onPressSkip = () => store.onboard.next() + const onPressNext = () => { + if (index >= ROUTES.length - 1) { + store.onboard.next() + } else { + setIndex(index + 1) + } + } + + const renderTabBar = (props: TabBarProps<Route>) => { + const inputRange = props.navigationState.routes.map((x, i) => i) + return ( + <View style={styles.tabBar}> + <View style={s.flex1} /> + {props.navigationState.routes.map((route, i) => { + const opacity = props.position.interpolate({ + inputRange, + outputRange: inputRange.map(inputIndex => + inputIndex === i ? 1 : 0.5, + ), + }) + + return ( + <TouchableOpacity + key={i} + style={styles.tabItem} + onPress={() => setIndex(i)}> + <Animated.Text style={{opacity}}>°</Animated.Text> + </TouchableOpacity> + ) + })} + <View style={s.flex1} /> + </View> + ) + } + + const FirstExplainer = SCENE_MAP[ROUTES[0]?.key as keyof typeof SCENE_MAP] + return ( + <CenteredView style={styles.container}> + {ROUTES.length > 1 ? ( + <TabView + navigationState={{index, routes: ROUTES}} + renderScene={renderScene} + renderTabBar={renderTabBar} + onIndexChange={setIndex} + initialLayout={{width: layout.width}} + tabBarPosition="bottom" + /> + ) : FirstExplainer ? ( + <FirstExplainer /> + ) : ( + <View /> + )} + <View style={styles.footer}> + <TouchableOpacity onPress={onPressSkip}> + <Text style={styles.footerBtn}>Skip</Text> + </TouchableOpacity> + <TouchableOpacity onPress={onPressNext}> + <Text style={[styles.footerBtn, styles.footerBtnNext]}>Next</Text> + </TouchableOpacity> + </View> + </CenteredView> + ) +} + +const styles = StyleSheet.create({ + container: { + height: '100%', + justifyContent: 'center', + paddingBottom: '10%', + }, + + tabBar: { + flexDirection: 'row', + }, + tabItem: { + alignItems: 'center', + padding: 16, + }, + + explainer: { + paddingHorizontal: 16, + }, + explainerIcon: { + flexDirection: 'row', + }, + explainerHeading: { + fontSize: 42, + fontWeight: 'bold', + textAlign: 'center', + marginBottom: 16, + }, + explainerHeadingIntro: { + lineHeight: 40, + }, + explainerHeadingBrand: {fontSize: 56}, + explainerDesc: { + fontSize: 18, + textAlign: 'center', + marginBottom: 16, + color: colors.gray5, + }, + explainerDescIntro: {fontSize: 24}, + explainerImg: { + resizeMode: 'contain', + maxWidth: '100%', + maxHeight: 330, + }, + + footer: { + flexDirection: 'row', + justifyContent: 'center', + paddingTop: 24, + }, + footerBtn: { + color: colors.blue3, + fontSize: 19, + paddingHorizontal: 36, + paddingVertical: 8, + }, + footerBtnNext: { + marginLeft: 10, + borderWidth: 1, + borderColor: colors.blue3, + borderRadius: 6, + }, +}) diff --git a/src/view/com/onboard/Follows.web.tsx b/src/view/com/onboard/Follows.web.tsx new file mode 100644 index 000000000..50e119e49 --- /dev/null +++ b/src/view/com/onboard/Follows.web.tsx @@ -0,0 +1,47 @@ +import React from 'react' +import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {SuggestedFollows} from '../discover/SuggestedFollows' +import {CenteredView} from '../util/Views.web' +import {Text} from '../util/text/Text' +import {useStores} from '../../../state' +import {s, colors} from '../../lib/styles' + +export const Follows = observer(() => { + const store = useStores() + + const onNoSuggestions = () => { + // no suggestions, bounce from this view + store.onboard.next() + } + const onPressNext = () => store.onboard.next() + + return ( + <SafeAreaView style={styles.container}> + <CenteredView style={styles.header}> + <Text type="title-lg"> + Follow these people to see their posts in your feed + </Text> + <TouchableOpacity onPress={onPressNext}> + <Text style={[styles.title, s.blue3, s.pr10]}>Next »</Text> + </TouchableOpacity> + </CenteredView> + <SuggestedFollows onNoSuggestions={onNoSuggestions} /> + </SafeAreaView> + ) +}) + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + }, + + header: { + paddingTop: 30, + paddingBottom: 40, + }, +}) diff --git a/src/view/screens/Login.web.tsx b/src/view/screens/Login.web.tsx new file mode 100644 index 000000000..77149090c --- /dev/null +++ b/src/view/screens/Login.web.tsx @@ -0,0 +1,163 @@ +import React, {useState} from 'react' +import { + Image, + SafeAreaView, + StyleSheet, + TouchableOpacity, + View, +} from 'react-native' +import {observer} from 'mobx-react-lite' +import {CenteredView} from '../com/util/Views' +import {Signin} from '../com/login/Signin' +import {CreateAccount} from '../com/login/CreateAccount' +import {Text} from '../com/util/text/Text' +import {ErrorBoundary} from '../com/util/ErrorBoundary' +import {colors} from '../lib/styles' +import {usePalette} from '../lib/hooks/usePalette' +import {CLOUD_SPLASH} from '../lib/assets' + +enum ScreenState { + S_SigninOrCreateAccount, + S_Signin, + S_CreateAccount, +} + +const SigninOrCreateAccount = ({ + onPressSignin, + onPressCreateAccount, +}: { + onPressSignin: () => void + onPressCreateAccount: () => void +}) => { + const pal = usePalette('default') + return ( + <> + <View style={styles.hero}> + <View style={styles.heroText}> + <Text style={styles.title}>Bluesky</Text> + <Text style={styles.subtitle}>[ private beta ]</Text> + </View> + </View> + <View testID="signinOrCreateAccount" style={styles.btns}> + <TouchableOpacity + testID="createAccountButton" + style={[pal.view, styles.btn]} + onPress={onPressCreateAccount}> + <Text style={[pal.link, styles.btnLabel]}>New account</Text> + </TouchableOpacity> + <TouchableOpacity + testID="signInButton" + style={[pal.view, styles.btn]} + onPress={onPressSignin}> + <Text style={[pal.link, styles.btnLabel]}>Sign in</Text> + </TouchableOpacity> + </View> + </> + ) +} + +export const Login = observer(() => { + const pal = usePalette('default') + const [screenState, setScreenState] = useState<ScreenState>( + ScreenState.S_SigninOrCreateAccount, + ) + + if (screenState === ScreenState.S_SigninOrCreateAccount) { + return ( + <CenteredView style={[styles.container, styles.vertCenter]}> + <ErrorBoundary> + <SigninOrCreateAccount + onPressSignin={() => setScreenState(ScreenState.S_Signin)} + onPressCreateAccount={() => + setScreenState(ScreenState.S_CreateAccount) + } + /> + </ErrorBoundary> + </CenteredView> + ) + } + + return ( + <CenteredView + style={[ + styles.container, + styles.containerBorder, + pal.view, + pal.borderDark, + ]}> + <SafeAreaView testID="noSessionView" style={styles.container}> + <ErrorBoundary> + {screenState === ScreenState.S_Signin ? ( + <Signin + onPressBack={() => + setScreenState(ScreenState.S_SigninOrCreateAccount) + } + /> + ) : undefined} + {screenState === ScreenState.S_CreateAccount ? ( + <CreateAccount + onPressBack={() => + setScreenState(ScreenState.S_SigninOrCreateAccount) + } + /> + ) : undefined} + </ErrorBoundary> + </SafeAreaView> + </CenteredView> + ) +}) + +const styles = StyleSheet.create({ + container: { + height: '100%', + }, + containerBorder: { + borderLeftWidth: 1, + borderRightWidth: 1, + }, + vertCenter: { + justifyContent: 'center', + }, + bgImg: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + }, + heroText: { + backgroundColor: colors.white, + paddingTop: 10, + paddingBottom: 20, + }, + btns: { + flexDirection: 'row', + paddingTop: 40, + }, + title: { + textAlign: 'center', + color: colors.blue3, + fontSize: 68, + fontWeight: 'bold', + }, + subtitle: { + textAlign: 'center', + color: colors.blue3, + fontSize: 18, + }, + btn: { + flex: 1, + borderRadius: 4, + paddingVertical: 16, + marginBottom: 20, + marginHorizontal: 20, + borderWidth: 1, + borderColor: colors.blue3, + }, + btnLabel: { + textAlign: 'center', + fontSize: 21, + fontWeight: '500', + color: colors.blue3, + }, +}) diff --git a/src/view/shell/web/index.tsx b/src/view/shell/web/index.tsx index fedc9c3d6..1059aa280 100644 --- a/src/view/shell/web/index.tsx +++ b/src/view/shell/web/index.tsx @@ -5,6 +5,7 @@ import {useStores} from '../../../state' import {match, MatchResult} from '../../routes' import {DesktopLeftColumn} from './left-column' import {DesktopRightColumn} from './right-column' +import {Onboard} from '../../screens/Onboard' import {Login} from '../../screens/Login' import {ErrorBoundary} from '../../com/util/ErrorBoundary' import {usePalette} from '../../lib/hooks/usePalette' @@ -22,6 +23,15 @@ export const WebShell: React.FC = observer(() => { </View> ) } + if (store.onboard.isOnboarding) { + return ( + <View style={styles.outerContainer}> + <ErrorBoundary> + <Onboard /> + </ErrorBoundary> + </View> + ) + } return ( <View style={[styles.outerContainer, pal.view]}> @@ -104,12 +114,6 @@ const styles = StyleSheet.create({ outerContainer: { height: '100%', }, - innerContainer: { - marginLeft: 'auto', - marginRight: 'auto', - width: '600px', - height: '100%', - }, visible: { display: 'flex', }, |