about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.native.tsx9
-rw-r--r--src/state/index.ts2
-rw-r--r--src/state/models/navigation.ts18
-rw-r--r--src/state/models/session.ts15
-rw-r--r--src/state/models/shell-ui.ts29
-rw-r--r--src/view/com/modals/LinkActions.tsx72
-rw-r--r--src/view/com/modals/Modal.tsx18
-rw-r--r--src/view/com/modals/SharePost.native.tsx43
-rw-r--r--src/view/com/modals/SharePost.tsx57
-rw-r--r--src/view/com/post-thread/PostThread.tsx12
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx2
-rw-r--r--src/view/com/util/DropdownBtn.tsx6
-rw-r--r--src/view/com/util/Link.tsx1
-rw-r--r--src/view/lib/strings.ts9
-rw-r--r--src/view/shell/mobile/TabsSelector.tsx11
15 files changed, 61 insertions, 243 deletions
diff --git a/src/App.native.tsx b/src/App.native.tsx
index 3d3e5f1b0..65a77c3dc 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -1,5 +1,6 @@
 import 'react-native-url-polyfill/auto'
 import React, {useState, useEffect} from 'react'
+import {Linking} from 'react-native'
 import {RootSiblingParent} from 'react-native-root-siblings'
 import {GestureHandlerRootView} from 'react-native-gesture-handler'
 import SplashScreen from 'react-native-splash-screen'
@@ -24,6 +25,14 @@ function App() {
       .then(store => {
         setRootStore(store)
         SplashScreen.hide()
+        Linking.getInitialURL().then((url: string | null) => {
+          if (url) {
+            store.nav.handleLink(url)
+          }
+        })
+        Linking.addEventListener('url', ({url}) => {
+          store.nav.handleLink(url)
+        })
       })
   }, [])
 
diff --git a/src/state/index.ts b/src/state/index.ts
index 872cd69f6..32efea3f3 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -33,6 +33,8 @@ export async function setupState() {
     if (!api.sessionManager.session && rootStore.session.isAuthed) {
       // reset session
       rootStore.session.clear()
+    } else if (api.sessionManager.session) {
+      rootStore.session.updateAuthTokens(api.sessionManager.session)
     }
   })
 
diff --git a/src/state/models/navigation.ts b/src/state/models/navigation.ts
index 758ae37d8..1e639b0f3 100644
--- a/src/state/models/navigation.ts
+++ b/src/state/models/navigation.ts
@@ -222,6 +222,24 @@ export class NavigationModel {
     this.tabs.find(t => t.id === ptr[0])?.setTitle(ptr[1], title)
   }
 
+  handleLink(url: string) {
+    let path
+    if (url.startsWith('/')) {
+      path = url
+    } else if (url.startsWith('http')) {
+      try {
+        path = new URL(url).pathname
+      } catch (e) {
+        console.error('Invalid url', url, e)
+        return
+      }
+    } else {
+      console.error('Invalid url', url)
+      return
+    }
+    this.navigate(path)
+  }
+
   // tab management
   // =
 
diff --git a/src/state/models/session.ts b/src/state/models/session.ts
index e10a08e86..0f1faeaba 100644
--- a/src/state/models/session.ts
+++ b/src/state/models/session.ts
@@ -1,6 +1,9 @@
 import {makeAutoObservable} from 'mobx'
 import {sessionClient as AtpApi} from '../../third-party/api/index'
-import type {SessionServiceClient} from '../../third-party/api/src/index'
+import type {
+  SessionServiceClient,
+  Session,
+} from '../../third-party/api/src/index'
 import type * as GetAccountsConfig from '../../third-party/api/src/client/types/com/atproto/server/getAccountsConfig'
 import {isObj, hasProp} from '../lib/type-guards'
 import {RootStoreModel} from './root-store'
@@ -88,6 +91,16 @@ export class SessionModel {
     this.data = data
   }
 
