diff --git a/src/App.native.tsx b/src/App.native.tsx index 8bb204923f..51090ae98c 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -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(() => { - + diff --git a/src/App.web.tsx b/src/App.web.tsx index 84d3b6cd60..c67fc26411 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -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 ( - + diff --git a/src/app.json b/src/app.json deleted file mode 100644 index 1f9e391e0d..0000000000 --- a/src/app.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "xyz.blueskyweb.app", - "displayName": "Bluesky" -} \ No newline at end of file diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 45a06f40a5..0000000000 --- a/src/index.js +++ /dev/null @@ -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'), -}) diff --git a/src/lib/link-meta/bsky.ts b/src/lib/link-meta/bsky.ts index c9c2ed31a5..0d8e8c69bf 100644 --- a/src/lib/link-meta/bsky.ts +++ b/src/lib/link-meta/bsky.ts @@ -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 { 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 } diff --git a/src/view/screens.tsx b/src/routes.tsx similarity index 86% rename from src/view/screens.tsx rename to src/routes.tsx index ab41aa650e..f90ecdc845 100644 --- a/src/view/screens.tsx +++ b/src/routes.tsx @@ -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() @@ -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 => , []) return ( - - - ) +export function RoutesContainer({children}: React.PropsWithChildren<{}>) { + return {children} } diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx index 596c2147d1..75a914ae5c 100644 --- a/src/view/com/util/Link.tsx +++ b/src/view/com/util/Link.tsx @@ -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() diff --git a/src/view/routes.ts b/src/view/routes.ts deleted file mode 100644 index cadf596f98..0000000000 --- a/src/view/routes.ts +++ /dev/null @@ -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 - visible: boolean -} -export type Route = [React.FC, string, IconProp, RegExp] -export type MatchResult = { - Com: React.FC - defaultTitle: string - icon: IconProp - params: Record - 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/(?[^/]+)')], - [ - ProfileFollowers, - 'Followers', - 'users', - r('/profile/(?[^/]+)/followers'), - ], - [ProfileFollows, 'Follows', 'users', r('/profile/(?[^/]+)/follows')], - [ - PostThread, - 'Post', - ['far', 'message'], - r('/profile/(?[^/]+)/post/(?[^/]+)'), - ], - [ - PostUpvotedBy, - 'Liked by', - 'heart', - r('/profile/(?[^/]+)/post/(?[^/]+)/upvoted-by'), - ], - [ - PostRepostedBy, - 'Reposted by', - 'retweet', - r('/profile/(?[^/]+)/post/(?[^/]+)/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, - } -} diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 89a30c0467..505b1fcfe8 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -18,7 +18,7 @@ import {ComposeIcon2} from 'lib/icons' const HEADER_HEIGHT = 42 type Props = NativeStackScreenProps -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() diff --git a/src/view/shell/mobile/Composer.tsx b/src/view/shell/Composer.tsx similarity index 96% rename from src/view/shell/mobile/Composer.tsx rename to src/view/shell/Composer.tsx index 5fca118bda..bb15053e78 100644 --- a/src/view/shell/mobile/Composer.tsx +++ b/src/view/shell/Composer.tsx @@ -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' diff --git a/src/view/shell/web/Composer.tsx b/src/view/shell/Composer.web.tsx similarity index 95% rename from src/view/shell/web/Composer.tsx rename to src/view/shell/Composer.web.tsx index 0d84842621..0a16483c83 100644 --- a/src/view/shell/web/Composer.tsx +++ b/src/view/shell/Composer.web.tsx @@ -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' diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index b179b630a9..63994c72a9 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -318,7 +318,7 @@ const styles = StyleSheet.create({ flex: 1, paddingTop: 20, paddingBottom: 50, - paddingLeft: 30, + paddingLeft: 20, }, viewDarkMode: { backgroundColor: '#1B1919', diff --git a/src/view/shell/web/DesktopHeader.tsx b/src/view/shell/desktop/Header.tsx similarity index 99% rename from src/view/shell/web/DesktopHeader.tsx rename to src/view/shell/desktop/Header.tsx index 8748ebbdee..594249ed40 100644 --- a/src/view/shell/web/DesktopHeader.tsx +++ b/src/view/shell/desktop/Header.tsx @@ -16,7 +16,7 @@ import { MagnifyingGlassIcon, CogIcon, } from 'lib/icons' -import {DesktopSearch} from './DesktopSearch' +import {DesktopSearch} from './Search' interface NavItemProps { count?: number diff --git a/src/view/shell/web/DesktopSearch.tsx b/src/view/shell/desktop/Search.tsx similarity index 98% rename from src/view/shell/web/DesktopSearch.tsx rename to src/view/shell/desktop/Search.tsx index 43f13ca2bc..b8bfe59f7b 100644 --- a/src/view/shell/web/DesktopSearch.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -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() diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/index.tsx similarity index 67% rename from src/view/shell/mobile/index.tsx rename to src/view/shell/index.tsx index 24353486dd..e5258e9007 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/index.tsx @@ -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' } /> - - - - 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} - /> + + + + + + + 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} + /> + ) }) diff --git a/src/view/shell/web/index.tsx b/src/view/shell/index.web.tsx similarity index 88% rename from src/view/shell/web/index.tsx rename to src/view/shell/index.web.tsx index 22bd55434f..8566525ca3 100644 --- a/src/view/shell/web/index.tsx +++ b/src/view/shell/index.web.tsx @@ -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 } - return + return if (!store.session.hasSession) { return (