mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	A lot of updates
This commit is contained in:
		| @@ -60,7 +60,7 @@ export const Index: React.FC = () => { | |||||||
|                   name = 'bell' |                   name = 'bell' | ||||||
|                   break |                   break | ||||||
|                 case 'Screen-Me': |                 case 'Screen-Me': | ||||||
|                   name = focused ? 'smile' : 'meh' |                   name = focused ? 'meh' : 'smile' | ||||||
|                   break |                   break | ||||||
|                 default: |                 default: | ||||||
|                   name = 'alert-octagon' |                   name = 'alert-octagon' | ||||||
|   | |||||||
| @@ -77,7 +77,7 @@ const BottomSheet: React.FC<Props> = ({ children, visible, handleDismiss }) => { | |||||||
|             { |             { | ||||||
|               top, |               top, | ||||||
|               backgroundColor: theme.background, |               backgroundColor: theme.background, | ||||||
|               paddingBottom: insets.bottom |               paddingBottom: insets.bottom || StyleConstants.Spacing.L | ||||||
|             } |             } | ||||||
|           ]} |           ]} | ||||||
|         > |         > | ||||||
| @@ -108,15 +108,16 @@ const styles = StyleSheet.create({ | |||||||
|   }, |   }, | ||||||
|   handle: { |   handle: { | ||||||
|     alignSelf: 'center', |     alignSelf: 'center', | ||||||
|     width: StyleConstants.Spacing.Global.PagePadding * 8, |     width: StyleConstants.Spacing.S * 8, | ||||||
|     height: StyleConstants.Spacing.Global.PagePadding / 2, |     height: StyleConstants.Spacing.S / 2, | ||||||
|     borderRadius: 100, |     borderRadius: 100, | ||||||
|     top: -StyleConstants.Spacing.M * 2 |     top: -StyleConstants.Spacing.M * 2 | ||||||
|   }, |   }, | ||||||
|   cancel: { |   cancel: { | ||||||
|     padding: StyleConstants.Spacing.S, |     padding: StyleConstants.Spacing.S, | ||||||
|     borderWidth: 1, |     borderWidth: 1, | ||||||
|     borderRadius: 100 |     borderRadius: 100, | ||||||
|  |     // marginBottom: StyleConstants.Spacing.L | ||||||
|   }, |   }, | ||||||
|   text: { |   text: { | ||||||
|     fontSize: StyleConstants.Font.Size.L, |     fontSize: StyleConstants.Font.Size.L, | ||||||
|   | |||||||
| @@ -6,16 +6,16 @@ import { useTheme } from 'src/utils/styles/ThemeManager' | |||||||
| import { StyleConstants } from 'src/utils/styles/constants' | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
|  |  | ||||||
| export interface Props { | export interface Props { | ||||||
|   onPressFunction: () => void |   onPress: () => void | ||||||
|   icon: string |   icon: string | ||||||
|   text: string |   text: string | ||||||
| } | } | ||||||
|  |  | ||||||
| const BottomSheetRow: React.FC<Props> = ({ onPressFunction, icon, text }) => { | const BottomSheetRow: React.FC<Props> = ({ onPress, icon, text }) => { | ||||||
|   const { theme } = useTheme() |   const { theme } = useTheme() | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Pressable onPress={() => onPressFunction()} style={styles.pressable}> |     <Pressable onPress={onPress} style={styles.pressable}> | ||||||
|       <Feather |       <Feather | ||||||
|         name={icon} |         name={icon} | ||||||
|         color={theme.primary} |         color={theme.primary} | ||||||
| @@ -28,6 +28,7 @@ const BottomSheetRow: React.FC<Props> = ({ onPressFunction, icon, text }) => { | |||||||
|  |  | ||||||
| const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||||||
|   pressable: { |   pressable: { | ||||||
|  |     width: '100%', | ||||||
|     flexDirection: 'row', |     flexDirection: 'row', | ||||||
|     marginBottom: StyleConstants.Spacing.L |     marginBottom: StyleConstants.Spacing.L | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -17,11 +17,11 @@ const HeaderLeft: React.FC<Props> = ({ onPress, text, icon }) => { | |||||||
|   return ( |   return ( | ||||||
|     <Pressable onPress={onPress} style={styles.base}> |     <Pressable onPress={onPress} style={styles.base}> | ||||||
|       {text ? ( |       {text ? ( | ||||||
|         <Text style={[styles.text, { color: theme.link }]}>{text}</Text> |         <Text style={[styles.text, { color: theme.primary }]}>{text}</Text> | ||||||
|       ) : ( |       ) : ( | ||||||
|         <Feather |         <Feather | ||||||
|           name={icon || 'chevron-left'} |           name={icon || 'chevron-left'} | ||||||
|           color={theme.link} |           color={theme.primary} | ||||||
|           size={StyleConstants.Font.Size.L} |           size={StyleConstants.Font.Size.L} | ||||||
|         /> |         /> | ||||||
|       )} |       )} | ||||||
|   | |||||||
| @@ -28,11 +28,11 @@ const HeaderRight: React.FC<PropsText | PropsIcon> = ({ | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Pressable onPress={onPress} style={styles.base}> |     <Pressable onPress={onPress} style={styles.base}> | ||||||
|       {text && <Text style={[styles.text, { color: theme.link }]}>{text}</Text>} |       {text && <Text style={[styles.text, { color: theme.primary }]}>{text}</Text>} | ||||||
|       {icon && ( |       {icon && ( | ||||||
|         <Feather |         <Feather | ||||||
|           name={icon} |           name={icon} | ||||||
|           color={theme.link} |           color={theme.primary} | ||||||
|           size={StyleConstants.Font.Size.L} |           size={StyleConstants.Font.Size.L} | ||||||
|         /> |         /> | ||||||
|       )} |       )} | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
| import React, { useCallback } from 'react' | import React, { useCallback } from 'react' | ||||||
| import { Text } from 'react-native' | import { StyleSheet, Text, View } from 'react-native' | ||||||
| import HTMLView, { HTMLViewNode } from 'react-native-htmlview' | import HTMLView from 'react-native-htmlview' | ||||||
| import { useNavigation } from '@react-navigation/native' | import { useNavigation } from '@react-navigation/native' | ||||||
|  |  | ||||||
| import Emojis from 'src/components/Timelines/Timeline/Shared/Emojis' | import Emojis from 'src/components/Timelines/Timeline/Shared/Emojis' | ||||||
| import { useTheme } from 'src/utils/styles/ThemeManager' | import { useTheme } from 'src/utils/styles/ThemeManager' | ||||||
|  | import { Feather } from '@expo/vector-icons' | ||||||
|  | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
|  |  | ||||||
| // Prevent going to the same hashtag multiple times | // Prevent going to the same hashtag multiple times | ||||||
| const renderNode = ({ | const renderNode = ({ | ||||||
| @@ -75,6 +77,11 @@ const renderNode = ({ | |||||||
|             }) |             }) | ||||||
|           }} |           }} | ||||||
|         > |         > | ||||||
|  |           <Feather | ||||||
|  |             name='external-link' | ||||||
|  |             size={StyleConstants.Font.Size.M} | ||||||
|  |             color={theme.link} | ||||||
|  |           />{' '} | ||||||
|           {showFullLink ? href : domain[1]} |           {showFullLink ? href : domain[1]} | ||||||
|         </Text> |         </Text> | ||||||
|       ) |       ) | ||||||
| @@ -88,7 +95,7 @@ export interface Props { | |||||||
|   emojis?: Mastodon.Emoji[] |   emojis?: Mastodon.Emoji[] | ||||||
|   mentions?: Mastodon.Mention[] |   mentions?: Mastodon.Mention[] | ||||||
|   showFullLink?: boolean |   showFullLink?: boolean | ||||||
|   linesTruncated?: number |   numberOfLines?: number | ||||||
| } | } | ||||||
|  |  | ||||||
| const ParseContent: React.FC<Props> = ({ | const ParseContent: React.FC<Props> = ({ | ||||||
| @@ -97,7 +104,7 @@ const ParseContent: React.FC<Props> = ({ | |||||||
|   emojis, |   emojis, | ||||||
|   mentions, |   mentions, | ||||||
|   showFullLink = false, |   showFullLink = false, | ||||||
|   linesTruncated = 10 |   numberOfLines = 10 | ||||||
| }) => { | }) => { | ||||||
|   const navigation = useNavigation() |   const navigation = useNavigation() | ||||||
|   const { theme } = useTheme() |   const { theme } = useTheme() | ||||||
| @@ -117,7 +124,11 @@ const ParseContent: React.FC<Props> = ({ | |||||||
|     [] |     [] | ||||||
|   ) |   ) | ||||||
|   const rootComponent = useCallback(({ children }) => { |   const rootComponent = useCallback(({ children }) => { | ||||||
|     return <Text numberOfLines={linesTruncated}>{children}</Text> |     return ( | ||||||
|  |       <Text numberOfLines={numberOfLines} style={styles.root}> | ||||||
|  |         {children} | ||||||
|  |       </Text> | ||||||
|  |     ) | ||||||
|   }, []) |   }, []) | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
| @@ -130,4 +141,10 @@ const ParseContent: React.FC<Props> = ({ | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const styles = StyleSheet.create({ | ||||||
|  |   root: { | ||||||
|  |     lineHeight: StyleConstants.Font.LineHeight.M | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  |  | ||||||
| export default ParseContent | export default ParseContent | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ const Timeline: React.FC<Props> = ({ | |||||||
|         }, |         }, | ||||||
|         { previous: true } |         { previous: true } | ||||||
|       ), |       ), | ||||||
|     [disableRefresh] |     [disableRefresh, flattenData] | ||||||
|   ) |   ) | ||||||
|   const flOnEndReach = useCallback( |   const flOnEndReach = useCallback( | ||||||
|     () => |     () => | ||||||
| @@ -89,7 +89,7 @@ const Timeline: React.FC<Props> = ({ | |||||||
|         direction: 'next', |         direction: 'next', | ||||||
|         id: flattenData[flattenData.length - 1].id |         id: flattenData[flattenData.length - 1].id | ||||||
|       }), |       }), | ||||||
|     [disableRefresh] |     [disableRefresh, flattenData] | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   let content |   let content | ||||||
| @@ -110,7 +110,7 @@ const Timeline: React.FC<Props> = ({ | |||||||
|           scrollEnabled={scrollEnabled} // For timeline in Account view |           scrollEnabled={scrollEnabled} // For timeline in Account view | ||||||
|           ItemSeparatorComponent={flItemSeparatorComponent} |           ItemSeparatorComponent={flItemSeparatorComponent} | ||||||
|           refreshing={!disableRefresh && isLoading} |           refreshing={!disableRefresh && isLoading} | ||||||
|           onEndReachedThreshold={!disableRefresh ? 0.5 : null} |           onEndReachedThreshold={!disableRefresh ? 1 : null} | ||||||
|           // require getItemLayout |           // require getItemLayout | ||||||
|           // {...(flattenPointer[0] && { initialScrollIndex: flattenPointer[0] })} |           // {...(flattenPointer[0] && { initialScrollIndex: flattenPointer[0] })} | ||||||
|         /> |         /> | ||||||
|   | |||||||
| @@ -106,7 +106,8 @@ const styles = StyleSheet.create({ | |||||||
|   statusView: { |   statusView: { | ||||||
|     flex: 1, |     flex: 1, | ||||||
|     flexDirection: 'column', |     flexDirection: 'column', | ||||||
|     padding: StyleConstants.Spacing.Global.PagePadding |     padding: StyleConstants.Spacing.Global.PagePadding, | ||||||
|  |     paddingBottom: StyleConstants.Spacing.M | ||||||
|   }, |   }, | ||||||
|   status: { |   status: { | ||||||
|     flex: 1, |     flex: 1, | ||||||
|   | |||||||
| @@ -1,13 +1,5 @@ | |||||||
| import React, { useCallback, useMemo, useState } from 'react' | import React, { useCallback, useMemo, useState } from 'react' | ||||||
| import { | import { ActionSheetIOS, Pressable, StyleSheet, Text, View } from 'react-native' | ||||||
|   ActionSheetIOS, |  | ||||||
|   Clipboard, |  | ||||||
|   Modal, |  | ||||||
|   Pressable, |  | ||||||
|   StyleSheet, |  | ||||||
|   Text, |  | ||||||
|   View |  | ||||||
| } from 'react-native' |  | ||||||
| import { useMutation, useQueryCache } from 'react-query' | import { useMutation, useQueryCache } from 'react-query' | ||||||
| import { Feather } from '@expo/vector-icons' | import { Feather } from '@expo/vector-icons' | ||||||
|  |  | ||||||
| @@ -17,6 +9,8 @@ import { useTheme } from 'src/utils/styles/ThemeManager' | |||||||
| import { toast } from 'src/components/toast' | import { toast } from 'src/components/toast' | ||||||
| import { useSelector } from 'react-redux' | import { useSelector } from 'react-redux' | ||||||
| import { StyleConstants } from 'src/utils/styles/constants' | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
|  | import BottomSheet from 'src/components/BottomSheet' | ||||||
|  | import BottomSheetRow from 'src/components/BottomSheet/Row' | ||||||
|  |  | ||||||
| const fireMutation = async ({ | const fireMutation = async ({ | ||||||
|   id, |   id, | ||||||
| @@ -275,96 +269,85 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => { | |||||||
|         /> |         /> | ||||||
|       </View> |       </View> | ||||||
|  |  | ||||||
|       <Modal |       <BottomSheet | ||||||
|         animationType='fade' |  | ||||||
|         presentationStyle='overFullScreen' |  | ||||||
|         transparent |  | ||||||
|         visible={bottomSheetVisible} |         visible={bottomSheetVisible} | ||||||
|  |         handleDismiss={() => setBottomSheetVisible(false)} | ||||||
|       > |       > | ||||||
|         <Pressable |         <BottomSheetRow | ||||||
|           style={styles.modalBackground} |           onPress={() => { | ||||||
|           onPress={() => setBottomSheetVisible(false)} |             ActionSheetIOS.showShareActionSheetWithOptions( | ||||||
|         > |               { | ||||||
|           <View style={styles.modalSheet}> |                 url: status.uri, | ||||||
|             <Pressable |                 excludedActivityTypes: [ | ||||||
|               onPress={() => |                   'com.apple.UIKit.activity.Mail', | ||||||
|                 ActionSheetIOS.showShareActionSheetWithOptions( |                   'com.apple.UIKit.activity.Print', | ||||||
|                   { |                   'com.apple.UIKit.activity.SaveToCameraRoll', | ||||||
|                     url: status.uri, |                   'com.apple.UIKit.activity.OpenInIBooks' | ||||||
|                     excludedActivityTypes: [ |                 ] | ||||||
|                       'com.apple.UIKit.activity.Mail', |               }, | ||||||
|                       'com.apple.UIKit.activity.Print', |               () => {}, | ||||||
|                       'com.apple.UIKit.activity.SaveToCameraRoll', |               () => { | ||||||
|                       'com.apple.UIKit.activity.OpenInIBooks' |                 setBottomSheetVisible(false) | ||||||
|                     ] |                 toast({ type: 'success', content: '分享成功' }) | ||||||
|                   }, |  | ||||||
|                   () => {}, |  | ||||||
|                   () => { |  | ||||||
|                     setBottomSheetVisible(false) |  | ||||||
|                     toast({ type: 'success', content: '分享成功' }) |  | ||||||
|                   } |  | ||||||
|                 ) |  | ||||||
|               } |               } | ||||||
|             > |             ) | ||||||
|               <Text>分享</Text> |           }} | ||||||
|             </Pressable> |           icon='share' | ||||||
|             <Pressable |           text={'分享嘟嘟'} | ||||||
|               onPress={() => { |         /> | ||||||
|                 Clipboard.setString(status.uri) |         {status.account.id === localAccountId && ( | ||||||
|                 setBottomSheetVisible(false) |           <BottomSheetRow | ||||||
|                 toast({ type: 'success', content: '链接复制成功' }) |             onPress={() => { | ||||||
|               }} |               setBottomSheetVisible(false) | ||||||
|             > |               mutateAction({ | ||||||
|               <Text>复制链接</Text> |                 id: status.id, | ||||||
|             </Pressable> |                 type: 'delete', | ||||||
|             {status.account.id === localAccountId && ( |                 stateKey: 'id' | ||||||
|               <Pressable |               }) | ||||||
|                 onPress={() => { |             }} | ||||||
|                   setBottomSheetVisible(false) |             icon='trash' | ||||||
|                   mutateAction({ |             text='删除嘟嘟' | ||||||
|                     id: status.id, |           /> | ||||||
|                     type: 'delete', |         )} | ||||||
|                     stateKey: 'id' |         {status.account.id === localAccountId && ( | ||||||
|                   }) |           <BottomSheetRow | ||||||
|                 }} |             onPress={() => { | ||||||
|               > |               console.warn('功能未开发') | ||||||
|                 <Text>删除</Text> |             }} | ||||||
|               </Pressable> |             icon='trash' | ||||||
|             )} |             text='删除并重发' | ||||||
|             <Text>(删除并重发)</Text> |           /> | ||||||
|             <Pressable |         )} | ||||||
|               onPress={() => { |         <BottomSheetRow | ||||||
|                 setBottomSheetVisible(false) |           onPress={() => { | ||||||
|                 mutateAction({ |             setBottomSheetVisible(false) | ||||||
|                   id: status.id, |             mutateAction({ | ||||||
|                   type: 'mute', |               id: status.id, | ||||||
|                   stateKey: 'muted', |               type: 'mute', | ||||||
|                   prevState: status.muted |               stateKey: 'muted', | ||||||
|                 }) |               prevState: status.muted | ||||||
|               }} |             }) | ||||||
|             > |           }} | ||||||
|               <Text>{status.muted ? '取消静音' : '静音'}</Text> |           icon='volume-x' | ||||||
|             </Pressable> |           text={status.muted ? '取消静音' : '静音'} | ||||||
|             {/* Also note that reblogs cannot be pinned. */} |         /> | ||||||
|             {status.account.id === localAccountId && ( |         {/* Also note that reblogs cannot be pinned. */} | ||||||
|               <Pressable |         {status.account.id === localAccountId && ( | ||||||
|                 onPress={() => { |           <BottomSheetRow | ||||||
|                   setBottomSheetVisible(false) |             onPress={() => { | ||||||
|                   mutateAction({ |               setBottomSheetVisible(false) | ||||||
|                     id: status.id, |               mutateAction({ | ||||||
|                     type: 'pin', |                 id: status.id, | ||||||
|                     stateKey: 'pinned', |                 type: 'pin', | ||||||
|                     prevState: status.pinned |                 stateKey: 'pinned', | ||||||
|                   }) |                 prevState: status.pinned | ||||||
|                 }} |               }) | ||||||
|               > |             }} | ||||||
|                 <Text>{status.pinned ? '取消置顶' : '置顶'}</Text> |             icon='anchor' | ||||||
|               </Pressable> |             text={status.pinned ? '取消置顶' : '置顶'} | ||||||
|             )} |           /> | ||||||
|             <Text>静音用户,屏蔽用户,屏蔽域名,举报用户</Text> |         )} | ||||||
|           </View> |       </BottomSheet> | ||||||
|         </Pressable> |  | ||||||
|       </Modal> |  | ||||||
|     </> |     </> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,15 @@ | |||||||
| import React, { useCallback } from 'react' | import React, { useCallback } from 'react' | ||||||
| import { Image, Pressable, StyleSheet, Text, View } from 'react-native' | import { Image, Pressable, StyleSheet, Text, View } from 'react-native' | ||||||
| import { useNavigation } from '@react-navigation/native' | import { useNavigation } from '@react-navigation/native' | ||||||
|  | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
|  | import { useTheme } from 'src/utils/styles/ThemeManager' | ||||||
|  |  | ||||||
| export interface Props { | export interface Props { | ||||||
|   card: Mastodon.Card |   card: Mastodon.Card | ||||||
| } | } | ||||||
|  |  | ||||||
| const Card: React.FC<Props> = ({ card }) => { | const Card: React.FC<Props> = ({ card }) => { | ||||||
|  |   const { theme } = useTheme() | ||||||
|   const navigation = useNavigation() |   const navigation = useNavigation() | ||||||
|   const onPress = useCallback(() => { |   const onPress = useCallback(() => { | ||||||
|     navigation.navigate('Screen-Shared-Webview', { |     navigation.navigate('Screen-Shared-Webview', { | ||||||
| @@ -15,20 +18,35 @@ const Card: React.FC<Props> = ({ card }) => { | |||||||
|   }, []) |   }, []) | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Pressable style={styles.card} onPress={onPress}> |     <Pressable | ||||||
|  |       style={[styles.card, { borderColor: theme.border }]} | ||||||
|  |       onPress={onPress} | ||||||
|  |     > | ||||||
|       {card.image && ( |       {card.image && ( | ||||||
|         <View style={styles.left}> |         <View style={styles.left}> | ||||||
|           <Image source={{ uri: card.image }} style={styles.image} /> |           <Image source={{ uri: card.image }} style={styles.image} /> | ||||||
|         </View> |         </View> | ||||||
|       )} |       )} | ||||||
|       <View style={styles.right}> |       <View style={styles.right}> | ||||||
|         <Text numberOfLines={1}>{card.title}</Text> |         <Text | ||||||
|  |           numberOfLines={2} | ||||||
|  |           style={[styles.rightTitle, { color: theme.primary }]} | ||||||
|  |         > | ||||||
|  |           {card.title} | ||||||
|  |         </Text> | ||||||
|         {card.description ? ( |         {card.description ? ( | ||||||
|           <Text numberOfLines={2}>{card.description}</Text> |           <Text | ||||||
|  |             numberOfLines={1} | ||||||
|  |             style={[styles.rightDescription, { color: theme.primary }]} | ||||||
|  |           > | ||||||
|  |             {card.description} | ||||||
|  |           </Text> | ||||||
|         ) : ( |         ) : ( | ||||||
|           <></> |           <></> | ||||||
|         )} |         )} | ||||||
|         <Text numberOfLines={1}>{card.url}</Text> |         <Text numberOfLines={1} style={{ color: theme.secondary }}> | ||||||
|  |           {card.url} | ||||||
|  |         </Text> | ||||||
|       </View> |       </View> | ||||||
|     </Pressable> |     </Pressable> | ||||||
|   ) |   ) | ||||||
| @@ -38,18 +56,30 @@ const styles = StyleSheet.create({ | |||||||
|   card: { |   card: { | ||||||
|     flex: 1, |     flex: 1, | ||||||
|     flexDirection: 'row', |     flexDirection: 'row', | ||||||
|     height: 70, |     height: StyleConstants.Avatar.L, | ||||||
|     marginTop: 12 |     marginTop: StyleConstants.Spacing.M, | ||||||
|  |     borderWidth: 0.5, | ||||||
|  |     borderRadius: 6 | ||||||
|   }, |   }, | ||||||
|   left: { |   left: { | ||||||
|     width: 70 |     width: StyleConstants.Avatar.L | ||||||
|   }, |   }, | ||||||
|   image: { |   image: { | ||||||
|     width: '100%', |     width: '100%', | ||||||
|     height: '100%' |     height: '100%', | ||||||
|  |     borderTopLeftRadius: 6, | ||||||
|  |     borderBottomLeftRadius: 6 | ||||||
|   }, |   }, | ||||||
|   right: { |   right: { | ||||||
|     flex: 1 |     flex: 1, | ||||||
|  |     padding: StyleConstants.Spacing.S | ||||||
|  |   }, | ||||||
|  |   rightTitle: { | ||||||
|  |     marginBottom: StyleConstants.Spacing.XS, | ||||||
|  |     fontWeight: StyleConstants.Font.Weight.Bold | ||||||
|  |   }, | ||||||
|  |   rightDescription: { | ||||||
|  |     marginBottom: StyleConstants.Spacing.XS | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import React from 'react' | import React from 'react' | ||||||
| import { Image, StyleSheet, Text } from 'react-native' | import { Image, StyleSheet, Text, View } from 'react-native' | ||||||
| import { useTheme } from 'src/utils/styles/ThemeManager' | import { useTheme } from 'src/utils/styles/ThemeManager' | ||||||
| import { StyleConstants } from 'src/utils/styles/constants' | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
|  |  | ||||||
| @@ -22,19 +22,20 @@ const Emojis: React.FC<Props> = ({ | |||||||
|   const styles = StyleSheet.create({ |   const styles = StyleSheet.create({ | ||||||
|     text: { |     text: { | ||||||
|       fontSize: size, |       fontSize: size, | ||||||
|       lineHeight: size + 2, |  | ||||||
|       color: theme.primary, |       color: theme.primary, | ||||||
|       ...(fontBold && { fontWeight: StyleConstants.Font.Weight.Bold }) |       ...(fontBold && { fontWeight: StyleConstants.Font.Weight.Bold }) | ||||||
|     }, |     }, | ||||||
|     image: { |     image: { | ||||||
|       width: size, |       width: size, | ||||||
|       height: size |       height: size, | ||||||
|  |       paddingTop: 1, | ||||||
|  |       marginBottom: -1 | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
|   const hasEmojis = content.match(regexEmoji) |   const hasEmojis = content.match(regexEmoji) | ||||||
|  |  | ||||||
|   return hasEmojis ? ( |   return ( | ||||||
|     <> |     <Text> | ||||||
|       {content.split(regexEmoji).map((str, i) => { |       {content.split(regexEmoji).map((str, i) => { | ||||||
|         if (str.match(regexEmoji)) { |         if (str.match(regexEmoji)) { | ||||||
|           const emojiShortcode = str.split(regexEmoji)[1] |           const emojiShortcode = str.split(regexEmoji)[1] | ||||||
| @@ -46,23 +47,26 @@ const Emojis: React.FC<Props> = ({ | |||||||
|               {emojiShortcode} |               {emojiShortcode} | ||||||
|             </Text> |             </Text> | ||||||
|           ) : ( |           ) : ( | ||||||
|             <Image |             <View key={i} style={styles.image}> | ||||||
|               key={i} |               <Image | ||||||
|               source={{ uri: emojis[emojiIndex].url }} |                 key={i} | ||||||
|               style={styles.image} |                 resizeMode='contain' | ||||||
|             /> |                 source={{ uri: emojis[emojiIndex].url }} | ||||||
|  |                 style={{ width: '100%', height: '100%' }} | ||||||
|  |               /> | ||||||
|  |             </View> | ||||||
|           ) |           ) | ||||||
|         } else { |         } else { | ||||||
|           return ( |           return str ? ( | ||||||
|             <Text key={i} style={styles.text}> |             <Text key={i} style={styles.text}> | ||||||
|               {str} |               {str} | ||||||
|             </Text> |             </Text> | ||||||
|  |           ) : ( | ||||||
|  |             undefined | ||||||
|           ) |           ) | ||||||
|         } |         } | ||||||
|       })} |       })} | ||||||
|     </> |     </Text> | ||||||
|   ) : ( |  | ||||||
|     <Text style={styles.text}>{content}</Text> |  | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -166,7 +166,7 @@ const HeaderDefault: React.FC<Props> = ({ | |||||||
|     <View> |     <View> | ||||||
|       <View style={styles.nameAndAction}> |       <View style={styles.nameAndAction}> | ||||||
|         <View style={styles.name}> |         <View style={styles.name}> | ||||||
|           {emojis ? ( |           {emojis?.length ? ( | ||||||
|             <Emojis |             <Emojis | ||||||
|               content={name} |               content={name} | ||||||
|               emojis={emojis} |               emojis={emojis} | ||||||
| @@ -174,7 +174,10 @@ const HeaderDefault: React.FC<Props> = ({ | |||||||
|               fontBold={true} |               fontBold={true} | ||||||
|             /> |             /> | ||||||
|           ) : ( |           ) : ( | ||||||
|             <Text numberOfLines={1} style={{ color: theme.primary }}> |             <Text | ||||||
|  |               numberOfLines={1} | ||||||
|  |               style={[styles.nameWithoutEmoji, { color: theme.primary }]} | ||||||
|  |             > | ||||||
|               {name} |               {name} | ||||||
|             </Text> |             </Text> | ||||||
|           )} |           )} | ||||||
| @@ -193,6 +196,7 @@ const HeaderDefault: React.FC<Props> = ({ | |||||||
|           /> |           /> | ||||||
|         )} |         )} | ||||||
|       </View> |       </View> | ||||||
|  |  | ||||||
|       <View style={styles.meta}> |       <View style={styles.meta}> | ||||||
|         <View> |         <View> | ||||||
|           <Text style={[styles.created_at, { color: theme.secondary }]}> |           <Text style={[styles.created_at, { color: theme.secondary }]}> | ||||||
| @@ -224,7 +228,7 @@ const HeaderDefault: React.FC<Props> = ({ | |||||||
|       > |       > | ||||||
|         {accountId !== localAccountId && ( |         {accountId !== localAccountId && ( | ||||||
|           <BottomSheetRow |           <BottomSheetRow | ||||||
|             onPressFunction={() => { |             onPress={() => { | ||||||
|               setModalVisible(false) |               setModalVisible(false) | ||||||
|               mutateAction({ |               mutateAction({ | ||||||
|                 id: accountId, |                 id: accountId, | ||||||
| @@ -238,7 +242,7 @@ const HeaderDefault: React.FC<Props> = ({ | |||||||
|         )} |         )} | ||||||
|         {accountId !== localAccountId && ( |         {accountId !== localAccountId && ( | ||||||
|           <BottomSheetRow |           <BottomSheetRow | ||||||
|             onPressFunction={() => { |             onPress={() => { | ||||||
|               setModalVisible(false) |               setModalVisible(false) | ||||||
|               mutateAction({ |               mutateAction({ | ||||||
|                 id: accountId, |                 id: accountId, | ||||||
| @@ -252,7 +256,7 @@ const HeaderDefault: React.FC<Props> = ({ | |||||||
|         )} |         )} | ||||||
|         {domain !== localDomain && ( |         {domain !== localDomain && ( | ||||||
|           <BottomSheetRow |           <BottomSheetRow | ||||||
|             onPressFunction={() => { |             onPress={() => { | ||||||
|               setModalVisible(false) |               setModalVisible(false) | ||||||
|               mutateAction({ |               mutateAction({ | ||||||
|                 id: domain, |                 id: domain, | ||||||
| @@ -265,7 +269,7 @@ const HeaderDefault: React.FC<Props> = ({ | |||||||
|         )} |         )} | ||||||
|         {accountId !== localAccountId && ( |         {accountId !== localAccountId && ( | ||||||
|           <BottomSheetRow |           <BottomSheetRow | ||||||
|             onPressFunction={() => { |             onPress={() => { | ||||||
|               setModalVisible(false) |               setModalVisible(false) | ||||||
|               mutateAction({ |               mutateAction({ | ||||||
|                 id: accountId, |                 id: accountId, | ||||||
| @@ -288,17 +292,20 @@ const styles = StyleSheet.create({ | |||||||
|     justifyContent: 'space-between' |     justifyContent: 'space-between' | ||||||
|   }, |   }, | ||||||
|   name: { |   name: { | ||||||
|     flexBasis: '80%', |     flexBasis: '90%', | ||||||
|     flexDirection: 'row' |     flexDirection: 'row' | ||||||
|   }, |   }, | ||||||
|  |   nameWithoutEmoji: { | ||||||
|  |     fontSize: StyleConstants.Font.Size.M, | ||||||
|  |     fontWeight: StyleConstants.Font.Weight.Bold | ||||||
|  |   }, | ||||||
|   action: { |   action: { | ||||||
|     flexBasis: '20%', |     alignItems: 'flex-end' | ||||||
|     alignItems: 'center' |  | ||||||
|   }, |   }, | ||||||
|   account: { |   account: { | ||||||
|     flexShrink: 1, |     flexShrink: 1, | ||||||
|     marginLeft: StyleConstants.Spacing.XS, |     marginLeft: StyleConstants.Spacing.XS | ||||||
|     lineHeight: StyleConstants.Font.Size.M + 2 |     // lineHeight: StyleConstants.Font.LineHeight.M | ||||||
|   }, |   }, | ||||||
|   meta: { |   meta: { | ||||||
|     flexDirection: 'row', |     flexDirection: 'row', | ||||||
|   | |||||||
| @@ -17,6 +17,10 @@ export default { | |||||||
|         dark: '深色模式', |         dark: '深色模式', | ||||||
|         cancel: '$t(common:buttons.cancel)' |         cancel: '$t(common:buttons.cancel)' | ||||||
|       } |       } | ||||||
|     } |     }, | ||||||
|  |     copyrights: { | ||||||
|  |       heading: '版权信息' | ||||||
|  |     }, | ||||||
|  |     version: '版本 v{{version}}' | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import React from 'react' | import React from 'react' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import { ActionSheetIOS } from 'react-native' | import { ActionSheetIOS, StyleSheet, Text } from 'react-native' | ||||||
| import { useDispatch, useSelector } from 'react-redux' | import { useDispatch, useSelector } from 'react-redux' | ||||||
|  |  | ||||||
| import { MenuContainer, MenuItem } from 'src/components/Menu' | import { MenuContainer, MenuItem } from 'src/components/Menu' | ||||||
| @@ -10,81 +10,101 @@ import { | |||||||
|   getSettingsLanguage, |   getSettingsLanguage, | ||||||
|   getSettingsTheme |   getSettingsTheme | ||||||
| } from 'src/utils/slices/settingsSlice' | } from 'src/utils/slices/settingsSlice' | ||||||
|  | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
| import { useTheme } from 'src/utils/styles/ThemeManager' | import { useTheme } from 'src/utils/styles/ThemeManager' | ||||||
|  |  | ||||||
| const ScreenMeSettings: React.FC = () => { | const ScreenMeSettings: React.FC = () => { | ||||||
|   const { t, i18n } = useTranslation('meSettings') |   const { t, i18n } = useTranslation('meSettings') | ||||||
|   const { setTheme } = useTheme() |   const { setTheme, theme } = useTheme() | ||||||
|   const settingsLanguage = useSelector(getSettingsLanguage) |   const settingsLanguage = useSelector(getSettingsLanguage) | ||||||
|   const settingsTheme = useSelector(getSettingsTheme) |   const settingsTheme = useSelector(getSettingsTheme) | ||||||
|   const dispatch = useDispatch() |   const dispatch = useDispatch() | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <MenuContainer marginTop={true}> |     <> | ||||||
|       <MenuItem |       <MenuContainer marginTop={true}> | ||||||
|         title={t('content.language.heading')} |         <MenuItem | ||||||
|         content={t(`content.language.options.${settingsLanguage}`)} |           title={t('content.language.heading')} | ||||||
|         iconBack='chevron-right' |           content={t(`content.language.options.${settingsLanguage}`)} | ||||||
|         onPress={() => |           iconBack='chevron-right' | ||||||
|           ActionSheetIOS.showActionSheetWithOptions( |           onPress={() => | ||||||
|             { |             ActionSheetIOS.showActionSheetWithOptions( | ||||||
|               options: [ |               { | ||||||
|                 t('content.language.options.zh'), |                 options: [ | ||||||
|                 t('content.language.options.en'), |                   t('content.language.options.zh'), | ||||||
|                 t('content.language.options.cancel') |                   t('content.language.options.en'), | ||||||
|               ], |                   t('content.language.options.cancel') | ||||||
|               cancelButtonIndex: 2 |                 ], | ||||||
|             }, |                 cancelButtonIndex: 2 | ||||||
|             buttonIndex => { |               }, | ||||||
|               switch (buttonIndex) { |               buttonIndex => { | ||||||
|                 case 0: |                 switch (buttonIndex) { | ||||||
|                   dispatch(changeLanguage('zh')) |                   case 0: | ||||||
|                   i18n.changeLanguage('zh') |                     dispatch(changeLanguage('zh')) | ||||||
|                   break |                     i18n.changeLanguage('zh') | ||||||
|                 case 1: |                     break | ||||||
|                   dispatch(changeLanguage('en')) |                   case 1: | ||||||
|                   i18n.changeLanguage('en') |                     dispatch(changeLanguage('en')) | ||||||
|                   break |                     i18n.changeLanguage('en') | ||||||
|  |                     break | ||||||
|  |                 } | ||||||
|               } |               } | ||||||
|             } |             ) | ||||||
|           ) |           } | ||||||
|         } |         /> | ||||||
|       /> |         <MenuItem | ||||||
|       <MenuItem |           title={t('content.theme.heading')} | ||||||
|         title={t('content.theme.heading')} |           content={t(`content.theme.options.${settingsTheme}`)} | ||||||
|         content={t(`content.theme.options.${settingsTheme}`)} |           iconBack='chevron-right' | ||||||
|         iconBack='chevron-right' |           onPress={() => | ||||||
|         onPress={() => |             ActionSheetIOS.showActionSheetWithOptions( | ||||||
|           ActionSheetIOS.showActionSheetWithOptions( |               { | ||||||
|             { |                 options: [ | ||||||
|               options: [ |                   t('content.theme.options.auto'), | ||||||
|                 t('content.theme.options.auto'), |                   t('content.theme.options.light'), | ||||||
|                 t('content.theme.options.light'), |                   t('content.theme.options.dark'), | ||||||
|                 t('content.theme.options.dark'), |                   t('content.theme.options.cancel') | ||||||
|                 t('content.theme.options.cancel') |                 ], | ||||||
|               ], |                 cancelButtonIndex: 3 | ||||||
|               cancelButtonIndex: 3 |               }, | ||||||
|             }, |               buttonIndex => { | ||||||
|             buttonIndex => { |                 switch (buttonIndex) { | ||||||
|               switch (buttonIndex) { |                   case 0: | ||||||
|                 case 0: |                     dispatch(changeTheme('auto')) | ||||||
|                   dispatch(changeTheme('auto')) |                     break | ||||||
|                   break |                   case 1: | ||||||
|                 case 1: |                     dispatch(changeTheme('light')) | ||||||
|                   dispatch(changeTheme('light')) |                     setTheme('light') | ||||||
|                   setTheme('light') |                     break | ||||||
|                   break |                   case 2: | ||||||
|                 case 2: |                     dispatch(changeTheme('dark')) | ||||||
|                   dispatch(changeTheme('dark')) |                     setTheme('dark') | ||||||
|                   setTheme('dark') |                     break | ||||||
|                   break |                 } | ||||||
|               } |               } | ||||||
|             } |             ) | ||||||
|           ) |           } | ||||||
|         } |         /> | ||||||
|       /> |       </MenuContainer> | ||||||
|     </MenuContainer> |       <MenuContainer> | ||||||
|  |         <MenuItem | ||||||
|  |           title={t('content.copyrights.heading')} | ||||||
|  |           iconBack='chevron-right' | ||||||
|  |         ></MenuItem> | ||||||
|  |         <Text style={[styles.version, { color: theme.secondary }]}> | ||||||
|  |           {t('content.version', { version: '1.0.0' })} | ||||||
|  |         </Text> | ||||||
|  |       </MenuContainer> | ||||||
|  |     </> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const styles = StyleSheet.create({ | ||||||
|  |   version: { | ||||||
|  |     textAlign: 'center', | ||||||
|  |     fontSize: StyleConstants.Font.Size.S, | ||||||
|  |     marginTop: StyleConstants.Spacing.M | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  |  | ||||||
| export default ScreenMeSettings | export default ScreenMeSettings | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ParseContent from 'src/components/ParseContent' | |||||||
| import { useTheme } from 'src/utils/styles/ThemeManager' | import { useTheme } from 'src/utils/styles/ThemeManager' | ||||||
| import { StyleConstants } from 'src/utils/styles/constants' | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
|  | import Emojis from 'src/components/Timelines/Timeline/Shared/Emojis' | ||||||
|  |  | ||||||
| export interface Props { | export interface Props { | ||||||
|   account: Mastodon.Account | undefined |   account: Mastodon.Account | undefined | ||||||
| @@ -17,7 +18,6 @@ const AccountInformation: React.FC<Props> = ({ account }) => { | |||||||
|   const { theme } = useTheme() |   const { theme } = useTheme() | ||||||
|   const [avatarLoaded, setAvatarLoaded] = useState(false) |   const [avatarLoaded, setAvatarLoaded] = useState(false) | ||||||
|  |  | ||||||
|   // add emoji support |  | ||||||
|   return ( |   return ( | ||||||
|     <View style={styles.information}> |     <View style={styles.information}> | ||||||
|       {/* <Text>Moved or not: {account.moved}</Text> */} |       {/* <Text>Moved or not: {account.moved}</Text> */} | ||||||
| @@ -29,27 +29,69 @@ const AccountInformation: React.FC<Props> = ({ account }) => { | |||||||
|         /> |         /> | ||||||
|       </ShimmerPlaceholder> |       </ShimmerPlaceholder> | ||||||
|  |  | ||||||
|       <Text style={[styles.display_name, { color: theme.primary }]}> |       <View style={styles.display_name}> | ||||||
|         {account?.display_name || account?.username} |         {account?.emojis ? ( | ||||||
|         {account?.bot && ( |           <Emojis | ||||||
|           <Feather name='hard-drive' style={styles.display_name} /> |             content={account?.display_name || account?.username} | ||||||
|  |             emojis={account.emojis} | ||||||
|  |             size={StyleConstants.Font.Size.L} | ||||||
|  |             fontBold={true} | ||||||
|  |           /> | ||||||
|  |         ) : ( | ||||||
|  |           <Text | ||||||
|  |             style={{ | ||||||
|  |               color: theme.primary, | ||||||
|  |               fontSize: StyleConstants.Font.Size.L, | ||||||
|  |               fontWeight: StyleConstants.Font.Weight.Bold | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             {account?.display_name || account?.username} | ||||||
|  |           </Text> | ||||||
|         )} |         )} | ||||||
|       </Text> |       </View> | ||||||
|  |  | ||||||
|       <Text style={[styles.account, { color: theme.secondary }]}> |       <View style={styles.account}> | ||||||
|         @{account?.acct} |         <Text | ||||||
|         {account?.locked && <Feather name='lock' />} |           style={{ | ||||||
|       </Text> |             color: theme.secondary, | ||||||
|  |             fontSize: StyleConstants.Font.Size.M | ||||||
|  |           }} | ||||||
|  |           selectable | ||||||
|  |         > | ||||||
|  |           @{account?.acct} | ||||||
|  |         </Text> | ||||||
|  |         {account?.locked && ( | ||||||
|  |           <Feather | ||||||
|  |             name='lock' | ||||||
|  |             style={styles.account_types} | ||||||
|  |             color={theme.secondary} | ||||||
|  |           /> | ||||||
|  |         )} | ||||||
|  |         {account?.bot && ( | ||||||
|  |           <Feather | ||||||
|  |             name='hard-drive' | ||||||
|  |             style={styles.account_types} | ||||||
|  |             color={theme.secondary} | ||||||
|  |           /> | ||||||
|  |         )} | ||||||
|  |       </View> | ||||||
|  |  | ||||||
|       {account?.fields && ( |       {account?.fields && account.fields.length > 0 && ( | ||||||
|         <View style={styles.fields}> |         <View style={[styles.fields, { borderTopColor: theme.border }]}> | ||||||
|           {account.fields.map((field, index) => ( |           {account.fields.map((field, index) => ( | ||||||
|             <View key={index} style={{ flex: 1, flexDirection: 'row' }}> |             <View | ||||||
|               <Text |               key={index} | ||||||
|  |               style={[styles.field, { borderBottomColor: theme.border }]} | ||||||
|  |             > | ||||||
|  |               <View | ||||||
|                 style={{ |                 style={{ | ||||||
|                   width: '30%', |                   flexBasis: '30%', | ||||||
|                   alignSelf: 'center', |                   alignItems: 'center', | ||||||
|                   color: theme.primary |                   justifyContent: 'center', | ||||||
|  |                   borderRightWidth: 1, | ||||||
|  |                   borderRightColor: theme.border, | ||||||
|  |                   paddingLeft: StyleConstants.Spacing.S, | ||||||
|  |                   paddingRight: StyleConstants.Spacing.S | ||||||
|                 }} |                 }} | ||||||
|               > |               > | ||||||
|                 <ParseContent |                 <ParseContent | ||||||
| @@ -57,17 +99,31 @@ const AccountInformation: React.FC<Props> = ({ account }) => { | |||||||
|                   size={StyleConstants.Font.Size.M} |                   size={StyleConstants.Font.Size.M} | ||||||
|                   emojis={account.emojis} |                   emojis={account.emojis} | ||||||
|                   showFullLink |                   showFullLink | ||||||
|                 />{' '} |                 /> | ||||||
|                 {field.verified_at && <Feather name='check-circle' />} |                 {field.verified_at && ( | ||||||
|               </Text> |                   <Feather | ||||||
|               <Text style={{ width: '70%', color: theme.primary }}> |                     name='check-circle' | ||||||
|  |                     size={StyleConstants.Font.Size.M} | ||||||
|  |                     color={theme.primary} | ||||||
|  |                   /> | ||||||
|  |                 )} | ||||||
|  |               </View> | ||||||
|  |               <View | ||||||
|  |                 style={{ | ||||||
|  |                   flexBasis: '70%', | ||||||
|  |                   alignItems: 'center', | ||||||
|  |                   justifyContent: 'center', | ||||||
|  |                   paddingLeft: StyleConstants.Spacing.S, | ||||||
|  |                   paddingRight: StyleConstants.Spacing.S | ||||||
|  |                 }} | ||||||
|  |               > | ||||||
|                 <ParseContent |                 <ParseContent | ||||||
|                   content={field.value} |                   content={field.value} | ||||||
|                   size={StyleConstants.Font.Size.M} |                   size={StyleConstants.Font.Size.M} | ||||||
|                   emojis={account.emojis} |                   emojis={account.emojis} | ||||||
|                   showFullLink |                   showFullLink | ||||||
|                 /> |                 /> | ||||||
|               </Text> |               </View> | ||||||
|             </View> |             </View> | ||||||
|           ))} |           ))} | ||||||
|         </View> |         </View> | ||||||
| @@ -83,41 +139,46 @@ const AccountInformation: React.FC<Props> = ({ account }) => { | |||||||
|         </View> |         </View> | ||||||
|       )} |       )} | ||||||
|  |  | ||||||
|       {account?.created_at && ( |       <View style={styles.created_at}> | ||||||
|         <View style={styles.created_at}> |         <Feather | ||||||
|           <Feather name='calendar' size={StyleConstants.Font.Size.M + 2} /> |           name='calendar' | ||||||
|           <Text |           size={StyleConstants.Font.Size.M + 2} | ||||||
|             style={{ |           color={theme.secondary} | ||||||
|               color: theme.primary, |           style={styles.created_at_icon} | ||||||
|               fontSize: StyleConstants.Font.Size.M, |         /> | ||||||
|               marginLeft: StyleConstants.Spacing.XS |         <Text | ||||||
|             }} |           style={{ | ||||||
|           > |             color: theme.secondary, | ||||||
|             {t('content.created_at', { |             fontSize: StyleConstants.Font.Size.M | ||||||
|               date: new Date(account.created_at).toLocaleDateString('zh-CN', { |           }} | ||||||
|  |         > | ||||||
|  |           {t( | ||||||
|  |             'content.created_at', | ||||||
|  |             { | ||||||
|  |               date: new Date(account?.created_at!).toLocaleDateString('zh-CN', { | ||||||
|                 year: 'numeric', |                 year: 'numeric', | ||||||
|                 month: 'long', |                 month: 'long', | ||||||
|                 day: 'numeric' |                 day: 'numeric' | ||||||
|               }) |               }) | ||||||
|             })} |             } || null | ||||||
|           </Text> |           )} | ||||||
|         </View> |         </Text> | ||||||
|       )} |       </View> | ||||||
|  |  | ||||||
|       <View style={styles.summary}> |       <View style={styles.summary}> | ||||||
|         <Text style={{ color: theme.primary }}> |         <Text style={{ color: theme.primary }}> | ||||||
|           {t('content.summary.statuses_count', { |           {t('content.summary.statuses_count', { | ||||||
|             count: account?.statuses_count |             count: account?.statuses_count || 0 | ||||||
|           })} |           })} | ||||||
|         </Text> |         </Text> | ||||||
|         <Text style={{ color: theme.primary }}> |         <Text style={{ color: theme.primary, textAlign: 'center' }}> | ||||||
|           {t('content.summary.followers_count', { |           {t('content.summary.followers_count', { | ||||||
|             count: account?.followers_count |             count: account?.followers_count || 0 | ||||||
|           })} |           })} | ||||||
|         </Text> |         </Text> | ||||||
|         <Text style={{ color: theme.primary }}> |         <Text style={{ color: theme.primary, textAlign: 'right' }}> | ||||||
|           {t('content.summary.following_count', { |           {t('content.summary.following_count', { | ||||||
|             count: account?.following_count |             count: account?.following_count || 0 | ||||||
|           })} |           })} | ||||||
|         </Text> |         </Text> | ||||||
|       </View> |       </View> | ||||||
| @@ -136,26 +197,40 @@ const styles = StyleSheet.create({ | |||||||
|     borderRadius: 8 |     borderRadius: 8 | ||||||
|   }, |   }, | ||||||
|   display_name: { |   display_name: { | ||||||
|     fontSize: StyleConstants.Font.Size.L, |     flexDirection: 'row', | ||||||
|     fontWeight: StyleConstants.Font.Weight.Bold, |  | ||||||
|     marginTop: StyleConstants.Spacing.M, |     marginTop: StyleConstants.Spacing.M, | ||||||
|     marginBottom: StyleConstants.Spacing.XS |     marginBottom: StyleConstants.Spacing.XS | ||||||
|   }, |   }, | ||||||
|   account: { |   account: { | ||||||
|     fontSize: StyleConstants.Font.Size.M, |     flexDirection: 'row', | ||||||
|     marginBottom: StyleConstants.Spacing.S |     alignItems: 'center', | ||||||
|  |     marginBottom: StyleConstants.Spacing.L | ||||||
|   }, |   }, | ||||||
|  |   account_types: { marginLeft: StyleConstants.Spacing.S }, | ||||||
|   fields: { |   fields: { | ||||||
|     marginBottom: StyleConstants.Spacing.S |     borderTopWidth: 0.5, | ||||||
|  |     marginBottom: StyleConstants.Spacing.M | ||||||
|  |   }, | ||||||
|  |   field: { | ||||||
|  |     flex: 1, | ||||||
|  |     flexDirection: 'row', | ||||||
|  |     borderBottomWidth: 0.5, | ||||||
|  |     paddingTop: StyleConstants.Spacing.S, | ||||||
|  |     paddingBottom: StyleConstants.Spacing.S | ||||||
|   }, |   }, | ||||||
|   note: { |   note: { | ||||||
|     marginBottom: StyleConstants.Spacing.M |     marginBottom: StyleConstants.Spacing.L | ||||||
|   }, |   }, | ||||||
|   created_at: { |   created_at: { | ||||||
|     flexDirection: 'row', |     flexDirection: 'row', | ||||||
|  |     alignItems: 'center', | ||||||
|     marginBottom: StyleConstants.Spacing.M |     marginBottom: StyleConstants.Spacing.M | ||||||
|   }, |   }, | ||||||
|  |   created_at_icon: { | ||||||
|  |     marginRight: StyleConstants.Spacing.XS | ||||||
|  |   }, | ||||||
|   summary: { |   summary: { | ||||||
|  |     flex: 1, | ||||||
|     flexDirection: 'row', |     flexDirection: 'row', | ||||||
|     justifyContent: 'space-between' |     justifyContent: 'space-between' | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,8 +1,11 @@ | |||||||
| import { useNavigation } from '@react-navigation/native' | import { useNavigation } from '@react-navigation/native' | ||||||
| import React, { useRef, useState } from 'react' | import React, { useRef, useState } from 'react' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
|  | import { ActionSheetIOS } from 'react-native' | ||||||
| import { createNativeStackNavigator } from 'react-native-screens/native-stack' | import { createNativeStackNavigator } from 'react-native-screens/native-stack' | ||||||
| import { WebView } from 'react-native-webview' | import { WebView } from 'react-native-webview' | ||||||
|  | import BottomSheet from 'src/components/BottomSheet' | ||||||
|  | import BottomSheetRow from 'src/components/BottomSheet/Row' | ||||||
|  |  | ||||||
| import { HeaderLeft, HeaderRight } from 'src/components/Header' | import { HeaderLeft, HeaderRight } from 'src/components/Header' | ||||||
|  |  | ||||||
| @@ -24,6 +27,7 @@ const ScreenSharedWebview: React.FC<Props> = ({ | |||||||
|   const navigation = useNavigation() |   const navigation = useNavigation() | ||||||
|   const { t } = useTranslation('sharedWebview') |   const { t } = useTranslation('sharedWebview') | ||||||
|   const [title, setTitle] = useState<string>(t('heading.loading')) |   const [title, setTitle] = useState<string>(t('heading.loading')) | ||||||
|  |   const [bottomSheet, showBottomSheet] = useState(false) | ||||||
|   const webview = useRef<WebView>(null) |   const webview = useRef<WebView>(null) | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
| @@ -40,19 +44,56 @@ const ScreenSharedWebview: React.FC<Props> = ({ | |||||||
|           ), |           ), | ||||||
|           headerRight: () => ( |           headerRight: () => ( | ||||||
|             <HeaderRight |             <HeaderRight | ||||||
|               icon='refresh-cw' |               icon='more-horizontal' | ||||||
|               onPress={() => webview.current?.reload()} |               onPress={() => showBottomSheet(true)} | ||||||
|             /> |             /> | ||||||
|           ) |           ) | ||||||
|         }} |         }} | ||||||
|       > |       > | ||||||
|         {() => ( |         {() => ( | ||||||
|           <WebView |           <> | ||||||
|             ref={webview} |             <WebView | ||||||
|             source={{ uri }} |               ref={webview} | ||||||
|             onLoad={({ nativeEvent }) => setTitle(nativeEvent.title)} |               source={{ uri }} | ||||||
|             onError={() => setTitle(t('heading.error'))} |               decelerationRate='normal' | ||||||
|           /> |               onLoad={({ nativeEvent }) => setTitle(nativeEvent.title)} | ||||||
|  |               onError={() => setTitle(t('heading.error'))} | ||||||
|  |             /> | ||||||
|  |             <BottomSheet | ||||||
|  |               visible={bottomSheet} | ||||||
|  |               handleDismiss={() => showBottomSheet(false)} | ||||||
|  |             > | ||||||
|  |               <BottomSheetRow | ||||||
|  |                 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) | ||||||
|  |                     } | ||||||
|  |                   ) | ||||||
|  |                 }} | ||||||
|  |                 icon='share' | ||||||
|  |                 text={'分享链接'} | ||||||
|  |               /> | ||||||
|  |               <BottomSheetRow | ||||||
|  |                 onPress={() => { | ||||||
|  |                   showBottomSheet(false) | ||||||
|  |                   webview.current?.reload() | ||||||
|  |                 }} | ||||||
|  |                 icon='refresh-cw' | ||||||
|  |                 text='刷新' | ||||||
|  |               /> | ||||||
|  |             </BottomSheet> | ||||||
|  |           </> | ||||||
|         )} |         )} | ||||||
|       </Stack.Screen> |       </Stack.Screen> | ||||||
|     </Stack.Navigator> |     </Stack.Navigator> | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import React, { createContext, useContext, useEffect, useState } from 'react' | import React, { createContext, useContext, useEffect, useState } from 'react' | ||||||
| import { Appearance } from 'react-native-appearance' | import { useColorScheme } from 'react-native-appearance' | ||||||
| import { useSelector } from 'react-redux' | import { useSelector } from 'react-redux' | ||||||
| import { ColorDefinitions, getTheme } from 'src/utils/styles/themes' | import { ColorDefinitions, getTheme } from 'src/utils/styles/themes' | ||||||
| import { getSettingsTheme } from '../slices/settingsSlice' | import { getSettingsTheme } from '../slices/settingsSlice' | ||||||
| @@ -19,11 +19,10 @@ export const ManageThemeContext = createContext<ContextType>({ | |||||||
| export const useTheme = () => useContext(ManageThemeContext) | export const useTheme = () => useContext(ManageThemeContext) | ||||||
|  |  | ||||||
| const ThemeManager: React.FC = ({ children }) => { | const ThemeManager: React.FC = ({ children }) => { | ||||||
|  |   const osTheme = useColorScheme() | ||||||
|   const userTheme = useSelector(getSettingsTheme) |   const userTheme = useSelector(getSettingsTheme) | ||||||
|   const currentMode = |   const currentMode = | ||||||
|     userTheme === 'auto' |     userTheme === 'auto' ? (osTheme as 'light' | 'dark') : userTheme | ||||||
|       ? (Appearance.getColorScheme() as 'light' | 'dark') |  | ||||||
|       : userTheme |  | ||||||
|  |  | ||||||
|   const [mode, setMode] = useState(currentMode) |   const [mode, setMode] = useState(currentMode) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,9 +4,10 @@ export const StyleConstants = { | |||||||
|   Font: { |   Font: { | ||||||
|     Size: { |     Size: { | ||||||
|       S: 12, |       S: 12, | ||||||
|       M: 14, |       M: 16, | ||||||
|       L: 18 |       L: 18 | ||||||
|     }, |     }, | ||||||
|  |     LineHeight: { M: 20 }, | ||||||
|     Weight: { |     Weight: { | ||||||
|       Bold: '600' as '600' |       Bold: '600' as '600' | ||||||
|     } |     } | ||||||
| @@ -19,7 +20,7 @@ export const StyleConstants = { | |||||||
|     L: Base * 6, |     L: Base * 6, | ||||||
|     XL: Base * 10, |     XL: Base * 10, | ||||||
|     Global: { |     Global: { | ||||||
|       PagePadding: Base * 6 |       PagePadding: Base * 4 | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user