diff options
-rw-r--r-- | src/state/models/navigation.ts | 30 | ||||
-rw-r--r-- | src/view/routes.ts | 2 | ||||
-rw-r--r-- | src/view/shell/mobile/Menu.tsx (renamed from src/view/screens/Menu.tsx) | 29 | ||||
-rw-r--r-- | src/view/shell/mobile/index.tsx | 89 |
4 files changed, 100 insertions, 50 deletions
diff --git a/src/state/models/navigation.ts b/src/state/models/navigation.ts index 8ca44dc7f..8d69e5c04 100644 --- a/src/state/models/navigation.ts +++ b/src/state/models/navigation.ts @@ -12,7 +12,7 @@ function genId() { // until we're fully sure what that is, the tabs are being repurposed into a fixed topology // - Tab 0: The "Default" tab // - Tab 1: The "Notifications" tab -// These tabs always retain the first 2 items in their history. +// These tabs always retain the first item in their history. // The default tab is used for basically everything except notifications. // -prf export enum TabPurpose { @@ -32,20 +32,14 @@ export type HistoryPtr = [number, number] export class NavigationTabModel { id = genId() history: HistoryItem[] - index = 1 + index = 0 isNewTab = false constructor(public fixedTabPurpose: TabPurpose) { if (fixedTabPurpose === TabPurpose.Notifs) { - this.history = [ - {url: '/menu', ts: Date.now(), id: genId()}, - {url: '/notifications', ts: Date.now(), id: genId()}, - ] + this.history = [{url: '/notifications', ts: Date.now(), id: genId()}] } else { - this.history = [ - {url: '/menu', ts: Date.now(), id: genId()}, - {url: '/', ts: Date.now(), id: genId()}, - ] + this.history = [{url: '/', ts: Date.now(), id: genId()}] } makeAutoObservable(this, { serialize: false, @@ -85,7 +79,7 @@ export class NavigationTabModel { getForwardList(n: number) { const start = Math.min(this.index + 1, this.history.length) - const end = Math.min(this.index + n, this.history.length) + const end = Math.min(this.index + n + 1, this.history.length) return this.history.slice(start, end).map((item, i) => ({ url: item.url, title: item.title, @@ -109,7 +103,7 @@ export class NavigationTabModel { this.history.length = this.index + 1 } // TEMP ensure the tab has its purpose's main view -prf - if (this.history.length < 2) { + if (this.history.length < 1) { const fixedUrl = this.fixedTabPurpose === TabPurpose.Notifs ? '/notifications' : '/' this.history.push({url: fixedUrl, ts: Date.now(), id: genId()}) @@ -142,17 +136,7 @@ export class NavigationTabModel { // a helper to bring the tab back to its base state // -prf fixedTabReset() { - if (this.index >= 1) { - // fall back in history to "main" view - if (this.index > 1) { - this.index = 1 - } - } else { - const url = - this.fixedTabPurpose === TabPurpose.Notifs ? '/notifications' : '/' - this.history = [this.history[0], {url, ts: Date.now(), id: genId()}] - this.index = 1 - } + this.index = 0 } goForward() { diff --git a/src/view/routes.ts b/src/view/routes.ts index e662e2cca..272a1b096 100644 --- a/src/view/routes.ts +++ b/src/view/routes.ts @@ -1,7 +1,6 @@ import React, {MutableRefObject} from 'react' import {FlatList} from 'react-native' import {IconProp} from '@fortawesome/fontawesome-svg-core' -import {Menu} from './screens/Menu' import {Home} from './screens/Home' import {Contacts} from './screens/Contacts' import {Search} from './screens/Search' @@ -34,7 +33,6 @@ export type MatchResult = { const r = (pattern: string) => new RegExp('^' + pattern + '([?]|$)', 'i') export const routes: Route[] = [ - [Menu, 'Menu', 'bars', r('/menu')], [Home, 'Home', 'house', r('/')], [Contacts, 'Contacts', ['far', 'circle-user'], r('/contacts')], [Search, 'Search', 'magnifying-glass', r('/search')], diff --git a/src/view/screens/Menu.tsx b/src/view/shell/mobile/Menu.tsx index fab0f6dec..173f9563c 100644 --- a/src/view/screens/Menu.tsx +++ b/src/view/shell/mobile/Menu.tsx @@ -8,26 +8,29 @@ import { ViewStyle, } from 'react-native' import VersionNumber from 'react-native-version-number' -import {s, colors} from '../lib/styles' -import {ScreenParams} from '../routes' -import {useStores} from '../../state' +import {s, colors} from '../../lib/styles' +import {useStores} from '../../../state' import { HomeIcon, UserGroupIcon, BellIcon, CogIcon, MagnifyingGlassIcon, -} from '../lib/icons' -import {UserAvatar} from '../com/util/UserAvatar' -import {ViewHeader} from '../com/util/ViewHeader' -import {CreateSceneModel} from '../../state/models/shell-ui' +} from '../../lib/icons' +import {UserAvatar} from '../../com/util/UserAvatar' +import {CreateSceneModel} from '../../../state/models/shell-ui' -export const Menu = ({navIdx, visible}: ScreenParams) => { +export const Menu = ({ + visible, + onClose, +}: { + visible: boolean + onClose: () => void +}) => { const store = useStores() useEffect(() => { if (visible) { - store.nav.setTitle(navIdx, 'Menu') // trigger a refresh in case memberships have changed recently store.me.refreshMemberships() } @@ -37,14 +40,18 @@ export const Menu = ({navIdx, visible}: ScreenParams) => { // = const onNavigate = (url: string) => { + onClose() if (url === '/notifications') { store.nav.switchTo(1, true) } else { store.nav.switchTo(0, true) - store.nav.navigate(url) + if (url !== '/') { + store.nav.navigate(url) + } } } const onPressCreateScene = () => { + onClose() store.shell.openModal(new CreateSceneModel()) } @@ -88,10 +95,8 @@ export const Menu = ({navIdx, visible}: ScreenParams) => { </TouchableOpacity> ) - /*TODO <MenuItem icon={['far', 'compass']} label="Discover" url="/" />*/ return ( <View style={styles.view}> - <ViewHeader title="Bluesky" subtitle="Private Beta" /> <TouchableOpacity style={styles.searchBtn} onPress={() => onNavigate('/search')}> diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx index 9acbdeca9..ef54af171 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/mobile/index.tsx @@ -24,6 +24,7 @@ import {useStores} from '../../../state' import {NavigationModel} from '../../../state/models/navigation' import {match, MatchResult} from '../../routes' import {Login} from '../../screens/Login' +import {Menu} from './Menu' import {Onboard} from '../../screens/Onboard' import {HorzSwipe} from '../../com/util/gestures/HorzSwipe' import {Modal} from '../../com/modals/Modal' @@ -109,6 +110,7 @@ const Btn = ({ export const MobileShell: React.FC = observer(() => { const store = useStores() + const [isMenuActive, setMenuActive] = useState(false) const [isTabsSelectorActive, setTabsSelectorActive] = useState(false) const scrollElRef = useRef<FlatList | undefined>() const winDim = useWindowDimensions() @@ -121,6 +123,9 @@ export const MobileShell: React.FC = observer(() => { const screenRenderDesc = constructScreenRenderDesc(store.nav) const onPressHome = () => { + if (isMenuActive) { + setMenuActive(false) + } if (store.nav.tab.fixedTabPurpose === 0) { if (store.nav.tab.current.url === '/') { scrollElRef.current?.scrollToOffset({offset: 0}) @@ -135,6 +140,9 @@ export const MobileShell: React.FC = observer(() => { } } const onPressNotifications = () => { + if (isMenuActive) { + setMenuActive(false) + } if (store.nav.tab.fixedTabPurpose === 1) { store.nav.tab.fixedTabReset() } else { @@ -203,15 +211,44 @@ export const MobileShell: React.FC = observer(() => { // navigation swipes // = + const canSwipeLeft = store.nav.tab.canGoBack || !isMenuActive + const canSwipeRight = isMenuActive const onNavSwipeEnd = (dx: number) => { - if (dx < 0 && store.nav.tab.canGoBack) { - store.nav.tab.goBack() + if (dx < 0) { + if (store.nav.tab.canGoBack) { + store.nav.tab.goBack() + } else { + setMenuActive(true) + } + } else if (dx > 0) { + if (isMenuActive) { + setMenuActive(false) + } } } - const swipeTransform = { - transform: [ - {translateX: Animated.multiply(swipeGestureInterp, winDim.width * -1)}, - ], + const swipeTranslateX = Animated.multiply( + swipeGestureInterp, + winDim.width * -1, + ) + const swipeTransform = store.nav.tab.canGoBack + ? {transform: [{translateX: swipeTranslateX}]} + : undefined + let menuTranslateX + if (isMenuActive) { + // menu is active, interpret swipes as closes + menuTranslateX = Animated.multiply(swipeGestureInterp, winDim.width * -1) + } else if (!store.nav.tab.canGoBack) { + // at back of history, interpret swipes as opens + menuTranslateX = Animated.subtract( + winDim.width * -1, + Animated.multiply(swipeGestureInterp, winDim.width), + ) + } else { + // not at back of history, leave off screen + menuTranslateX = winDim.width * -1 + } + const menuSwipeTransform = { + transform: [{translateX: menuTranslateX}], } const swipeOpacity = { opacity: swipeGestureInterp.interpolate({ @@ -219,12 +256,13 @@ export const MobileShell: React.FC = observer(() => { outputRange: [0, 0.6, 0], }), } - const tabMenuTransform = { - transform: [{translateY: Animated.multiply(tabMenuInterp.value, -320)}], - } - const newTabTransform = { - transform: [{scale: newTabInterp}], - } + // TODO + // const tabMenuTransform = { + // transform: [{translateY: Animated.multiply(tabMenuInterp, -320)}], + // } + // const newTabTransform = { + // transform: [{scale: newTabInterp}], + // } if (!store.session.hasSession) { return ( @@ -252,6 +290,7 @@ export const MobileShell: React.FC = observer(() => { const isAtHome = store.nav.tab.current.url === '/' const isAtNotifications = store.nav.tab.current.url === '/notifications' + return ( <View style={styles.outerContainer}> <SafeAreaView style={styles.innerContainer}> @@ -260,11 +299,21 @@ export const MobileShell: React.FC = observer(() => { useNativeDriver panX={swipeGestureInterp} swipeEnabled - canSwipeLeft={store.nav.tab.canGoBack} + canSwipeLeft={canSwipeLeft} + canSwipeRight={canSwipeRight} onSwipeEnd={onNavSwipeEnd}> <ScreenContainer style={styles.screenContainer}> {screenRenderDesc.screens.map( ({Com, navIdx, params, key, current, previous}) => { + if (isMenuActive) { + // HACK menu is active, treat current as previous + if (previous) { + previous = false + } else if (current) { + current = false + previous = true + } + } return ( <Screen key={key} @@ -299,6 +348,9 @@ export const MobileShell: React.FC = observer(() => { }, )} </ScreenContainer> + <Animated.View style={[styles.menuDrawer, menuSwipeTransform]}> + <Menu visible={isMenuActive} onClose={() => setMenuActive(false)} /> + </Animated.View> </HorzSwipe> </SafeAreaView> {isTabsSelectorActive ? ( @@ -423,6 +475,17 @@ const styles = StyleSheet.create({ backgroundColor: '#000', opacity: 0.5, }, + menuDrawer: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + borderTopWidth: 1, + borderTopColor: colors.gray2, + borderRightWidth: 1, + borderRightColor: colors.gray2, + }, topBarProtector: { position: 'absolute', top: 0, |