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