diff options
Diffstat (limited to 'src/view/shell/web')
-rw-r--r-- | src/view/shell/web/Composer.tsx | 66 | ||||
-rw-r--r-- | src/view/shell/web/DesktopHeader.tsx | 222 | ||||
-rw-r--r-- | src/view/shell/web/DesktopSearch.tsx | 139 | ||||
-rw-r--r-- | src/view/shell/web/index.tsx | 150 |
4 files changed, 0 insertions, 577 deletions
diff --git a/src/view/shell/web/Composer.tsx b/src/view/shell/web/Composer.tsx deleted file mode 100644 index 0d8484262..000000000 --- a/src/view/shell/web/Composer.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react' -import {observer} from 'mobx-react-lite' -import {StyleSheet, View} from 'react-native' -import {ComposePost} from '../../com/composer/ComposePost' -import {ComposerOpts} from 'state/models/shell-ui' -import {usePalette} from 'lib/hooks/usePalette' - -export const Composer = observer( - ({ - active, - replyTo, - imagesOpen, - onPost, - onClose, - }: { - active: boolean - winHeight: number - replyTo?: ComposerOpts['replyTo'] - imagesOpen?: ComposerOpts['imagesOpen'] - onPost?: ComposerOpts['onPost'] - onClose: () => void - }) => { - const pal = usePalette('default') - - // rendering - // = - - if (!active) { - return <View /> - } - - return ( - <View style={styles.mask}> - <View style={[styles.container, pal.view]}> - <ComposePost - replyTo={replyTo} - imagesOpen={imagesOpen} - onPost={onPost} - onClose={onClose} - /> - </View> - </View> - ) - }, -) - -const styles = StyleSheet.create({ - mask: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - backgroundColor: '#000c', - alignItems: 'center', - justifyContent: 'center', - }, - container: { - maxWidth: 600, - width: '100%', - paddingVertical: 0, - paddingHorizontal: 2, - borderRadius: 8, - marginBottom: '10vh', - }, -}) diff --git a/src/view/shell/web/DesktopHeader.tsx b/src/view/shell/web/DesktopHeader.tsx deleted file mode 100644 index 8748ebbde..000000000 --- a/src/view/shell/web/DesktopHeader.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import React from 'react' -import {observer} from 'mobx-react-lite' -import {Pressable, StyleSheet, TouchableOpacity, View} from 'react-native' -import {Text} from 'view/com/util/text/Text' -import {UserAvatar} from 'view/com/util/UserAvatar' -import {usePalette} from 'lib/hooks/usePalette' -import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' -import {useStores} from 'state/index' -import {colors} from 'lib/styles' -import { - ComposeIcon, - HomeIcon, - HomeIconSolid, - BellIcon, - BellIconSolid, - MagnifyingGlassIcon, - CogIcon, -} from 'lib/icons' -import {DesktopSearch} from './DesktopSearch' - -interface NavItemProps { - count?: number - href: string - icon: JSX.Element - iconFilled: JSX.Element - isProfile?: boolean -} -export const NavItem = observer( - ({count, href, icon, iconFilled}: NavItemProps) => { - const store = useStores() - const hoverBg = useColorSchemeStyle( - styles.navItemHoverBgLight, - styles.navItemHoverBgDark, - ) - const isCurrent = store.nav.tab.current.url === href - const onPress = () => store.nav.navigate(href) - return ( - <Pressable - style={state => [ - styles.navItem, - // @ts-ignore Pressable state differs for RNW -prf - (state.hovered || isCurrent) && hoverBg, - ]} - onPress={onPress}> - <View style={[styles.navItemIconWrapper]}> - {isCurrent ? iconFilled : icon} - {typeof count === 'number' && count > 0 && ( - <Text type="button" style={styles.navItemCount}> - {count} - </Text> - )} - </View> - </Pressable> - ) - }, -) - -export const ProfileItem = observer(() => { - const store = useStores() - const hoverBg = useColorSchemeStyle( - styles.navItemHoverBgLight, - styles.navItemHoverBgDark, - ) - const href = `/profile/${store.me.handle}` - const isCurrent = store.nav.tab.current.url === href - const onPress = () => store.nav.navigate(href) - return ( - <Pressable - style={state => [ - styles.navItem, - // @ts-ignore Pressable state differs for RNW -prf - (state.hovered || isCurrent) && hoverBg, - ]} - onPress={onPress}> - <View style={[styles.navItemIconWrapper]}> - <UserAvatar - handle={store.me.handle} - displayName={store.me.displayName} - avatar={store.me.avatar} - size={28} - /> - </View> - </Pressable> - ) -}) - -export const DesktopHeader = observer(function DesktopHeader({}: { - canGoBack?: boolean -}) { - const store = useStores() - const pal = usePalette('default') - const onPressCompose = () => store.shell.openComposer({}) - - return ( - <View style={[styles.header, pal.borderDark, pal.view]}> - <Text type="title-xl" style={[pal.text, styles.title]}> - Bluesky - </Text> - <View style={styles.space30} /> - <NavItem - href="/" - icon={<HomeIcon size={24} />} - iconFilled={<HomeIconSolid size={24} />} - /> - <View style={styles.space15} /> - <NavItem - href="/search" - icon={<MagnifyingGlassIcon size={24} />} - iconFilled={<MagnifyingGlassIcon strokeWidth={3} size={24} />} - /> - <View style={styles.space15} /> - <NavItem - href="/notifications" - count={store.me.notifications.unreadCount} - icon={<BellIcon size={24} />} - iconFilled={<BellIconSolid size={24} />} - /> - <View style={styles.spaceFlex} /> - <TouchableOpacity style={[styles.newPostBtn]} onPress={onPressCompose}> - <View style={styles.newPostBtnIconWrapper}> - <ComposeIcon - size={16} - strokeWidth={2} - style={styles.newPostBtnLabel} - /> - </View> - <Text type="md" style={styles.newPostBtnLabel}> - New Post - </Text> - </TouchableOpacity> - <View style={styles.space20} /> - <DesktopSearch /> - <View style={styles.space15} /> - <ProfileItem /> - <NavItem - href="/settings" - icon={<CogIcon strokeWidth={2} size={28} />} - iconFilled={<CogIcon strokeWidth={2.5} size={28} />} - /> - </View> - ) -}) - -const styles = StyleSheet.create({ - header: { - flexDirection: 'row', - alignItems: 'center', - // paddingTop: 18, - // paddingBottom: 18, - paddingLeft: 30, - paddingRight: 40, - borderBottomWidth: 1, - zIndex: 1, - }, - - spaceFlex: { - flex: 1, - }, - space15: { - width: 15, - }, - space20: { - width: 20, - }, - space30: { - width: 30, - }, - - title: {}, - - navItem: { - paddingTop: 14, - paddingBottom: 10, - paddingHorizontal: 10, - alignItems: 'center', - borderBottomWidth: 2, - borderBottomColor: 'transparent', - }, - navItemHoverBgLight: { - borderBottomWidth: 2, - borderBottomColor: colors.blue3, - }, - navItemHoverBgDark: { - borderBottomWidth: 2, - backgroundColor: colors.blue3, - }, - navItemIconWrapper: { - alignItems: 'center', - justifyContent: 'center', - width: 28, - height: 28, - marginBottom: 2, - }, - navItemCount: { - position: 'absolute', - top: 0, - left: 15, - backgroundColor: colors.red3, - color: colors.white, - fontSize: 12, - fontWeight: 'bold', - paddingHorizontal: 4, - borderRadius: 6, - }, - - newPostBtn: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - borderRadius: 24, - paddingTop: 8, - paddingBottom: 8, - paddingHorizontal: 18, - backgroundColor: colors.blue3, - }, - newPostBtnIconWrapper: { - marginRight: 8, - }, - newPostBtnLabel: { - color: colors.white, - }, -}) diff --git a/src/view/shell/web/DesktopSearch.tsx b/src/view/shell/web/DesktopSearch.tsx deleted file mode 100644 index 43f13ca2b..000000000 --- a/src/view/shell/web/DesktopSearch.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React from 'react' -import {TextInput, View, StyleSheet, TouchableOpacity, Text} from 'react-native' -import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' -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' - -export const DesktopSearch = observer(function DesktopSearch() { - const store = useStores() - const pal = usePalette('default') - const textInput = React.useRef<TextInput>(null) - const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) - const [query, setQuery] = React.useState<string>('') - const autocompleteView = React.useMemo<UserAutocompleteViewModel>( - () => new UserAutocompleteViewModel(store), - [store], - ) - - const onChangeQuery = (text: string) => { - setQuery(text) - if (text.length > 0 && isInputFocused) { - autocompleteView.setActive(true) - autocompleteView.setPrefix(text) - } else { - autocompleteView.setActive(false) - } - } - - const onPressCancelSearch = () => { - setQuery('') - autocompleteView.setActive(false) - } - - return ( - <View style={styles.container}> - <View style={[pal.borderDark, pal.view, styles.search]}> - <View style={[styles.inputContainer]}> - <MagnifyingGlassIcon - size={18} - style={[pal.textLight, styles.iconWrapper]} - /> - <TextInput - testID="searchTextInput" - ref={textInput} - placeholder="Search" - placeholderTextColor={pal.colors.textLight} - selectTextOnFocus - returnKeyType="search" - value={query} - style={[pal.textLight, styles.input]} - onFocus={() => setIsInputFocused(true)} - onBlur={() => setIsInputFocused(false)} - onChangeText={onChangeQuery} - /> - {query ? ( - <View style={styles.cancelBtn}> - <TouchableOpacity onPress={onPressCancelSearch}> - <Text style={[pal.link]}>Cancel</Text> - </TouchableOpacity> - </View> - ) : undefined} - </View> - </View> - - {query !== '' && ( - <View style={[pal.view, pal.borderDark, styles.resultsContainer]}> - {autocompleteView.searchRes.length ? ( - <> - {autocompleteView.searchRes.map((item, i) => ( - <ProfileCard - key={item.did} - handle={item.handle} - displayName={item.displayName} - avatar={item.avatar} - noBorder={i === 0} - /> - ))} - </> - ) : ( - <View> - <Text style={[pal.textLight, styles.noResults]}> - No results found for {autocompleteView.prefix} - </Text> - </View> - )} - </View> - )} - </View> - ) -}) - -const styles = StyleSheet.create({ - container: { - position: 'relative', - width: 300, - }, - search: { - paddingHorizontal: 10, - width: 300, - borderRadius: 20, - borderWidth: 1, - }, - inputContainer: { - flexDirection: 'row', - }, - iconWrapper: { - paddingVertical: 7, - marginRight: 4, - }, - input: { - flex: 1, - fontSize: 16, - width: '100%', - paddingTop: 7, - paddingBottom: 7, - }, - cancelBtn: { - paddingRight: 4, - paddingLeft: 10, - paddingVertical: 7, - }, - resultsContainer: { - // @ts-ignore supported by web - position: 'fixed', - marginTop: 40, - - flexDirection: 'column', - width: 300, - borderWidth: 1, - borderRadius: 6, - paddingVertical: 4, - }, - noResults: { - textAlign: 'center', - paddingVertical: 10, - }, -}) diff --git a/src/view/shell/web/index.tsx b/src/view/shell/web/index.tsx deleted file mode 100644 index a76ae8060..000000000 --- a/src/view/shell/web/index.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react' -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 {Text} from 'view/com/util/text/Text' -import {Composer} from './Composer' -import {usePalette} from 'lib/hooks/usePalette' -import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' -import {s, colors} from 'lib/styles' -import {isMobileWeb} from 'platform/detection' - -export const WebShell: React.FC = observer(() => { - const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark) - const store = useStores() - const screenRenderDesc = constructScreenRenderDesc(store.nav) - - if (isMobileWeb) { - return <NoMobileWeb /> - } - - if (!store.session.hasSession) { - return ( - <View style={styles.outerContainer}> - <Login /> - <ModalsContainer /> - </View> - ) - } - - return ( - <View style={[styles.outerContainer, pageBg]}> - <DesktopHeader /> - {screenRenderDesc.screens.map(({Com, navIdx, params, key, current}) => ( - <View - key={key} - style={[s.hContentRegion, current ? styles.visible : styles.hidden]}> - <ErrorBoundary> - <Com params={params} navIdx={navIdx} visible={current} /> - </ErrorBoundary> - </View> - ))} - <Composer - active={store.shell.isComposerActive} - onClose={() => store.shell.closeComposer()} - winHeight={0} - replyTo={store.shell.composerOpts?.replyTo} - imagesOpen={store.shell.composerOpts?.imagesOpen} - onPost={store.shell.composerOpts?.onPost} - /> - <ModalsContainer /> - <Lightbox /> - </View> - ) -}) - -/** - * This method produces the information needed by the shell to - * render the current screens with screen-caching behaviors. - */ -type ScreenRenderDesc = MatchResult & { - key: string - navIdx: string - current: boolean - previous: boolean - isNewTab: boolean -} -function constructScreenRenderDesc(nav: NavigationModel): { - icon: IconProp - hasNewTab: boolean - screens: ScreenRenderDesc[] -} { - let hasNewTab = false - let icon: IconProp = 'magnifying-glass' - let screens: ScreenRenderDesc[] = [] - for (const tab of nav.tabs) { - const tabScreens = [ - ...tab.getBackList(5), - Object.assign({}, tab.current, {index: tab.index}), - ] - const parsedTabScreens = tabScreens.map(screen => { - const isCurrent = nav.isCurrentScreen(tab.id, screen.index) - const isPrevious = nav.isCurrentScreen(tab.id, screen.index + 1) - const matchRes = match(screen.url) - if (isCurrent) { - icon = matchRes.icon - } - hasNewTab = hasNewTab || tab.isNewTab - return Object.assign(matchRes, { - key: `t${tab.id}-s${screen.index}`, - navIdx: `${tab.id}-${screen.id}`, - current: isCurrent, - previous: isPrevious, - isNewTab: tab.isNewTab, - }) as ScreenRenderDesc - }) - screens = screens.concat(parsedTabScreens) - } - return { - icon, - hasNewTab, - screens, - } -} - -function NoMobileWeb() { - const pal = usePalette('default') - return ( - <View style={[pal.view, styles.noMobileWeb]}> - <Text type="title-2xl" style={s.pb20}> - We're so sorry! - </Text> - <Text type="lg"> - This app is not available for mobile Web yet. Please open it on your - desktop or download the iOS app. - </Text> - </View> - ) -} - -const styles = StyleSheet.create({ - outerContainer: { - height: '100%', - }, - bgLight: { - backgroundColor: colors.white, - }, - bgDark: { - backgroundColor: colors.black, // TODO - }, - visible: { - display: 'flex', - }, - hidden: { - display: 'none', - }, - noMobileWeb: { - height: '100%', - justifyContent: 'center', - paddingHorizontal: 20, - paddingBottom: 40, - }, -}) |