diff options
-rw-r--r-- | src/state/models/shell.ts | 10 | ||||
-rw-r--r-- | src/view/com/modals/Modal.tsx | 10 | ||||
-rw-r--r-- | src/view/com/modals/TabsSelector.tsx | 376 | ||||
-rw-r--r-- | src/view/shell/mobile/LocationNavigator.tsx | 176 | ||||
-rw-r--r-- | src/view/shell/mobile/index.tsx | 15 |
5 files changed, 1 insertions, 586 deletions
diff --git a/src/state/models/shell.ts b/src/state/models/shell.ts index 0a07ce686..8cb0ff9e7 100644 --- a/src/state/models/shell.ts +++ b/src/state/models/shell.ts @@ -2,14 +2,6 @@ import {makeAutoObservable} from 'mobx' import {ProfileViewModel} from './profile-view' import * as Post from '../../third-party/api/src/types/app/bsky/post' -export class TabsSelectorModel { - name = 'tabs-selector' - - constructor() { - makeAutoObservable(this) - } -} - export interface LinkActionsModelOpts { newTab?: boolean } @@ -62,7 +54,6 @@ export class EditProfileModel { export class ShellModel { isModalActive = false activeModal: - | TabsSelectorModel | LinkActionsModel | SharePostModel | ComposePostModel @@ -75,7 +66,6 @@ export class ShellModel { openModal( modal: - | TabsSelectorModel | LinkActionsModel | SharePostModel | ComposePostModel diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index f3a69d2af..6282b5af1 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -7,7 +7,6 @@ import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' import * as models from '../../../state/models/shell' -import * as TabsSelectorModal from './TabsSelector' import * as LinkActionsModal from './LinkActions' import * as SharePostModal from './SharePost.native' import * as ComposePostModal from './ComposePost' @@ -38,14 +37,7 @@ export const Modal = observer(function Modal() { let snapPoints: (string | number)[] = CLOSED_SNAPPOINTS let element - if (store.shell.activeModal?.name === 'tabs-selector') { - snapPoints = TabsSelectorModal.snapPoints - element = ( - <TabsSelectorModal.Component - {...(store.shell.activeModal as models.TabsSelectorModel)} - /> - ) - } else if (store.shell.activeModal?.name === 'link-actions') { + if (store.shell.activeModal?.name === 'link-actions') { snapPoints = LinkActionsModal.snapPoints element = ( <LinkActionsModal.Component diff --git a/src/view/com/modals/TabsSelector.tsx b/src/view/com/modals/TabsSelector.tsx deleted file mode 100644 index b4991cf74..000000000 --- a/src/view/com/modals/TabsSelector.tsx +++ /dev/null @@ -1,376 +0,0 @@ -import React, {createRef, useRef, useMemo, useState} from 'react' -import {observer} from 'mobx-react-lite' -import { - Image, - ScrollView, - StyleSheet, - Text, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native' -import Animated, { - useSharedValue, - useAnimatedStyle, - withTiming, - runOnJS, -} from 'react-native-reanimated' -import {IconProp} from '@fortawesome/fontawesome-svg-core' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import Swipeable from 'react-native-gesture-handler/Swipeable' -import LinearGradient from 'react-native-linear-gradient' -import {useStores} from '../../../state' -import {s, colors, gradients} from '../../lib/styles' -import {DEF_AVATAR} from '../../lib/assets' -import {match} from '../../routes' -import {LinkActionsModel} from '../../../state/models/shell' - -const TAB_HEIGHT = 42 - -export const snapPoints = [500] - -export const Component = observer(() => { - const store = useStores() - const [closingTabIndex, setClosingTabIndex] = useState<number | undefined>( - undefined, - ) - const closeInterp = useSharedValue<number>(0) - const tabsRef = useRef<ScrollView>(null) - const tabRefs = useMemo( - () => - Array.from({length: store.nav.tabs.length}).map(() => - createRef<Animated.View>(), - ), - [store.nav.tabs.length], - ) - - // events - // = - - const onPressNewTab = () => { - store.nav.newTab('/') - onClose() - } - const onPressCloneTab = () => { - store.nav.newTab(store.nav.tab.current.url) - onClose() - } - const onPressShareTab = () => { - onClose() - store.shell.openModal( - new LinkActionsModel( - store.nav.tab.current.url, - store.nav.tab.current.title || 'This Page', - {newTab: false}, - ), - ) - } - const onPressChangeTab = (tabIndex: number) => { - store.nav.setActiveTab(tabIndex) - onClose() - } - const doCloseTab = (index: number) => store.nav.closeTab(index) - const onCloseTab = (tabIndex: number) => { - setClosingTabIndex(tabIndex) - closeInterp.value = 0 - closeInterp.value = withTiming(1, {duration: 300}, () => { - runOnJS(setClosingTabIndex)(undefined) - runOnJS(doCloseTab)(tabIndex) - }) - } - const onNavigate = (url: string) => { - store.nav.navigate(url) - onClose() - } - const onClose = () => { - store.shell.closeModal() - } - const onLayout = () => { - // focus the current tab - const targetTab = tabRefs[store.nav.tabIndex] - if (tabsRef.current && targetTab.current) { - targetTab.current.measureLayout?.( - tabsRef.current, - (_left: number, top: number) => { - tabsRef.current?.scrollTo({y: top, animated: false}) - }, - () => {}, - ) - } - } - - // rendering - // = - - const FatMenuItem = ({ - icon, - label, - url, - gradient, - }: { - icon: IconProp - label: string - url: string - gradient: keyof typeof gradients - }) => ( - <TouchableOpacity - style={[styles.fatMenuItem, styles.fatMenuItemMargin]} - onPress={() => onNavigate(url)}> - <LinearGradient - style={[styles.fatMenuItemIconWrapper]} - colors={[gradients[gradient].start, gradients[gradient].end]} - start={{x: 0, y: 0}} - end={{x: 1, y: 1}}> - <FontAwesomeIcon icon={icon} style={styles.fatMenuItemIcon} size={24} /> - </LinearGradient> - <Text style={styles.fatMenuItemLabel} numberOfLines={1}> - {label} - </Text> - </TouchableOpacity> - ) - - const renderSwipeActions = () => { - return <View style={[s.p2]} /> - } - - const currentTabIndex = store.nav.tabIndex - const closingTabAnimStyle = useAnimatedStyle(() => ({ - height: TAB_HEIGHT * (1 - closeInterp.value), - opacity: 1 - closeInterp.value, - marginBottom: 4 * (1 - closeInterp.value), - })) - return ( - <View onLayout={onLayout}> - <View style={[s.p10, styles.section]}> - <View style={styles.fatMenuItems}> - <TouchableOpacity - style={styles.fatMenuItem} - onPress={() => onNavigate(`/profile/${store.me.name || ''}`)}> - <Image style={styles.fatMenuImage} source={DEF_AVATAR} /> - <Text style={styles.fatMenuItemLabel} numberOfLines={1}> - {store.me.displayName || store.me.name || 'My profile'} - </Text> - </TouchableOpacity> - <FatMenuItem icon="house" label="Feed" url="/" gradient="primary" /> - <FatMenuItem - icon="bell" - label="Notifications" - url="/notifications" - gradient="purple" - /> - <FatMenuItem - icon="gear" - label="Settings" - url="/settings" - gradient="blue" - /> - </View> - </View> - <View style={[s.p10, styles.section]}> - <View style={styles.btns}> - <TouchableWithoutFeedback onPress={onPressShareTab}> - <View style={[styles.btn]}> - <View style={styles.btnIcon}> - <FontAwesomeIcon size={16} icon="share" /> - </View> - <Text style={styles.btnText}>Share</Text> - </View> - </TouchableWithoutFeedback> - <TouchableWithoutFeedback onPress={onPressCloneTab}> - <View style={[styles.btn]}> - <View style={styles.btnIcon}> - <FontAwesomeIcon size={16} icon={['far', 'clone']} /> - </View> - <Text style={styles.btnText}>Clone tab</Text> - </View> - </TouchableWithoutFeedback> - <TouchableWithoutFeedback onPress={onPressNewTab}> - <View style={[styles.btn]}> - <View style={styles.btnIcon}> - <FontAwesomeIcon size={16} icon="plus" /> - </View> - <Text style={styles.btnText}>New tab</Text> - </View> - </TouchableWithoutFeedback> - </View> - </View> - <View style={[s.p10, styles.section, styles.sectionGrayBg]}> - <ScrollView ref={tabsRef} style={styles.tabs}> - {store.nav.tabs.map((tab, tabIndex) => { - const {icon} = match(tab.current.url) - const isActive = tabIndex === currentTabIndex - const isClosing = closingTabIndex === tabIndex - return ( - <Swipeable - key={tab.id} - renderLeftActions={renderSwipeActions} - renderRightActions={renderSwipeActions} - leftThreshold={100} - rightThreshold={100} - onSwipeableWillOpen={() => onCloseTab(tabIndex)}> - <Animated.View - style={[ - styles.tabOuter, - isClosing ? closingTabAnimStyle : undefined, - ]}> - <Animated.View - ref={tabRefs[tabIndex]} - style={[ - styles.tab, - styles.existing, - isActive && styles.active, - ]}> - <TouchableWithoutFeedback - onPress={() => onPressChangeTab(tabIndex)}> - <View style={styles.tabInner}> - <View style={styles.tabIcon}> - <FontAwesomeIcon size={20} icon={icon} /> - </View> - <Text - ellipsizeMode="tail" - numberOfLines={1} - suppressHighlighting={true} - style={[ - styles.tabText, - isActive && styles.tabTextActive, - ]}> - {tab.current.title || tab.current.url} - </Text> - </View> - </TouchableWithoutFeedback> - <TouchableWithoutFeedback - onPress={() => onCloseTab(tabIndex)}> - <View style={styles.tabClose}> - <FontAwesomeIcon - size={14} - icon="x" - style={styles.tabCloseIcon} - /> - </View> - </TouchableWithoutFeedback> - </Animated.View> - </Animated.View> - </Swipeable> - ) - })} - </ScrollView> - </View> - </View> - ) -}) - -const styles = StyleSheet.create({ - section: { - borderBottomColor: colors.gray2, - borderBottomWidth: 1, - }, - sectionGrayBg: { - backgroundColor: colors.gray1, - }, - fatMenuItems: { - flexDirection: 'row', - marginTop: 10, - marginBottom: 10, - }, - fatMenuItem: { - width: 80, - alignItems: 'center', - marginRight: 6, - }, - fatMenuItemMargin: { - marginRight: 14, - }, - fatMenuItemIconWrapper: { - borderRadius: 6, - width: 60, - height: 60, - justifyContent: 'center', - alignItems: 'center', - marginBottom: 5, - shadowColor: '#000', - shadowOpacity: 0.2, - shadowOffset: {width: 0, height: 2}, - shadowRadius: 2, - }, - fatMenuItemIcon: { - color: colors.white, - }, - fatMenuImage: { - borderRadius: 30, - width: 60, - height: 60, - marginBottom: 5, - }, - fatMenuItemLabel: { - fontSize: 13, - }, - tabs: { - height: 240, - }, - tabOuter: { - height: TAB_HEIGHT + 4, - overflow: 'hidden', - }, - tab: { - flexDirection: 'row', - height: TAB_HEIGHT, - backgroundColor: colors.gray1, - alignItems: 'center', - borderRadius: 4, - }, - tabInner: { - flexDirection: 'row', - flex: 1, - alignItems: 'center', - paddingLeft: 12, - paddingVertical: 12, - }, - existing: { - borderColor: colors.gray4, - borderWidth: 1, - }, - active: { - backgroundColor: colors.white, - borderColor: colors.black, - borderWidth: 1, - }, - tabIcon: {}, - tabText: { - flex: 1, - paddingHorizontal: 10, - fontSize: 16, - }, - tabTextActive: { - fontWeight: '500', - }, - tabClose: { - paddingVertical: 16, - paddingRight: 16, - }, - tabCloseIcon: { - color: '#655', - }, - btns: { - flexDirection: 'row', - paddingTop: 2, - }, - btn: { - flexDirection: 'row', - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: colors.gray1, - borderRadius: 4, - marginRight: 5, - paddingLeft: 12, - paddingRight: 16, - paddingVertical: 10, - }, - btnIcon: { - marginRight: 8, - }, - btnText: { - fontWeight: '500', - fontSize: 16, - }, -}) diff --git a/src/view/shell/mobile/LocationNavigator.tsx b/src/view/shell/mobile/LocationNavigator.tsx deleted file mode 100644 index 28ca23101..000000000 --- a/src/view/shell/mobile/LocationNavigator.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import React, {useState, useRef} from 'react' -import {StyleSheet, Text, TextInput, TouchableOpacity, View} from 'react-native' -import LinearGradient from 'react-native-linear-gradient' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {IconProp} from '@fortawesome/fontawesome-svg-core' -import {s, gradients, colors} from '../../lib/styles' - -export function LocationNavigator({ - url, - onNavigate, - onDismiss, -}: { - url: string - onNavigate: (url: string) => void - onDismiss: () => void -}) { - const [searchText, onChangeSearchText] = useState(url !== '/' ? url : '') - const inputRef = useRef<TextInput | null>(null) - - const onFocusSearchText = () => { - if (inputRef.current && searchText.length) { - // select the text on focus - inputRef.current.setNativeProps({ - selection: {start: 0, end: searchText.length}, - }) - } - } - - const FatMenuItem = ({ - icon, - label, - url, - gradient, - }: { - icon: IconProp - label: string - url: string - gradient: keyof typeof gradients - }) => ( - <TouchableOpacity - style={styles.fatMenuItem} - onPress={() => onNavigate(url)}> - <LinearGradient - style={[styles.fatMenuItemIconWrapper]} - colors={[gradients[gradient].start, gradients[gradient].end]} - start={{x: 0, y: 0}} - end={{x: 1, y: 1}}> - <FontAwesomeIcon icon={icon} style={styles.fatMenuItemIcon} size={24} /> - </LinearGradient> - <Text style={styles.fatMenuItemLabel}>{label}</Text> - </TouchableOpacity> - ) - - const ThinMenuItem = ({label, url}: {label: string; url: string}) => ( - <TouchableOpacity - style={styles.thinMenuItem} - onPress={() => onNavigate(url)}> - <Text style={styles.thinMenuItemLabel}>{label}</Text> - </TouchableOpacity> - ) - - return ( - <View style={styles.menu}> - <View style={styles.searchContainer}> - <FontAwesomeIcon - icon="magnifying-glass" - size={18} - style={styles.searchIcon} - /> - <TextInput - autoFocus - ref={inputRef} - value={searchText} - style={styles.searchInput} - onChangeText={onChangeSearchText} - onFocus={onFocusSearchText} - /> - <TouchableOpacity onPress={() => onDismiss()}> - <Text style={[s.blue3, s.f15]}>Cancel</Text> - </TouchableOpacity> - </View> - <View style={styles.menuItemsContainer}> - <View style={styles.fatMenuItems}> - <FatMenuItem icon="house" label="Feed" url="/" gradient="primary" /> - <FatMenuItem - icon="bell" - label="Notifications" - url="/notifications" - gradient="purple" - /> - <FatMenuItem - icon={['far', 'user']} - label="My Profile" - url="/" - gradient="blue" - /> - <FatMenuItem icon="gear" label="Settings" url="/" gradient="blue" /> - </View> - <View style={styles.thinMenuItems}> - <ThinMenuItem label="Send us feedback" url="/" /> - <ThinMenuItem label="Get help..." url="/" /> - <ThinMenuItem label="Settings" url="/" /> - </View> - </View> - </View> - ) -} - -const styles = StyleSheet.create({ - menu: { - position: 'absolute', - left: 0, - top: 0, - width: '100%', - height: '100%', - backgroundColor: '#fff', - opacity: 1, - }, - searchContainer: { - flexDirection: 'row', - backgroundColor: colors.gray1, - borderBottomWidth: 1, - borderColor: colors.gray2, - paddingHorizontal: 16, - paddingTop: 48, - paddingBottom: 8, - }, - searchIcon: { - color: colors.gray5, - marginRight: 8, - }, - searchInput: { - flex: 1, - }, - menuItemsContainer: { - paddingVertical: 30, - paddingHorizontal: 8, - }, - fatMenuItems: { - flexDirection: 'row', - marginBottom: 20, - }, - fatMenuItem: { - width: 86, - alignItems: 'center', - marginRight: 6, - }, - fatMenuItemIconWrapper: { - borderRadius: 6, - width: 50, - height: 50, - justifyContent: 'center', - alignItems: 'center', - marginBottom: 5, - shadowColor: '#000', - shadowOpacity: 0.2, - shadowOffset: {width: 0, height: 2}, - shadowRadius: 2, - }, - fatMenuItemIcon: { - color: colors.white, - }, - fatMenuItemLabel: { - fontSize: 12, - }, - thinMenuItems: { - paddingHorizontal: 18, - }, - thinMenuItem: { - paddingVertical: 4, - }, - thinMenuItemLabel: { - color: colors.blue3, - fontSize: 16, - }, -}) diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx index cdac003f0..7b5dd4e91 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/mobile/index.tsx @@ -28,7 +28,6 @@ import {NavigationModel} from '../../../state/models/navigation' import {match, MatchResult} from '../../routes' import {Login} from '../../screens/Login' import {Modal} from '../../com/modals/Modal' -import {LocationNavigator} from './LocationNavigator' import {MainMenu} from './MainMenu' import {TabsSelector} from './TabsSelector' import {s, colors} from '../../lib/styles' @@ -99,7 +98,6 @@ const Btn = ({ export const MobileShell: React.FC = observer(() => { const store = useStores() - const [isLocationMenuActive, setLocationMenuActive] = useState(false) const [isMainMenuActive, setMainMenuActive] = useState(false) const [isTabsSelectorActive, setTabsSelectorActive] = useState(false) const scrollElRef = useRef<FlatList | undefined>() @@ -107,12 +105,6 @@ export const MobileShell: React.FC = observer(() => { const swipeGestureInterp = useSharedValue<number>(0) const screenRenderDesc = constructScreenRenderDesc(store.nav) - const onNavigateLocation = (url: string) => { - setLocationMenuActive(false) - store.nav.navigate(url) - } - const onDismissLocationNavigator = () => setLocationMenuActive(false) - const onPressHome = () => { if (store.nav.tab.current.url === '/') { scrollElRef.current?.scrollToOffset({offset: 0}) @@ -225,13 +217,6 @@ export const MobileShell: React.FC = observer(() => { active={isTabsSelectorActive} onClose={() => setTabsSelectorActive(false)} /> - {isLocationMenuActive && ( - <LocationNavigator - url={store.nav.tab.current.url} - onNavigate={onNavigateLocation} - onDismiss={onDismissLocationNavigator} - /> - )} </View> ) }) |