1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Option to change how to handle links

This commit is contained in:
Zhiyuan Zheng
2020-12-20 18:41:28 +01:00
parent 09b960d368
commit 8281d2b0b4
12 changed files with 220 additions and 276 deletions

View File

@ -2,12 +2,12 @@ import React, { useCallback, useState } from 'react'
import { Pressable, Text, View } from 'react-native'
import HTMLView from 'react-native-htmlview'
import { useNavigation } from '@react-navigation/native'
import Emojis from '@components/Timelines/Timeline/Shared/Emojis'
import { useTheme } from '@utils/styles/ThemeManager'
import { Feather } from '@expo/vector-icons'
import { StyleConstants } from '@utils/styles/constants'
import { LinearGradient } from 'expo-linear-gradient'
import openLink from '@root/utils/openLink'
// Prevent going to the same hashtag multiple times
const renderNode = ({
@ -82,11 +82,7 @@ const renderNode = ({
color: theme.link,
fontSize: StyleConstants.Font.Size[size]
}}
onPress={() => {
navigation.navigate('Screen-Shared-Webview', {
uri: href
})
}}
onPress={async () => await openLink(href)}
>
<Feather
name='external-link'

View File

@ -1,8 +1,8 @@
import React, { useCallback } from 'react'
import React from 'react'
import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import openLink from '@root/utils/openLink'
export interface Props {
card: Mastodon.Card
@ -10,17 +10,11 @@ export interface Props {
const TimelineCard: React.FC<Props> = ({ card }) => {
const { theme } = useTheme()
const navigation = useNavigation()
const onPress = useCallback(() => {
navigation.navigate('Screen-Shared-Webview', {
uri: card.url
})
}, [])
return (
<Pressable
style={[styles.card, { borderColor: theme.border }]}
onPress={onPress}
onPress={async () => await openLink(card.url)}
>
{card.image && (
<View style={styles.left}>

View File

@ -1,8 +1,6 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { Feather } from '@expo/vector-icons'
import Emojis from '@components/Timelines/Timeline/Shared/Emojis'
import relativeTime from '@utils/relativeTime'
import { getLocalUrl } from '@utils/slices/instancesSlice'
@ -13,6 +11,7 @@ import { StyleConstants } from '@utils/styles/constants'
import HeaderDefaultActionsAccount from '@components/Timelines/Timeline/Shared/HeaderDefault/ActionsAccount'
import HeaderDefaultActionsStatus from '@components/Timelines/Timeline/Shared/HeaderDefault/ActionsStatus'
import HeaderDefaultActionsDomain from '@components/Timelines/Timeline/Shared/HeaderDefault/ActionsDomain'
import openLink from '@root/utils/openLink'
export interface Props {
queryKey?: QueryKey.Timeline
@ -33,7 +32,6 @@ const TimelineHeaderDefault: React.FC<Props> = ({
const account = status.account.acct
const { theme } = useTheme()
const navigation = useNavigation()
const localDomain = useSelector(getLocalUrl)
const [since, setSince] = useState(relativeTime(status.created_at))
const [modalVisible, setBottomSheetVisible] = useState(false)
@ -49,12 +47,12 @@ const TimelineHeaderDefault: React.FC<Props> = ({
}, [since])
const onPressAction = useCallback(() => setBottomSheetVisible(true), [])
const onPressApplication = useCallback(() => {
status.application!.website &&
navigation.navigate('Screen-Shared-Webview', {
uri: status.application!.website
})
}, [])
const onPressApplication = useCallback(
async () =>
status.application!.website &&
(await openLink(status.application!.website)),
[]
)
const pressableAction = useMemo(
() => (

View File

@ -8,7 +8,6 @@ import {
} from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { Feather } from '@expo/vector-icons'
import Emojis from '@components/Timelines/Timeline/Shared/Emojis'
import relativeTime from '@utils/relativeTime'
import { useTheme } from '@utils/styles/ThemeManager'
@ -17,6 +16,7 @@ import { useQuery } from 'react-query'
import { relationshipFetch } from '@utils/fetches/relationshipFetch'
import client from '@api/client'
import { toast } from '@components/toast'
import openLink from '@root/utils/openLink'
export interface Props {
notification: Mastodon.Notification
@ -49,11 +49,12 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
}, 1000)
}, [since])
const applicationOnPress = useCallback(() => {
navigation.navigate('Screen-Shared-Webview', {
uri: notification.status?.application!.website
})
}, [])
const applicationOnPress = useCallback(
async () =>
notification.status?.application.website &&
(await openLink(notification.status.application.website)),
[]
)
const relationshipOnPress = useCallback(() => {
client({

View File

@ -18,6 +18,14 @@ export default {
cancel: '$t(common:buttons.cancel)'
}
},
browser: {
heading: '打开链接',
options: {
internal: '应用内',
external: '系统浏览器',
cancel: '$t(common:buttons.cancel)'
}
},
copyrights: {
heading: '版权信息'
},

View File

@ -5,8 +5,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { MenuContainer, MenuRow } from '@components/Menu'
import {
changeBrowser,
changeLanguage,
changeTheme,
getSettingsBrowser,
getSettingsLanguage,
getSettingsTheme
} from '@utils/slices/settingsSlice'
@ -18,6 +20,7 @@ const ScreenMeSettings: React.FC = () => {
const { setTheme, theme } = useTheme()
const settingsLanguage = useSelector(getSettingsLanguage)
const settingsTheme = useSelector(getSettingsTheme)
const settingsBrowser = useSelector(getSettingsBrowser)
const dispatch = useDispatch()
return (
@ -85,6 +88,33 @@ const ScreenMeSettings: React.FC = () => {
)
}
/>
<MenuRow
title={t('content.browser.heading')}
content={t(`content.browser.options.${settingsBrowser}`)}
iconBack='chevron-right'
onPress={() =>
ActionSheetIOS.showActionSheetWithOptions(
{
options: [
t('content.browser.options.internal'),
t('content.browser.options.external'),
t('content.browser.options.cancel')
],
cancelButtonIndex: 2
},
buttonIndex => {
switch (buttonIndex) {
case 0:
dispatch(changeBrowser('internal'))
break
case 1:
dispatch(changeBrowser('external'))
break
}
}
)
}
/>
</MenuContainer>
<MenuContainer>
<MenuRow

View File

@ -1,104 +0,0 @@
import React, { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ActionSheetIOS } from 'react-native'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { WebView } from 'react-native-webview'
import BottomSheet from '@components/BottomSheet'
import { HeaderLeft, HeaderRight } from '@components/Header'
import { MenuContainer, MenuRow } from '@components/Menu'
const Stack = createNativeStackNavigator()
export interface Props {
route: {
params: {
uri: string
}
}
}
const ScreenSharedWebview: React.FC<Props> = ({
route: {
params: { uri }
}
}) => {
const { t } = useTranslation('sharedWebview')
const [title, setTitle] = useState<string>(t('heading.loading'))
const [bottomSheet, showBottomSheet] = useState(false)
const webview = useRef<WebView>(null)
return (
<Stack.Navigator>
<Stack.Screen
name='Screen-Shared-Webview-Root'
options={({ navigation }) => ({
title,
headerLeft: () => (
<HeaderLeft
icon='chevron-down'
onPress={() => navigation.goBack()}
/>
),
headerRight: () => (
<HeaderRight
icon='more-horizontal'
onPress={() => showBottomSheet(true)}
/>
)
})}
>
{() => (
<>
<WebView
allowsBackForwardNavigationGestures
ref={webview}
source={{ uri }}
decelerationRate='normal'
onLoad={({ nativeEvent }) => setTitle(nativeEvent.title)}
onError={() => setTitle(t('heading.error'))}
/>
<BottomSheet
visible={bottomSheet}
handleDismiss={() => showBottomSheet(false)}
>
<MenuContainer>
<MenuRow
onPress={() => {
ActionSheetIOS.showShareActionSheetWithOptions(
{
url: uri,
excludedActivityTypes: [
'com.apple.UIKit.activity.Mail',
'com.apple.UIKit.activity.Print',
'com.apple.UIKit.activity.SaveToCameraRoll',
'com.apple.UIKit.activity.OpenInIBooks'
]
},
() => {},
() => {
showBottomSheet(false)
}
)
}}
iconFront='share'
title={'分享链接'}
/>
<MenuRow
onPress={() => {
showBottomSheet(false)
webview.current?.reload()
}}
iconFront='refresh-cw'
title='刷新'
/>
</MenuContainer>
</BottomSheet>
</>
)}
</Stack.Screen>
</Stack.Navigator>
)
}
export default ScreenSharedWebview

View File

@ -3,7 +3,6 @@ import React from 'react'
import ScreenSharedAccount from '@screens/Shared/Account'
import ScreenSharedHashtag from '@screens/Shared/Hashtag'
import ScreenSharedToot from '@screens/Shared/Toot'
import ScreenSharedWebview from '@screens/Shared/Webview'
import Compose from '@screens/Shared/Compose'
import ComposeEditAttachment from '@screens/Shared/Compose/EditAttachment'
import ScreenSharedSearch from '@screens/Shared/Search'
@ -56,14 +55,6 @@ const sharedScreens = (Stack: any) => {
)
})}
/>,
<Stack.Screen
key='Screen-Shared-Webview'
name='Screen-Shared-Webview'
component={ScreenSharedWebview}
options={() => ({
stackPresentation: 'modal'
})}
/>,
<Stack.Screen
key='Screen-Shared-Compose'
name='Screen-Shared-Compose'

17
src/utils/openLink.ts Normal file
View File

@ -0,0 +1,17 @@
import { store } from '@root/store'
import * as Linking from 'expo-linking'
import * as WebBrowser from 'expo-web-browser'
import { getSettingsBrowser } from './slices/settingsSlice'
const openLink = async (url: string) => {
switch (getSettingsBrowser(store.getState())) {
case 'internal':
await WebBrowser.openBrowserAsync(url)
break
case 'external':
await Linking.openURL(url)
break
}
}
export default openLink

View File

@ -6,11 +6,13 @@ import { RootState } from '@root/store'
export type SettingsState = {
language: 'zh' | 'en' | undefined
theme: 'light' | 'dark' | 'auto'
browser: 'internal' | 'external'
}
const initialState = {
language: undefined,
theme: 'auto'
theme: 'auto',
browser: 'internal'
}
// export const updateLocal = createAsyncThunk(
@ -70,6 +72,12 @@ const settingsSlice = createSlice({
action: PayloadAction<NonNullable<SettingsState['theme']>>
) => {
state.theme = action.payload
},
changeBrowser: (
state,
action: PayloadAction<NonNullable<SettingsState['browser']>>
) => {
state.browser = action.payload
}
}
// extraReducers: builder => {
@ -81,6 +89,11 @@ const settingsSlice = createSlice({
export const getSettingsLanguage = (state: RootState) => state.settings.language
export const getSettingsTheme = (state: RootState) => state.settings.theme
export const getSettingsBrowser = (state: RootState) => state.settings.browser
export const { changeLanguage, changeTheme } = settingsSlice.actions
export const {
changeLanguage,
changeTheme,
changeBrowser
} = settingsSlice.actions
export default settingsSlice.reducer