Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Get MVP of web app running

+158 -46
+5 -2
src/App.web.tsx
··· 1 1 import React, {useState, useEffect} from 'react' 2 + import {SafeAreaProvider} from 'react-native-safe-area-context' 2 3 import * as view from './view/index' 3 4 import {RootStoreModel, setupState, RootStoreProvider} from './state' 4 - import {DesktopWebShell} from './view/shell/desktop-web' 5 + import {WebShell} from './view/shell/web' 5 6 // import Toast from 'react-native-root-toast' TODO 6 7 7 8 function App() { ··· 22 23 23 24 return ( 24 25 <RootStoreProvider value={rootStore}> 25 - <DesktopWebShell /> 26 + <SafeAreaProvider> 27 + <WebShell /> 28 + </SafeAreaProvider> 26 29 </RootStoreProvider> 27 30 ) 28 31 // <Toast.ToastContainer /> TODO
+3 -3
src/view/com/login/CreateAccount.tsx
··· 15 15 } from '@fortawesome/react-native-fontawesome' 16 16 import {ComAtprotoAccountCreate} from '@atproto/api' 17 17 import * as EmailValidator from 'email-validator' 18 - import {useAnalytics} from '@segment/analytics-react-native' 18 + // import {useAnalytics} from '@segment/analytics-react-native' TODO 19 19 import {LogoTextHero} from './Logo' 20 20 import {Picker} from '../util/Picker' 21 21 import {TextLink} from '../util/Link' ··· 32 32 import {usePalette} from '../../lib/hooks/usePalette' 33 33 34 34 export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => { 35 - const {track} = useAnalytics() 35 + // const {track} = useAnalytics() TODO 36 36 const pal = usePalette('default') 37 37 const store = useStores() 38 38 const [isProcessing, setIsProcessing] = useState<boolean>(false) ··· 109 109 password, 110 110 inviteCode, 111 111 }) 112 - track('Create Account') 112 + // track('Create Account') TODO 113 113 } catch (e: any) { 114 114 let errMsg = e.toString() 115 115 if (e instanceof ComAtprotoAccountCreate.InvalidInviteCodeError) {
+5 -5
src/view/com/login/Signin.tsx
··· 14 14 } from '@fortawesome/react-native-fontawesome' 15 15 import * as EmailValidator from 'email-validator' 16 16 import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api' 17 - import {useAnalytics} from '@segment/analytics-react-native' 17 + // import {useAnalytics} from '@segment/analytics-react-native' TODO 18 18 import {LogoTextHero} from './Logo' 19 19 import {Text} from '../util/text/Text' 20 20 import {UserAvatar} from '../util/UserAvatar' ··· 153 153 onSelectAccount: (account?: AccountData) => void 154 154 onPressBack: () => void 155 155 }) => { 156 - const {track} = useAnalytics() 156 + // const {track} = useAnalytics() TODO 157 157 const pal = usePalette('default') 158 158 const [isProcessing, setIsProcessing] = React.useState(false) 159 159 ··· 161 161 if (account.accessJwt && account.refreshJwt) { 162 162 setIsProcessing(true) 163 163 if (await store.session.resumeSession(account)) { 164 - track('Sign In', {resumedSession: true}) 164 + // track('Sign In', {resumedSession: true}) TODO 165 165 setIsProcessing(false) 166 166 return 167 167 } ··· 261 261 onPressBack: () => void 262 262 onPressForgotPassword: () => void 263 263 }) => { 264 - const {track} = useAnalytics() 264 + // const {track} = useAnalytics() TODO 265 265 const pal = usePalette('default') 266 266 const [isProcessing, setIsProcessing] = useState<boolean>(false) 267 267 const [handle, setHandle] = useState<string>(initialHandle) ··· 302 302 handle: fullHandle, 303 303 password, 304 304 }) 305 - track('Sign In', {resumedSession: false}) 305 + // track('Sign In', {resumedSession: false}) TODO 306 306 } catch (e: any) { 307 307 const errMsg = e.toString() 308 308 store.log.warn('Failed to login', e)
+5 -1
src/view/com/util/forms/DropdownButton.tsx
··· 76 76 onPress={onPress} 77 77 hitSlop={HITSLOP} 78 78 // Fix an issue where specific references cause runtime error in jest environment 79 - ref={process.env.JEST_WORKER_ID != null ? null : ref}> 79 + ref={ 80 + typeof process !== 'undefined' && process.env.JEST_WORKER_ID != null 81 + ? null 82 + : ref 83 + }> 80 84 {children} 81 85 </TouchableOpacity> 82 86 )
-35
src/view/shell/desktop-web/index.tsx
··· 1 - import React from 'react' 2 - import {observer} from 'mobx-react-lite' 3 - import {View, StyleSheet} from 'react-native' 4 - import {DesktopLeftColumn} from './left-column' 5 - import {DesktopRightColumn} from './right-column' 6 - import {useStores} from '../../../state' 7 - 8 - export const DesktopWebShell: React.FC = observer(({children}) => { 9 - const store = useStores() 10 - return ( 11 - <View style={styles.outerContainer}> 12 - {store.session.hasSession ? ( 13 - <> 14 - <DesktopLeftColumn /> 15 - <View style={styles.innerContainer}>{children}</View> 16 - <DesktopRightColumn /> 17 - </> 18 - ) : ( 19 - <View style={styles.innerContainer}>{children}</View> 20 - )} 21 - </View> 22 - ) 23 - }) 24 - 25 - const styles = StyleSheet.create({ 26 - outerContainer: { 27 - height: '100%', 28 - }, 29 - innerContainer: { 30 - marginLeft: 'auto', 31 - marginRight: 'auto', 32 - width: '600px', 33 - height: '100%', 34 - }, 35 - })
src/view/shell/desktop-web/left-column.tsx src/view/shell/web/left-column.tsx
src/view/shell/desktop-web/right-column.tsx src/view/shell/web/right-column.tsx
+140
src/view/shell/web/index.tsx
··· 1 + import React from 'react' 2 + import {observer} from 'mobx-react-lite' 3 + import {View, StyleSheet, Text} from 'react-native' 4 + import {useStores} from '../../../state' 5 + import {match, MatchResult} from '../../routes' 6 + // import {DesktopLeftColumn} from './left-column' 7 + // import {DesktopRightColumn} from './right-column' 8 + import {Login} from '../../screens/Login' 9 + import {ErrorBoundary} from '../../com/util/ErrorBoundary' 10 + import {usePalette} from '../../lib/hooks/usePalette' 11 + import {s} from '../../lib/styles' 12 + 13 + export const WebShell: React.FC = observer(() => { 14 + const pal = usePalette('default') 15 + const store = useStores() 16 + const screenRenderDesc = constructScreenRenderDesc(store.nav) 17 + 18 + if (!store.session.hasSession) { 19 + return ( 20 + <View style={styles.outerContainer}> 21 + <View style={styles.innerContainer}> 22 + <Login /> 23 + </View> 24 + </View> 25 + ) 26 + } 27 + 28 + return ( 29 + <View style={[styles.outerContainer, pal.view]}> 30 + <View style={styles.innerContainer}> 31 + {screenRenderDesc.screens.map(({Com, navIdx, params, key, current}) => ( 32 + <View 33 + key={key} 34 + style={[s.h100pct, current ? styles.visible : styles.hidden]}> 35 + <ErrorBoundary> 36 + <Com params={params} navIdx={navIdx} visible={current} /> 37 + </ErrorBoundary> 38 + </View> 39 + ))} 40 + </View> 41 + </View> 42 + ) 43 + // TODO 44 + // <Modal /> 45 + // <Lightbox /> 46 + // <Composer 47 + // active={store.shell.isComposerActive} 48 + // onClose={() => store.shell.closeComposer()} 49 + // winHeight={winDim.height} 50 + // replyTo={store.shell.composerOpts?.replyTo} 51 + // imagesOpen={store.shell.composerOpts?.imagesOpen} 52 + // onPost={store.shell.composerOpts?.onPost} 53 + // /> 54 + // return ( 55 + // <View style={styles.outerContainer}> 56 + // {store.session.hasSession ? ( 57 + // <> 58 + // <DesktopLeftColumn /> 59 + // <View style={styles.innerContainer}> 60 + // <Text>Hello, world! (Logged in)</Text> 61 + // {children} 62 + // </View> 63 + // <DesktopRightColumn /> 64 + // </> 65 + // ) : ( 66 + // <View style={styles.innerContainer}> 67 + // <Text>Hello, world! (Logged out)</Text> 68 + // {children} 69 + // </View> 70 + // )} 71 + // </View> 72 + // ) 73 + }) 74 + 75 + /** 76 + * This method produces the information needed by the shell to 77 + * render the current screens with screen-caching behaviors. 78 + */ 79 + type ScreenRenderDesc = MatchResult & { 80 + key: string 81 + navIdx: string 82 + current: boolean 83 + previous: boolean 84 + isNewTab: boolean 85 + } 86 + function constructScreenRenderDesc(nav: NavigationModel): { 87 + icon: IconProp 88 + hasNewTab: boolean 89 + screens: ScreenRenderDesc[] 90 + } { 91 + let hasNewTab = false 92 + let icon: IconProp = 'magnifying-glass' 93 + let screens: ScreenRenderDesc[] = [] 94 + for (const tab of nav.tabs) { 95 + const tabScreens = [ 96 + ...tab.getBackList(5), 97 + Object.assign({}, tab.current, {index: tab.index}), 98 + ] 99 + const parsedTabScreens = tabScreens.map(screen => { 100 + const isCurrent = nav.isCurrentScreen(tab.id, screen.index) 101 + const isPrevious = nav.isCurrentScreen(tab.id, screen.index + 1) 102 + const matchRes = match(screen.url) 103 + if (isCurrent) { 104 + icon = matchRes.icon 105 + } 106 + hasNewTab = hasNewTab || tab.isNewTab 107 + return Object.assign(matchRes, { 108 + key: `t${tab.id}-s${screen.index}`, 109 + navIdx: `${tab.id}-${screen.id}`, 110 + current: isCurrent, 111 + previous: isPrevious, 112 + isNewTab: tab.isNewTab, 113 + }) as ScreenRenderDesc 114 + }) 115 + screens = screens.concat(parsedTabScreens) 116 + } 117 + return { 118 + icon, 119 + hasNewTab, 120 + screens, 121 + } 122 + } 123 + 124 + const styles = StyleSheet.create({ 125 + outerContainer: { 126 + height: '100%', 127 + }, 128 + innerContainer: { 129 + marginLeft: 'auto', 130 + marginRight: 'auto', 131 + width: '600px', 132 + height: '100%', 133 + }, 134 + visible: { 135 + display: 'flex', 136 + }, 137 + hidden: { 138 + display: 'none', 139 + }, 140 + })