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,