Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update login/create-account and onboard for web

+428 -6
+5
public/index.html
··· 12 12 /* These styles make the root element full-height */ 13 13 #app-root { display:flex; height:100%; } 14 14 15 + /* Remove focus state on inputs */ 16 + input:focus { 17 + outline: 0; 18 + } 19 + 15 20 /* These styles are for src/view/com/modals/WebModal */ 16 21 div[data-modal-overlay] { 17 22 position: fixed;
+203
src/view/com/onboard/FeatureExplainer.web.tsx
··· 1 + import React, {useState} from 'react' 2 + import { 3 + Animated, 4 + Image, 5 + SafeAreaView, 6 + StyleSheet, 7 + TouchableOpacity, 8 + useWindowDimensions, 9 + View, 10 + } from 'react-native' 11 + import {TabView, SceneMap, Route, TabBarProps} from 'react-native-tab-view' 12 + import { 13 + FontAwesomeIcon, 14 + FontAwesomeIconStyle, 15 + } from '@fortawesome/react-native-fontawesome' 16 + import {CenteredView} from '../util/Views.web' 17 + import {Text} from '../util/text/Text' 18 + import {useStores} from '../../../state' 19 + import {s, colors} from '../../lib/styles' 20 + import {TABS_EXPLAINER} from '../../lib/assets' 21 + import {TABS_ENABLED} from '../../../build-flags' 22 + 23 + const ROUTES = TABS_ENABLED 24 + ? [ 25 + {key: 'intro', title: 'Intro'}, 26 + {key: 'tabs', title: 'Tabs'}, 27 + ] 28 + : [{key: 'intro', title: 'Intro'}] 29 + 30 + const Intro = () => ( 31 + <View style={styles.explainer}> 32 + <Text 33 + style={[styles.explainerHeading, s.normal, styles.explainerHeadingIntro]}> 34 + Welcome to{' '} 35 + <Text style={[s.bold, s.blue3, styles.explainerHeadingBrand]}> 36 + Bluesky 37 + </Text> 38 + </Text> 39 + <Text style={[styles.explainerDesc, styles.explainerDescIntro]}> 40 + This is an early beta. Your feedback is appreciated! 41 + </Text> 42 + </View> 43 + ) 44 + 45 + const Tabs = () => ( 46 + <View style={styles.explainer}> 47 + <View style={styles.explainerIcon}> 48 + <View style={s.flex1} /> 49 + <FontAwesomeIcon 50 + icon={['far', 'clone']} 51 + style={[s.black as FontAwesomeIconStyle, s.mb5]} 52 + size={36} 53 + /> 54 + <View style={s.flex1} /> 55 + </View> 56 + <Text style={styles.explainerHeading}>Tabs</Text> 57 + <Text style={styles.explainerDesc}> 58 + Never lose your place! Long-press to open posts and profiles in a new tab. 59 + </Text> 60 + <Text style={styles.explainerDesc}> 61 + <Image source={TABS_EXPLAINER} style={styles.explainerImg} /> 62 + </Text> 63 + </View> 64 + ) 65 + 66 + const SCENE_MAP = { 67 + intro: Intro, 68 + tabs: Tabs, 69 + } 70 + const renderScene = SceneMap(SCENE_MAP) 71 + 72 + export const FeatureExplainer = () => { 73 + const layout = useWindowDimensions() 74 + const store = useStores() 75 + const [index, setIndex] = useState(0) 76 + 77 + const onPressSkip = () => store.onboard.next() 78 + const onPressNext = () => { 79 + if (index >= ROUTES.length - 1) { 80 + store.onboard.next() 81 + } else { 82 + setIndex(index + 1) 83 + } 84 + } 85 + 86 + const renderTabBar = (props: TabBarProps<Route>) => { 87 + const inputRange = props.navigationState.routes.map((x, i) => i) 88 + return ( 89 + <View style={styles.tabBar}> 90 + <View style={s.flex1} /> 91 + {props.navigationState.routes.map((route, i) => { 92 + const opacity = props.position.interpolate({ 93 + inputRange, 94 + outputRange: inputRange.map(inputIndex => 95 + inputIndex === i ? 1 : 0.5, 96 + ), 97 + }) 98 + 99 + return ( 100 + <TouchableOpacity 101 + key={i} 102 + style={styles.tabItem} 103 + onPress={() => setIndex(i)}> 104 + <Animated.Text style={{opacity}}>&deg;</Animated.Text> 105 + </TouchableOpacity> 106 + ) 107 + })} 108 + <View style={s.flex1} /> 109 + </View> 110 + ) 111 + } 112 + 113 + const FirstExplainer = SCENE_MAP[ROUTES[0]?.key as keyof typeof SCENE_MAP] 114 + return ( 115 + <CenteredView style={styles.container}> 116 + {ROUTES.length > 1 ? ( 117 + <TabView 118 + navigationState={{index, routes: ROUTES}} 119 + renderScene={renderScene} 120 + renderTabBar={renderTabBar} 121 + onIndexChange={setIndex} 122 + initialLayout={{width: layout.width}} 123 + tabBarPosition="bottom" 124 + /> 125 + ) : FirstExplainer ? ( 126 + <FirstExplainer /> 127 + ) : ( 128 + <View /> 129 + )} 130 + <View style={styles.footer}> 131 + <TouchableOpacity onPress={onPressSkip}> 132 + <Text style={styles.footerBtn}>Skip</Text> 133 + </TouchableOpacity> 134 + <TouchableOpacity onPress={onPressNext}> 135 + <Text style={[styles.footerBtn, styles.footerBtnNext]}>Next</Text> 136 + </TouchableOpacity> 137 + </View> 138 + </CenteredView> 139 + ) 140 + } 141 + 142 + const styles = StyleSheet.create({ 143 + container: { 144 + height: '100%', 145 + justifyContent: 'center', 146 + paddingBottom: '10%', 147 + }, 148 + 149 + tabBar: { 150 + flexDirection: 'row', 151 + }, 152 + tabItem: { 153 + alignItems: 'center', 154 + padding: 16, 155 + }, 156 + 157 + explainer: { 158 + paddingHorizontal: 16, 159 + }, 160 + explainerIcon: { 161 + flexDirection: 'row', 162 + }, 163 + explainerHeading: { 164 + fontSize: 42, 165 + fontWeight: 'bold', 166 + textAlign: 'center', 167 + marginBottom: 16, 168 + }, 169 + explainerHeadingIntro: { 170 + lineHeight: 40, 171 + }, 172 + explainerHeadingBrand: {fontSize: 56}, 173 + explainerDesc: { 174 + fontSize: 18, 175 + textAlign: 'center', 176 + marginBottom: 16, 177 + color: colors.gray5, 178 + }, 179 + explainerDescIntro: {fontSize: 24}, 180 + explainerImg: { 181 + resizeMode: 'contain', 182 + maxWidth: '100%', 183 + maxHeight: 330, 184 + }, 185 + 186 + footer: { 187 + flexDirection: 'row', 188 + justifyContent: 'center', 189 + paddingTop: 24, 190 + }, 191 + footerBtn: { 192 + color: colors.blue3, 193 + fontSize: 19, 194 + paddingHorizontal: 36, 195 + paddingVertical: 8, 196 + }, 197 + footerBtnNext: { 198 + marginLeft: 10, 199 + borderWidth: 1, 200 + borderColor: colors.blue3, 201 + borderRadius: 6, 202 + }, 203 + })
+47
src/view/com/onboard/Follows.web.tsx
··· 1 + import React from 'react' 2 + import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native' 3 + import {observer} from 'mobx-react-lite' 4 + import {SuggestedFollows} from '../discover/SuggestedFollows' 5 + import {CenteredView} from '../util/Views.web' 6 + import {Text} from '../util/text/Text' 7 + import {useStores} from '../../../state' 8 + import {s, colors} from '../../lib/styles' 9 + 10 + export const Follows = observer(() => { 11 + const store = useStores() 12 + 13 + const onNoSuggestions = () => { 14 + // no suggestions, bounce from this view 15 + store.onboard.next() 16 + } 17 + const onPressNext = () => store.onboard.next() 18 + 19 + return ( 20 + <SafeAreaView style={styles.container}> 21 + <CenteredView style={styles.header}> 22 + <Text type="title-lg"> 23 + Follow these people to see their posts in your feed 24 + </Text> 25 + <TouchableOpacity onPress={onPressNext}> 26 + <Text style={[styles.title, s.blue3, s.pr10]}>Next &raquo;</Text> 27 + </TouchableOpacity> 28 + </CenteredView> 29 + <SuggestedFollows onNoSuggestions={onNoSuggestions} /> 30 + </SafeAreaView> 31 + ) 32 + }) 33 + 34 + const styles = StyleSheet.create({ 35 + container: { 36 + flex: 1, 37 + }, 38 + title: { 39 + fontSize: 24, 40 + fontWeight: 'bold', 41 + }, 42 + 43 + header: { 44 + paddingTop: 30, 45 + paddingBottom: 40, 46 + }, 47 + })
+163
src/view/screens/Login.web.tsx
··· 1 + import React, {useState} from 'react' 2 + import { 3 + Image, 4 + SafeAreaView, 5 + StyleSheet, 6 + TouchableOpacity, 7 + View, 8 + } from 'react-native' 9 + import {observer} from 'mobx-react-lite' 10 + import {CenteredView} from '../com/util/Views' 11 + import {Signin} from '../com/login/Signin' 12 + import {CreateAccount} from '../com/login/CreateAccount' 13 + import {Text} from '../com/util/text/Text' 14 + import {ErrorBoundary} from '../com/util/ErrorBoundary' 15 + import {colors} from '../lib/styles' 16 + import {usePalette} from '../lib/hooks/usePalette' 17 + import {CLOUD_SPLASH} from '../lib/assets' 18 + 19 + enum ScreenState { 20 + S_SigninOrCreateAccount, 21 + S_Signin, 22 + S_CreateAccount, 23 + } 24 + 25 + const SigninOrCreateAccount = ({ 26 + onPressSignin, 27 + onPressCreateAccount, 28 + }: { 29 + onPressSignin: () => void 30 + onPressCreateAccount: () => void 31 + }) => { 32 + const pal = usePalette('default') 33 + return ( 34 + <> 35 + <View style={styles.hero}> 36 + <View style={styles.heroText}> 37 + <Text style={styles.title}>Bluesky</Text> 38 + <Text style={styles.subtitle}>[ private beta ]</Text> 39 + </View> 40 + </View> 41 + <View testID="signinOrCreateAccount" style={styles.btns}> 42 + <TouchableOpacity 43 + testID="createAccountButton" 44 + style={[pal.view, styles.btn]} 45 + onPress={onPressCreateAccount}> 46 + <Text style={[pal.link, styles.btnLabel]}>New account</Text> 47 + </TouchableOpacity> 48 + <TouchableOpacity 49 + testID="signInButton" 50 + style={[pal.view, styles.btn]} 51 + onPress={onPressSignin}> 52 + <Text style={[pal.link, styles.btnLabel]}>Sign in</Text> 53 + </TouchableOpacity> 54 + </View> 55 + </> 56 + ) 57 + } 58 + 59 + export const Login = observer(() => { 60 + const pal = usePalette('default') 61 + const [screenState, setScreenState] = useState<ScreenState>( 62 + ScreenState.S_SigninOrCreateAccount, 63 + ) 64 + 65 + if (screenState === ScreenState.S_SigninOrCreateAccount) { 66 + return ( 67 + <CenteredView style={[styles.container, styles.vertCenter]}> 68 + <ErrorBoundary> 69 + <SigninOrCreateAccount 70 + onPressSignin={() => setScreenState(ScreenState.S_Signin)} 71 + onPressCreateAccount={() => 72 + setScreenState(ScreenState.S_CreateAccount) 73 + } 74 + /> 75 + </ErrorBoundary> 76 + </CenteredView> 77 + ) 78 + } 79 + 80 + return ( 81 + <CenteredView 82 + style={[ 83 + styles.container, 84 + styles.containerBorder, 85 + pal.view, 86 + pal.borderDark, 87 + ]}> 88 + <SafeAreaView testID="noSessionView" style={styles.container}> 89 + <ErrorBoundary> 90 + {screenState === ScreenState.S_Signin ? ( 91 + <Signin 92 + onPressBack={() => 93 + setScreenState(ScreenState.S_SigninOrCreateAccount) 94 + } 95 + /> 96 + ) : undefined} 97 + {screenState === ScreenState.S_CreateAccount ? ( 98 + <CreateAccount 99 + onPressBack={() => 100 + setScreenState(ScreenState.S_SigninOrCreateAccount) 101 + } 102 + /> 103 + ) : undefined} 104 + </ErrorBoundary> 105 + </SafeAreaView> 106 + </CenteredView> 107 + ) 108 + }) 109 + 110 + const styles = StyleSheet.create({ 111 + container: { 112 + height: '100%', 113 + }, 114 + containerBorder: { 115 + borderLeftWidth: 1, 116 + borderRightWidth: 1, 117 + }, 118 + vertCenter: { 119 + justifyContent: 'center', 120 + }, 121 + bgImg: { 122 + position: 'absolute', 123 + top: 0, 124 + left: 0, 125 + width: '100%', 126 + height: '100%', 127 + }, 128 + heroText: { 129 + backgroundColor: colors.white, 130 + paddingTop: 10, 131 + paddingBottom: 20, 132 + }, 133 + btns: { 134 + flexDirection: 'row', 135 + paddingTop: 40, 136 + }, 137 + title: { 138 + textAlign: 'center', 139 + color: colors.blue3, 140 + fontSize: 68, 141 + fontWeight: 'bold', 142 + }, 143 + subtitle: { 144 + textAlign: 'center', 145 + color: colors.blue3, 146 + fontSize: 18, 147 + }, 148 + btn: { 149 + flex: 1, 150 + borderRadius: 4, 151 + paddingVertical: 16, 152 + marginBottom: 20, 153 + marginHorizontal: 20, 154 + borderWidth: 1, 155 + borderColor: colors.blue3, 156 + }, 157 + btnLabel: { 158 + textAlign: 'center', 159 + fontSize: 21, 160 + fontWeight: '500', 161 + color: colors.blue3, 162 + }, 163 + })
+10 -6
src/view/shell/web/index.tsx
··· 5 5 import {match, MatchResult} from '../../routes' 6 6 import {DesktopLeftColumn} from './left-column' 7 7 import {DesktopRightColumn} from './right-column' 8 + import {Onboard} from '../../screens/Onboard' 8 9 import {Login} from '../../screens/Login' 9 10 import {ErrorBoundary} from '../../com/util/ErrorBoundary' 10 11 import {usePalette} from '../../lib/hooks/usePalette' ··· 19 20 return ( 20 21 <View style={styles.outerContainer}> 21 22 <Login /> 23 + </View> 24 + ) 25 + } 26 + if (store.onboard.isOnboarding) { 27 + return ( 28 + <View style={styles.outerContainer}> 29 + <ErrorBoundary> 30 + <Onboard /> 31 + </ErrorBoundary> 22 32 </View> 23 33 ) 24 34 } ··· 102 112 103 113 const styles = StyleSheet.create({ 104 114 outerContainer: { 105 - height: '100%', 106 - }, 107 - innerContainer: { 108 - marginLeft: 'auto', 109 - marginRight: 'auto', 110 - width: '600px', 111 115 height: '100%', 112 116 }, 113 117 visible: {