mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Updates
This commit is contained in:
		
							
								
								
									
										4
									
								
								App.tsx
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								App.tsx
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| import { ActionSheetProvider } from '@expo/react-native-action-sheet' | ||||
| import Index from '@root/Index' | ||||
| import dev from '@root/startup/dev' | ||||
| // import sentry from '@root/startup/sentry' | ||||
| import sentry from '@root/startup/sentry' | ||||
| import log from '@root/startup/log' | ||||
| import audio from '@root/startup/audio' | ||||
| import onlineStatus from '@root/startup/onlineStatus' | ||||
| @@ -22,7 +22,7 @@ if (Platform.OS === 'android') { | ||||
|  | ||||
|  | ||||
| dev() | ||||
| // sentry() | ||||
| sentry() | ||||
| audio() | ||||
| onlineStatus() | ||||
|  | ||||
|   | ||||
| @@ -62,7 +62,6 @@ | ||||
|     "react-native-reanimated": "2.0.0-rc.0", | ||||
|     "react-native-safe-area-context": "3.1.9", | ||||
|     "react-native-screens": "~2.15.0", | ||||
|     "react-native-shimmer-placeholder": "^2.0.6", | ||||
|     "react-native-svg": "12.1.0", | ||||
|     "react-native-tab-view": "^2.15.2", | ||||
|     "react-native-tab-view-viewpager-adapter": "^1.1.0", | ||||
| @@ -72,6 +71,7 @@ | ||||
|     "react-redux": "^7.2.2", | ||||
|     "react-timeago": "^5.2.0", | ||||
|     "redux-persist": "^6.0.0", | ||||
|     "rn-placeholder": "^3.0.3", | ||||
|     "sentry-expo": "^3.0.4", | ||||
|     "tslib": "^2.0.3" | ||||
|   }, | ||||
|   | ||||
							
								
								
									
										2
									
								
								src/@types/mastodon.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/@types/mastodon.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -25,7 +25,7 @@ declare namespace Mastodon { | ||||
|     following_count: number | ||||
|  | ||||
|     // Others | ||||
|     moved?: Status | ||||
|     moved?: Account | ||||
|     fields: Field[] | ||||
|     bot: boolean | ||||
|     source: Source | ||||
|   | ||||
							
								
								
									
										8
									
								
								src/@types/react-navigation.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								src/@types/react-navigation.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -33,7 +33,13 @@ declare namespace Nav { | ||||
|       hashtag: Mastodon.Tag['name'] | ||||
|     } | ||||
|     'Screen-Shared-ImagesViewer': { | ||||
|       imageUrls: (IImageInfo & { | ||||
|       imageUrls: ({ | ||||
|         url: string | ||||
|         width?: number | ||||
|         height?: number | ||||
|         originUrl?: string | ||||
|         props?: any | ||||
|       } & { | ||||
|         preview_url: Mastodon.AttachmentImage['preview_url'] | ||||
|         remote_url: Mastodon.AttachmentImage['remote_url'] | ||||
|         imageIndex: number | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import ScreenNotifications from '@screens/Notifications' | ||||
| import ScreenPublic from '@screens/Public' | ||||
| import { useTimelineQuery } from '@utils/queryHooks/timeline' | ||||
| import { | ||||
|   getLocalAccount, | ||||
|   getLocalActiveIndex, | ||||
|   getLocalNotification, | ||||
|   localUpdateAccountPreferences, | ||||
| @@ -32,7 +33,7 @@ import React, { | ||||
|   useMemo, | ||||
|   useRef | ||||
| } from 'react' | ||||
| import { Platform, StatusBar } from 'react-native' | ||||
| import { Image, Platform, StatusBar } from 'react-native' | ||||
| import Toast from 'react-native-toast-message' | ||||
| import { useDispatch, useSelector } from 'react-redux' | ||||
|  | ||||
| @@ -120,7 +121,7 @@ const Index: React.FC<Props> = ({ localCorrupt }) => { | ||||
|       if (!prevNotification || !prevNotification.latestTime) { | ||||
|         dispatch( | ||||
|           localUpdateNotification({ | ||||
|             unread: true | ||||
|             unread: false | ||||
|           }) | ||||
|         ) | ||||
|       } else if ( | ||||
| @@ -160,6 +161,7 @@ const Index: React.FC<Props> = ({ localCorrupt }) => { | ||||
|  | ||||
|     routeNameRef.current = currentRouteName | ||||
|   }, []) | ||||
|   const localAccount = useSelector(getLocalAccount) | ||||
|   const tabNavigatorScreenOptions = useCallback( | ||||
|     ({ route }): BottomTabNavigationOptions => ({ | ||||
|       tabBarIcon: ({ | ||||
| @@ -171,32 +173,43 @@ const Index: React.FC<Props> = ({ localCorrupt }) => { | ||||
|         color: string | ||||
|         size: number | ||||
|       }) => { | ||||
|         let name: any | ||||
|         let updateColor: string = color | ||||
|         console.log() | ||||
|         switch (route.name) { | ||||
|           case 'Screen-Local': | ||||
|             name = 'Home' | ||||
|             break | ||||
|             return <Icon name='Home' size={size} color={color} /> | ||||
|           case 'Screen-Public': | ||||
|             name = 'Globe' | ||||
|             !focused && (updateColor = theme.secondary) | ||||
|             break | ||||
|             return ( | ||||
|               <Icon | ||||
|                 name='Globe' | ||||
|                 size={size} | ||||
|                 color={!focused ? theme.secondary : color} | ||||
|               /> | ||||
|             ) | ||||
|           case 'Screen-Post': | ||||
|             name = 'Plus' | ||||
|             break | ||||
|             return <Icon name='Plus' size={size} color={color} /> | ||||
|           case 'Screen-Notifications': | ||||
|             name = 'Bell' | ||||
|             break | ||||
|             return <Icon name='Bell' size={size} color={color} /> | ||||
|           case 'Screen-Me': | ||||
|             name = focused ? 'Meh' : 'Smile' | ||||
|             !focused && (updateColor = theme.secondary) | ||||
|             break | ||||
|             return localActiveIndex !== null ? ( | ||||
|               <Image | ||||
|                 source={{ uri: localAccount?.avatarStatic }} | ||||
|                 style={{ | ||||
|                   width: size + 2, | ||||
|                   height: size + 2, | ||||
|                   borderRadius: size, | ||||
|                   borderWidth: focused ? 2 : 0, | ||||
|                   borderColor: focused ? theme.secondary : color | ||||
|                 }} | ||||
|               /> | ||||
|             ) : ( | ||||
|               <Icon | ||||
|                 name={focused ? 'Meh' : 'Smile'} | ||||
|                 size={size} | ||||
|                 color={!focused ? theme.secondary : color} | ||||
|               /> | ||||
|             ) | ||||
|           default: | ||||
|             name = 'AlertOctagon' | ||||
|             break | ||||
|             return <Icon name='AlertOctagon' size={size} color={color} /> | ||||
|         } | ||||
|         return <Icon name={name} size={size} color={updateColor} /> | ||||
|       }, | ||||
|       ...(Platform.OS === 'android' && { | ||||
|         tabBarVisible: | ||||
| @@ -208,7 +221,7 @@ const Index: React.FC<Props> = ({ localCorrupt }) => { | ||||
|           getFocusedRouteNameFromRoute(route) !== 'Screen-Me-Switch' | ||||
|       }) | ||||
|     }), | ||||
|     [] | ||||
|     [localActiveIndex, localAccount] | ||||
|   ) | ||||
|   const tabNavigatorTabBarOptions = useMemo( | ||||
|     () => ({ | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { Blurhash } from 'gl-react-blurhash' | ||||
| import React, { useCallback, useEffect, useState } from 'react' | ||||
| import { | ||||
|   Image, | ||||
|   ImageStyle, | ||||
|   Pressable, | ||||
|   StyleProp, | ||||
|   StyleSheet, | ||||
| @@ -78,6 +79,7 @@ export interface Props { | ||||
|   dimension?: { width: number; height: number } | ||||
|   onPress?: () => void | ||||
|   style?: StyleProp<ViewStyle> | ||||
|   imageStyle?: StyleProp<ImageStyle> | ||||
| } | ||||
|  | ||||
| const GracefullyImage: React.FC<Props> = ({ | ||||
| @@ -87,7 +89,8 @@ const GracefullyImage: React.FC<Props> = ({ | ||||
|   blurhash, | ||||
|   dimension, | ||||
|   onPress, | ||||
|   style | ||||
|   style, | ||||
|   imageStyle | ||||
| }) => { | ||||
|   const { mode, theme } = useTheme() | ||||
|  | ||||
| @@ -125,9 +128,16 @@ const GracefullyImage: React.FC<Props> = ({ | ||||
|   const children = useCallback(() => { | ||||
|     if (imageVisible && !hidden) { | ||||
|       if (cache) { | ||||
|         return <ImageCache uri={imageVisible} style={styles.image} /> | ||||
|         return ( | ||||
|           <ImageCache uri={imageVisible} style={[styles.image, imageStyle]} /> | ||||
|         ) | ||||
|       } else { | ||||
|         return <Image source={{ uri: imageVisible }} style={styles.image} /> | ||||
|         return ( | ||||
|           <Image | ||||
|             source={{ uri: imageVisible }} | ||||
|             style={[styles.image, imageStyle]} | ||||
|           /> | ||||
|         ) | ||||
|       } | ||||
|     } else if (blurhash) { | ||||
|       return ( | ||||
| @@ -143,19 +153,17 @@ const GracefullyImage: React.FC<Props> = ({ | ||||
|           <Blurhash hash={blurhash} /> | ||||
|         </Surface> | ||||
|       ) | ||||
|     } else { | ||||
|       return ( | ||||
|         <View | ||||
|           style={[styles.image, { backgroundColor: theme.shimmerDefault }]} | ||||
|         /> | ||||
|       ) | ||||
|     } | ||||
|   }, [hidden, mode, imageVisible]) | ||||
|  | ||||
|   return ( | ||||
|     <Pressable | ||||
|       children={children} | ||||
|       style={[style, dimension && { ...dimension }]} | ||||
|       style={[ | ||||
|         style, | ||||
|         { backgroundColor: theme.shimmerDefault }, | ||||
|         dimension && { ...dimension } | ||||
|       ]} | ||||
|       {...(onPress | ||||
|         ? hidden | ||||
|           ? { disabled: true } | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import { useTranslation } from 'react-i18next' | ||||
| import { Alert, Image, StyleSheet, Text, TextInput, View } from 'react-native' | ||||
| import { useQueryClient } from 'react-query' | ||||
| import { useDispatch, useSelector } from 'react-redux' | ||||
| import { Placeholder, Fade } from 'rn-placeholder' | ||||
| import InstanceAuth from './Instance/Auth' | ||||
| import InstanceInfo from './Instance/Info' | ||||
| import { toast } from './toast' | ||||
| @@ -209,47 +210,60 @@ const ComponentInstance: React.FC<Props> = ({ | ||||
|           </Text> | ||||
|         ) : null} | ||||
|         <View> | ||||
|           <InstanceInfo | ||||
|             visible={instanceQuery.data?.title !== undefined} | ||||
|             header={t('server.information.name')} | ||||
|             content={instanceQuery.data?.title || undefined} | ||||
|             potentialWidth={10} | ||||
|           /> | ||||
|           <InstanceInfo | ||||
|             visible={instanceQuery.data?.short_description !== undefined} | ||||
|             header={t('server.information.description.heading')} | ||||
|             content={instanceQuery.data?.short_description || undefined} | ||||
|             potentialLines={5} | ||||
|           /> | ||||
|           <View style={styles.instanceStats}> | ||||
|           <Placeholder | ||||
|             {...(instanceQuery.isFetching && { | ||||
|               Animation: props => ( | ||||
|                 <Fade | ||||
|                   {...props} | ||||
|                   style={{ backgroundColor: theme.shimmerHighlight }} | ||||
|                 /> | ||||
|               ) | ||||
|             })} | ||||
|           > | ||||
|             <InstanceInfo | ||||
|               style={styles.stat1} | ||||
|               visible={instanceQuery.data?.stats?.user_count !== undefined} | ||||
|               header={t('server.information.accounts')} | ||||
|               content={ | ||||
|                 instanceQuery.data?.stats?.user_count?.toString() || undefined | ||||
|               } | ||||
|               potentialWidth={4} | ||||
|               visible={instanceQuery.data?.title !== undefined} | ||||
|               header={t('server.information.name')} | ||||
|               content={instanceQuery.data?.title || undefined} | ||||
|               potentialWidth={2} | ||||
|             /> | ||||
|             <InstanceInfo | ||||
|               style={styles.stat2} | ||||
|               visible={instanceQuery.data?.stats?.status_count !== undefined} | ||||
|               header={t('server.information.statuses')} | ||||
|               content={ | ||||
|                 instanceQuery.data?.stats?.status_count?.toString() || undefined | ||||
|               } | ||||
|               potentialWidth={4} | ||||
|               visible={instanceQuery.data?.short_description !== undefined} | ||||
|               header={t('server.information.description.heading')} | ||||
|               content={instanceQuery.data?.short_description || undefined} | ||||
|               potentialLines={5} | ||||
|             /> | ||||
|             <InstanceInfo | ||||
|               style={styles.stat3} | ||||
|               visible={instanceQuery.data?.stats?.domain_count !== undefined} | ||||
|               header={t('server.information.domains')} | ||||
|               content={ | ||||
|                 instanceQuery.data?.stats?.domain_count?.toString() || undefined | ||||
|               } | ||||
|               potentialWidth={4} | ||||
|             /> | ||||
|           </View> | ||||
|             <View style={styles.instanceStats}> | ||||
|               <InstanceInfo | ||||
|                 style={styles.stat1} | ||||
|                 visible={instanceQuery.data?.stats?.user_count !== undefined} | ||||
|                 header={t('server.information.accounts')} | ||||
|                 content={ | ||||
|                   instanceQuery.data?.stats?.user_count?.toString() || undefined | ||||
|                 } | ||||
|                 potentialWidth={4} | ||||
|               /> | ||||
|               <InstanceInfo | ||||
|                 style={styles.stat2} | ||||
|                 visible={instanceQuery.data?.stats?.status_count !== undefined} | ||||
|                 header={t('server.information.statuses')} | ||||
|                 content={ | ||||
|                   instanceQuery.data?.stats?.status_count?.toString() || | ||||
|                   undefined | ||||
|                 } | ||||
|                 potentialWidth={4} | ||||
|               /> | ||||
|               <InstanceInfo | ||||
|                 style={styles.stat3} | ||||
|                 visible={instanceQuery.data?.stats?.domain_count !== undefined} | ||||
|                 header={t('server.information.domains')} | ||||
|                 content={ | ||||
|                   instanceQuery.data?.stats?.domain_count?.toString() || | ||||
|                   undefined | ||||
|                 } | ||||
|                 potentialWidth={4} | ||||
|               /> | ||||
|             </View> | ||||
|           </Placeholder> | ||||
|           {type === 'local' ? ( | ||||
|             <View style={styles.disclaimer}> | ||||
|               <Icon | ||||
| @@ -258,12 +272,12 @@ const ComponentInstance: React.FC<Props> = ({ | ||||
|                 color={theme.secondary} | ||||
|                 style={styles.disclaimerIcon} | ||||
|               /> | ||||
|               <Text | ||||
|                 style={[styles.disclaimerText, { color: theme.secondary }]} | ||||
|                 onPress={() => Linking.openURL('https://tooot.app/privacy')} | ||||
|               > | ||||
|               <Text style={[styles.disclaimerText, { color: theme.secondary }]}> | ||||
|                 {t('server.disclaimer')} | ||||
|                 <Text style={{ color: theme.blue }}> | ||||
|                 <Text | ||||
|                   style={{ color: theme.blue }} | ||||
|                   onPress={() => Linking.openURL('https://tooot.app/privacy')} | ||||
|                 > | ||||
|                   https://tooot.app/privacy | ||||
|                 </Text> | ||||
|               </Text> | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| import { ParseHTML } from '@components/Parse' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
| import { LinearGradient } from 'expo-linear-gradient' | ||||
| import React from 'react' | ||||
| import { useTranslation } from 'react-i18next' | ||||
| import { Dimensions, StyleSheet, Text, View, ViewStyle } from 'react-native' | ||||
| import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder' | ||||
| import { StyleSheet, Text, View, ViewStyle } from 'react-native' | ||||
| import { PlaceholderLine } from 'rn-placeholder' | ||||
|  | ||||
| export interface Props { | ||||
|   style?: ViewStyle | ||||
| @@ -17,46 +16,36 @@ export interface Props { | ||||
| } | ||||
|  | ||||
| const InstanceInfo = React.memo( | ||||
|   ({ | ||||
|     style, | ||||
|     visible, | ||||
|     header, | ||||
|     content, | ||||
|     potentialWidth, | ||||
|     potentialLines = 1 | ||||
|   }: Props) => { | ||||
|   ({ style, header, content, potentialWidth, potentialLines = 1 }: Props) => { | ||||
|     const { t } = useTranslation('componentInstance') | ||||
|     const { theme } = useTheme() | ||||
|     const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient) | ||||
|  | ||||
|     return ( | ||||
|       <View style={[styles.base, style]}> | ||||
|         <Text style={[styles.header, { color: theme.primary }]}>{header}</Text> | ||||
|         <ShimmerPlaceholder | ||||
|           visible={visible} | ||||
|           stopAutoRun | ||||
|           width={ | ||||
|             potentialWidth | ||||
|               ? potentialWidth * StyleConstants.Font.Size.M | ||||
|               : Dimensions.get('screen').width - | ||||
|                 StyleConstants.Spacing.Global.PagePadding * 4 | ||||
|           } | ||||
|           height={StyleConstants.Font.LineHeight.M * potentialLines} | ||||
|           shimmerColors={[ | ||||
|             theme.shimmerDefault, | ||||
|             theme.shimmerHighlight, | ||||
|             theme.shimmerDefault | ||||
|           ]} | ||||
|         > | ||||
|           {content ? ( | ||||
|             <ParseHTML | ||||
|               content={content} | ||||
|               size={'M'} | ||||
|               numberOfLines={5} | ||||
|               expandHint={t('server.information.description.expandHint')} | ||||
|         {content ? ( | ||||
|           <ParseHTML | ||||
|             content={content} | ||||
|             size={'M'} | ||||
|             numberOfLines={5} | ||||
|             expandHint={t('server.information.description.expandHint')} | ||||
|           /> | ||||
|         ) : ( | ||||
|           Array.from(Array(potentialLines)).map((_, i) => ( | ||||
|             <PlaceholderLine | ||||
|               key={i} | ||||
|               width={ | ||||
|                 potentialWidth | ||||
|                   ? potentialWidth * StyleConstants.Font.Size.M | ||||
|                   : undefined | ||||
|               } | ||||
|               height={StyleConstants.Font.LineHeight.M} | ||||
|               color={theme.shimmerDefault} | ||||
|               noMargin | ||||
|               style={{ borderRadius: 0 }} | ||||
|             /> | ||||
|           ) : null} | ||||
|         </ShimmerPlaceholder> | ||||
|           )) | ||||
|         )} | ||||
|       </View> | ||||
|     ) | ||||
|   } | ||||
|   | ||||
| @@ -27,15 +27,10 @@ const ParseEmojis: React.FC<Props> = ({ | ||||
|         ...StyleConstants.FontStyle[size], | ||||
|         ...(fontBold && { fontWeight: StyleConstants.Font.Weight.Bold }) | ||||
|       }, | ||||
|       imageContainer: { | ||||
|         paddingVertical: | ||||
|           (StyleConstants.Font.LineHeight[size] - | ||||
|             StyleConstants.Font.Size[size]) / | ||||
|           3 | ||||
|       }, | ||||
|       image: { | ||||
|         width: StyleConstants.Font.Size[size], | ||||
|         height: StyleConstants.Font.Size[size] | ||||
|         height: StyleConstants.Font.Size[size], | ||||
|         transform: [{ translateY: size === 'L' ? -3 : -1 }] | ||||
|       } | ||||
|     }) | ||||
|   }, [mode]) | ||||
| @@ -58,13 +53,11 @@ const ParseEmojis: React.FC<Props> = ({ | ||||
|                 <Text key={i}> | ||||
|                   {/* When emoji starts a paragraph, lineHeight will break */} | ||||
|                   {i === 0 ? <Text> </Text> : null} | ||||
|                   <View style={styles.imageContainer}> | ||||
|                     <Image | ||||
|                       transitionDuration={0} | ||||
|                       uri={emojis[emojiIndex].url} | ||||
|                       style={[styles.image]} | ||||
|                     /> | ||||
|                   </View> | ||||
|                   <Image | ||||
|                     transitionDuration={0} | ||||
|                     uri={emojis[emojiIndex].url} | ||||
|                     style={[styles.image]} | ||||
|                   /> | ||||
|                 </Text> | ||||
|               ) | ||||
|             } else { | ||||
|   | ||||
| @@ -104,7 +104,8 @@ const renderNode = ({ | ||||
|             key={index} | ||||
|             style={{ | ||||
|               color: theme.blue, | ||||
|               ...StyleConstants.FontStyle[size] | ||||
|               ...StyleConstants.FontStyle[size], | ||||
|               alignItems: 'center' | ||||
|             }} | ||||
|             onPress={async () => | ||||
|               !disableDetails && !shouldBeTag | ||||
| @@ -114,14 +115,17 @@ const renderNode = ({ | ||||
|                   }) | ||||
|             } | ||||
|           > | ||||
|             {content || (showFullLink ? href : domain[1])} | ||||
|             {!shouldBeTag ? ( | ||||
|               <Icon | ||||
|                 color={theme.blue} | ||||
|                 name='ExternalLink' | ||||
|                 size={StyleConstants.Font.Size[size]} | ||||
|                 style={{ | ||||
|                   transform: [{ translateY: size === 'L' ? -3 : -1 }] | ||||
|                 }} | ||||
|               /> | ||||
|             ) : null} | ||||
|             {content || (showFullLink ? href : domain[1])} | ||||
|           </Text> | ||||
|         ) | ||||
|       } | ||||
|   | ||||
| @@ -125,7 +125,8 @@ const styles = StyleSheet.create({ | ||||
|   base: { | ||||
|     flex: 1, | ||||
|     flexDirection: 'column', | ||||
|     padding: StyleConstants.Spacing.Global.PagePadding | ||||
|     padding: StyleConstants.Spacing.Global.PagePadding, | ||||
|     paddingBottom: 0 | ||||
|   }, | ||||
|   header: { | ||||
|     flex: 1, | ||||
|   | ||||
| @@ -118,7 +118,7 @@ const TimelineDefault: React.FC<Props> = ({ | ||||
| const styles = StyleSheet.create({ | ||||
|   statusView: { | ||||
|     padding: StyleConstants.Spacing.Global.PagePadding, | ||||
|     paddingBottom: StyleConstants.Spacing.S | ||||
|     paddingBottom: 0 | ||||
|   }, | ||||
|   header: { | ||||
|     flex: 1, | ||||
|   | ||||
| @@ -123,7 +123,7 @@ const TimelineNotifications: React.FC<Props> = ({ | ||||
| const styles = StyleSheet.create({ | ||||
|   notificationView: { | ||||
|     padding: StyleConstants.Spacing.Global.PagePadding, | ||||
|     paddingBottom: StyleConstants.Spacing.M | ||||
|     paddingBottom: 0 | ||||
|   }, | ||||
|   header: { | ||||
|     flex: 1, | ||||
|   | ||||
| @@ -110,7 +110,9 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|         reblog, | ||||
|         payload: { | ||||
|           property: 'reblogged', | ||||
|           currentValue: status.reblogged | ||||
|           currentValue: status.reblogged, | ||||
|           propertyCount: 'reblogs_count', | ||||
|           countValue: status.reblogs_count | ||||
|         } | ||||
|       }), | ||||
|     [status.reblogged] | ||||
| @@ -124,7 +126,9 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|         reblog, | ||||
|         payload: { | ||||
|           property: 'favourited', | ||||
|           currentValue: status.favourited | ||||
|           currentValue: status.favourited, | ||||
|           propertyCount: 'favourites_count', | ||||
|           countValue: status.favourites_count | ||||
|         } | ||||
|       }), | ||||
|     [status.favourited] | ||||
| @@ -138,7 +142,9 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|         reblog, | ||||
|         payload: { | ||||
|           property: 'bookmarked', | ||||
|           currentValue: status.bookmarked | ||||
|           currentValue: status.bookmarked, | ||||
|           propertyCount: undefined, | ||||
|           countValue: undefined | ||||
|         } | ||||
|       }), | ||||
|     [status.bookmarked] | ||||
| @@ -156,7 +162,7 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|           <Text | ||||
|             style={{ | ||||
|               color: theme.secondary, | ||||
|               ...StyleConstants.FontStyle.M, | ||||
|               fontSize: StyleConstants.Font.Size.M, | ||||
|               marginLeft: StyleConstants.Spacing.XS | ||||
|             }} | ||||
|           > | ||||
| @@ -182,8 +188,8 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|         {status.reblogs_count > 0 && ( | ||||
|           <Text | ||||
|             style={{ | ||||
|               color: theme.secondary, | ||||
|               ...StyleConstants.FontStyle.M, | ||||
|               color: iconColorAction(status.reblogged), | ||||
|               fontSize: StyleConstants.Font.Size.M, | ||||
|               marginLeft: StyleConstants.Spacing.XS | ||||
|             }} | ||||
|           > | ||||
| @@ -205,9 +211,10 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|         {status.favourites_count > 0 && ( | ||||
|           <Text | ||||
|             style={{ | ||||
|               color: theme.secondary, | ||||
|               ...StyleConstants.FontStyle.M, | ||||
|               marginLeft: StyleConstants.Spacing.XS | ||||
|               color: iconColorAction(status.favourited), | ||||
|               fontSize: StyleConstants.Font.Size.M, | ||||
|               marginLeft: StyleConstants.Spacing.XS, | ||||
|               marginTop: 0 | ||||
|             }} | ||||
|           > | ||||
|             {status.favourites_count} | ||||
| @@ -264,15 +271,14 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => { | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   actions: { | ||||
|     flexDirection: 'row', | ||||
|     marginTop: StyleConstants.Spacing.S | ||||
|     flexDirection: 'row' | ||||
|   }, | ||||
|   action: { | ||||
|     flex: 1, | ||||
|     flexDirection: 'row', | ||||
|     justifyContent: 'center', | ||||
|     alignItems: 'center', | ||||
|     paddingVertical: StyleConstants.Spacing.S | ||||
|     minHeight: StyleConstants.Font.Size.L + StyleConstants.Spacing.S * 4 | ||||
|   } | ||||
| }) | ||||
|  | ||||
|   | ||||
| @@ -54,9 +54,11 @@ const TimelineHeaderNotification: React.FC<Props> = ({ | ||||
|         /> | ||||
|         <View style={styles.meta}> | ||||
|           <HeaderSharedCreated created_at={notification.created_at} /> | ||||
|           <HeaderSharedVisibility | ||||
|             visibility={notification.status?.visibility} | ||||
|           /> | ||||
|           {notification.status?.visibility ? ( | ||||
|             <HeaderSharedVisibility | ||||
|               visibility={notification.status.visibility} | ||||
|             /> | ||||
|           ) : null} | ||||
|           <HeaderSharedMuted muted={notification.status?.muted} /> | ||||
|           <HeaderSharedApplication | ||||
|             application={notification.status?.application} | ||||
|   | ||||
| @@ -5,20 +5,34 @@ import React from 'react' | ||||
| import { StyleSheet } from 'react-native' | ||||
|  | ||||
| export interface Props { | ||||
|   visibility?: Mastodon.Status['visibility'] | ||||
|   visibility: Mastodon.Status['visibility'] | ||||
| } | ||||
|  | ||||
| const HeaderSharedVisibility: React.FC<Props> = ({ visibility }) => { | ||||
|   const { theme } = useTheme() | ||||
|  | ||||
|   return visibility && visibility === 'private' ? ( | ||||
|     <Icon | ||||
|       name='Lock' | ||||
|       size={StyleConstants.Font.Size.S} | ||||
|       color={theme.secondary} | ||||
|       style={styles.visibility} | ||||
|     /> | ||||
|   ) : null | ||||
|   switch (visibility) { | ||||
|     case 'private': | ||||
|       return ( | ||||
|         <Icon | ||||
|           name='Lock' | ||||
|           size={StyleConstants.Font.Size.S} | ||||
|           color={theme.secondary} | ||||
|           style={styles.visibility} | ||||
|         /> | ||||
|       ) | ||||
|     case 'direct': | ||||
|       return ( | ||||
|         <Icon | ||||
|           name='Mail' | ||||
|           size={StyleConstants.Font.Size.S} | ||||
|           color={theme.secondary} | ||||
|           style={styles.visibility} | ||||
|         /> | ||||
|       ) | ||||
|     default: | ||||
|       return null | ||||
|   } | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| export default { | ||||
|   content: { | ||||
|     moved: 'User moved', | ||||
|     created_at: 'Registered: {{date}}', | ||||
|     summary: { | ||||
|       statuses_count: '{{count}} toots', | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| export default { | ||||
|   content: { | ||||
|     moved: '账户已迁移', | ||||
|     created_at: '注册时间:{{date}}', | ||||
|     summary: { | ||||
|       statuses_count: '{{count}} 条嘟文', | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import Button from '@components/Button' | ||||
| import ComponentInstance from '@components/Instance' | ||||
| import { useNavigation } from '@react-navigation/native' | ||||
| import { useAccountCheckQuery } from '@utils/queryHooks/accountCheck' | ||||
| import { | ||||
|   getLocalActiveIndex, | ||||
|   getLocalInstances, | ||||
| @@ -38,19 +37,15 @@ const AccountButton: React.FC<Props> = ({ | ||||
|   const queryClient = useQueryClient() | ||||
|   const navigation = useNavigation() | ||||
|   const dispatch = useDispatch() | ||||
|   const { isLoading, data } = useAccountCheckQuery({ | ||||
|     id: instance.account.id, | ||||
|     index, | ||||
|     options: { retry: false } | ||||
|   }) | ||||
|  | ||||
|   return ( | ||||
|     <Button | ||||
|       type='text' | ||||
|       disabled={disabled} | ||||
|       loading={isLoading} | ||||
|       style={styles.button} | ||||
|       content={`@${data?.acct || '...'}@${instance.uri}${disabled ? ' ✓' : ''}`} | ||||
|       content={`@${instance.account.acct}@${instance.uri}${ | ||||
|         disabled ? ' ✓' : '' | ||||
|       }`} | ||||
|       onPress={() => { | ||||
|         dispatch(localUpdateActiveIndex(index)) | ||||
|         queryClient.clear() | ||||
|   | ||||
| @@ -25,8 +25,6 @@ import accountInitialState from './Account/utils/initialState' | ||||
| import accountReducer from './Account/utils/reducer' | ||||
| import { SharedAccountProp } from './sharedScreens' | ||||
|  | ||||
| // Moved account example: https://m.cmx.im/web/accounts/27812 | ||||
|  | ||||
| const ScreenSharedAccount: React.FC<SharedAccountProp> = ({ | ||||
|   route: { | ||||
|     params: { account } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import React, { createRef, useEffect } from 'react' | ||||
| import { Animated, StyleSheet, View } from 'react-native' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
| import React, { useCallback } from 'react' | ||||
| import { StyleSheet, View } from 'react-native' | ||||
| import { Placeholder, Fade } from 'rn-placeholder' | ||||
| import AccountInformationAccount from './Information/Account' | ||||
| import AccountInformationActions from './Information/Actions' | ||||
| import AccountInformationAvatar from './Information/Avatar' | ||||
| @@ -20,65 +22,50 @@ const AccountInformation: React.FC<Props> = ({ | ||||
|   account, | ||||
|   ownAccount = false | ||||
| }) => { | ||||
|   const shimmerNameRef = createRef<any>() | ||||
|   const shimmerAccountRef = createRef<any>() | ||||
|   const shimmerCreatedRef = createRef<any>() | ||||
|   const shimmerStatsRef = createRef<any>() | ||||
|   useEffect(() => { | ||||
|     const informationAnimated = Animated.stagger(400, [ | ||||
|       Animated.parallel([ | ||||
|         shimmerNameRef.current?.getAnimated(), | ||||
|         shimmerAccountRef.current?.getAnimated(), | ||||
|         shimmerCreatedRef.current?.getAnimated(), | ||||
|         shimmerStatsRef.current?.ref1.getAnimated(), | ||||
|         shimmerStatsRef.current?.ref2.getAnimated(), | ||||
|         shimmerStatsRef.current?.ref3.getAnimated() | ||||
|       ]) | ||||
|     ]) | ||||
|     Animated.loop(informationAnimated).start() | ||||
|   }, []) | ||||
|   const { mode, theme } = useTheme() | ||||
|  | ||||
|   const animation = useCallback( | ||||
|     props => ( | ||||
|       <Fade {...props} style={{ backgroundColor: theme.shimmerHighlight }} /> | ||||
|     ), | ||||
|     [mode] | ||||
|   ) | ||||
|  | ||||
|   return ( | ||||
|     <View style={styles.base}> | ||||
|       {/* <Text>Moved or not: {account.moved}</Text> */} | ||||
|       <View style={styles.avatarAndActions}> | ||||
|         <AccountInformationAvatar account={account} /> | ||||
|         <View style={styles.actions}> | ||||
|           {ownAccount ? ( | ||||
|             <AccountInformationSwitch /> | ||||
|           ) : ( | ||||
|             <AccountInformationActions account={account} /> | ||||
|           )} | ||||
|       <Placeholder Animation={animation}> | ||||
|         <View style={styles.avatarAndActions}> | ||||
|           <AccountInformationAvatar account={account} /> | ||||
|           <View style={styles.actions}> | ||||
|             {ownAccount ? ( | ||||
|               <AccountInformationSwitch /> | ||||
|             ) : ( | ||||
|               <AccountInformationActions account={account} /> | ||||
|             )} | ||||
|           </View> | ||||
|         </View> | ||||
|       </View> | ||||
|  | ||||
|       <AccountInformationName ref={shimmerNameRef} account={account} /> | ||||
|         <AccountInformationName account={account} /> | ||||
|  | ||||
|       <AccountInformationAccount | ||||
|         ref={shimmerAccountRef} | ||||
|         account={account} | ||||
|         ownAccount={ownAccount} | ||||
|       /> | ||||
|         <AccountInformationAccount account={account} ownAccount={ownAccount} /> | ||||
|  | ||||
|       {!ownAccount ? ( | ||||
|         <> | ||||
|           {account?.fields && account.fields.length > 0 ? ( | ||||
|             <AccountInformationFields account={account} /> | ||||
|           ) : null} | ||||
|           {account?.note && | ||||
|           account.note.length > 0 && | ||||
|           account.note !== '<p></p>' ? ( | ||||
|             // Empty notes might generate empty p tag | ||||
|             <AccountInformationNotes account={account} /> | ||||
|           ) : null} | ||||
|           <AccountInformationCreated | ||||
|             ref={shimmerCreatedRef} | ||||
|             account={account} | ||||
|           /> | ||||
|         </> | ||||
|       ) : null} | ||||
|         {!ownAccount ? ( | ||||
|           <> | ||||
|             {account?.fields && account.fields.length > 0 ? ( | ||||
|               <AccountInformationFields account={account} /> | ||||
|             ) : null} | ||||
|             {account?.note && | ||||
|             account.note.length > 0 && | ||||
|             account.note !== '<p></p>' ? ( | ||||
|               // Empty notes might generate empty p tag | ||||
|               <AccountInformationNotes account={account} /> | ||||
|             ) : null} | ||||
|             <AccountInformationCreated account={account} /> | ||||
|           </> | ||||
|         ) : null} | ||||
|  | ||||
|       <AccountInformationStats ref={shimmerStatsRef} account={account} /> | ||||
|         <AccountInformationStats account={account} ownAccount={ownAccount} /> | ||||
|       </Placeholder> | ||||
|     </View> | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -1,79 +1,109 @@ | ||||
| import Icon from '@components/Icon' | ||||
| import { getLocalUri } from '@utils/slices/instancesSlice' | ||||
| import { getLocalAccount, getLocalUri } from '@utils/slices/instancesSlice' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
| import { LinearGradient } from 'expo-linear-gradient' | ||||
| import React, { forwardRef } from 'react' | ||||
| import React, { useMemo } from 'react' | ||||
| import { StyleSheet, Text, View } from 'react-native' | ||||
| import ShimmerPlaceholder, { | ||||
|   createShimmerPlaceholder | ||||
| } from 'react-native-shimmer-placeholder' | ||||
| import { useSelector } from 'react-redux' | ||||
| import { PlaceholderLine } from 'rn-placeholder' | ||||
|  | ||||
| export interface Props { | ||||
|   account: Mastodon.Account | undefined | ||||
|   ownAccount?: boolean | ||||
| } | ||||
|  | ||||
| const AccountInformationAccount = forwardRef<ShimmerPlaceholder, Props>( | ||||
|   ({ account, ownAccount }, ref) => { | ||||
|     const { theme } = useTheme() | ||||
|     const localUri = useSelector(getLocalUri) | ||||
| const AccountInformationAccount: React.FC<Props> = ({ | ||||
|   account, | ||||
|   ownAccount | ||||
| }) => { | ||||
|   const { theme } = useTheme() | ||||
|   const localAccount = useSelector(getLocalAccount) | ||||
|   const localUri = useSelector(getLocalUri) | ||||
|  | ||||
|     const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient) | ||||
|   const movedStyle = useMemo( | ||||
|     () => | ||||
|       StyleSheet.create({ | ||||
|         base: { | ||||
|           textDecorationLine: account?.moved ? 'line-through' : undefined | ||||
|         } | ||||
|       }), | ||||
|     [account?.moved] | ||||
|   ) | ||||
|   const movedContent = useMemo(() => { | ||||
|     if (account?.moved) { | ||||
|       return ( | ||||
|         <Text | ||||
|           style={[ | ||||
|             styles.moved, | ||||
|             { color: theme.secondary, ...StyleConstants.FontStyle.M } | ||||
|           ]} | ||||
|           selectable | ||||
|         > | ||||
|           @{account.moved.acct} | ||||
|         </Text> | ||||
|       ) | ||||
|     } | ||||
|   }, [account?.moved]) | ||||
|  | ||||
|   if (account || (ownAccount && localAccount !== undefined)) { | ||||
|     return ( | ||||
|       <ShimmerPlaceholder | ||||
|         ref={ref} | ||||
|         visible={account?.acct !== undefined} | ||||
|         width={StyleConstants.Font.Size.M * 8} | ||||
|         height={StyleConstants.Font.LineHeight.M} | ||||
|         style={{ marginBottom: StyleConstants.Spacing.L }} | ||||
|         shimmerColors={[ | ||||
|           theme.shimmerDefault, | ||||
|           theme.shimmerHighlight, | ||||
|           theme.shimmerDefault | ||||
|         ]} | ||||
|       <View | ||||
|         style={[styles.base, { flexDirection: 'row', alignItems: 'center' }]} | ||||
|       > | ||||
|         <View style={styles.account}> | ||||
|           <Text | ||||
|             style={{ | ||||
|         <Text | ||||
|           style={[ | ||||
|             movedStyle.base, | ||||
|             { | ||||
|               color: theme.secondary, | ||||
|               ...StyleConstants.FontStyle.M | ||||
|             }} | ||||
|             selectable | ||||
|           > | ||||
|             @{account?.acct} | ||||
|             {ownAccount ? `@${localUri}` : null} | ||||
|           </Text> | ||||
|           {account?.locked ? ( | ||||
|             <Icon | ||||
|               name='Lock' | ||||
|               style={styles.type} | ||||
|               color={theme.secondary} | ||||
|               size={StyleConstants.Font.Size.M} | ||||
|             /> | ||||
|           ) : null} | ||||
|           {account?.bot ? ( | ||||
|             <Icon | ||||
|               name='HardDrive' | ||||
|               style={styles.type} | ||||
|               color={theme.secondary} | ||||
|               size={StyleConstants.Font.Size.M} | ||||
|             /> | ||||
|           ) : null} | ||||
|         </View> | ||||
|       </ShimmerPlaceholder> | ||||
|             } | ||||
|           ]} | ||||
|           selectable | ||||
|         > | ||||
|           @{ownAccount ? localAccount?.acct : account?.acct} | ||||
|           {ownAccount ? `@${localUri}` : null} | ||||
|         </Text> | ||||
|         {movedContent} | ||||
|         {account?.locked ? ( | ||||
|           <Icon | ||||
|             name='Lock' | ||||
|             style={styles.type} | ||||
|             color={theme.secondary} | ||||
|             size={StyleConstants.Font.Size.M} | ||||
|           /> | ||||
|         ) : null} | ||||
|         {account?.bot ? ( | ||||
|           <Icon | ||||
|             name='HardDrive' | ||||
|             style={styles.type} | ||||
|             color={theme.secondary} | ||||
|             size={StyleConstants.Font.Size.M} | ||||
|           /> | ||||
|         ) : null} | ||||
|       </View> | ||||
|     ) | ||||
|   } else { | ||||
|     return ( | ||||
|       <PlaceholderLine | ||||
|         width={StyleConstants.Font.Size.M * 2} | ||||
|         height={StyleConstants.Font.LineHeight.M} | ||||
|         color={theme.shimmerDefault} | ||||
|         noMargin | ||||
|         style={styles.base} | ||||
|       /> | ||||
|     ) | ||||
|   } | ||||
| ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   account: { | ||||
|     flexDirection: 'row', | ||||
|     alignItems: 'center' | ||||
|   base: { | ||||
|     borderRadius: 0, | ||||
|     marginBottom: StyleConstants.Spacing.L | ||||
|   }, | ||||
|   type: { marginLeft: StyleConstants.Spacing.S } | ||||
|   type: { marginLeft: StyleConstants.Spacing.S }, | ||||
|   moved: { | ||||
|     marginLeft: StyleConstants.Spacing.S | ||||
|   } | ||||
| }) | ||||
|  | ||||
| export default React.memo( | ||||
|   | ||||
| @@ -4,12 +4,29 @@ import { useNavigation } from '@react-navigation/native' | ||||
| import { useRelationshipQuery } from '@utils/queryHooks/relationship' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import React from 'react' | ||||
| import { useTranslation } from 'react-i18next' | ||||
| import { StyleSheet } from 'react-native' | ||||
|  | ||||
| export interface Props { | ||||
|   account: Mastodon.Account | undefined | ||||
| } | ||||
|  | ||||
| const GoToMoved = ({ account }: { account: Mastodon.Account }) => { | ||||
|   const { t } = useTranslation('sharedAccount') | ||||
|   const navigation = useNavigation() | ||||
|   const query = useRelationshipQuery({ id: account.id }) | ||||
|  | ||||
|   return query.data && !query.data.blocked_by ? ( | ||||
|     <Button | ||||
|       type='text' | ||||
|       content={t('content.moved')} | ||||
|       onPress={() => | ||||
|         navigation.push('Screen-Shared-Account', { account: account.moved }) | ||||
|       } | ||||
|     /> | ||||
|   ) : null | ||||
| } | ||||
|  | ||||
| const Conversation = ({ account }: { account: Mastodon.Account }) => { | ||||
|   const navigation = useNavigation() | ||||
|   const query = useRelationshipQuery({ id: account.id }) | ||||
| @@ -32,10 +49,14 @@ const Conversation = ({ account }: { account: Mastodon.Account }) => { | ||||
|  | ||||
| const AccountInformationActions: React.FC<Props> = ({ account }) => { | ||||
|   return account && account.id ? ( | ||||
|     <> | ||||
|       <Conversation account={account} /> | ||||
|       <RelationshipOutgoing id={account.id} /> | ||||
|     </> | ||||
|     account.moved ? ( | ||||
|       <GoToMoved account={account} /> | ||||
|     ) : ( | ||||
|       <> | ||||
|         <Conversation account={account} /> | ||||
|         <RelationshipOutgoing id={account.id} /> | ||||
|       </> | ||||
|     ) | ||||
|   ) : null | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,27 +1,29 @@ | ||||
| import GracefullyImage from '@components/GracefullyImage' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import React from 'react' | ||||
| import React, { useMemo } from 'react' | ||||
| import { StyleSheet } from 'react-native' | ||||
|  | ||||
| export interface Props { | ||||
|   account: Mastodon.Account | undefined | ||||
| } | ||||
|  | ||||
| const AccountInformationAvatar = React.memo( | ||||
|   ({ account }: Props) => { | ||||
|     return ( | ||||
|       <GracefullyImage | ||||
|         style={styles.base} | ||||
|         uri={{ original: account?.avatar }} | ||||
|         dimension={{ | ||||
|           width: StyleConstants.Avatar.L, | ||||
|           height: StyleConstants.Avatar.L | ||||
|         }} | ||||
|       /> | ||||
|     ) | ||||
|   }, | ||||
|   (_, next) => next.account === undefined | ||||
| ) | ||||
| const AccountInformationAvatar: React.FC<Props> = ({ account }) => { | ||||
|   const dimension = useMemo( | ||||
|     () => ({ | ||||
|       width: StyleConstants.Avatar.L, | ||||
|       height: StyleConstants.Avatar.L | ||||
|     }), | ||||
|     [] | ||||
|   ) | ||||
|  | ||||
|   return ( | ||||
|     <GracefullyImage | ||||
|       style={styles.base} | ||||
|       uri={{ original: account?.avatar }} | ||||
|       dimension={dimension} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   base: { borderRadius: 8, overflow: 'hidden' } | ||||
|   | ||||
| @@ -1,76 +1,74 @@ | ||||
| import Icon from '@components/Icon' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
| import { LinearGradient } from 'expo-linear-gradient' | ||||
| import React, { forwardRef } from 'react' | ||||
| import React from 'react' | ||||
| import { useTranslation } from 'react-i18next' | ||||
| import { StyleSheet, Text, View } from 'react-native' | ||||
| import ShimmerPlaceholder, { | ||||
|   createShimmerPlaceholder | ||||
| } from 'react-native-shimmer-placeholder' | ||||
| import { PlaceholderLine } from 'rn-placeholder' | ||||
|  | ||||
| export interface Props { | ||||
|   account: Mastodon.Account | undefined | ||||
| } | ||||
|  | ||||
| const AccountInformationCreated = forwardRef<ShimmerPlaceholder, Props>( | ||||
|   ({ account }, ref) => { | ||||
|     const { i18n } = useTranslation() | ||||
|     const { theme } = useTheme() | ||||
|     const { t } = useTranslation('sharedAccount') | ||||
|     const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient) | ||||
| const AccountInformationCreated: React.FC<Props> = ({ account }) => { | ||||
|   const { i18n } = useTranslation() | ||||
|   const { theme } = useTheme() | ||||
|   const { t } = useTranslation('sharedAccount') | ||||
|  | ||||
|   if (account) { | ||||
|     return ( | ||||
|       <ShimmerPlaceholder | ||||
|         ref={ref} | ||||
|         visible={account?.created_at !== undefined} | ||||
|         width={StyleConstants.Font.Size.S * 8} | ||||
|         height={StyleConstants.Font.LineHeight.S} | ||||
|         style={{ marginBottom: StyleConstants.Spacing.M }} | ||||
|         shimmerColors={[ | ||||
|           theme.shimmerDefault, | ||||
|           theme.shimmerHighlight, | ||||
|           theme.shimmerDefault | ||||
|         ]} | ||||
|       <View | ||||
|         style={[styles.base, { flexDirection: 'row', alignItems: 'center' }]} | ||||
|       > | ||||
|         <View style={styles.created}> | ||||
|           <Icon | ||||
|             name='Calendar' | ||||
|             size={StyleConstants.Font.Size.S} | ||||
|             color={theme.secondary} | ||||
|             style={styles.icon} | ||||
|           /> | ||||
|           <Text | ||||
|             style={{ | ||||
|               color: theme.secondary, | ||||
|               ...StyleConstants.FontStyle.S | ||||
|             }} | ||||
|           > | ||||
|             {t('content.created_at', { | ||||
|               date: new Date(account?.created_at || '').toLocaleDateString( | ||||
|                 i18n.language, | ||||
|                 { | ||||
|                   year: 'numeric', | ||||
|                   month: 'long', | ||||
|                   day: 'numeric' | ||||
|                 } | ||||
|               ) | ||||
|             })} | ||||
|           </Text> | ||||
|         </View> | ||||
|       </ShimmerPlaceholder> | ||||
|         <Icon | ||||
|           name='Calendar' | ||||
|           size={StyleConstants.Font.Size.S} | ||||
|           color={theme.secondary} | ||||
|           style={styles.icon} | ||||
|         /> | ||||
|         <Text | ||||
|           style={{ | ||||
|             color: theme.secondary, | ||||
|             ...StyleConstants.FontStyle.S | ||||
|           }} | ||||
|         > | ||||
|           {t('content.created_at', { | ||||
|             date: new Date(account?.created_at || '').toLocaleDateString( | ||||
|               i18n.language, | ||||
|               { | ||||
|                 year: 'numeric', | ||||
|                 month: 'long', | ||||
|                 day: 'numeric' | ||||
|               } | ||||
|             ) | ||||
|           })} | ||||
|         </Text> | ||||
|       </View> | ||||
|     ) | ||||
|   } else { | ||||
|     return ( | ||||
|       <PlaceholderLine | ||||
|         width={StyleConstants.Font.Size.S * 3} | ||||
|         height={StyleConstants.Font.LineHeight.S} | ||||
|         color={theme.shimmerDefault} | ||||
|         noMargin | ||||
|         style={styles.base} | ||||
|       /> | ||||
|     ) | ||||
|   } | ||||
| ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   created: { | ||||
|     flexDirection: 'row', | ||||
|     alignItems: 'center' | ||||
|   base: { | ||||
|     borderRadius: 0, | ||||
|     marginBottom: StyleConstants.Spacing.M | ||||
|   }, | ||||
|   icon: { | ||||
|     marginRight: StyleConstants.Spacing.XS | ||||
|   } | ||||
| }) | ||||
|  | ||||
| export default AccountInformationCreated | ||||
| export default React.memo( | ||||
|   AccountInformationCreated, | ||||
|   (_, next) => next.account === undefined | ||||
| ) | ||||
|   | ||||
| @@ -1,52 +1,80 @@ | ||||
| import { ParseEmojis } from '@components/Parse' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import { useTheme } from '@utils/styles/ThemeManager' | ||||
| import { LinearGradient } from 'expo-linear-gradient' | ||||
| import React, { forwardRef } from 'react' | ||||
| import { StyleSheet } from 'react-native' | ||||
| import ShimmerPlaceholder, { | ||||
|   createShimmerPlaceholder | ||||
| } from 'react-native-shimmer-placeholder' | ||||
| import React, { useMemo } from 'react' | ||||
| import { StyleSheet, Text, View } from 'react-native' | ||||
| import { PlaceholderLine } from 'rn-placeholder' | ||||
|  | ||||
| export interface Props { | ||||
|   account: Mastodon.Account | undefined | ||||
| } | ||||
|  | ||||
| const AccountInformationName = forwardRef<ShimmerPlaceholder, Props>( | ||||
|   ({ account }, ref) => { | ||||
|     const { theme } = useTheme() | ||||
|     const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient) | ||||
| const AccountInformationName: React.FC<Props> = ({ account }) => { | ||||
|   const { theme } = useTheme() | ||||
|  | ||||
|     return ( | ||||
|       <ShimmerPlaceholder | ||||
|         ref={ref} | ||||
|         visible={ | ||||
|           account?.display_name !== undefined || account?.username !== undefined | ||||
|   const movedStyle = useMemo( | ||||
|     () => | ||||
|       StyleSheet.create({ | ||||
|         base: { | ||||
|           textDecorationLine: account?.moved ? 'line-through' : undefined | ||||
|         } | ||||
|         width={StyleConstants.Font.Size.L * 8} | ||||
|         height={StyleConstants.Font.LineHeight.L} | ||||
|         style={styles.name} | ||||
|         shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} | ||||
|       > | ||||
|         {account ? ( | ||||
|       }), | ||||
|     [account?.moved] | ||||
|   ) | ||||
|   const movedContent = useMemo(() => { | ||||
|     if (account?.moved) { | ||||
|       return ( | ||||
|         <View style={styles.moved}> | ||||
|           <ParseEmojis | ||||
|             content={account.moved.display_name || account.moved.username} | ||||
|             emojis={account.moved.emojis} | ||||
|             size='L' | ||||
|             fontBold | ||||
|           /> | ||||
|         </View> | ||||
|       ) | ||||
|     } | ||||
|   }, [account?.moved]) | ||||
|  | ||||
|   if (account) { | ||||
|     return ( | ||||
|       <View style={[styles.base, { flexDirection: 'row' }]}> | ||||
|         <Text style={movedStyle.base}> | ||||
|           <ParseEmojis | ||||
|             content={account.display_name || account.username} | ||||
|             emojis={account.emojis} | ||||
|             size='L' | ||||
|             fontBold | ||||
|           /> | ||||
|         ) : null} | ||||
|       </ShimmerPlaceholder> | ||||
|         </Text> | ||||
|         {movedContent} | ||||
|       </View> | ||||
|     ) | ||||
|   } else { | ||||
|     return ( | ||||
|       <PlaceholderLine | ||||
|         width={StyleConstants.Font.Size.L * 2} | ||||
|         height={StyleConstants.Font.LineHeight.L} | ||||
|         color={theme.shimmerDefault} | ||||
|         noMargin | ||||
|         style={styles.base} | ||||
|       /> | ||||
|     ) | ||||
|   } | ||||
| ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   name: { | ||||
|     flexDirection: 'row', | ||||
|   base: { | ||||
|     borderRadius: 0, | ||||
|     marginTop: StyleConstants.Spacing.M, | ||||
|     marginBottom: StyleConstants.Spacing.XS | ||||
|   }, | ||||
|   moved: { | ||||
|     marginLeft: StyleConstants.Spacing.S | ||||
|   } | ||||
| }) | ||||
|  | ||||
| export default AccountInformationName | ||||
| export default React.memo( | ||||
|   AccountInformationName, | ||||
|   (_, next) => next.account === undefined | ||||
| ) | ||||
|   | ||||
| @@ -1,106 +1,93 @@ | ||||
| import { useNavigation } from '@react-navigation/native' | ||||
| import { StyleConstants } from '@root/utils/styles/constants' | ||||
| import { useTheme } from '@root/utils/styles/ThemeManager' | ||||
| import { LinearGradient } from 'expo-linear-gradient' | ||||
| import React, { createRef, forwardRef, useImperativeHandle } from 'react' | ||||
| import React from 'react' | ||||
| import { useTranslation } from 'react-i18next' | ||||
| import { StyleSheet, Text, View } from 'react-native' | ||||
| import ShimmerPlaceholder, { | ||||
|   createShimmerPlaceholder | ||||
| } from 'react-native-shimmer-placeholder' | ||||
| import { PlaceholderLine } from 'rn-placeholder' | ||||
|  | ||||
| export interface Props { | ||||
|   account: Mastodon.Account | undefined | ||||
|   ownAccount?: boolean | ||||
| } | ||||
|  | ||||
| const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => { | ||||
| const AccountInformationStats: React.FC<Props> = ({ account, ownAccount }) => { | ||||
|   const navigation = useNavigation() | ||||
|   const { theme } = useTheme() | ||||
|   const { t } = useTranslation('sharedAccount') | ||||
|   const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient) | ||||
|  | ||||
|   const ref1 = createRef<ShimmerPlaceholder>() | ||||
|   const ref2 = createRef<ShimmerPlaceholder>() | ||||
|   const ref3 = createRef<ShimmerPlaceholder>() | ||||
|   useImperativeHandle(ref, () => ({ | ||||
|     get ref1 () { | ||||
|       return ref1.current | ||||
|     }, | ||||
|     get ref2 () { | ||||
|       return ref2.current | ||||
|     }, | ||||
|     get ref3 () { | ||||
|       return ref3.current | ||||
|     } | ||||
|   })) | ||||
|  | ||||
|   return ( | ||||
|     <View style={styles.stats}> | ||||
|       <ShimmerPlaceholder | ||||
|         ref={ref1} | ||||
|         visible={account !== undefined} | ||||
|         width={StyleConstants.Font.Size.S * 5} | ||||
|         height={StyleConstants.Font.LineHeight.S} | ||||
|         shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} | ||||
|       > | ||||
|         <Text style={[styles.stat, { color: theme.primary }]}> | ||||
|           {t('content.summary.statuses_count', { | ||||
|     <View style={[styles.stats, { flexDirection: 'row' }]}> | ||||
|       {account ? ( | ||||
|         <Text | ||||
|           style={[styles.stat, { color: theme.primary }]} | ||||
|           children={t('content.summary.statuses_count', { | ||||
|             count: account?.statuses_count || 0 | ||||
|           })} | ||||
|         </Text> | ||||
|       </ShimmerPlaceholder> | ||||
|       <ShimmerPlaceholder | ||||
|         ref={ref2} | ||||
|         visible={account !== undefined} | ||||
|         width={StyleConstants.Font.Size.S * 5} | ||||
|         height={StyleConstants.Font.LineHeight.S} | ||||
|         shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} | ||||
|       > | ||||
|           onPress={() => | ||||
|             ownAccount && navigation.push('Screen-Shared-Account', { account }) | ||||
|           } | ||||
|         /> | ||||
|       ) : ( | ||||
|         <PlaceholderLine | ||||
|           width={StyleConstants.Font.Size.S * 1.25} | ||||
|           height={StyleConstants.Font.LineHeight.S} | ||||
|           color={theme.shimmerDefault} | ||||
|           noMargin | ||||
|           style={{ borderRadius: 0 }} | ||||
|         /> | ||||
|       )} | ||||
|       {account ? ( | ||||
|         <Text | ||||
|           style={[styles.stat, { color: theme.primary, textAlign: 'right' }]} | ||||
|           children={t('content.summary.following_count', { | ||||
|             count: account?.following_count || 0 | ||||
|           })} | ||||
|           onPress={() => | ||||
|             account && | ||||
|             navigation.push('Screen-Shared-Relationships', { | ||||
|               account, | ||||
|               initialType: 'following' | ||||
|             }) | ||||
|           } | ||||
|         > | ||||
|           {t('content.summary.following_count', { | ||||
|             count: account?.following_count || 0 | ||||
|           })} | ||||
|         </Text> | ||||
|       </ShimmerPlaceholder> | ||||
|       <ShimmerPlaceholder | ||||
|         ref={ref3} | ||||
|         visible={account !== undefined} | ||||
|         width={StyleConstants.Font.Size.S * 5} | ||||
|         height={StyleConstants.Font.LineHeight.S} | ||||
|         shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} | ||||
|       > | ||||
|         /> | ||||
|       ) : ( | ||||
|         <PlaceholderLine | ||||
|           width={StyleConstants.Font.Size.S * 1.25} | ||||
|           height={StyleConstants.Font.LineHeight.S} | ||||
|           color={theme.shimmerDefault} | ||||
|           noMargin | ||||
|           style={{ borderRadius: 0 }} | ||||
|         /> | ||||
|       )} | ||||
|       {account ? ( | ||||
|         <Text | ||||
|           style={[styles.stat, { color: theme.primary, textAlign: 'center' }]} | ||||
|           children={t('content.summary.followers_count', { | ||||
|             count: account?.followers_count || 0 | ||||
|           })} | ||||
|           onPress={() => | ||||
|             account && | ||||
|             navigation.push('Screen-Shared-Relationships', { | ||||
|               account, | ||||
|               initialType: 'followers' | ||||
|             }) | ||||
|           } | ||||
|         > | ||||
|           {t('content.summary.followers_count', { | ||||
|             count: account?.followers_count || 0 | ||||
|           })} | ||||
|         </Text> | ||||
|       </ShimmerPlaceholder> | ||||
|         /> | ||||
|       ) : ( | ||||
|         <PlaceholderLine | ||||
|           width={StyleConstants.Font.Size.S * 1.25} | ||||
|           height={StyleConstants.Font.LineHeight.S} | ||||
|           color={theme.shimmerDefault} | ||||
|           noMargin | ||||
|           style={{ borderRadius: 0 }} | ||||
|         /> | ||||
|       )} | ||||
|     </View> | ||||
|   ) | ||||
| }) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   stats: { | ||||
|     flex: 1, | ||||
|     flexDirection: 'row', | ||||
|     justifyContent: 'space-between' | ||||
|   }, | ||||
|   stat: { | ||||
|   | ||||
| @@ -2,49 +2,14 @@ import { HeaderLeft, HeaderRight } from '@components/Header' | ||||
| import { StyleConstants } from '@utils/styles/constants' | ||||
| import { findIndex } from 'lodash' | ||||
| import React, { useCallback, useState } from 'react' | ||||
| import { | ||||
|   Image, | ||||
|   Platform, | ||||
|   Share, | ||||
|   StatusBar, | ||||
|   StyleSheet, | ||||
|   Text | ||||
| } from 'react-native' | ||||
| import { Image, Platform, Share, StatusBar, StyleSheet, Text } from 'react-native' | ||||
| import ImageViewer from 'react-native-image-zoom-viewer' | ||||
| import { IImageInfo } from 'react-native-image-zoom-viewer/built/image-viewer.type' | ||||
| import { useSafeAreaInsets } from 'react-native-safe-area-context' | ||||
| import { createNativeStackNavigator } from 'react-native-screens/native-stack' | ||||
| import { SharedImagesViewerProp } from './sharedScreens' | ||||
|  | ||||
| const Stack = createNativeStackNavigator() | ||||
|  | ||||
| const TheImage = ({ | ||||
|   style, | ||||
|   source, | ||||
|   imageUrls | ||||
| }: { | ||||
|   style: any | ||||
|   source: { uri: string } | ||||
|   imageUrls: (IImageInfo & { | ||||
|     preview_url: Mastodon.AttachmentImage['preview_url'] | ||||
|     remote_url: Mastodon.AttachmentImage['remote_url'] | ||||
|     imageIndex: number | ||||
|   })[] | ||||
| }) => { | ||||
|   const [imageVisible, setImageVisible] = useState(false) | ||||
|   Image.getSize(source.uri, () => setImageVisible(true)) | ||||
|   return ( | ||||
|     <Image | ||||
|       style={style} | ||||
|       source={{ | ||||
|         uri: imageVisible | ||||
|           ? source.uri | ||||
|           : imageUrls[findIndex(imageUrls, ['url', source.uri])].preview_url | ||||
|       }} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| const ScreenSharedImagesViewer: React.FC<SharedImagesViewerProp> = ({ | ||||
|   route: { | ||||
|     params: { imageUrls, imageIndex } | ||||
| @@ -64,15 +29,17 @@ const ScreenSharedImagesViewer: React.FC<SharedImagesViewerProp> = ({ | ||||
|           index={initialIndex} | ||||
|           imageUrls={imageUrls} | ||||
|           pageAnimateTime={250} | ||||
|           enableSwipeDown={true} | ||||
|           useNativeDriver={true} | ||||
|           enableSwipeDown | ||||
|           useNativeDriver | ||||
|           swipeDownThreshold={100} | ||||
|           renderIndicator={() => <></>} | ||||
|           saveToLocalByLongPress={false} | ||||
|           onSwipeDown={() => navigation.goBack()} | ||||
|           style={{ flex: 1, marginBottom: 44 + safeAreaInsets.bottom }} | ||||
|           onChange={index => index !== undefined && setCurrentIndex(index)} | ||||
|           renderImage={props => <TheImage {...props} imageUrls={imageUrls} />} | ||||
|           renderImage={prop => { | ||||
|             return <Image {...prop} resizeMode={'contain'} /> | ||||
|           }} | ||||
|         /> | ||||
|       </> | ||||
|     ), | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| import client from '@api/client' | ||||
| import NetInfo from '@react-native-community/netinfo' | ||||
| import { store } from '@root/store' | ||||
| import { localRemoveInstance } from '@utils/slices/instancesSlice' | ||||
| import { | ||||
|   localRemoveInstance, | ||||
|   localUpdateAccount | ||||
| } from '@utils/slices/instancesSlice' | ||||
| import log from './log' | ||||
|  | ||||
| const netInfo = async (): Promise<{ | ||||
| @@ -31,6 +34,12 @@ const netInfo = async (): Promise<{ | ||||
|             store.dispatch(localRemoveInstance(activeIndex)) | ||||
|             return Promise.resolve({ connected: true, corruputed: '' }) | ||||
|           } else { | ||||
|             store.dispatch( | ||||
|               localUpdateAccount({ | ||||
|                 acct: res.acct, | ||||
|                 avatarStatic: res.avatar_static | ||||
|               }) | ||||
|             ) | ||||
|             return Promise.resolve({ connected: true }) | ||||
|           } | ||||
|         }) | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import log from './log' | ||||
|  | ||||
| const sentry = () => { | ||||
|   log('log', 'Sentry', 'initializing') | ||||
|   return Sentry.init({ | ||||
|   Sentry.init({ | ||||
|     dsn: Constants.manifest.extra.sentryDSN, | ||||
|     enableInExpoDevelopment: false, | ||||
|     debug: __DEV__ | ||||
|   | ||||
| @@ -242,8 +242,16 @@ export type MutationVarsTimelineUpdateStatusProperty = { | ||||
|   reblog?: boolean | ||||
|   payload: | ||||
|     | { | ||||
|         property: 'bookmarked' | 'favourited' | 'muted' | 'pinned' | 'reblogged' | ||||
|         property: 'bookmarked' | 'muted' | 'pinned' | ||||
|         currentValue: boolean | ||||
|         propertyCount: undefined | ||||
|         countValue: undefined | ||||
|       } | ||||
|     | { | ||||
|         property: 'favourited' | 'reblogged' | ||||
|         currentValue: boolean | ||||
|         propertyCount: 'favourites_count' | 'reblogs_count' | ||||
|         countValue: number | ||||
|       } | ||||
|     | { | ||||
|         property: 'poll' | ||||
|   | ||||
| @@ -19,6 +19,13 @@ const updateConversation = ({ | ||||
|           typeof payload.currentValue === 'boolean' | ||||
|             ? !payload.currentValue | ||||
|             : true | ||||
|         if (payload.propertyCount) { | ||||
|           if (typeof payload.currentValue === 'boolean' && payload.currentValue) { | ||||
|             item.last_status[payload.propertyCount] = payload.countValue - 1 | ||||
|           } else { | ||||
|             item.last_status[payload.propertyCount] = payload.countValue + 1 | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       return item | ||||
|   } | ||||
|   | ||||
| @@ -16,6 +16,13 @@ const updateNotification = ({ | ||||
|           typeof payload.currentValue === 'boolean' | ||||
|             ? !payload.currentValue | ||||
|             : true | ||||
|         if (payload.propertyCount) { | ||||
|           if (typeof payload.currentValue === 'boolean' && payload.currentValue) { | ||||
|             item.status[payload.propertyCount] = payload.countValue - 1 | ||||
|           } else { | ||||
|             item.status[payload.propertyCount] = payload.countValue + 1 | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       return item | ||||
|   } | ||||
|   | ||||
| @@ -23,11 +23,25 @@ const updateStatus = ({ | ||||
|           typeof payload.currentValue === 'boolean' | ||||
|             ? !payload.currentValue | ||||
|             : true | ||||
|         if (payload.propertyCount) { | ||||
|           if (typeof payload.currentValue === 'boolean' && payload.currentValue) { | ||||
|             item.reblog![payload.propertyCount] = payload.countValue - 1 | ||||
|           } else { | ||||
|             item.reblog![payload.propertyCount] = payload.countValue + 1 | ||||
|           } | ||||
|         } | ||||
|       } else { | ||||
|         item[payload.property] = | ||||
|           typeof payload.currentValue === 'boolean' | ||||
|             ? !payload.currentValue | ||||
|             : true | ||||
|         if (payload.propertyCount) { | ||||
|           if (typeof payload.currentValue === 'boolean' && payload.currentValue) { | ||||
|             item[payload.propertyCount] = payload.countValue - 1 | ||||
|           } else { | ||||
|             item[payload.propertyCount] = payload.countValue + 1 | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       return item | ||||
|   } | ||||
|   | ||||
| @@ -15,6 +15,8 @@ export type InstanceLocal = { | ||||
|   uri: Mastodon.Instance['uri'] | ||||
|   account: { | ||||
|     id: Mastodon.Account['id'] | ||||
|     acct: Mastodon.Account['acct'] | ||||
|     avatarStatic: Mastodon.Account['avatar_static'] | ||||
|     preferences: Mastodon.Preferences | ||||
|   } | ||||
|   notification: { | ||||
| @@ -64,7 +66,7 @@ export const localAddInstance = createAsyncThunk( | ||||
|     const instanceLocal: InstancesState['local'] = store.getState().instances | ||||
|       .local | ||||
|  | ||||
|     const { id } = await client<Mastodon.Account>({ | ||||
|     const { id, acct, avatar_static } = await client<Mastodon.Account>({ | ||||
|       method: 'get', | ||||
|       instance: 'remote', | ||||
|       instanceDomain: url, | ||||
| @@ -108,6 +110,8 @@ export const localAddInstance = createAsyncThunk( | ||||
|         uri, | ||||
|         account: { | ||||
|           id, | ||||
|           acct, | ||||
|           avatarStatic: avatar_static, | ||||
|           preferences | ||||
|         }, | ||||
|         notification: { | ||||
| @@ -182,6 +186,19 @@ const instancesSlice = createSlice({ | ||||
|         throw new Error('Set index cannot be found') | ||||
|       } | ||||
|     }, | ||||
|     localUpdateAccount: ( | ||||
|       state, | ||||
|       action: PayloadAction< | ||||
|         Pick<InstanceLocal['account'], 'acct' & 'avatarStatic'> | ||||
|       > | ||||
|     ) => { | ||||
|       if (state.local.activeIndex !== null) { | ||||
|         state.local.instances[state.local.activeIndex].account = { | ||||
|           ...state.local.instances[state.local.activeIndex].account, | ||||
|           ...action.payload | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     localUpdateNotification: ( | ||||
|       state, | ||||
|       action: PayloadAction<Partial<InstanceLocal['notification']>> | ||||
| @@ -273,6 +290,7 @@ export const getRemoteUrl = ({ instances: { remote } }: RootState) => remote.url | ||||
|  | ||||
| export const { | ||||
|   localUpdateActiveIndex, | ||||
|   localUpdateAccount, | ||||
|   localUpdateNotification, | ||||
|   remoteUpdate | ||||
| } = instancesSlice.actions | ||||
|   | ||||
| @@ -9212,6 +9212,11 @@ rimraf@~2.2.6: | ||||
|   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" | ||||
|   integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= | ||||
|  | ||||
| rn-placeholder@^3.0.3: | ||||
|   version "3.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/rn-placeholder/-/rn-placeholder-3.0.3.tgz#98f635b263ee003af2a984eed32d86ade308df35" | ||||
|   integrity sha512-EmVeLT8zDcTPilQZ2OHO/IiYUy2gApKGgbshDZBX0C4qxsn0cFATwgwOwyz8O7Vwg1Hul97Ci95hu7d6Js6XMQ== | ||||
|  | ||||
| rsvp@^4.8.4: | ||||
|   version "4.8.5" | ||||
|   resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user