+  updateAuthTokens(session: Session) {
+    if (this.data) {
+      this.setState({
+        ...this.data,
+        accessJwt: session.accessJwt,
+        refreshJwt: session.refreshJwt,
+      })
+    }
+  }
+
   private configureApi(): boolean {
     if (!this.data) {
       return false
diff --git a/src/state/models/shell-ui.ts b/src/state/models/shell-ui.ts
index 73b1bd56e..13d720730 100644
--- a/src/state/models/shell-ui.ts
+++ b/src/state/models/shell-ui.ts
@@ -2,23 +2,6 @@ import {makeAutoObservable} from 'mobx'
 import {ProfileViewModel} from './profile-view'
 import * as Post from '../../third-party/api/src/client/types/app/bsky/feed/post'
 
-export interface LinkActionsModelOpts {
-  newTab?: boolean
-}
-export class LinkActionsModel {
-  name = 'link-actions'
-  newTab: boolean
-
-  constructor(
-    public href: string,
-    public title: string,
-    opts?: LinkActionsModelOpts,
-  ) {
-    makeAutoObservable(this)
-    this.newTab = typeof opts?.newTab === 'boolean' ? opts.newTab : true
-  }
-}
-
 export class ConfirmModel {
   name = 'confirm'
 
@@ -31,14 +14,6 @@ export class ConfirmModel {
   }
 }
 
-export class SharePostModel {
-  name = 'share-post'
-
-  constructor(public href: string) {
-    makeAutoObservable(this)
-  }
-}
-
 export class EditProfileModel {
   name = 'edit-profile'
 
@@ -85,9 +60,7 @@ export interface ComposerOpts {
 export class ShellUiModel {
   isModalActive = false
   activeModal:
-    | LinkActionsModel
     | ConfirmModel
-    | SharePostModel
     | EditProfileModel
     | CreateSceneModel
     | ServerInputModel
@@ -101,9 +74,7 @@ export class ShellUiModel {
 
   openModal(
     modal:
-      | LinkActionsModel
       | ConfirmModel
-      | SharePostModel
       | EditProfileModel
       | CreateSceneModel
       | ServerInputModel,
diff --git a/src/view/com/modals/LinkActions.tsx b/src/view/com/modals/LinkActions.tsx
deleted file mode 100644
index deb1518ec..000000000
--- a/src/view/com/modals/LinkActions.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-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,
-  newTab,
-}: {
-  title: string
-  href: string
-  newTab: boolean
-}) {
-  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 (
-    <View>
-      <Text style={[s.textCenter, s.bold, s.mb10, s.f16]}>{title || href}</Text>
-      <View style={s.p10}>
-        {newTab ? (
-          <TouchableOpacity onPress={onPressOpenNewTab} style={styles.btn}>
-            <FontAwesomeIcon
-              icon="arrow-up-right-from-square"
-              style={styles.icon}
-            />
-            <Text style={[s.f16, s.black]}>Open in new tab</Text>
-          </TouchableOpacity>
-        ) : undefined}
-        <TouchableOpacity onPress={onPressCopy} style={styles.btn}>
-          <FontAwesomeIcon icon="link" style={styles.icon} />
-          <Text style={[s.f16, s.black]}>Copy to clipboard</Text>
-        </TouchableOpacity>
-      </View>
-    </View>
-  )
-}
-
-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
index 3317fef08..9d7033411 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -7,9 +7,7 @@ import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
 
 import * as models from '../../../state/models/shell-ui'
 
-import * as LinkActionsModal from './LinkActions'
 import * as ConfirmModal from './Confirm'
-import * as SharePostModal from './SharePost.native'
 import * as EditProfileModal from './EditProfile'
 import * as CreateSceneModal from './CreateScene'
 import * as InviteToSceneModal from './InviteToScene'
@@ -41,27 +39,13 @@ export const Modal = observer(function Modal() {
 
   let snapPoints: (string | number)[] = CLOSED_SNAPPOINTS
   let element
-  if (store.shell.activeModal?.name === 'link-actions') {
-    snapPoints = LinkActionsModal.snapPoints
-    element = (
-      <LinkActionsModal.Component
-        {...(store.shell.activeModal as models.LinkActionsModel)}
-      />
-    )
-  } else if (store.shell.activeModal?.name === 'confirm') {
+  if (store.shell.activeModal?.name === 'confirm') {
     snapPoints = ConfirmModal.snapPoints
     element = (
       <ConfirmModal.Component
         {...(store.shell.activeModal as models.ConfirmModel)}
       />
     )
-  } else if (store.shell.activeModal?.name === 'share-post') {
-    snapPoints = SharePostModal.snapPoints
-    element = (
-      <SharePostModal.Component
-        {...(store.shell.activeModal as models.SharePostModel)}
-      />
-    )
   } else if (store.shell.activeModal?.name === 'edit-profile') {
     snapPoints = EditProfileModal.snapPoints
     element = (
diff --git a/src/view/com/modals/SharePost.native.tsx b/src/view/com/modals/SharePost.native.tsx
deleted file mode 100644
index 01692fb74..000000000
--- a/src/view/com/modals/SharePost.native.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react'
-import {Button, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
-import Toast from '../util/Toast'
-import Clipboard from '@react-native-clipboard/clipboard'
-import {s} from '../../lib/styles'
-import {useStores} from '../../../state'
-
-export const snapPoints = ['30%']
-
-export function Component({href}: {href: string}) {
-  const store = useStores()
-  const onPressCopy = () => {
-    Clipboard.setString(href)
-    Toast.show('Link copied', {
-      position: Toast.positions.TOP,
-    })
-    store.shell.closeModal()
-  }
-  const onClose = () => store.shell.closeModal()
-
-  return (
-    <View>
-      <Text style={[s.textCenter, s.bold, s.mb10]}>Share this post</Text>
-      <Text style={[s.textCenter, s.mb10]}>{href}</Text>
-      <Button title="Copy to clipboard" onPress={onPressCopy} />
-      <View style={s.p10}>
-        <TouchableOpacity onPress={onClose} style={styles.closeBtn}>
-          <Text style={s.textCenter}>Close</Text>
-        </TouchableOpacity>
-      </View>
-    </View>
-  )
-}
-
-const styles = StyleSheet.create({
-  closeBtn: {
-    width: '100%',
-    borderColor: '#000',
-    borderWidth: 1,
-    borderRadius: 4,
-    padding: 10,
-  },
-})
diff --git a/src/view/com/modals/SharePost.tsx b/src/view/com/modals/SharePost.tsx
deleted file mode 100644
index d6d4bb5c1..000000000
--- a/src/view/com/modals/SharePost.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React, {forwardRef, useState, useImperativeHandle} from 'react'
-import {Button, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
-import {Modal} from './WebModal'
-import Toast from '../util/Toast'
-import {s} from '../../lib/styles'
-
-export const ShareModal = forwardRef(function ShareModal({}: {}, ref) {
-  const [isOpen, setIsOpen] = useState<boolean>(false)
-  const [uri, setUri] = useState<string>('')
-
-  useImperativeHandle(ref, () => ({
-    open(uri: string) {
-      console.log('sharing', uri)
-      setUri(uri)
-      setIsOpen(true)
-    },
-  }))
-
-  const onPressCopy = () => {
-    // TODO
-    Toast.show('Link copied', {
-      position: Toast.positions.TOP,
-    })
-  }
-  const onClose = () => {
-    setIsOpen(false)
-  }
-
-  return (
-    <>
-      {isOpen && (
-        <Modal onClose={onClose}>
-          <View>
-            <Text style={[s.textCenter, s.bold, s.mb10]}>Share this post</Text>
-            <Text style={[s.textCenter, s.mb10]}>{uri}</Text>
-            <Button title="Copy to clipboard" onPress={onPressCopy} />
-            <View style={s.p10}>
-              <TouchableOpacity onPress={onClose} style={styles.closeBtn}>
-                <Text style={s.textCenter}>Close</Text>
-              </TouchableOpacity>
-            </View>
-          </View>
-        </Modal>
-      )}
-    </>
-  )
-})
-
-const styles = StyleSheet.create({
-  closeBtn: {
-    width: '100%',
-    borderColor: '#000',
-    borderWidth: 1,
-    borderRadius: 4,
-    padding: 10,
-  },
-})
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx
index 6b7f96e06..d819cc76c 100644
--- a/src/view/com/post-thread/PostThread.tsx
+++ b/src/view/com/post-thread/PostThread.tsx
@@ -6,7 +6,6 @@ import {
   PostThreadViewPostModel,
 } from '../../../state/models/post-thread-view'
 import {useStores} from '../../../state'
-import {SharePostModel} from '../../../state/models/shell-ui'
 import {PostThreadItem} from './PostThreadItem'
 import {ErrorMessage} from '../util/ErrorMessage'
 
@@ -17,11 +16,6 @@ export const PostThread = observer(function PostThread({
   uri: string
   view: PostThreadViewModel
 }) {
-  const store = useStores()
-
-  const onPressShare = (uri: string) => {
-    store.shell.openModal(new SharePostModel(uri))
-  }
   const onRefresh = () => {
     view?.refresh().catch(err => console.error('Failed to refresh', err))
   }
@@ -55,11 +49,7 @@ export const PostThread = observer(function PostThread({
   // =
   const posts = view.thread ? Array.from(flattenThread(view.thread)) : []
   const renderItem = ({item}: {item: PostThreadViewPostModel}) => (
-    <PostThreadItem
-      item={item}
-      onPressShare={onPressShare}
-      onPostReply={onRefresh}
-    />
+    <PostThreadItem item={item} onPostReply={onRefresh} />
   )
   return (
     <FlatList
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index eb420a40e..83aac1010 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -21,11 +21,9 @@ const PARENT_REPLY_LINE_LENGTH = 8
 
 export const PostThreadItem = observer(function PostThreadItem({
   item,
-  onPressShare,
   onPostReply,
 }: {
   item: PostThreadViewPostModel
-  onPressShare: (_uri: string) => void
   onPostReply: () => void
 }) {
   const store = useStores()
diff --git a/src/view/com/util/DropdownBtn.tsx b/src/view/com/util/DropdownBtn.tsx
index 85e8453fd..01c9259a2 100644
--- a/src/view/com/util/DropdownBtn.tsx
+++ b/src/view/com/util/DropdownBtn.tsx
@@ -1,5 +1,6 @@
 import React, {useRef} from 'react'
 import {
+  Share,
   StyleProp,
   StyleSheet,
   Text,
@@ -12,8 +13,9 @@ import {IconProp} from '@fortawesome/fontawesome-svg-core'
 import RootSiblings from 'react-native-root-siblings'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {colors} from '../../lib/styles'
+import {toShareUrl} from '../../lib/strings'
 import {useStores} from '../../../state'
-import {SharePostModel, ConfirmModel} from '../../../state/models/shell-ui'
+import {ConfirmModel} from '../../../state/models/shell-ui'
 import {TABS_ENABLED} from '../../../build-flags'
 
 export interface DropdownItem {
@@ -96,7 +98,7 @@ export function PostDropdownBtn({
       icon: 'share',
       label: 'Share...',
       onPress() {
-        store.shell.openModal(new SharePostModel(itemHref))
+        Share.share({url: toShareUrl(itemHref)})
       },
     },
     isAuthor
diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx
index ff3d25cb5..8f94115e1 100644
--- a/src/view/com/util/Link.tsx
+++ b/src/view/com/util/Link.tsx
@@ -10,7 +10,6 @@ import {
 } from 'react-native'
 import {useStores} from '../../../state'
 import {RootStoreModel} from '../../../state'
-import {LinkActionsModel} from '../../../state/models/shell-ui'
 
 export const Link = observer(function Link({
   style,
diff --git a/src/view/lib/strings.ts b/src/view/lib/strings.ts
index b12fbd398..84ee07f37 100644
--- a/src/view/lib/strings.ts
+++ b/src/view/lib/strings.ts
@@ -140,3 +140,12 @@ export function toNiceDomain(url: string): string {
     return url
   }
 }
+
+export function toShareUrl(url: string) {
+  if (!url.startsWith('https')) {
+    const urlp = new URL('https://bsky.app')
+    urlp.pathname = url
+    url = urlp.toString()
+  }
+  return url
+}
diff --git a/src/view/shell/mobile/TabsSelector.tsx b/src/view/shell/mobile/TabsSelector.tsx
index 1210da91f..4b246728f 100644
--- a/src/view/shell/mobile/TabsSelector.tsx
+++ b/src/view/shell/mobile/TabsSelector.tsx
@@ -2,6 +2,7 @@ import React, {createRef, useRef, useMemo, useEffect, useState} from 'react'
 import {observer} from 'mobx-react-lite'
 import {
   ScrollView,
+  Share,
   StyleSheet,
   Text,
   TouchableWithoutFeedback,
@@ -20,8 +21,8 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import Swipeable from 'react-native-gesture-handler/Swipeable'
 import {useStores} from '../../../state'
 import {s, colors} from '../../lib/styles'
+import {toShareUrl} from '../../lib/strings'
 import {match} from '../../routes'
-import {LinkActionsModel} from '../../../state/models/shell-ui'
 
 const TAB_HEIGHT = 42
 
@@ -69,13 +70,7 @@ export const TabsSelector = observer(
     }
     const onPressShareTab = () => {
       onClose()
-      store.shell.openModal(
-        new LinkActionsModel(
-          store.nav.tab.current.url,
-          store.nav.tab.current.title || 'This Page',
-          {newTab: false},
-        ),
-      )
+      Share.share({url: toShareUrl(store.nav.tab.current.url)})
     }
     const onPressChangeTab = (tabIndex: number) => {
       store.nav.setActiveTab(tabIndex)