From 6835caa7609259a64ba0aa2c9ddb34b7d5eb7793 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 2 Sep 2022 11:51:46 -0500 Subject: [PATCH] Add more robust modals controller --- src/state/index.ts | 2 + src/state/models/root-store.ts | 2 + src/state/models/shell.ts | 28 +++++++++++ src/view/com/modals/LinkActions.tsx | 62 +++++++++++++++++++++++++ src/view/com/modals/Modal.tsx | 45 ++++++++++++++++++ src/view/screens/Home.tsx | 5 +- src/view/shell/mobile/index.tsx | 38 ++++++++------- src/view/shell/mobile/location-menu.tsx | 14 ++++-- 8 files changed, 173 insertions(+), 23 deletions(-) create mode 100644 src/state/models/shell.ts create mode 100644 src/view/com/modals/LinkActions.tsx create mode 100644 src/view/com/modals/Modal.tsx diff --git a/src/state/index.ts b/src/state/index.ts index 9ebf321e..0749a6cc 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -5,6 +5,8 @@ import * as libapi from './lib/api' import * as storage from './lib/storage' // import * as auth from './auth' TODO +import {ShellModel} from './models/shell' + const ROOT_STATE_STORAGE_KEY = 'root' export async function setupState() { diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts index 0dab4167..47213f9d 100644 --- a/src/state/models/root-store.ts +++ b/src/state/models/root-store.ts @@ -8,11 +8,13 @@ import {createContext, useContext} from 'react' import {isObj, hasProp} from '../lib/type-guards' import {SessionModel} from './session' import {NavigationModel} from './navigation' +import {ShellModel} from './shell' import {MeModel} from './me' export class RootStoreModel { session = new SessionModel() nav = new NavigationModel() + shell = new ShellModel() me = new MeModel(this) constructor(public api: AdxClient) { diff --git a/src/state/models/shell.ts b/src/state/models/shell.ts new file mode 100644 index 00000000..6755393c --- /dev/null +++ b/src/state/models/shell.ts @@ -0,0 +1,28 @@ +import {makeAutoObservable} from 'mobx' + +export class LinkActionsModel { + name = 'link-actions' + + constructor(public href: string, public title: string) { + makeAutoObservable(this) + } +} + +export class ShellModel { + isModalActive = false + activeModal: LinkActionsModel | undefined + + constructor() { + makeAutoObservable(this) + } + + openModal(modal: LinkActionsModel) { + this.isModalActive = true + this.activeModal = modal + } + + closeModal() { + this.isModalActive = false + this.activeModal = undefined + } +} diff --git a/src/view/com/modals/LinkActions.tsx b/src/view/com/modals/LinkActions.tsx new file mode 100644 index 00000000..aa185026 --- /dev/null +++ b/src/view/com/modals/LinkActions.tsx @@ -0,0 +1,62 @@ +import React from 'react' +import Toast from '../util/Toast' +import Clipboard from '@react-native-clipboard/clipboard' +import {StyleSheet, Text, TouchableOpacity, View} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {useStores} from '../../../state' +import {s, colors} from '../../lib/styles' + +export const snapPoints = ['30%'] + +export function Component({title, href}: {title: string; href: string}) { + const store = useStores() + + const onPressOpenNewTab = () => { + store.shell.closeModal() + store.nav.newTab(href) + } + + const onPressCopy = () => { + Clipboard.setString(href) + store.shell.closeModal() + Toast.show('Link copied', { + position: Toast.positions.TOP, + }) + } + + return ( + + {title || href} + + + + Open in new tab + + + + Copy to clipboard + + + + ) +} + +const styles = StyleSheet.create({ + btn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + borderColor: colors.gray5, + borderWidth: 1, + borderRadius: 4, + padding: 10, + marginBottom: 10, + }, + icon: { + marginRight: 8, + }, +}) diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx new file mode 100644 index 00000000..172dd0ad --- /dev/null +++ b/src/view/com/modals/Modal.tsx @@ -0,0 +1,45 @@ +import React, {useRef} from 'react' +import {View} from 'react-native' +import {observer} from 'mobx-react-lite' +import BottomSheet from '@gorhom/bottom-sheet' +import {useStores} from '../../../state' +import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' + +import * as LinkActionsModal from './LinkActions' + +export const Modal = observer(function Modal() { + const store = useStores() + const bottomSheetRef = useRef(null) + + const onShareBottomSheetChange = (snapPoint: number) => { + if (snapPoint === -1) { + store.shell.closeModal() + } + } + const onClose = () => { + bottomSheetRef.current?.close() + } + + if (!store.shell.isModalActive) { + return + } + + let snapPoints, element + if (store.shell.activeModal?.name === 'link-actions') { + snapPoints = LinkActionsModal.snapPoints + element = + } else { + return + } + + return ( + + {element} + + ) +}) diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 299e9cde..b29e042b 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -1,5 +1,6 @@ import React, {useState, useEffect} from 'react' import {View} from 'react-native' +import {observer} from 'mobx-react-lite' import {Feed} from '../com/feed/Feed' import {FAB} from '../com/util/FloatingActionButton' import {useStores} from '../../state' @@ -7,7 +8,7 @@ import {FeedViewModel} from '../../state/models/feed-view' import {ScreenParams} from '../routes' import {s} from '../lib/styles' -export function Home({visible}: ScreenParams) { +export const Home = observer(function Home({visible}: ScreenParams) { const [hasSetup, setHasSetup] = useState(false) const [feedView, setFeedView] = useState() const store = useStores() @@ -38,4 +39,4 @@ export function Home({visible}: ScreenParams) { ) -} +}) diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx index 8ce2e63d..35a1b395 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/mobile/index.tsx @@ -15,6 +15,7 @@ import {IconProp} from '@fortawesome/fontawesome-svg-core' import {useStores} from '../../../state' import {NavigationModel} from '../../../state/models/navigation' import {match, MatchResult} from '../../routes' +import {Modal} from '../../com/modals/Modal' import {TabsSelectorModal} from './tabs-selector' import {LocationNavigator} from './location-navigator' import {createBackMenu, createForwardMenu} from './history-menu' @@ -93,10 +94,10 @@ const Btn = ({ } export const MobileShell: React.FC = observer(() => { - const stores = useStores() + const store = useStores() const tabSelectorRef = useRef<{open: () => void}>() const [isLocationMenuActive, setLocationMenuActive] = useState(false) - const screenRenderDesc = constructScreenRenderDesc(stores.nav) + const screenRenderDesc = constructScreenRenderDesc(store.nav) const onPressAvi = () => createAccountsMenu() const onPressLocation = () => setLocationMenuActive(true) @@ -104,22 +105,22 @@ export const MobileShell: React.FC = observer(() => { const onNavigateLocation = (url: string) => { setLocationMenuActive(false) - stores.nav.navigate(url) + store.nav.navigate(url) } const onDismissLocationNavigator = () => setLocationMenuActive(false) - const onPressBack = () => stores.nav.tab.goBack() - const onPressForward = () => stores.nav.tab.goForward() - const onPressHome = () => stores.nav.navigate('/') - const onPressNotifications = () => stores.nav.navigate('/notifications') + const onPressBack = () => store.nav.tab.goBack() + const onPressForward = () => store.nav.tab.goForward() + const onPressHome = () => store.nav.navigate('/') + const onPressNotifications = () => store.nav.navigate('/notifications') const onPressTabs = () => tabSelectorRef.current?.open() - const onLongPressBack = () => createBackMenu(stores.nav.tab) - const onLongPressForward = () => createForwardMenu(stores.nav.tab) + const onLongPressBack = () => createBackMenu(store.nav.tab) + const onLongPressForward = () => createForwardMenu(store.nav.tab) - const onNewTab = () => stores.nav.newTab('/') - const onChangeTab = (tabIndex: number) => stores.nav.setActiveTab(tabIndex) - const onCloseTab = (tabIndex: number) => stores.nav.closeTab(tabIndex) + const onNewTab = () => store.nav.newTab('/') + const onChangeTab = (tabIndex: number) => store.nav.setActiveTab(tabIndex) + const onCloseTab = (tabIndex: number) => store.nav.closeTab(tabIndex) return ( @@ -129,7 +130,7 @@ export const MobileShell: React.FC = observer(() => { @@ -151,13 +152,13 @@ export const MobileShell: React.FC = observer(() => { @@ -167,15 +168,16 @@ export const MobileShell: React.FC = observer(() => { + {isLocationMenuActive && ( diff --git a/src/view/shell/mobile/location-menu.tsx b/src/view/shell/mobile/location-menu.tsx index b5f4d803..fc851f77 100644 --- a/src/view/shell/mobile/location-menu.tsx +++ b/src/view/shell/mobile/location-menu.tsx @@ -29,11 +29,17 @@ export function createLocationMenu(): RootSiblings { Share onPressItem(0)}> Copy Link + onPressItem(0)}> + + Duplicate Tab + ), @@ -58,18 +64,20 @@ const styles = StyleSheet.create({ backgroundColor: '#fff', borderRadius: 14, opacity: 1, - paddingVertical: 2, + paddingVertical: 6, }, menuItem: { flexDirection: 'row', alignItems: 'center', - paddingVertical: 8, + paddingVertical: 6, paddingLeft: 10, paddingRight: 30, }, menuItemBorder: { borderTopWidth: 1, borderTopColor: colors.gray1, + marginTop: 4, + paddingTop: 12, }, icon: { marginLeft: 6,