mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Merge branch 'main' into release
This commit is contained in:
@ -53,26 +53,17 @@ const GracefullyImage = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { reduceMotionEnabled } = useAccessibility()
|
const { reduceMotionEnabled } = useAccessibility()
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const [originalFailed, setOriginalFailed] = useState(false)
|
|
||||||
const [imageLoaded, setImageLoaded] = useState(false)
|
const [imageLoaded, setImageLoaded] = useState(false)
|
||||||
|
|
||||||
const source = originalFailed
|
const source = {
|
||||||
? { uri: uri.remote || undefined }
|
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
||||||
: {
|
}
|
||||||
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
|
||||||
}
|
|
||||||
|
|
||||||
const onLoad = () => {
|
const onLoad = () => {
|
||||||
setImageLoaded(true)
|
setImageLoaded(true)
|
||||||
if (setImageDimensions && source.uri) {
|
if (setImageDimensions && source.uri) {
|
||||||
Image.getSize(source.uri, (width, height) => setImageDimensions({ width, height }))
|
Image.getSize(source.uri, (width, height) => setImageDimensions({ width, height }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const onError = () => {
|
|
||||||
if (!originalFailed) {
|
|
||||||
setOriginalFailed(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const blurhashView = useMemo(() => {
|
const blurhashView = useMemo(() => {
|
||||||
if (hidden || !imageLoaded) {
|
if (hidden || !imageLoaded) {
|
||||||
@ -101,10 +92,11 @@ const GracefullyImage = ({
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<FastImage
|
<FastImage
|
||||||
source={source}
|
source={{
|
||||||
|
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
||||||
|
}}
|
||||||
style={[{ flex: 1 }, imageStyle]}
|
style={[{ flex: 1 }, imageStyle]}
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
onError={onError}
|
|
||||||
/>
|
/>
|
||||||
{blurhashView}
|
{blurhashView}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
@ -13,6 +13,8 @@ import { useQueryClient } from '@tanstack/react-query'
|
|||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
import { useRoute } from '@react-navigation/native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
id: Mastodon.Account['id']
|
id: Mastodon.Account['id']
|
||||||
@ -122,9 +124,12 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { name } = useRoute()
|
||||||
|
const isPageNotifications = name === 'Tab-Notifications-Root'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||||
{canFollowNotify && query.data?.following ? (
|
{!isPageNotifications && canFollowNotify && query.data?.following ? (
|
||||||
<Button
|
<Button
|
||||||
type='icon'
|
type='icon'
|
||||||
content={query.data.notifying ? 'BellOff' : 'Bell'}
|
content={query.data.notifying ? 'BellOff' : 'Bell'}
|
||||||
@ -151,7 +156,7 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
|
|||||||
loading={query.isLoading || mutation.isLoading}
|
loading={query.isLoading || mutation.isLoading}
|
||||||
disabled={query.isError || query.data?.blocked_by}
|
disabled={query.isError || query.data?.blocked_by}
|
||||||
/>
|
/>
|
||||||
</>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
{
|
{
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"OK": "",
|
"OK": "ОК",
|
||||||
"apply": "",
|
"apply": "Применить",
|
||||||
"cancel": "",
|
"cancel": "Отменить",
|
||||||
"discard": "",
|
"discard": "Отклонить",
|
||||||
"continue": "",
|
"continue": "Продолжить",
|
||||||
"create": "",
|
"create": "Создать",
|
||||||
"delete": "",
|
"delete": "Удалить",
|
||||||
"done": "",
|
"done": "Готово",
|
||||||
"confirm": ""
|
"confirm": "Подтвердить"
|
||||||
},
|
},
|
||||||
"customEmoji": {
|
"customEmoji": {
|
||||||
"accessibilityLabel": ""
|
"accessibilityLabel": "Пользовательские эмодзи {{emoji}}"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"success": {
|
"success": {
|
||||||
"message": ""
|
"message": "Успешно {{function}}"
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"message": ""
|
"message": ""
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": ""
|
"message": "Ошибка {{function}}, попробуйте ещё раз"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"separator": "",
|
"separator": ", ",
|
||||||
"discard": {
|
"discard": {
|
||||||
"title": "",
|
"title": "Изменения не сохранены",
|
||||||
"message": ""
|
"message": "Ваши изменения не были сохранены. Вы хотите отменить изменения?"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,13 +15,13 @@
|
|||||||
"action_false": "Chặn người này",
|
"action_false": "Chặn người này",
|
||||||
"action_true": "Bỏ chặn người dùng",
|
"action_true": "Bỏ chặn người dùng",
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": ""
|
"title": "Bạn có chắc muốn chặn {{username}}?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"action": "Báo cáo và chặn",
|
"action": "Báo cáo và chặn",
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": ""
|
"title": "Bạn có chắc muốn báo cáo {{username}}?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
"action_false": "封鎖使用者",
|
"action_false": "封鎖使用者",
|
||||||
"action_true": "解除封鎖使用者",
|
"action_true": "解除封鎖使用者",
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": ""
|
"title": "確認封鎖使用者 @{{username}}?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"action": "檢舉並封鎖使用者",
|
"action": "檢舉並封鎖使用者",
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": ""
|
"title": "確認檢舉並封鎖使用者 @{{username}}?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import navigationRef from '@helpers/navigationRef'
|
import navigationRef from '@helpers/navigationRef'
|
||||||
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Dimensions, Image, Pressable } from 'react-native'
|
import { Dimensions, Image } from 'react-native'
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
@ -12,13 +11,14 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AccountHeader: React.FC<Props> = ({ account }) => {
|
const AccountHeader: React.FC<Props> = ({ account }) => {
|
||||||
const { colors } = useTheme()
|
|
||||||
const topInset = useSafeAreaInsets().top
|
const topInset = useSafeAreaInsets().top
|
||||||
|
|
||||||
useSelector(getInstanceActive)
|
useSelector(getInstanceActive)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<GracefullyImage
|
||||||
|
uri={{ original: account?.header, static: account?.header_static }}
|
||||||
|
style={{ height: Dimensions.get('window').width / 3 + topInset }}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (account) {
|
if (account) {
|
||||||
Image.getSize(account.header, (width, height) =>
|
Image.getSize(account.header, (width, height) =>
|
||||||
@ -30,15 +30,7 @@ const AccountHeader: React.FC<Props> = ({ account }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<GracefullyImage
|
|
||||||
uri={{ original: account?.header, static: account?.header_static }}
|
|
||||||
style={{
|
|
||||||
height: Dimensions.get('window').width / 3 + topInset,
|
|
||||||
backgroundColor: colors.disabled
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Pressable>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,51 +17,39 @@ export interface Props {
|
|||||||
account: Mastodon.Account | undefined
|
account: Mastodon.Account | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const AccountInformation = React.memo(
|
const AccountInformation: React.FC<Props> = ({ account }) => {
|
||||||
({ account }: Props) => {
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
const { name } = useRoute()
|
const { name } = useRoute()
|
||||||
const myInfo = name !== 'Tab-Shared-Account'
|
const myInfo = name !== 'Tab-Shared-Account'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
<Placeholder
|
<Placeholder
|
||||||
Animation={props => (
|
Animation={props => (
|
||||||
<Fade {...props} style={{ backgroundColor: colors.shimmerHighlight }} />
|
<Fade {...props} style={{ backgroundColor: colors.shimmerHighlight }} />
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<View style={styles.avatarAndActions}>
|
<View style={styles.avatarAndActions}>
|
||||||
<AccountInformationAvatar account={account} myInfo={myInfo} />
|
<AccountInformationAvatar account={account} myInfo={myInfo} />
|
||||||
<AccountInformationActions account={account} myInfo={myInfo} />
|
<AccountInformationActions account={account} myInfo={myInfo} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<AccountInformationName account={account} />
|
<AccountInformationName account={account} />
|
||||||
|
|
||||||
<AccountInformationAccount account={account} />
|
<AccountInformationAccount account={account} />
|
||||||
|
|
||||||
<AccountInformationFields account={account} myInfo={myInfo} />
|
<AccountInformationFields account={account} myInfo={myInfo} />
|
||||||
|
|
||||||
<AccountInformationNote account={account} myInfo={myInfo} />
|
<AccountInformationNote account={account} myInfo={myInfo} />
|
||||||
|
|
||||||
<AccountInformationCreated account={account} hidden={myInfo} />
|
<AccountInformationCreated account={account} hidden={myInfo} />
|
||||||
|
|
||||||
<AccountInformationStats account={account} myInfo={myInfo} />
|
<AccountInformationStats account={account} myInfo={myInfo} />
|
||||||
</Placeholder>
|
</Placeholder>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
(prev, next) => {
|
|
||||||
let skipUpdate = true
|
|
||||||
if (prev.account?.id !== next.account?.id) {
|
|
||||||
skipUpdate = false
|
|
||||||
}
|
|
||||||
if (prev.account?.acct === next.account?.acct) {
|
|
||||||
skipUpdate = false
|
|
||||||
}
|
|
||||||
return skipUpdate
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
base: {
|
base: {
|
||||||
|
@ -6,7 +6,6 @@ import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
|||||||
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Pressable } from 'react-native'
|
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -18,7 +17,15 @@ const AccountInformationAvatar: React.FC<Props> = ({ account, myInfo }) => {
|
|||||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
useSelector(getInstanceActive)
|
useSelector(getInstanceActive)
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<GracefullyImage
|
||||||
|
key={account?.avatar}
|
||||||
|
style={{
|
||||||
|
borderRadius: 8,
|
||||||
|
overflow: 'hidden',
|
||||||
|
width: StyleConstants.Avatar.L,
|
||||||
|
height: StyleConstants.Avatar.L
|
||||||
|
}}
|
||||||
|
uri={{ original: account?.avatar, static: account?.avatar_static }}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (account) {
|
if (account) {
|
||||||
if (myInfo) {
|
if (myInfo) {
|
||||||
@ -33,19 +40,7 @@ const AccountInformationAvatar: React.FC<Props> = ({ account, myInfo }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
/>
|
||||||
borderRadius: 8,
|
|
||||||
overflow: 'hidden',
|
|
||||||
width: StyleConstants.Avatar.L,
|
|
||||||
height: StyleConstants.Avatar.L
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GracefullyImage
|
|
||||||
key={account?.avatar}
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
uri={{ original: account?.avatar, static: account?.avatar_static }}
|
|
||||||
/>
|
|
||||||
</Pressable>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ const addInstance = createAsyncThunk(
|
|||||||
appData,
|
appData,
|
||||||
url: domain,
|
url: domain,
|
||||||
token,
|
token,
|
||||||
uri: instance.uri,
|
uri: instance.uri.replace(/^https?:\/\//, ''), // Pleroma includes schema
|
||||||
urls: instance.urls,
|
urls: instance.urls,
|
||||||
account: {
|
account: {
|
||||||
id,
|
id,
|
||||||
|
@ -280,7 +280,7 @@ export const getInstanceUrl = ({ instances: { instances } }: RootState) =>
|
|||||||
instances[findInstanceActive(instances)]?.url
|
instances[findInstanceActive(instances)]?.url
|
||||||
|
|
||||||
export const getInstanceUri = ({ instances: { instances } }: RootState) =>
|
export const getInstanceUri = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.uri.replace(/^https?:\/\//, '') // Pleroma has schema
|
instances[findInstanceActive(instances)]?.uri
|
||||||
|
|
||||||
export const getInstanceUrls = ({ instances: { instances } }: RootState) =>
|
export const getInstanceUrls = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.urls
|
instances[findInstanceActive(instances)]?.urls
|
||||||
|
Reference in New Issue
Block a user