mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Updates
This commit is contained in:
		| @@ -13,61 +13,59 @@ export interface Props { | ||||
|   fontBold?: boolean | ||||
| } | ||||
|  | ||||
| const ParseEmojis: React.FC<Props> = ({ | ||||
|   content, | ||||
|   emojis, | ||||
|   size = 'M', | ||||
|   fontBold = false | ||||
| }) => { | ||||
|   const { mode, theme } = useTheme() | ||||
|   const styles = useMemo(() => { | ||||
|     return StyleSheet.create({ | ||||
|       text: { | ||||
|         color: theme.primary, | ||||
|         ...StyleConstants.FontStyle[size], | ||||
|         ...(fontBold && { fontWeight: StyleConstants.Font.Weight.Bold }) | ||||
|       }, | ||||
|       image: { | ||||
|         width: StyleConstants.Font.Size[size], | ||||
|         height: StyleConstants.Font.Size[size], | ||||
|         transform: [{ translateY: size === 'L' ? -3 : -1 }] | ||||
|       } | ||||
|     }) | ||||
|   }, [mode]) | ||||
| const ParseEmojis = React.memo( | ||||
|   ({ content, emojis, size = 'M', fontBold = false }: Props) => { | ||||
|     const { mode, theme } = useTheme() | ||||
|     const styles = useMemo(() => { | ||||
|       return StyleSheet.create({ | ||||
|         text: { | ||||
|           color: theme.primary, | ||||
|           ...StyleConstants.FontStyle[size], | ||||
|           ...(fontBold && { fontWeight: StyleConstants.Font.Weight.Bold }) | ||||
|         }, | ||||
|         image: { | ||||
|           width: StyleConstants.Font.Size[size], | ||||
|           height: StyleConstants.Font.Size[size], | ||||
|           transform: [{ translateY: size === 'L' ? -3 : -1 }] | ||||
|         } | ||||
|       }) | ||||
|     }, [mode]) | ||||
|  | ||||
|   return ( | ||||
|     <Text style={styles.text}> | ||||
|       {emojis ? ( | ||||
|         content | ||||
|           .split(regexEmoji) | ||||
|           .filter(f => f) | ||||
|           .map((str, i) => { | ||||
|             if (str.match(regexEmoji)) { | ||||
|               const emojiShortcode = str.split(regexEmoji)[1] | ||||
|               const emojiIndex = emojis.findIndex(emoji => { | ||||
|                 return emojiShortcode === `:${emoji.shortcode}:` | ||||
|               }) | ||||
|               return emojiIndex === -1 ? ( | ||||
|                 <Text key={i}>{emojiShortcode}</Text> | ||||
|               ) : ( | ||||
|                 <Text key={i}> | ||||
|                   {/* When emoji starts a paragraph, lineHeight will break */} | ||||
|                   {i === 0 ? <Text> </Text> : null} | ||||
|                   <FastImage | ||||
|                     source={{ uri: emojis[emojiIndex].url }} | ||||
|                     style={styles.image} | ||||
|                   /> | ||||
|                 </Text> | ||||
|               ) | ||||
|             } else { | ||||
|               return <Text key={i}>{str}</Text> | ||||
|             } | ||||
|           }) | ||||
|       ) : ( | ||||
|         <Text>{content}</Text> | ||||
|       )} | ||||
|     </Text> | ||||
|   ) | ||||
| } | ||||
|     return ( | ||||
|       <Text style={styles.text}> | ||||
|         {emojis ? ( | ||||
|           content | ||||
|             .split(regexEmoji) | ||||
|             .filter(f => f) | ||||
|             .map((str, i) => { | ||||
|               if (str.match(regexEmoji)) { | ||||
|                 const emojiShortcode = str.split(regexEmoji)[1] | ||||
|                 const emojiIndex = emojis.findIndex(emoji => { | ||||
|                   return emojiShortcode === `:${emoji.shortcode}:` | ||||
|                 }) | ||||
|                 return emojiIndex === -1 ? ( | ||||
|                   <Text key={i}>{emojiShortcode}</Text> | ||||
|                 ) : ( | ||||
|                   <Text key={i}> | ||||
|                     {/* When emoji starts a paragraph, lineHeight will break */} | ||||
|                     {i === 0 ? <Text> </Text> : null} | ||||
|                     <FastImage | ||||
|                       source={{ uri: emojis[emojiIndex].url }} | ||||
|                       style={styles.image} | ||||
|                     /> | ||||
|                   </Text> | ||||
|                 ) | ||||
|               } else { | ||||
|                 return <Text key={i}>{str}</Text> | ||||
|               } | ||||
|             }) | ||||
|         ) : ( | ||||
|           <Text>{content}</Text> | ||||
|         )} | ||||
|       </Text> | ||||
|     ) | ||||
|   }, | ||||
|   (prev, next) => prev.content === next.content | ||||
| ) | ||||
|  | ||||
| export default React.memo(ParseEmojis, () => true) | ||||
| export default ParseEmojis | ||||
|   | ||||
| @@ -30,8 +30,8 @@ const renderNode = ({ | ||||
|   theme: any | ||||
|   node: any | ||||
|   index: number | ||||
|   size: 'M' | 'L' | ||||
|   navigation: StackNavigationProp<Nav.LocalStackParamList> | ||||
|   size: 'S' | 'M' | 'L' | ||||
|   navigation: StackNavigationProp<Nav.TabLocalStackParamList> | ||||
|   mentions?: Mastodon.Mention[] | ||||
|   tags?: Mastodon.Tag[] | ||||
|   showFullLink: boolean | ||||
| @@ -145,7 +145,7 @@ const renderNode = ({ | ||||
|  | ||||
| export interface Props { | ||||
|   content: string | ||||
|   size?: 'M' | 'L' | ||||
|   size?: 'S' | 'M' | 'L' | ||||
|   emojis?: Mastodon.Emoji[] | ||||
|   mentions?: Mastodon.Mention[] | ||||
|   tags?: Mastodon.Tag[] | ||||
| @@ -167,7 +167,7 @@ const ParseHTML: React.FC<Props> = ({ | ||||
|   disableDetails = false | ||||
| }) => { | ||||
|   const navigation = useNavigation< | ||||
|     StackNavigationProp<Nav.LocalStackParamList> | ||||
|     StackNavigationProp<Nav.TabLocalStackParamList> | ||||
|   >() | ||||
|   const route = useRoute() | ||||
|   const { theme } = useTheme() | ||||
|   | ||||
| @@ -82,11 +82,10 @@ const Timeline: React.FC<Props> = ({ | ||||
|   const navigation = useNavigation() | ||||
|   useEffect(() => { | ||||
|     const unsubscribe = navigation.addListener('focus', props => { | ||||
|       if (props.target && props.target.includes('Screen-Notifications-Root')) { | ||||
|       if (props.target && props.target.includes('Tab-Notifications-Root')) { | ||||
|         if (flattenData.length) { | ||||
|           dispatch( | ||||
|             localUpdateNotification({ | ||||
|               unread: false, | ||||
|               latestTime: (flattenData[0] as Mastodon.Notification).created_at | ||||
|             }) | ||||
|           ) | ||||
|   | ||||
| @@ -47,6 +47,6 @@ export default { | ||||
|       heading: 'Help us improve', | ||||
|       description: 'Collecting only non-user relative usage' | ||||
|     }, | ||||
|     version: 'Version v{{version}}' | ||||
|     version: 'Version v{{version}}  ({{releaseChannel}})' | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -47,6 +47,6 @@ export default { | ||||
|       heading: '帮助我们改进', | ||||
|       description: '收集不与用户相关联的使用信息' | ||||
|     }, | ||||
|     version: '版本 v{{version}}' | ||||
|     version: '版本 v{{version}} ({{releaseChannel}})' | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -90,7 +90,7 @@ const ScreenTabs: React.FC<ScreenTabsProp> = ({ navigation }) => { | ||||
|     }), | ||||
|     [localActiveIndex, localAccount] | ||||
|   ) | ||||
|   const tabNavigatorTabBarOptions = useMemo( | ||||
|   const tabBarOptions = useMemo( | ||||
|     () => ({ | ||||
|       activeTintColor: theme.primary, | ||||
|       inactiveTintColor: | ||||
| @@ -100,7 +100,7 @@ const ScreenTabs: React.FC<ScreenTabsProp> = ({ navigation }) => { | ||||
|     }), | ||||
|     [theme, localActiveIndex] | ||||
|   ) | ||||
|   const tabScreenLocalListeners = useCallback( | ||||
|   const localListeners = useCallback( | ||||
|     () => ({ | ||||
|       tabPress: (e: any) => { | ||||
|         if (!(localActiveIndex !== null)) { | ||||
| @@ -110,7 +110,7 @@ const ScreenTabs: React.FC<ScreenTabsProp> = ({ navigation }) => { | ||||
|     }), | ||||
|     [localActiveIndex] | ||||
|   ) | ||||
|   const tabScreenComposeListeners = useMemo( | ||||
|   const composeListeners = useMemo( | ||||
|     () => ({ | ||||
|       tabPress: (e: any) => { | ||||
|         e.preventDefault() | ||||
| @@ -122,8 +122,8 @@ const ScreenTabs: React.FC<ScreenTabsProp> = ({ navigation }) => { | ||||
|     }), | ||||
|     [localActiveIndex] | ||||
|   ) | ||||
|   const tabScreenComposeComponent = useCallback(() => null, []) | ||||
|   const tabScreenNotificationsListeners = useCallback( | ||||
|   const composeComponent = useCallback(() => null, []) | ||||
|   const notificationsListeners = useCallback( | ||||
|     () => ({ | ||||
|       tabPress: (e: any) => { | ||||
|         if (!(localActiveIndex !== null)) { | ||||
| @@ -144,66 +144,67 @@ const ScreenTabs: React.FC<ScreenTabsProp> = ({ navigation }) => { | ||||
|     } | ||||
|   }) | ||||
|   const prevNotification = useSelector(getLocalNotification) | ||||
|   useEffect(() => { | ||||
|     if (queryNotification.data?.pages) { | ||||
|       const flattenData = queryNotification.data.pages.flatMap(d => [...d]) | ||||
|       const latestNotificationTime = flattenData.length | ||||
|         ? (flattenData[0] as Mastodon.Notification).created_at | ||||
|         : undefined | ||||
|   const notificationsOptions = useMemo(() => { | ||||
|     const badge = { | ||||
|       show: { | ||||
|         tabBarBadge: '', | ||||
|         tabBarBadgeStyle: { | ||||
|           transform: [{ scale: 0.5 }], | ||||
|           backgroundColor: theme.red | ||||
|         } | ||||
|       }, | ||||
|       hide: { | ||||
|         tabBarBadgeStyle: { | ||||
|           transform: [{ scale: 0.5 }], | ||||
|           backgroundColor: theme.red | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     const flattenData = queryNotification.data?.pages.flatMap(d => [...d]) | ||||
|     const latestNotificationTime = flattenData?.length | ||||
|       ? (flattenData[0] as Mastodon.Notification).created_at | ||||
|       : undefined | ||||
|  | ||||
|       if (!prevNotification || !prevNotification.latestTime) { | ||||
|         dispatch(localUpdateNotification({ unread: false })) | ||||
|       } else if ( | ||||
|     if (prevNotification?.latestTime) { | ||||
|       if ( | ||||
|         latestNotificationTime && | ||||
|         new Date(prevNotification.latestTime) < new Date(latestNotificationTime) | ||||
|       ) { | ||||
|         dispatch( | ||||
|           localUpdateNotification({ | ||||
|             unread: true, | ||||
|             latestTime: latestNotificationTime | ||||
|           }) | ||||
|         ) | ||||
|         return badge.show | ||||
|       } else { | ||||
|         return badge.hide | ||||
|       } | ||||
|     } else { | ||||
|       if (latestNotificationTime) { | ||||
|         return badge.show | ||||
|       } else { | ||||
|         return badge.hide | ||||
|       } | ||||
|     } | ||||
|   }, [queryNotification.data?.pages]) | ||||
|   }, [prevNotification, queryNotification.data?.pages]) | ||||
|  | ||||
|   return ( | ||||
|     <Tab.Navigator | ||||
|       initialRouteName={localActiveIndex !== null ? 'Tab-Local' : 'Tab-Me'} | ||||
|       screenOptions={screenOptions} | ||||
|       tabBarOptions={tabNavigatorTabBarOptions} | ||||
|       tabBarOptions={tabBarOptions} | ||||
|     > | ||||
|       <Tab.Screen | ||||
|         name='Tab-Local' | ||||
|         component={TabLocal} | ||||
|         listeners={tabScreenLocalListeners} | ||||
|         listeners={localListeners} | ||||
|       /> | ||||
|       <Tab.Screen name='Tab-Public' component={TabPublic} /> | ||||
|       <Tab.Screen | ||||
|         name='Tab-Compose' | ||||
|         component={tabScreenComposeComponent} | ||||
|         listeners={tabScreenComposeListeners} | ||||
|         component={composeComponent} | ||||
|         listeners={composeListeners} | ||||
|       /> | ||||
|       <Tab.Screen | ||||
|         name='Tab-Notifications' | ||||
|         component={TabNotifications} | ||||
|         listeners={tabScreenNotificationsListeners} | ||||
|         options={ | ||||
|           prevNotification && prevNotification.unread | ||||
|             ? { | ||||
|                 tabBarBadge: '', | ||||
|                 tabBarBadgeStyle: { | ||||
|                   transform: [{ scale: 0.5 }], | ||||
|                   backgroundColor: theme.red | ||||
|                 } | ||||
|               } | ||||
|             : { | ||||
|                 tabBarBadgeStyle: { | ||||
|                   transform: [{ scale: 0.5 }], | ||||
|                   backgroundColor: theme.red | ||||
|                 } | ||||
|               } | ||||
|         } | ||||
|         listeners={notificationsListeners} | ||||
|         options={notificationsOptions} | ||||
|       /> | ||||
|       <Tab.Screen name='Tab-Me' component={TabMe} /> | ||||
|     </Tab.Navigator> | ||||
|   | ||||
| @@ -29,7 +29,10 @@ const SettingsAnalytics: React.FC = () => { | ||||
|         } | ||||
|       /> | ||||
|       <Text style={[styles.version, { color: theme.secondary }]}> | ||||
|         {t('content.version', { version: Constants.manifest.version })} | ||||
|         {t('content.version', { | ||||
|           version: Constants.manifest.version, | ||||
|           releaseChannel: Constants.manifest.releaseChannel || 'dev' | ||||
|         })} | ||||
|       </Text> | ||||
|     </MenuContainer> | ||||
|   ) | ||||
|   | ||||
| @@ -75,7 +75,7 @@ const AccountInformation: React.FC<Props> = ({ account, myInfo = false }) => { | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   base: { | ||||
|     marginTop: -StyleConstants.Spacing.Global.PagePadding * 3, | ||||
|     marginTop: -StyleConstants.Avatar.L / 2, | ||||
|     padding: StyleConstants.Spacing.Global.PagePadding | ||||
|   }, | ||||
|   avatarAndActions: { | ||||
|   | ||||
| @@ -25,7 +25,7 @@ const AccountInformationFields = React.memo( | ||||
|             > | ||||
|               <ParseHTML | ||||
|                 content={field.name} | ||||
|                 size={'M'} | ||||
|                 size={'S'} | ||||
|                 emojis={account.emojis} | ||||
|                 showFullLink | ||||
|                 numberOfLines={5} | ||||
| @@ -42,7 +42,7 @@ const AccountInformationFields = React.memo( | ||||
|             <View style={styles.fieldRight}> | ||||
|               <ParseHTML | ||||
|                 content={field.value} | ||||
|                 size={'M'} | ||||
|                 size={'S'} | ||||
|                 emojis={account.emojis} | ||||
|                 showFullLink | ||||
|                 numberOfLines={5} | ||||
|   | ||||
| @@ -20,7 +20,6 @@ export type InstanceLocal = { | ||||
|     preferences: Mastodon.Preferences | ||||
|   } | ||||
|   notification: { | ||||
|     unread: boolean | ||||
|     latestTime?: Mastodon.Notification['created_at'] | ||||
|   } | ||||
| } | ||||
| @@ -115,7 +114,7 @@ export const localAddInstance = createAsyncThunk( | ||||
|           preferences | ||||
|         }, | ||||
|         notification: { | ||||
|           unread: false | ||||
|           latestTime: undefined | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
| @@ -203,10 +202,8 @@ const instancesSlice = createSlice({ | ||||
|       state, | ||||
|       action: PayloadAction<Partial<InstanceLocal['notification']>> | ||||
|     ) => { | ||||
|       state.local.instances[state.local.activeIndex!].notification = { | ||||
|         ...state.local.instances[state.local.activeIndex!].notification, | ||||
|         ...action.payload | ||||
|       } | ||||
|       state.local.instances[state.local.activeIndex!].notification = | ||||
|         action.payload | ||||
|     }, | ||||
|     remoteUpdate: ( | ||||
|       state, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user