Restructure the shell folder and fix the header positioning

This commit is contained in:
Paul Frazee
2023-03-10 13:24:50 -06:00
parent 3b0d72b776
commit ebb0b11774
16 changed files with 146 additions and 239 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ import {observer} from 'mobx-react-lite'
import {ThemeProvider} from 'lib/ThemeContext'
import * as view from './view/index'
import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {MobileShell} from './view/shell/mobile'
import {Shell} from './view/shell'
import {s} from 'lib/styles'
import * as notifee from 'lib/notifee'
import * as analytics from 'lib/analytics'
@@ -54,7 +54,7 @@ const App = observer(() => {
<analytics.Provider>
<RootStoreProvider value={rootStore}>
<SafeAreaProvider>
<MobileShell />
<Shell />
</SafeAreaProvider>
</RootStoreProvider>
</analytics.Provider>
+2 -2
View File
@@ -3,7 +3,7 @@ import {SafeAreaProvider} from 'react-native-safe-area-context'
import {getInitialURL} from 'platform/urls'
import * as view from './view/index'
import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {WebShell} from './view/shell/web'
import {Shell} from './view/shell/index'
import {ToastContainer} from './view/com/util/Toast.web'
function App() {
@@ -33,7 +33,7 @@ function App() {
return (
<RootStoreProvider value={rootStore}>
<SafeAreaProvider>
<WebShell />
<Shell />
</SafeAreaProvider>
<ToastContainer />
</RootStoreProvider>
-4
View File
@@ -1,4 +0,0 @@
{
"name": "xyz.blueskyweb.app",
"displayName": "Bluesky"
}
-12
View File
@@ -1,12 +0,0 @@
/**
* @format
*/
import {AppRegistry} from 'react-native'
import App from './App'
AppRegistry.registerComponent('App', () => App)
AppRegistry.runApplication('App', {
rootTag: document.getElementById('root'),
})
+77 -76
View File
@@ -1,19 +1,20 @@
import {LikelyType, LinkMeta} from './link-meta'
import {match as matchRoute} from 'view/routes'
// import {match as matchRoute} from 'view/routes'
import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers'
import {RootStoreModel} from 'state/index'
import {PostThreadViewModel} from 'state/models/post-thread-view'
import {ComposerOptsQuote} from 'state/models/shell-ui'
import {Home} from 'view/screens/Home'
import {Search} from 'view/screens/Search'
import {Notifications} from 'view/screens/Notifications'
import {PostThread} from 'view/screens/PostThread'
import {PostUpvotedBy} from 'view/screens/PostUpvotedBy'
import {PostRepostedBy} from 'view/screens/PostRepostedBy'
import {Profile} from 'view/screens/Profile'
import {ProfileFollowers} from 'view/screens/ProfileFollowers'
import {ProfileFollows} from 'view/screens/ProfileFollows'
// TODO
// import {Home} from 'view/screens/Home'
// import {Search} from 'view/screens/Search'
// import {Notifications} from 'view/screens/Notifications'
// import {PostThread} from 'view/screens/PostThread'
// import {PostUpvotedBy} from 'view/screens/PostUpvotedBy'
// import {PostRepostedBy} from 'view/screens/PostRepostedBy'
// import {Profile} from 'view/screens/Profile'
// import {ProfileFollowers} from 'view/screens/ProfileFollowers'
// import {ProfileFollows} from 'view/screens/ProfileFollows'
// NOTE
// this is a hack around the lack of hosted social metadata
@@ -24,77 +25,77 @@ export async function extractBskyMeta(
url: string,
): Promise<LinkMeta> {
url = convertBskyAppUrlIfNeeded(url)
const route = matchRoute(url)
// const route = matchRoute(url)
let meta: LinkMeta = {
likelyType: LikelyType.AtpData,
url,
title: route.defaultTitle,
// title: route.defaultTitle,
}
if (route.Com === Home) {
meta = {
...meta,
title: 'Bluesky',
description: 'A new kind of social network',
}
} else if (route.Com === Search) {
meta = {
...meta,
title: 'Search - Bluesky',
description: 'A new kind of social network',
}
} else if (route.Com === Notifications) {
meta = {
...meta,
title: 'Notifications - Bluesky',
description: 'A new kind of social network',
}
} else if (
route.Com === PostThread ||
route.Com === PostUpvotedBy ||
route.Com === PostRepostedBy
) {
// post and post-related screens
const threadUri = makeRecordUri(
route.params.name,
'app.bsky.feed.post',
route.params.rkey,
)
const threadView = new PostThreadViewModel(store, {
uri: threadUri,
depth: 0,
})
await threadView.setup().catch(_err => undefined)
const title = [
route.Com === PostUpvotedBy
? 'Likes on a post by'
: route.Com === PostRepostedBy
? 'Reposts of a post by'
: 'Post by',
threadView.thread?.post.author.displayName ||
threadView.thread?.post.author.handle ||
'a bluesky user',
].join(' ')
meta = {
...meta,
title,
description: threadView.thread?.postRecord?.text,
}
} else if (
route.Com === Profile ||
route.Com === ProfileFollowers ||
route.Com === ProfileFollows
) {
// profile and profile-related screens
const profile = await store.profiles.getProfile(route.params.name)
if (profile?.data) {
meta = {
...meta,
title: profile.data.displayName || profile.data.handle,
description: profile.data.description,
}
}
}
// if (route.Com === Home) {
// meta = {
// ...meta,
// title: 'Bluesky',
// description: 'A new kind of social network',
// }
// } else if (route.Com === Search) {
// meta = {
// ...meta,
// title: 'Search - Bluesky',
// description: 'A new kind of social network',
// }
// } else if (route.Com === Notifications) {
// meta = {
// ...meta,
// title: 'Notifications - Bluesky',
// description: 'A new kind of social network',
// }
// } else if (
// route.Com === PostThread ||
// route.Com === PostUpvotedBy ||
// route.Com === PostRepostedBy
// ) {
// // post and post-related screens
// const threadUri = makeRecordUri(
// route.params.name,
// 'app.bsky.feed.post',
// route.params.rkey,
// )
// const threadView = new PostThreadViewModel(store, {
// uri: threadUri,
// depth: 0,
// })
// await threadView.setup().catch(_err => undefined)
// const title = [
// route.Com === PostUpvotedBy
// ? 'Likes on a post by'
// : route.Com === PostRepostedBy
// ? 'Reposts of a post by'
// : 'Post by',
// threadView.thread?.post.author.displayName ||
// threadView.thread?.post.author.handle ||
// 'a bluesky user',
// ].join(' ')
// meta = {
// ...meta,
// title,
// description: threadView.thread?.postRecord?.text,
// }
// } else if (
// route.Com === Profile ||
// route.Com === ProfileFollowers ||
// route.Com === ProfileFollows
// ) {
// // profile and profile-related screens
// const profile = await store.profiles.getProfile(route.params.name)
// if (profile?.data) {
// meta = {
// ...meta,
// title: profile.data.displayName || profile.data.handle,
// description: profile.data.description,
// }
// }
// }
return meta
}
+19 -23
View File
@@ -11,21 +11,21 @@ import {
State,
} from 'lib/routes/types'
import {Drawer} from './shell/Drawer'
import {BottomBar} from './shell/BottomBar'
import {Drawer} from './view/shell/Drawer'
import {BottomBar} from './view/shell/BottomBar'
import {HomeScreen} from './screens/Home'
import {SearchScreen} from './screens/Search'
import {NotificationsScreen} from './screens/Notifications'
import {SettingsScreen} from './screens/Settings'
import {ProfileScreen} from './screens/Profile'
import {ProfileFollowersScreen} from './screens/ProfileFollowers'
import {ProfileFollowsScreen} from './screens/ProfileFollows'
import {PostThreadScreen} from './screens/PostThread'
import {PostUpvotedByScreen} from './screens/PostUpvotedBy'
import {PostRepostedByScreen} from './screens/PostRepostedBy'
import {DebugScreen} from './screens/Debug'
import {LogScreen} from './screens/Log'
import {HomeScreen} from './view/screens/Home'
import {SearchScreen} from './view/screens/Search'
import {NotificationsScreen} from './view/screens/Notifications'
import {SettingsScreen} from './view/screens/Settings'
import {ProfileScreen} from './view/screens/Profile'
import {ProfileFollowersScreen} from './view/screens/ProfileFollowers'
import {ProfileFollowsScreen} from './view/screens/ProfileFollows'
import {PostThreadScreen} from './view/screens/PostThread'
import {PostUpvotedByScreen} from './view/screens/PostUpvotedBy'
import {PostRepostedByScreen} from './view/screens/PostRepostedBy'
import {DebugScreen} from './view/screens/Debug'
import {LogScreen} from './view/screens/Log'
const HomeDrawer = createDrawerNavigator()
const HomeTab = createNativeStackNavigator<HomeTabNavigatorParams>()
@@ -52,7 +52,7 @@ function r(pattern: string): Route {
match(path: string) {
const res = matcherRe.exec(path)
if (res) {
return {params: res.groups}
return {params: res.groups || {}}
}
return undefined
},
@@ -101,7 +101,7 @@ export const LINKING = {
getPathFromState(state: State) {
// find the current node in the navigation tree
let node = state.routes[state.index]
let node = state.routes[state.index || 0]
while (node.state?.routes && typeof node.state?.index === 'number') {
node = node.state?.routes[node.state?.index]
}
@@ -239,7 +239,7 @@ function SearchTabNavigator() {
)
}
function TabsNavigator() {
export function TabsNavigator() {
const tabBar = React.useCallback(props => <BottomBar {...props} />, [])
return (
<Tab.Navigator
@@ -257,10 +257,6 @@ function TabsNavigator() {
)
}
export function Screens() {
return (
<NavigationContainer linking={LINKING}>
<TabsNavigator />
</NavigationContainer>
)
export function RoutesContainer({children}: React.PropsWithChildren<{}>) {
return <NavigationContainer linking={LINKING}>{children}</NavigationContainer>
}
+2 -2
View File
@@ -15,7 +15,7 @@ import {useLinkProps, useNavigation} from '@react-navigation/native'
import {Text} from './text/Text'
import {TypographyVariant} from 'lib/ThemeContext'
import {NavigationProp} from 'lib/routes/types'
import {matchPath} from 'view/screens'
import {matchPath} from '../../../Routes'
import {useStores, RootStoreModel} from 'state/index'
import {convertBskyAppUrlIfNeeded} from 'lib/strings/url-helpers'
@@ -36,7 +36,7 @@ export const Link = observer(function Link({
children?: React.ReactNode
noFeedback?: boolean
}) {
let {...props} = useLinkProps({to: href})
let {...props} = useLinkProps({to: href || ''})
const store = useStores()
const navigation = useNavigation<NavigationProp>()
-82
View File
@@ -1,82 +0,0 @@
import React from 'react'
import {IconProp} from '@fortawesome/fontawesome-svg-core'
import {Home} from './screens/Home'
import {Search} from './screens/Search'
import {Notifications} from './screens/Notifications'
import {NotFound} from './screens/NotFound'
import {PostThread} from './screens/PostThread'
import {PostUpvotedBy} from './screens/PostUpvotedBy'
import {PostRepostedBy} from './screens/PostRepostedBy'
import {Profile} from './screens/Profile'
import {ProfileFollowers} from './screens/ProfileFollowers'
import {ProfileFollows} from './screens/ProfileFollows'
import {Settings} from './screens/Settings'
import {Debug} from './screens/Debug'
import {Log} from './screens/Log'
export type ScreenParams = {
navIdx: string
params: Record<string, any>
visible: boolean
}
export type Route = [React.FC<ScreenParams>, string, IconProp, RegExp]
export type MatchResult = {
Com: React.FC<ScreenParams>
defaultTitle: string
icon: IconProp
params: Record<string, any>
isNotFound?: boolean
}
const r = (pattern: string) => new RegExp('^' + pattern + '([?]|$)', 'i')
export const routes: Route[] = [
[Home, 'Home', 'house', r('/')],
[Search, 'Search', 'magnifying-glass', r('/search')],
[Notifications, 'Notifications', 'bell', r('/notifications')],
[Settings, 'Settings', 'bell', r('/settings')],
[Profile, 'User', ['far', 'user'], r('/profile/(?<name>[^/]+)')],
[
ProfileFollowers,
'Followers',
'users',
r('/profile/(?<name>[^/]+)/followers'),
],
[ProfileFollows, 'Follows', 'users', r('/profile/(?<name>[^/]+)/follows')],
[
PostThread,
'Post',
['far', 'message'],
r('/profile/(?<name>[^/]+)/post/(?<rkey>[^/]+)'),
],
[
PostUpvotedBy,
'Liked by',
'heart',
r('/profile/(?<name>[^/]+)/post/(?<rkey>[^/]+)/upvoted-by'),
],
[
PostRepostedBy,
'Reposted by',
'retweet',
r('/profile/(?<name>[^/]+)/post/(?<rkey>[^/]+)/reposted-by'),
],
[Debug, 'Debug', 'house', r('/sys/debug')],
[Log, 'Log', 'house', r('/sys/log')],
]
export function match(url: string): MatchResult {
for (const [Com, defaultTitle, icon, pattern] of routes) {
const res = pattern.exec(url)
if (res) {
// TODO: query params
return {Com, defaultTitle, icon, params: res.groups || {}}
}
}
return {
Com: NotFound,
defaultTitle: 'Not found',
icon: 'magnifying-glass',
params: {},
isNotFound: true,
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ import {ComposeIcon2} from 'lib/icons'
const HEADER_HEIGHT = 42
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export const HomeScreen = observer(function Home({}: Props) {
export const HomeScreen = observer(function Home(_opts: Props) {
const store = useStores()
const onMainScroll = useOnMainScroll(store)
const {screen, track} = useAnalytics()
@@ -1,7 +1,7 @@
import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite'
import {Animated, Easing, Platform, StyleSheet, View} from 'react-native'
import {ComposePost} from '../../com/composer/ComposePost'
import {ComposePost} from '../com/composer/ComposePost'
import {ComposerOpts} from 'state/models/shell-ui'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
import {usePalette} from 'lib/hooks/usePalette'
@@ -1,7 +1,7 @@
import React from 'react'
import {observer} from 'mobx-react-lite'
import {StyleSheet, View} from 'react-native'
import {ComposePost} from '../../com/composer/ComposePost'
import {ComposePost} from '../com/composer/ComposePost'
import {ComposerOpts} from 'state/models/shell-ui'
import {usePalette} from 'lib/hooks/usePalette'
+1 -1
View File
@@ -318,7 +318,7 @@ const styles = StyleSheet.create({
flex: 1,
paddingTop: 20,
paddingBottom: 50,
paddingLeft: 30,
paddingLeft: 20,
},
viewDarkMode: {
backgroundColor: '#1B1919',
@@ -16,7 +16,7 @@ import {
MagnifyingGlassIcon,
CogIcon,
} from 'lib/icons'
import {DesktopSearch} from './DesktopSearch'
import {DesktopSearch} from './Search'
interface NavItemProps {
count?: number
@@ -5,7 +5,7 @@ import {observer} from 'mobx-react-lite'
import {useStores} from 'state/index'
import {usePalette} from 'lib/hooks/usePalette'
import {MagnifyingGlassIcon} from 'lib/icons'
import {ProfileCard} from '../../com/profile/ProfileCard'
import {ProfileCard} from '../com/profile/ProfileCard'
export const DesktopSearch = observer(function DesktopSearch() {
const store = useStores()
@@ -1,23 +1,28 @@
import React from 'react'
import {observer} from 'mobx-react-lite'
import {StatusBar, StyleSheet, useWindowDimensions, View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {useStores} from 'state/index'
import {Login} from '../../screens/Login'
import {ModalsContainer} from '../../com/modals/Modal'
import {Lightbox} from '../../com/lightbox/Lightbox'
import {Text} from '../../com/util/text/Text'
import {Login} from '../screens/Login'
import {ModalsContainer} from '../com/modals/Modal'
import {Lightbox} from '../com/lightbox/Lightbox'
import {Text} from '../com/util/text/Text'
import {Composer} from './Composer'
import {s} from 'lib/styles'
import {useTheme} from 'lib/ThemeContext'
import {usePalette} from 'lib/hooks/usePalette'
import {RoutesContainer, TabsNavigator} from '../../Routes'
import {Screens} from '../../screens'
export const MobileShell: React.FC = observer(() => {
export const Shell: React.FC = observer(() => {
const theme = useTheme()
const pal = usePalette('default')
const store = useStores()
const winDim = useWindowDimensions()
const safeAreaInsets = useSafeAreaInsets()
const containerPadding = React.useMemo(
() => ({height: '100%', paddingTop: safeAreaInsets.top}),
[safeAreaInsets],
)
if (store.hackUpgradeNeeded) {
return (
@@ -73,18 +78,22 @@ export const MobileShell: React.FC = observer(() => {
theme.colorScheme === 'dark' ? 'light-content' : 'dark-content'
}
/>
<Screens />
<ModalsContainer />
<Lightbox />
<Composer
active={store.shell.isComposerActive}
onClose={() => store.shell.closeComposer()}
winHeight={winDim.height}
replyTo={store.shell.composerOpts?.replyTo}
imagesOpen={store.shell.composerOpts?.imagesOpen}
onPost={store.shell.composerOpts?.onPost}
quote={store.shell.composerOpts?.quote}
/>
<RoutesContainer>
<View style={containerPadding}>
<TabsNavigator />
</View>
<ModalsContainer />
<Lightbox />
<Composer
active={store.shell.isComposerActive}
onClose={() => store.shell.closeComposer()}
winHeight={winDim.height}
replyTo={store.shell.composerOpts?.replyTo}
imagesOpen={store.shell.composerOpts?.imagesOpen}
onPost={store.shell.composerOpts?.onPost}
quote={store.shell.composerOpts?.quote}
/>
</RoutesContainer>
</View>
)
})
@@ -3,23 +3,22 @@ import {observer} from 'mobx-react-lite'
import {View, StyleSheet} from 'react-native'
import {IconProp} from '@fortawesome/fontawesome-svg-core'
import {useStores} from 'state/index'
import {NavigationModel} from 'state/models/navigation'
import {match, MatchResult} from '../../routes'
import {DesktopHeader} from './DesktopHeader'
import {Login} from '../../screens/Login'
import {ErrorBoundary} from '../../com/util/ErrorBoundary'
import {Lightbox} from '../../com/lightbox/Lightbox'
import {ModalsContainer} from '../../com/modals/Modal'
import {match, MatchResult} from '../routes'
import {DesktopHeader} from './desktop/Header'
import {Login} from '../screens/Login'
import {ErrorBoundary} from '../com/util/ErrorBoundary'
import {Lightbox} from '../com/lightbox/Lightbox'
import {ModalsContainer} from '../com/modals/Modal'
import {Text} from 'view/com/util/text/Text'
import {Composer} from './Composer'
import {Composer} from './Composer.web'
import {usePalette} from 'lib/hooks/usePalette'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {s, colors} from 'lib/styles'
import {isMobileWeb} from 'platform/detection'
import {Screens} from '../../screens'
import {RoutesContainer} from '../../Routes'
export const WebShell: React.FC = observer(() => {
export const Shell: React.FC = observer(() => {
const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
const store = useStores()
const screenRenderDesc = constructScreenRenderDesc(store.nav)
@@ -28,7 +27,7 @@ export const WebShell: React.FC = observer(() => {
return <NoMobileWeb />
}
return <Screens />
return <RoutesContainer />
if (!store.session.hasSession) {
return (