mirror of https://github.com/tooot-app/app
Update translations
This commit is contained in:
parent
5c4f7ce8c7
commit
5a248716bf
|
@ -85,8 +85,7 @@ const client = async <T = unknown>({
|
|||
ctx.bold(' API '),
|
||||
ctx.bold('response'),
|
||||
error.response.status,
|
||||
error.response.data.error,
|
||||
error.request
|
||||
error.response.data.error
|
||||
)
|
||||
return Promise.reject(error.response)
|
||||
} else if (error.request) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useRef } from 'react'
|
||||
import React from 'react'
|
||||
import { Dimensions, Modal, StyleSheet, View } from 'react-native'
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
} from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import * as Linking from 'expo-linking'
|
||||
import { debounce } from 'lodash'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
@ -36,7 +37,7 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
const navigation = useNavigation()
|
||||
const dispatch = useDispatch()
|
||||
const queryClient = useQueryClient()
|
||||
const { t } = useTranslation('meRoot')
|
||||
const { t } = useTranslation('componentInstance')
|
||||
const { theme } = useTheme()
|
||||
const [instanceDomain, setInstanceDomain] = useState<string | undefined>()
|
||||
const [appData, setApplicationData] = useState<InstanceLocal['appData']>()
|
||||
|
@ -92,13 +93,14 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
.length
|
||||
) {
|
||||
Alert.alert(
|
||||
'域名已存在',
|
||||
'可以登录同个域名的另外一个账户,现有账户🈚️用',
|
||||
t('update.local.alert.title'),
|
||||
t('update.local.alert.message'),
|
||||
[
|
||||
{ text: '取消', style: 'cancel' },
|
||||
{ text: t('update.local.alert.buttons.cancel'), style: 'cancel' },
|
||||
{
|
||||
text: '继续',
|
||||
text: t('update.local.alert.buttons.continue'),
|
||||
onPress: () => {
|
||||
setApplicationData(undefined)
|
||||
applicationQuery.refetch()
|
||||
}
|
||||
}
|
||||
|
@ -116,8 +118,8 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
]
|
||||
dispatch(remoteUpdate(instanceDomain))
|
||||
queryClient.resetQueries(queryKey)
|
||||
toast({ type: 'success', message: '重置成功' })
|
||||
navigation.navigate('Screen-Public', { screen: 'Screen-Public-Root' })
|
||||
toast({ type: 'success', message: t('update.remote.succeed') })
|
||||
navigation.goBack()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -143,9 +145,9 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
const buttonContent = useMemo(() => {
|
||||
switch (type) {
|
||||
case 'local':
|
||||
return t('content.login.button')
|
||||
return t('server.button.local')
|
||||
case 'remote':
|
||||
return '登记'
|
||||
return t('server.button.remote')
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
@ -176,7 +178,7 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
keyboardType='url'
|
||||
textContentType='URL'
|
||||
onSubmitEditing={onSubmitEditing}
|
||||
placeholder={t('content.login.server.placeholder')}
|
||||
placeholder={t('server.textInput.placeholder')}
|
||||
placeholderTextColor={theme.secondary}
|
||||
returnKeyType='go'
|
||||
/>
|
||||
|
@ -191,21 +193,21 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
<View>
|
||||
<InstanceInfo
|
||||
visible={instanceQuery.data?.title !== undefined}
|
||||
header='实例名称'
|
||||
header={t('server.information.name')}
|
||||
content={instanceQuery.data?.title || undefined}
|
||||
potentialWidth={10}
|
||||
/>
|
||||
<InstanceInfo
|
||||
visible={instanceQuery.data?.short_description !== undefined}
|
||||
header='实例介绍'
|
||||
header={t('server.information.description.heading')}
|
||||
content={instanceQuery.data?.short_description || undefined}
|
||||
potentialLines={5}
|
||||
/>
|
||||
<View style={styles.instanceStats}>
|
||||
<InstanceInfo
|
||||
style={{ alignItems: 'flex-start' }}
|
||||
visible={instanceQuery.data?.stats?.user_count !== null}
|
||||
header='用户总数'
|
||||
visible={instanceQuery.data?.stats?.user_count === null}
|
||||
header={t('server.information.accounts')}
|
||||
content={
|
||||
instanceQuery.data?.stats?.user_count?.toString() || undefined
|
||||
}
|
||||
|
@ -213,8 +215,8 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
/>
|
||||
<InstanceInfo
|
||||
style={{ alignItems: 'center' }}
|
||||
visible={instanceQuery.data?.stats?.status_count !== null}
|
||||
header='嘟嘟总数'
|
||||
visible={instanceQuery.data?.stats?.status_count === null}
|
||||
header={t('server.information.statuses')}
|
||||
content={
|
||||
instanceQuery.data?.stats?.status_count?.toString() || undefined
|
||||
}
|
||||
|
@ -222,22 +224,31 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
/>
|
||||
<InstanceInfo
|
||||
style={{ alignItems: 'flex-end' }}
|
||||
visible={instanceQuery.data?.stats?.domain_count !== null}
|
||||
header='嘟嘟总数'
|
||||
visible={instanceQuery.data?.stats?.domain_count === null}
|
||||
header={t('server.information.domains')}
|
||||
content={
|
||||
instanceQuery.data?.stats?.domain_count?.toString() || undefined
|
||||
}
|
||||
potentialWidth={4}
|
||||
/>
|
||||
</View>
|
||||
<Text style={[styles.disclaimer, { color: theme.secondary }]}>
|
||||
<View style={styles.disclaimer}>
|
||||
<Icon
|
||||
name='Lock'
|
||||
size={StyleConstants.Font.Size.M}
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={theme.secondary}
|
||||
/>{' '}
|
||||
本站不留存任何信息
|
||||
</Text>
|
||||
style={styles.disclaimerIcon}
|
||||
/>
|
||||
<Text
|
||||
style={[styles.disclaimerText, { color: theme.secondary }]}
|
||||
onPress={() => Linking.openURL('https://tooot.app/privacy')}
|
||||
>
|
||||
{t('server.disclaimer')}
|
||||
<Text style={{ color: theme.blue }}>
|
||||
https://tooot.app/privacy
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
@ -275,9 +286,18 @@ const styles = StyleSheet.create({
|
|||
flexDirection: 'row'
|
||||
},
|
||||
disclaimer: {
|
||||
...StyleConstants.FontStyle.S,
|
||||
flexDirection: 'row',
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginVertical: StyleConstants.Spacing.M
|
||||
},
|
||||
disclaimerIcon: {
|
||||
marginTop:
|
||||
(StyleConstants.Font.LineHeight.S - StyleConstants.Font.Size.S) / 2,
|
||||
marginRight: StyleConstants.Spacing.XS
|
||||
},
|
||||
disclaimerText: {
|
||||
flex: 1,
|
||||
...StyleConstants.FontStyle.S
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ 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'
|
||||
|
||||
|
@ -24,6 +25,7 @@ const InstanceInfo = React.memo(
|
|||
potentialWidth,
|
||||
potentialLines = 1
|
||||
}: Props) => {
|
||||
const { t } = useTranslation('componentInstance')
|
||||
const { theme } = useTheme()
|
||||
const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient)
|
||||
|
||||
|
@ -40,14 +42,18 @@ const InstanceInfo = React.memo(
|
|||
StyleConstants.Spacing.Global.PagePadding * 4
|
||||
}
|
||||
height={StyleConstants.Font.LineHeight.M * potentialLines}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
shimmerColors={[
|
||||
theme.shimmerDefault,
|
||||
theme.shimmerHighlight,
|
||||
theme.shimmerDefault
|
||||
]}
|
||||
>
|
||||
{content ? (
|
||||
<ParseHTML
|
||||
content={content}
|
||||
size={'M'}
|
||||
numberOfLines={5}
|
||||
expandHint='介绍'
|
||||
expandHint={t('server.information.description.expandHint')}
|
||||
/>
|
||||
) : null}
|
||||
</ShimmerPlaceholder>
|
||||
|
|
|
@ -19,7 +19,7 @@ export interface Props {
|
|||
switchDisabled?: boolean
|
||||
switchOnValueChange?: () => void
|
||||
|
||||
iconBack?: 'ChevronRight' | 'Check'
|
||||
iconBack?: 'ChevronRight' | 'ExternalLink'
|
||||
iconBackColor?: ColorDefinitions
|
||||
|
||||
loading?: boolean
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { StyleSheet, Text } from 'react-native'
|
||||
import { Image } from 'react-native-expo-image-cache'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
|
||||
const regexEmoji = new RegExp(/(:[A-Za-z0-9_]+:)/)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import layoutAnimation from '@utils/styles/layoutAnimation'
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { LinearGradient } from 'expo-linear-gradient'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import HTMLView from 'react-native-htmlview'
|
||||
|
||||
|
@ -191,6 +192,7 @@ const ParseHTML: React.FC<Props> = ({
|
|||
}, [])
|
||||
const rootComponent = useCallback(
|
||||
({ children }) => {
|
||||
const { t } = useTranslation('componentParse')
|
||||
const lineHeight = StyleConstants.Font.LineHeight[size]
|
||||
|
||||
const [expandAllow, setExpandAllow] = useState(false)
|
||||
|
@ -249,7 +251,9 @@ const ParseHTML: React.FC<Props> = ({
|
|||
color: theme.primary
|
||||
}}
|
||||
>
|
||||
{`${expanded ? '折叠' : '展开'}${expandHint}`}
|
||||
{expanded
|
||||
? t('HTML.expanded.true', { hint: expandHint })
|
||||
: t('HTML.expanded.false', { hint: expandHint })}
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</Pressable>
|
||||
|
|
|
@ -16,7 +16,7 @@ export interface Props {
|
|||
|
||||
const RelationshipOutgoing = React.memo(
|
||||
({ id }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation('componentRelationship')
|
||||
|
||||
const query = useRelationshipQuery({ id })
|
||||
|
||||
|
@ -30,12 +30,12 @@ const RelationshipOutgoing = React.memo(
|
|||
[res]
|
||||
)
|
||||
},
|
||||
onError: (err: any, { type }) => {
|
||||
onError: (err: any, { payload: { action } }) => {
|
||||
haptics('Error')
|
||||
toast({
|
||||
type: 'error',
|
||||
message: t('common:toastMessage.error.message', {
|
||||
function: t(`relationship:${type}.function`)
|
||||
function: t(`button.${action}.function`)
|
||||
}),
|
||||
...(err.status &&
|
||||
typeof err.status === 'number' &&
|
||||
|
@ -52,15 +52,15 @@ const RelationshipOutgoing = React.memo(
|
|||
let onPress: () => void
|
||||
|
||||
if (query.isError) {
|
||||
content = t('relationship:button.error')
|
||||
content = t('button.error')
|
||||
onPress = () => {}
|
||||
} else {
|
||||
if (query.data?.blocked_by) {
|
||||
content = t('relationship:button.blocked_by')
|
||||
content = t('button.blocked_by')
|
||||
onPress = () => null
|
||||
} else {
|
||||
if (query.data?.blocking) {
|
||||
content = t('relationship:button.blocking')
|
||||
content = t('button.blocking')
|
||||
onPress = () =>
|
||||
mutation.mutate({
|
||||
id,
|
||||
|
@ -72,7 +72,7 @@ const RelationshipOutgoing = React.memo(
|
|||
})
|
||||
} else {
|
||||
if (query.data?.following) {
|
||||
content = t('relationship:button.following')
|
||||
content = t('button.following')
|
||||
onPress = () =>
|
||||
mutation.mutate({
|
||||
id,
|
||||
|
@ -84,7 +84,7 @@ const RelationshipOutgoing = React.memo(
|
|||
})
|
||||
} else {
|
||||
if (query.data?.requested) {
|
||||
content = t('relationship:button.requested')
|
||||
content = t('button.requested')
|
||||
onPress = () =>
|
||||
mutation.mutate({
|
||||
id,
|
||||
|
@ -95,7 +95,7 @@ const RelationshipOutgoing = React.memo(
|
|||
}
|
||||
})
|
||||
} else {
|
||||
content = t('relationship:button.default')
|
||||
content = t('button.default')
|
||||
onPress = () =>
|
||||
mutation.mutate({
|
||||
id,
|
||||
|
|
|
@ -10,7 +10,7 @@ export interface Props {
|
|||
}
|
||||
|
||||
const RelativeTime: React.FC<Props> = ({ date }) => {
|
||||
const { t } = useTranslation('relativeTime')
|
||||
const { t } = useTranslation('componentRelativeTime')
|
||||
|
||||
return (
|
||||
<TimeAgo
|
||||
|
|
|
@ -15,7 +15,7 @@ export interface Props {
|
|||
|
||||
const TimelineEmpty: React.FC<Props> = ({ status, refetch }) => {
|
||||
const { mode, theme } = useTheme()
|
||||
const { t, i18n } = useTranslation('timeline')
|
||||
const { t, i18n } = useTranslation('componentTimeline')
|
||||
|
||||
const children = useMemo(() => {
|
||||
switch (status) {
|
||||
|
|
|
@ -20,7 +20,7 @@ const TimelineEnd: React.FC<Props> = ({ hasNextPage }) => {
|
|||
) : (
|
||||
<Text style={[styles.text, { color: theme.secondary }]}>
|
||||
<Trans
|
||||
i18nKey='timeline:shared.end.message'
|
||||
i18nKey='componentTimeline:end.message'
|
||||
components={[
|
||||
<Icon
|
||||
name='Coffee'
|
||||
|
|
|
@ -4,11 +4,13 @@ import { StyleConstants } from '@root/utils/styles/constants'
|
|||
import { useTheme } from '@root/utils/styles/ThemeManager'
|
||||
import { updatePublicRemoteNotice } from '@utils/slices/contextsSlice'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
const TimelineHeader = React.memo(
|
||||
() => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const dispatch = useDispatch()
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
|
@ -16,7 +18,7 @@ const TimelineHeader = React.memo(
|
|||
return (
|
||||
<View style={[styles.base, { borderColor: theme.border }]}>
|
||||
<Text style={[styles.text, { color: theme.primary }]}>
|
||||
一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字一大堆文字{' '}
|
||||
{t('header.explanation')}
|
||||
<Text
|
||||
style={{ color: theme.blue }}
|
||||
onPress={() => {
|
||||
|
@ -27,7 +29,7 @@ const TimelineHeader = React.memo(
|
|||
})
|
||||
}}
|
||||
>
|
||||
前往设置{' '}
|
||||
{t('header.button')}
|
||||
<Icon
|
||||
name='ArrowRight'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
|
|
|
@ -18,7 +18,7 @@ const TimelineActioned: React.FC<Props> = ({
|
|||
action,
|
||||
notification = false
|
||||
}) => {
|
||||
const { t } = useTranslation('timeline')
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { theme } = useTheme()
|
||||
const navigation = useNavigation()
|
||||
const name = account.display_name || account.username
|
||||
|
|
|
@ -11,14 +11,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Platform,
|
||||
Pressable,
|
||||
Share,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
} from 'react-native'
|
||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
import { useQueryClient } from 'react-query'
|
||||
|
||||
export interface Props {
|
||||
|
@ -29,7 +22,7 @@ export interface Props {
|
|||
|
||||
const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
||||
const navigation = useNavigation()
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { theme } = useTheme()
|
||||
const iconColor = theme.secondary
|
||||
const iconColorAction = (state: boolean) =>
|
||||
|
@ -84,7 +77,7 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
|||
type: 'error',
|
||||
message: t('common:toastMessage.error.message', {
|
||||
function: t(
|
||||
`timeline:shared.actions.${correctParam.payload.property}.function`
|
||||
`shared.actions.${correctParam.payload.property}.function`
|
||||
)
|
||||
}),
|
||||
...(err.status &&
|
||||
|
@ -95,7 +88,7 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
|||
description: err.data.error
|
||||
})
|
||||
})
|
||||
queryClient.setQueryData(queryKey, oldData)
|
||||
queryClient.invalidateQueries(queryKey)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -150,18 +143,6 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
|||
}),
|
||||
[status.bookmarked]
|
||||
)
|
||||
const onPressShare = useCallback(() => {
|
||||
switch (Platform.OS) {
|
||||
case 'ios':
|
||||
return Share.share({
|
||||
url: status.uri
|
||||
})
|
||||
case 'android':
|
||||
return Share.share({
|
||||
message: status.uri
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
const childrenReply = useMemo(
|
||||
() => (
|
||||
|
@ -220,12 +201,6 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
|||
),
|
||||
[status.bookmarked]
|
||||
)
|
||||
const childrenShare = useMemo(
|
||||
() => (
|
||||
<Icon name='Share2' color={iconColor} size={StyleConstants.Font.Size.L} />
|
||||
),
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -256,12 +231,6 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
|||
onPress={onPressBookmark}
|
||||
children={childrenBookmark}
|
||||
/>
|
||||
|
||||
<Pressable
|
||||
style={styles.action}
|
||||
onPress={onPressShare}
|
||||
children={childrenShare}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
|
@ -269,16 +238,13 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
|
|||
|
||||
const styles = StyleSheet.create({
|
||||
actions: {
|
||||
width: '100%',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
marginTop: StyleConstants.Spacing.S
|
||||
},
|
||||
action: {
|
||||
width: '20%',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingVertical: StyleConstants.Spacing.S
|
||||
}
|
||||
})
|
||||
|
|
|
@ -17,7 +17,7 @@ export interface Props {
|
|||
}
|
||||
|
||||
const TimelineAttachment: React.FC<Props> = ({ status }) => {
|
||||
const { t } = useTranslation('timeline')
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
const [sensitiveShown, setSensitiveShown] = useState(status.sensitive)
|
||||
const onPressBlurView = useCallback(() => {
|
||||
|
|
|
@ -22,7 +22,7 @@ const AttachmentUnsupported: React.FC<Props> = ({
|
|||
sensitiveShown,
|
||||
attachment
|
||||
}) => {
|
||||
const { t } = useTranslation('timeline')
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
|
|
|
@ -17,7 +17,7 @@ const TimelineContent: React.FC<Props> = ({
|
|||
highlighted = false,
|
||||
disableDetails = false
|
||||
}) => {
|
||||
const { t } = useTranslation('timeline')
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -20,30 +20,29 @@ const HeaderActionsAccount: React.FC<Props> = ({
|
|||
account,
|
||||
setBottomSheetVisible
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const mutateion = useTimelineMutation({
|
||||
queryClient,
|
||||
onSuccess: (_, { type }) => {
|
||||
onSuccess: (_, { payload: { property } }) => {
|
||||
haptics('Success')
|
||||
toast({
|
||||
type: 'success',
|
||||
message: t('common:toastMessage.success.message', {
|
||||
function: t(
|
||||
`timeline:shared.header.default.actions.account.${type}.function`,
|
||||
{ acct: account.acct }
|
||||
)
|
||||
function: t(`shared.header.actions.account.${property}.function`, {
|
||||
acct: account.acct
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
onError: (err: any, { type }) => {
|
||||
onError: (err: any, { payload: { property } }) => {
|
||||
haptics('Error')
|
||||
toast({
|
||||
type: 'error',
|
||||
message: t('common:toastMessage.error.message', {
|
||||
function: t(
|
||||
`timeline:shared.header.default.actions.account.${type}.function`,
|
||||
`shared.header.actions.account.${property}.function`,
|
||||
{ acct: account.acct }
|
||||
)
|
||||
}),
|
||||
|
@ -64,7 +63,7 @@ const HeaderActionsAccount: React.FC<Props> = ({
|
|||
return (
|
||||
<MenuContainer>
|
||||
<MenuHeader
|
||||
heading={t('timeline:shared.header.default.actions.account.heading')}
|
||||
heading={t('shared.header.actions.account.heading')}
|
||||
/>
|
||||
<MenuRow
|
||||
onPress={() => {
|
||||
|
@ -77,7 +76,7 @@ const HeaderActionsAccount: React.FC<Props> = ({
|
|||
})
|
||||
}}
|
||||
iconFront='EyeOff'
|
||||
title={t('timeline:shared.header.default.actions.account.mute.button', {
|
||||
title={t('shared.header.actions.account.mute.button', {
|
||||
acct: account.acct
|
||||
})}
|
||||
/>
|
||||
|
@ -93,7 +92,7 @@ const HeaderActionsAccount: React.FC<Props> = ({
|
|||
}}
|
||||
iconFront='XCircle'
|
||||
title={t(
|
||||
'timeline:shared.header.default.actions.account.block.button',
|
||||
'shared.header.actions.account.block.button',
|
||||
{
|
||||
acct: account.acct
|
||||
}
|
||||
|
@ -111,7 +110,7 @@ const HeaderActionsAccount: React.FC<Props> = ({
|
|||
}}
|
||||
iconFront='Flag'
|
||||
title={t(
|
||||
'timeline:shared.header.default.actions.account.report.button',
|
||||
'shared.header.actions.account.reports.button',
|
||||
{
|
||||
acct: account.acct
|
||||
}
|
|
@ -22,7 +22,7 @@ const HeaderActionsDomain: React.FC<Props> = ({
|
|||
domain,
|
||||
setBottomSheetVisible
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const queryClient = useQueryClient()
|
||||
const mutation = useTimelineMutation({
|
||||
queryClient,
|
||||
|
@ -30,9 +30,7 @@ const HeaderActionsDomain: React.FC<Props> = ({
|
|||
toast({
|
||||
type: 'success',
|
||||
message: t('common:toastMessage.success.message', {
|
||||
function: t(
|
||||
`timeline:shared.header.default.actions.domain.block.function`
|
||||
)
|
||||
function: t(`shared.header.actions.domain.block.function`)
|
||||
})
|
||||
})
|
||||
queryClient.invalidateQueries(queryKey)
|
||||
|
@ -41,20 +39,19 @@ const HeaderActionsDomain: React.FC<Props> = ({
|
|||
|
||||
return (
|
||||
<MenuContainer>
|
||||
<MenuHeader
|
||||
heading={t(`timeline:shared.header.default.actions.domain.heading`)}
|
||||
/>
|
||||
<MenuHeader heading={t(`shared.header.actions.domain.heading`)} />
|
||||
<MenuRow
|
||||
onPress={() => {
|
||||
Alert.alert(
|
||||
t('timeline:shared.header.default.actions.domain.alert.title'),
|
||||
t('timeline:shared.header.default.actions.domain.alert.message'),
|
||||
t('shared.header.actions.domain.alert.title', { domain }),
|
||||
t('shared.header.actions.domain.alert.message'),
|
||||
[
|
||||
{ text: t('common:buttons.cancel'), style: 'cancel' },
|
||||
{
|
||||
text: t(
|
||||
'timeline:shared.header.default.actions.domain.alert.confirm'
|
||||
),
|
||||
text: t('shared.header.actions.domain.alert.buttons.cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: t('shared.header.actions.domain.alert.buttons.confirm'),
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
setBottomSheetVisible(false)
|
||||
|
@ -69,7 +66,7 @@ const HeaderActionsDomain: React.FC<Props> = ({
|
|||
)
|
||||
}}
|
||||
iconFront='CloudOff'
|
||||
title={t(`timeline:shared.header.default.actions.domain.block.button`, {
|
||||
title={t(`shared.header.actions.domain.block.button`, {
|
||||
domain
|
||||
})}
|
||||
/>
|
|
@ -7,17 +7,20 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Pressable, StyleSheet } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import HeaderActionsAccount from './ActionsAccount'
|
||||
import HeaderActionsDomain from './ActionsDomain'
|
||||
import HeaderActionsStatus from './ActionsStatus'
|
||||
import HeaderActionsAccount from './Account'
|
||||
import HeaderActionsDomain from './Domain'
|
||||
import HeaderActionsShare from './Share'
|
||||
import HeaderActionsStatus from './Status'
|
||||
|
||||
export interface Props {
|
||||
queryKey: QueryKeyTimeline
|
||||
status: Mastodon.Status
|
||||
url?: string
|
||||
type?: 'status' | 'account'
|
||||
}
|
||||
|
||||
const HeaderActions = React.memo(
|
||||
({ queryKey, status }: Props) => {
|
||||
({ queryKey, status, url, type }: Props) => {
|
||||
const { theme } = useTheme()
|
||||
|
||||
const localAccount = useSelector(getLocalAccount)
|
||||
|
@ -73,6 +76,14 @@ const HeaderActions = React.memo(
|
|||
setBottomSheetVisible={setBottomSheetVisible}
|
||||
/>
|
||||
)}
|
||||
|
||||
{url && type ? (
|
||||
<HeaderActionsShare
|
||||
url={url}
|
||||
type={type}
|
||||
setBottomSheetVisible={setBottomSheetVisible}
|
||||
/>
|
||||
) : null}
|
||||
</BottomSheet>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import MenuContainer from '@components/Menu/Container'
|
||||
import MenuHeader from '@components/Menu/Header'
|
||||
import MenuRow from '@components/Menu/Row'
|
||||
import { toast } from '@components/toast'
|
||||
import {
|
||||
QueryKeyTimeline,
|
||||
useTimelineMutation
|
||||
} from '@utils/queryHooks/timeline'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform, Share } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
type: 'status' | 'account'
|
||||
url: string
|
||||
setBottomSheetVisible: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
const HeaderActionsShare: React.FC<Props> = ({
|
||||
type,
|
||||
url,
|
||||
setBottomSheetVisible
|
||||
}) => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
return (
|
||||
<MenuContainer>
|
||||
<MenuHeader heading={t(`shared.header.actions.share.${type}.heading`)} />
|
||||
<MenuRow
|
||||
iconFront='Share2'
|
||||
title={t(`shared.header.actions.share.${type}.button`)}
|
||||
onPress={async () => {
|
||||
switch (Platform.OS) {
|
||||
case 'ios':
|
||||
await Share.share({
|
||||
url
|
||||
})
|
||||
break
|
||||
case 'android':
|
||||
await Share.share({
|
||||
message: url
|
||||
})
|
||||
break
|
||||
}
|
||||
setBottomSheetVisible(false)
|
||||
}}
|
||||
/>
|
||||
</MenuContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeaderActionsShare
|
|
@ -23,7 +23,7 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||
setBottomSheetVisible
|
||||
}) => {
|
||||
const navigation = useNavigation()
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const mutation = useTimelineMutation({
|
||||
|
@ -38,7 +38,7 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||
type: 'error',
|
||||
message: t('common:toastMessage.error.message', {
|
||||
function: t(
|
||||
`timeline:shared.header.default.actions.status.${theFunction}.function`
|
||||
`shared.header.actions.status.${theFunction}.function`
|
||||
)
|
||||
}),
|
||||
...(err.status &&
|
||||
|
@ -56,7 +56,7 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||
return (
|
||||
<MenuContainer>
|
||||
<MenuHeader
|
||||
heading={t('timeline:shared.header.default.actions.status.heading')}
|
||||
heading={t('shared.header.actions.status.heading')}
|
||||
/>
|
||||
<MenuRow
|
||||
onPress={() => {
|
||||
|
@ -69,20 +69,20 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||
})
|
||||
}}
|
||||
iconFront='Trash'
|
||||
title={t('timeline:shared.header.default.actions.status.delete.button')}
|
||||
title={t('shared.header.actions.status.delete.button')}
|
||||
/>
|
||||
<MenuRow
|
||||
onPress={() => {
|
||||
Alert.alert(
|
||||
t('timeline:shared.header.default.actions.status.edit.alert.title'),
|
||||
t('shared.header.actions.status.edit.alert.title'),
|
||||
t(
|
||||
'timeline:shared.header.default.actions.status.edit.alert.message'
|
||||
'shared.header.actions.status.edit.alert.message'
|
||||
),
|
||||
[
|
||||
{ text: t('common:buttons.cancel'), style: 'cancel' },
|
||||
{ text: t('shared.header.actions.status.edit.alert.buttons.cancel'), style: 'cancel' },
|
||||
{
|
||||
text: t(
|
||||
'timeline:shared.header.default.actions.status.edit.alert.confirm'
|
||||
'shared.header.actions.status.edit.alert.buttons.confirm'
|
||||
),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
|
@ -105,8 +105,8 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||
]
|
||||
)
|
||||
}}
|
||||
iconFront='Trash'
|
||||
title={t('timeline:shared.header.default.actions.status.edit.button')}
|
||||
iconFront='Edit'
|
||||
title={t('shared.header.actions.status.edit.button')}
|
||||
/>
|
||||
<MenuRow
|
||||
onPress={() => {
|
||||
|
@ -122,10 +122,10 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||
title={
|
||||
status.muted
|
||||
? t(
|
||||
'timeline:shared.header.default.actions.status.mute.button.negative'
|
||||
'shared.header.actions.status.mute.button.negative'
|
||||
)
|
||||
: t(
|
||||
'timeline:shared.header.default.actions.status.mute.button.positive'
|
||||
'shared.header.actions.status.mute.button.positive'
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -145,10 +145,10 @@ const HeaderActionsStatus: React.FC<Props> = ({
|
|||
title={
|
||||
status.pinned
|
||||
? t(
|
||||
'timeline:shared.header.default.actions.status.pin.button.negative'
|
||||
'shared.header.actions.status.pin.button.negative'
|
||||
)
|
||||
: t(
|
||||
'timeline:shared.header.default.actions.status.pin.button.positive'
|
||||
'shared.header.actions.status.pin.button.positive'
|
||||
)
|
||||
}
|
||||
/>
|
|
@ -21,7 +21,7 @@ export interface Props {
|
|||
}
|
||||
|
||||
const HeaderConversation: React.FC<Props> = ({ queryKey, conversation }) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const mutation = useTimelineMutation({
|
||||
|
@ -32,7 +32,7 @@ const HeaderConversation: React.FC<Props> = ({ queryKey, conversation }) => {
|
|||
toast({
|
||||
type: 'error',
|
||||
message: t('common:toastMessage.error.message', {
|
||||
function: t(`timeline:shared.header.conversation.delete.function`)
|
||||
function: t(`shared.header.conversation.delete.function`)
|
||||
}),
|
||||
...(err.status &&
|
||||
typeof err.status === 'number' &&
|
||||
|
|
|
@ -27,7 +27,14 @@ const TimelineHeaderDefault: React.FC<Props> = ({ queryKey, status }) => {
|
|||
</View>
|
||||
</View>
|
||||
|
||||
{queryKey ? <HeaderActions queryKey={queryKey} status={status} /> : null}
|
||||
{queryKey ? (
|
||||
<HeaderActions
|
||||
queryKey={queryKey}
|
||||
status={status}
|
||||
url={status.url || status.uri}
|
||||
type='status'
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface Props {
|
|||
|
||||
const HeaderSharedApplication: React.FC<Props> = ({ application }) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('timeline')
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
return application && application.name !== 'Web' ? (
|
||||
<Text
|
||||
|
|
|
@ -2,7 +2,6 @@ import RelativeTime from '@components/RelativeTime'
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
|
@ -11,7 +10,6 @@ export interface Props {
|
|||
|
||||
const HeaderSharedCreated: React.FC<Props> = ({ created_at }) => {
|
||||
const { theme } = useTheme()
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
return (
|
||||
<Text style={[styles.created_at, { color: theme.secondary }]}>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { ParseEmojis } from '@components/Parse'
|
|||
import RelativeTime from '@components/RelativeTime'
|
||||
import { toast } from '@components/toast'
|
||||
import {
|
||||
MutationVarsTimelineUpdateStatusProperty,
|
||||
QueryKeyTimeline,
|
||||
useTimelineMutation
|
||||
} from '@utils/queryHooks/timeline'
|
||||
|
@ -32,7 +33,7 @@ const TimelinePoll: React.FC<Props> = ({
|
|||
sameAccount
|
||||
}) => {
|
||||
const { mode, theme } = useTheme()
|
||||
const { t } = useTranslation('timeline')
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
|
||||
const [allOptions, setAllOptions] = useState(
|
||||
new Array(poll.options.length).fill(false)
|
||||
|
@ -42,19 +43,21 @@ const TimelinePoll: React.FC<Props> = ({
|
|||
const mutation = useTimelineMutation({
|
||||
queryClient,
|
||||
onSuccess: true,
|
||||
onError: (err: any) => {
|
||||
onError: (err: any, params) => {
|
||||
const theParams = params as MutationVarsTimelineUpdateStatusProperty
|
||||
haptics('Error')
|
||||
toast({
|
||||
type: 'error',
|
||||
message: '投票错误',
|
||||
message: t('common:toastMessage.error.message', {
|
||||
function: t(`shared.poll.meta.button.${theParams.payload.type}`)
|
||||
}),
|
||||
...(err.status &&
|
||||
typeof err.status === 'number' &&
|
||||
err.data &&
|
||||
err.data.error &&
|
||||
typeof err.data.error === 'string' && {
|
||||
description: err.data.error
|
||||
}),
|
||||
autoHide: false
|
||||
})
|
||||
})
|
||||
queryClient.invalidateQueries(queryKey)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as Analytics from 'expo-firebase-analytics'
|
||||
import * as Sentry from 'sentry-expo'
|
||||
|
||||
const analytics = (event: string, params?: { [key: string]: string }) => {
|
||||
Analytics.logEvent(event, params).catch(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export default {
|
||||
common: require('./common').default,
|
||||
|
||||
relativeTime: require('./components/relativeTime').default
|
||||
componentRelativeTime: require('./components/relativeTime').default,
|
||||
}
|
||||
|
|
|
@ -15,12 +15,18 @@ export default {
|
|||
meListsList: require('./screens/meListsList').default,
|
||||
meSettings: require('./screens/meSettings').default,
|
||||
meSettingsUpdateRemote: require('./screens/meSettingsUpdateRemote').default,
|
||||
meSwitch: require('./screens/meSwitch').default,
|
||||
|
||||
sharedAccount: require('./screens/sharedAccount').default,
|
||||
sharedToot: require('./screens/sharedToot').default,
|
||||
sharedAnnouncements: require('./screens/sharedAnnouncements').default,
|
||||
sharedCompose: require('./screens/sharedCompose').default,
|
||||
sharedRelationships: require('./screens/sharedRelationships').default,
|
||||
sharedSearch: require('./screens/sharedSearch').default,
|
||||
sharedToot: require('./screens/sharedToot').default,
|
||||
|
||||
relationship: require('./components/relationship').default,
|
||||
relativeTime: require('./components/relativeTime').default,
|
||||
timeline: require('./components/timeline').default
|
||||
componentInstance: require('./components/instance').default,
|
||||
componentParse: require('./components/parse').default,
|
||||
componentRelationship: require('./components/relationship').default,
|
||||
componentRelativeTime: require('./components/relativeTime').default,
|
||||
componentTimeline: require('./components/timeline').default
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
export default {
|
||||
server: {
|
||||
textInput: { placeholder: '输入社区服务器地址' },
|
||||
button: {
|
||||
local: '登录',
|
||||
remote: '围观'
|
||||
},
|
||||
information: {
|
||||
name: '社区名称',
|
||||
description: { heading: '社区简介', expandHint: '简介' },
|
||||
accounts: '用户总数',
|
||||
statuses: '嘟文总数',
|
||||
domains: '连结总数'
|
||||
},
|
||||
disclaimer:
|
||||
'登录过程将使用系统浏览器,您的账户登录信息tooot应用无法读取。详见:'
|
||||
},
|
||||
update: {
|
||||
local: {
|
||||
alert: {
|
||||
title: '此社区已登录',
|
||||
message: '您可以登录同个社区的另一个账户,不影响现有账户',
|
||||
buttons: {
|
||||
cancel: '$t(common:buttons.cancel)',
|
||||
continue: '继续'
|
||||
}
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
succeed: '围观登记成功'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export default {
|
||||
HTML: {
|
||||
expanded: {
|
||||
true: '折叠{{hint}}',
|
||||
false: '展开{{hint}}'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,9 +5,17 @@ export default {
|
|||
button: '重试'
|
||||
},
|
||||
success: {
|
||||
message: '🈳️🈚️1一物'
|
||||
message: '空无一物'
|
||||
}
|
||||
},
|
||||
end: {
|
||||
message: '居然刷到底了,喝杯 <0 /> 吧'
|
||||
},
|
||||
header: {
|
||||
explanation:
|
||||
'围观的社区可能不属于已经登录的社区的已知连结,因此只可围观嘟文,不能进行操作。设置里可以切换想要围观的社区。',
|
||||
button: '前往设置'
|
||||
},
|
||||
shared: {
|
||||
actioned: {
|
||||
pinned: '置顶',
|
||||
|
@ -22,17 +30,14 @@ export default {
|
|||
}
|
||||
},
|
||||
actions: {
|
||||
favourite: {
|
||||
favourited: {
|
||||
function: '喜欢嘟文'
|
||||
// button: '隐藏 {{acct}} 的嘟文'
|
||||
},
|
||||
reblog: {
|
||||
reblogged: {
|
||||
function: '转嘟'
|
||||
// button: '屏蔽 {{acct}}'
|
||||
},
|
||||
bookmark: {
|
||||
bookmarked: {
|
||||
function: '收藏嘟文'
|
||||
// button: '举报 {{acct}}'
|
||||
}
|
||||
},
|
||||
attachment: {
|
||||
|
@ -40,16 +45,13 @@ export default {
|
|||
button: '显示敏感内容'
|
||||
},
|
||||
unsupported: {
|
||||
text: '文件读取错误',
|
||||
text: '附件读取错误',
|
||||
button: '尝试远程链接'
|
||||
}
|
||||
},
|
||||
content: {
|
||||
expandHint: '隐藏内容'
|
||||
},
|
||||
end: {
|
||||
message: '居然刷到底了,喝杯 <0 /> 吧'
|
||||
},
|
||||
header: {
|
||||
shared: {
|
||||
application: '发自于 {{application}}'
|
||||
|
@ -59,59 +61,73 @@ export default {
|
|||
function: '删除私信'
|
||||
}
|
||||
},
|
||||
default: {
|
||||
actions: {
|
||||
account: {
|
||||
heading: '关于用户',
|
||||
mute: {
|
||||
function: '隐藏 @{{acct}} 的嘟文',
|
||||
button: '隐藏 @{{acct}} 的嘟文'
|
||||
},
|
||||
block: {
|
||||
function: '屏蔽 @{{acct}}',
|
||||
button: '屏蔽 @{{acct}}'
|
||||
},
|
||||
report: {
|
||||
function: '举报 @{{acct}}',
|
||||
button: '举报 @{{acct}}'
|
||||
actions: {
|
||||
account: {
|
||||
heading: '关于用户',
|
||||
mute: {
|
||||
function: '隐藏 @{{acct}} 的嘟文',
|
||||
button: '隐藏 @{{acct}} 的嘟文'
|
||||
},
|
||||
block: {
|
||||
function: '屏蔽 @{{acct}}',
|
||||
button: '屏蔽 @{{acct}}'
|
||||
},
|
||||
reports: {
|
||||
function: '举报 @{{acct}}',
|
||||
button: '举报 @{{acct}}'
|
||||
}
|
||||
},
|
||||
domain: {
|
||||
heading: '关于社区',
|
||||
block: {
|
||||
function: '屏蔽社区',
|
||||
button: '屏蔽社区 {{domain}}'
|
||||
},
|
||||
alert: {
|
||||
title: '确定要屏蔽 {{domain}} 吗?',
|
||||
message:
|
||||
'多数情况下,隐藏或屏蔽特定用户即可。\n\n屏蔽之后,来自此社区的所有内容将不再出现在你的时间轴里。同时,来自该社区的关注者将被移除。请谨慎使用。',
|
||||
buttons: {
|
||||
confirm: '确定屏蔽整个社区',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
}
|
||||
},
|
||||
share: {
|
||||
status: { heading: '分享嘟文', button: '分享此条嘟文的链接' },
|
||||
account: { heading: '分享用户', button: '分享此用户的链接' }
|
||||
},
|
||||
status: {
|
||||
heading: '关于嘟文',
|
||||
delete: {
|
||||
function: '删除',
|
||||
button: '删除此条嘟文'
|
||||
},
|
||||
edit: {
|
||||
function: '删除',
|
||||
button: '删除并重新编辑此条嘟文',
|
||||
alert: {
|
||||
title: '确认删除嘟文?',
|
||||
message:
|
||||
'确定要删除这条嘟文并重新编辑它吗?所有相关的转嘟和喜欢都会被清除,回复将会失去关联。',
|
||||
buttons: {
|
||||
confirm: '删除并重新编辑',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
}
|
||||
},
|
||||
domain: {
|
||||
heading: '关于域名',
|
||||
block: {
|
||||
function: '屏蔽域名',
|
||||
button: '屏蔽域名 {{domain}}'
|
||||
mute: {
|
||||
function: '静音',
|
||||
button: {
|
||||
positive: '静音此条嘟文及对话',
|
||||
negative: '取消静音此条嘟文及对话'
|
||||
}
|
||||
},
|
||||
status: {
|
||||
heading: '关于嘟嘟',
|
||||
delete: {
|
||||
function: '删除',
|
||||
button: '删除次条嘟文'
|
||||
},
|
||||
edit: {
|
||||
function: '删除',
|
||||
button: '删除并重新编辑次条嘟文',
|
||||
alert: {
|
||||
title: '确认删除嘟嘟?',
|
||||
message:
|
||||
'你确定要删除这条嘟文并重新编辑它吗?所有相关的转嘟和喜欢都会被清除,回复将会失去关联。',
|
||||
confirm: '删除并重新编辑'
|
||||
}
|
||||
},
|
||||
mute: {
|
||||
function: '静音',
|
||||
button: {
|
||||
positive: '静音此条嘟文及对话',
|
||||
negative: '取消静音此条嘟文及对话'
|
||||
}
|
||||
},
|
||||
pin: {
|
||||
function: '置顶',
|
||||
button: {
|
||||
positive: '置顶此条嘟文',
|
||||
negative: '取消置顶此条嘟文'
|
||||
}
|
||||
pin: {
|
||||
function: '置顶',
|
||||
button: {
|
||||
positive: '置顶此条嘟文',
|
||||
negative: '取消置顶此条嘟文'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,5 @@ export default {
|
|||
left: '我的关注',
|
||||
right: '本站嘟嘟'
|
||||
}
|
||||
},
|
||||
content: {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
export default {
|
||||
heading: '我的长毛象',
|
||||
content: {
|
||||
login: {
|
||||
server: {
|
||||
placeholder: '请输入服务器'
|
||||
},
|
||||
button: '登录'
|
||||
},
|
||||
collections: {
|
||||
conversations: '$t(meConversations:heading)',
|
||||
bookmarks: '$t(meBookmarks:heading)',
|
||||
favourites: '$t(meFavourites:heading)',
|
||||
lists: '$t(meLists:heading)',
|
||||
announcements: '$t(sharedAnnouncements:heading)'
|
||||
announcements: {
|
||||
heading: '$t(sharedAnnouncements:heading)',
|
||||
content: {
|
||||
unread: '{{amount}} 条未读公告',
|
||||
read: '无未读公告'
|
||||
}
|
||||
}
|
||||
},
|
||||
settings: '$t(meSettings:heading)',
|
||||
logout: {
|
||||
|
|
|
@ -19,24 +19,30 @@ export default {
|
|||
}
|
||||
},
|
||||
browser: {
|
||||
heading: '打开链接',
|
||||
heading: '外部链接',
|
||||
options: {
|
||||
internal: '应用内',
|
||||
external: '系统浏览器',
|
||||
internal: '应用内打开',
|
||||
external: '浏览器打开',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
heading: '$t(meSettingsUpdateRemote:heading)',
|
||||
description: '外站只能看不能玩'
|
||||
description: '外站只能浏览不能玩'
|
||||
},
|
||||
cache: {
|
||||
heading: '清空缓存',
|
||||
empty: '暂无缓存'
|
||||
},
|
||||
support: {
|
||||
heading: '赞助 tooot 开发'
|
||||
},
|
||||
review: {
|
||||
heading: '给 tooot 打分'
|
||||
},
|
||||
analytics: {
|
||||
heading: '帮助我们改进',
|
||||
description: '允许我们收集不与用户相关联的使用信息'
|
||||
description: '收集不与用户相关联的使用信息'
|
||||
},
|
||||
version: '版本 v{{version}}'
|
||||
}
|
||||
|
|
|
@ -1,45 +1,3 @@
|
|||
export default {
|
||||
heading: '外站链接',
|
||||
content: {
|
||||
language: {
|
||||
heading: '切换语言',
|
||||
options: {
|
||||
zh: '简体中文',
|
||||
en: 'English',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
},
|
||||
theme: {
|
||||
heading: '应用外观',
|
||||
options: {
|
||||
auto: '跟随系统',
|
||||
light: '浅色模式',
|
||||
dark: '深色模式',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
},
|
||||
browser: {
|
||||
heading: '打开链接',
|
||||
options: {
|
||||
internal: '应用内',
|
||||
external: '系统浏览器',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
heading: '外站链接',
|
||||
description: '外站只能看不能玩'
|
||||
},
|
||||
cache: {
|
||||
heading: '清空缓存'
|
||||
},
|
||||
analytics: {
|
||||
heading: '帮助我们改进',
|
||||
description: '允许我们收集不与用户相关联的使用信息'
|
||||
},
|
||||
copyrights: {
|
||||
heading: '版权信息'
|
||||
},
|
||||
version: '版本 v{{version}}'
|
||||
}
|
||||
heading: '外站社区'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
heading: '切换账号',
|
||||
content: {
|
||||
existing: '选择已有账号',
|
||||
new: '登录新社区'
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
export default {
|
||||
heading: '通知',
|
||||
content: {}
|
||||
heading: '通知'
|
||||
}
|
||||
|
|
|
@ -4,6 +4,5 @@ export default {
|
|||
left: '跨站关注',
|
||||
right: '外站嘟嘟'
|
||||
}
|
||||
},
|
||||
content: {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,10 @@
|
|||
export default {
|
||||
heading: {
|
||||
loading: '加载中…',
|
||||
error: '加载错误'
|
||||
},
|
||||
content: {
|
||||
created_at: '加入时间:{{date}}',
|
||||
summary: {
|
||||
statuses_count: '{{count}} 条嘟文',
|
||||
following_count: '关注 {{count}} 人',
|
||||
followers_count: '被 {{count}} 人关注'
|
||||
},
|
||||
segments: {
|
||||
left: '所有嘟嘟',
|
||||
middle: '嘟嘟和回复',
|
||||
right: '所有媒体'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
export default {
|
||||
heading: '公告',
|
||||
content: {}
|
||||
content: {
|
||||
published: '发布于 <0 />',
|
||||
button: {
|
||||
read: '已读',
|
||||
unread: '标记已读'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
export default {
|
||||
heading: {
|
||||
left: {
|
||||
button: '退出编辑',
|
||||
alert: {
|
||||
title: '确认退出编辑?',
|
||||
buttons: {
|
||||
exit: '退出编辑',
|
||||
continue: '继续编辑'
|
||||
}
|
||||
}
|
||||
},
|
||||
right: {
|
||||
button: {
|
||||
default: '发嘟嘟',
|
||||
conversation: '发私信',
|
||||
reply: '发布回复',
|
||||
edit: '发嘟嘟'
|
||||
},
|
||||
alert: {
|
||||
title: '发布失败',
|
||||
button: '返回重试'
|
||||
}
|
||||
}
|
||||
},
|
||||
content: {
|
||||
root: {
|
||||
header: {
|
||||
postingAs: '用 @{{acct}}@{{domain}} 发布',
|
||||
spoilerInput: {
|
||||
placeholder: '折叠部分的警告信息'
|
||||
},
|
||||
textInput: {
|
||||
placeholder: '想说点什么'
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
attachments: {
|
||||
sensitive: '标记媒体为敏感内容'
|
||||
},
|
||||
poll: {
|
||||
option: {
|
||||
placeholder: {
|
||||
single: '单选项',
|
||||
multiple: '多选项'
|
||||
}
|
||||
},
|
||||
multiple: {
|
||||
heading: '可选项',
|
||||
options: {
|
||||
single: '单选',
|
||||
multiple: '多选',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
},
|
||||
expiration: {
|
||||
heading: '有效期',
|
||||
options: {
|
||||
'300': '5分钟',
|
||||
'1800': '30分钟',
|
||||
'3600': '1小时',
|
||||
'21600': '6小时',
|
||||
'86400': '1天',
|
||||
'259200': '3天',
|
||||
'604800': '7天',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
attachment: {
|
||||
actions: {
|
||||
options: {
|
||||
library: '从相册上传',
|
||||
photo: '拍摄上传',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
},
|
||||
library: {
|
||||
alert: {
|
||||
title: '无读取权限',
|
||||
message: '需要相片权限才能上传附件',
|
||||
buttons: {
|
||||
settings: '去系统设置',
|
||||
cancel: '取消上传'
|
||||
}
|
||||
}
|
||||
},
|
||||
photo: {
|
||||
alert: {
|
||||
title: '无拍照权限',
|
||||
message: '需要相机权限才能上传附件',
|
||||
buttons: {
|
||||
settings: '去系统设置',
|
||||
cancel: '取消上传'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
failed: {
|
||||
alert: {
|
||||
title: '上传失败',
|
||||
button: '返回重试'
|
||||
}
|
||||
}
|
||||
},
|
||||
visibility: {
|
||||
title: '嘟文可见范围',
|
||||
options: {
|
||||
public: '公开',
|
||||
unlisted: '不公开',
|
||||
private: '仅关注者',
|
||||
direct: '私信',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
editAttachment: {
|
||||
header: {
|
||||
left: '取消修改',
|
||||
right: {
|
||||
button: '应用修改',
|
||||
succeed: {
|
||||
title: '修改成功',
|
||||
button: '好的'
|
||||
},
|
||||
failed: {
|
||||
title: '修改失败',
|
||||
button: '返回重试'
|
||||
}
|
||||
}
|
||||
},
|
||||
content: {
|
||||
altText: {
|
||||
heading: '为附件添加文字说明',
|
||||
placeholder:
|
||||
'你可以为附件添加文字说明,以便更多人可以查看他们(包括视力障碍或视力受损人士)。\n\n优质的描述应该简洁明了,但要准确地描述照片中的内容,以便用户理解其含义。'
|
||||
},
|
||||
imageFocus: '在预览图上拖动圆圈,以选择缩略图的焦点'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export default {
|
||||
heading: {
|
||||
segments: {
|
||||
left: '关注中',
|
||||
right: '关注者'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
export default {
|
||||
heading: '对话',
|
||||
content: {
|
||||
header: {
|
||||
prefix: '搜索',
|
||||
placeholder: '些什么'
|
||||
},
|
||||
empty: {
|
||||
general:
|
||||
'输入关键词搜索<bold>$t(sharedSearch:content.sections.accounts)</bold>、<bold>$t(sharedSearch:content.sections.hashtags)</bold>或者<bold>$t(sharedSearch:content.sections.statuses)</bold>',
|
||||
advanced: {
|
||||
header: '高级搜索格式',
|
||||
example: {
|
||||
account:
|
||||
'$t(sharedSearch:content.header.prefix)$t(sharedSearch:content.sections.accounts)',
|
||||
hashtag:
|
||||
'$t(sharedSearch:content.header.prefix)$t(sharedSearch:content.sections.hashtags)',
|
||||
statusLink:
|
||||
'$t(sharedSearch:content.header.prefix)指定$t(sharedSearch:content.sections.statuses)',
|
||||
accountLink:
|
||||
'$t(sharedSearch:content.header.prefix)$t(sharedSearch:content.sections.accounts)'
|
||||
}
|
||||
}
|
||||
},
|
||||
sections: {
|
||||
accounts: '用户',
|
||||
hashtags: '话题标签',
|
||||
statuses: '嘟文'
|
||||
},
|
||||
notFound: '找不到 <bold>{{searchTerm}}</bold> 相关的 {{type}}'
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
export default {
|
||||
heading: '对话',
|
||||
content: {}
|
||||
heading: '对话'
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import ScreenMeRoot from '@screens/Me/Root'
|
|||
import ScreenMeListsList from '@screens/Me/Root/Lists/List'
|
||||
import ScreenMeSettings from '@screens/Me/Settings'
|
||||
import ScreenMeSwitch from '@screens/Me/Switch'
|
||||
import UpdateRemote from '@screens/Me/UpdateRemote'
|
||||
import ScreenMeUpdateRemote from '@screens/Me/UpdateRemote'
|
||||
import sharedScreens from '@screens/Shared/sharedScreens'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
@ -89,7 +89,9 @@ const ScreenMe: React.FC = () => {
|
|||
headerTitle: t('meListsList:heading', { list: route.params.title }),
|
||||
...(Platform.OS === 'android' && {
|
||||
headerCenter: () => (
|
||||
<HeaderCenter content={t('meListsList:heading')} />
|
||||
<HeaderCenter
|
||||
content={t('meListsList:heading', { list: route.params.title })}
|
||||
/>
|
||||
)
|
||||
}),
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||
|
@ -110,7 +112,7 @@ const ScreenMe: React.FC = () => {
|
|||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Me-Settings-UpdateRemote'
|
||||
component={UpdateRemote}
|
||||
component={ScreenMeUpdateRemote}
|
||||
options={({ navigation }: any) => ({
|
||||
headerTitle: t('meSettingsUpdateRemote:heading'),
|
||||
...(Platform.OS === 'android' && {
|
||||
|
@ -127,17 +129,11 @@ const ScreenMe: React.FC = () => {
|
|||
options={({ navigation }: any) => ({
|
||||
stackPresentation: 'fullScreenModal',
|
||||
headerShown: false,
|
||||
headerTitle: t('meSettings:heading'),
|
||||
...(Platform.OS === 'android' && {
|
||||
headerCenter: () => (
|
||||
<HeaderCenter content={t('meSettings:heading')} />
|
||||
)
|
||||
}),
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||
})}
|
||||
/>
|
||||
|
||||
{sharedScreens(Stack)}
|
||||
{sharedScreens(Stack as any)}
|
||||
</Stack.Navigator>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||
import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
|
||||
|
||||
const Collections: React.FC = () => {
|
||||
const { t } = useTranslation('meRoot')
|
||||
const navigation = useNavigation()
|
||||
|
@ -15,9 +14,9 @@ const Collections: React.FC = () => {
|
|||
if (data) {
|
||||
const amount = data.filter(announcement => !announcement.read).length
|
||||
if (amount) {
|
||||
return `${amount} 条未读公告`
|
||||
return t('content.collections.announcements.content.unread', { amount })
|
||||
} else {
|
||||
return '无未读公告'
|
||||
return t('content.collections.announcements.content.read')
|
||||
}
|
||||
}
|
||||
}, [data])
|
||||
|
@ -51,7 +50,7 @@ const Collections: React.FC = () => {
|
|||
<MenuRow
|
||||
iconFront='Clipboard'
|
||||
iconBack='ChevronRight'
|
||||
title={t('content.collections.announcements')}
|
||||
title={t('content.collections.announcements.heading')}
|
||||
content={announcementContent}
|
||||
loading={isFetching}
|
||||
onPress={() =>
|
||||
|
|
|
@ -116,9 +116,11 @@ const ScreenMeSettings: React.FC = () => {
|
|||
cancelButtonIndex: i18n.languages.length
|
||||
},
|
||||
buttonIndex => {
|
||||
haptics('Success')
|
||||
dispatch(changeLanguage(availableLanguages[buttonIndex]))
|
||||
i18n.changeLanguage(availableLanguages[buttonIndex])
|
||||
if (buttonIndex < i18n.languages.length) {
|
||||
haptics('Success')
|
||||
dispatch(changeLanguage(availableLanguages[buttonIndex]))
|
||||
i18n.changeLanguage(availableLanguages[buttonIndex])
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
|
@ -226,7 +228,7 @@ const ScreenMeSettings: React.FC = () => {
|
|||
onPress={() => Linking.openURL('https://www.patreon.com/xmflsct')}
|
||||
/>
|
||||
<MenuRow
|
||||
title={t('content.copyrights.heading')}
|
||||
title={t('content.review.heading')}
|
||||
content={
|
||||
<Icon
|
||||
name='Star'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { HeaderCenter, HeaderLeft } from '@components/Header'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Platform, StyleSheet } from 'react-native'
|
||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||
import ScreenMeSwitchRoot from './Switch/Root'
|
||||
|
@ -7,6 +8,7 @@ import ScreenMeSwitchRoot from './Switch/Root'
|
|||
const Stack = createNativeStackNavigator()
|
||||
|
||||
const ScreenMeSwitch: React.FC = ({ navigation }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Stack.Navigator
|
||||
screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}
|
||||
|
@ -15,9 +17,9 @@ const ScreenMeSwitch: React.FC = ({ navigation }) => {
|
|||
name='Screen-Me-Switch-Root'
|
||||
component={ScreenMeSwitchRoot}
|
||||
options={{
|
||||
headerTitle: '切换账号',
|
||||
headerTitle: t('meSwitch:heading'),
|
||||
...(Platform.OS === 'android' && {
|
||||
headerCenter: () => <HeaderCenter content='切换账号' />
|
||||
headerCenter: () => <HeaderCenter content={t('meSwitch:heading')} />
|
||||
}),
|
||||
headerLeft: () => (
|
||||
<HeaderLeft content='X' onPress={() => navigation.goBack()} />
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
|
@ -59,7 +60,8 @@ const AccountButton: React.FC<Props> = ({
|
|||
)
|
||||
}
|
||||
|
||||
const ScreenMeSwitchRoot = () => {
|
||||
const ScreenMeSwitchRoot: React.FC = () => {
|
||||
const { t } = useTranslation('meSwitch')
|
||||
const { theme } = useTheme()
|
||||
const localInstances = useSelector(getLocalInstances)
|
||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||
|
@ -70,15 +72,11 @@ const ScreenMeSwitchRoot = () => {
|
|||
style={{ flex: 1 }}
|
||||
>
|
||||
<ScrollView keyboardShouldPersistTaps='handled'>
|
||||
<View style={styles.firstSection}>
|
||||
<View
|
||||
style={[styles.firstSection, { borderBottomColor: theme.border }]}
|
||||
>
|
||||
<Text style={[styles.header, { color: theme.primary }]}>
|
||||
登录新的服务器
|
||||
</Text>
|
||||
<ComponentInstance type='local' disableHeaderImage goBack />
|
||||
</View>
|
||||
<View style={[styles.secondSection, { borderTopColor: theme.border }]}>
|
||||
<Text style={[styles.header, { color: theme.primary }]}>
|
||||
选择已有账号
|
||||
{t('content.existing')}
|
||||
</Text>
|
||||
<View style={styles.accountButtons}>
|
||||
{localInstances.length
|
||||
|
@ -93,6 +91,13 @@ const ScreenMeSwitchRoot = () => {
|
|||
: null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.secondSection}>
|
||||
<Text style={[styles.header, { color: theme.primary }]}>
|
||||
{t('content.new')}
|
||||
</Text>
|
||||
<ComponentInstance type='local' disableHeaderImage goBack />
|
||||
</View>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
)
|
||||
|
@ -105,12 +110,13 @@ const styles = StyleSheet.create({
|
|||
paddingVertical: StyleConstants.Spacing.S
|
||||
},
|
||||
firstSection: {
|
||||
marginTop: StyleConstants.Spacing.S
|
||||
marginTop: StyleConstants.Spacing.S,
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingBottom: StyleConstants.Spacing.S,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth
|
||||
},
|
||||
secondSection: {
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingTop: StyleConstants.Spacing.M,
|
||||
borderTopWidth: StyleSheet.hairlineWidth
|
||||
paddingTop: StyleConstants.Spacing.M
|
||||
},
|
||||
accountButtons: {
|
||||
flex: 1,
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react'
|
|||
import { KeyboardAvoidingView, Platform } from 'react-native'
|
||||
import { ScrollView } from 'react-native-gesture-handler'
|
||||
|
||||
const UpdateRemote: React.FC = () => {
|
||||
const ScreenMeUpdateRemote: React.FC = () => {
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
|
@ -16,4 +16,4 @@ const UpdateRemote: React.FC = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export default UpdateRemote
|
||||
export default ScreenMeUpdateRemote
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import BottomSheet from '@components/BottomSheet'
|
||||
import { HeaderRight } from '@components/Header'
|
||||
import Timeline from '@components/Timelines/Timeline'
|
||||
import HeaderActionsAccount from '@components/Timelines/Timeline/Shared/HeaderActions/ActionsAccount'
|
||||
import HeaderActionsAccount from '@components/Timelines/Timeline/Shared/HeaderActions/Account'
|
||||
import HeaderActionsShare from '@components/Timelines/Timeline/Shared/HeaderActions/Share'
|
||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
|
@ -97,6 +98,12 @@ const ScreenSharedAccount: React.FC<SharedAccountProp> = ({
|
|||
setBottomSheetVisible={setBottomSheetVisible}
|
||||
/>
|
||||
)}
|
||||
|
||||
<HeaderActionsShare
|
||||
url={account.url}
|
||||
type='account'
|
||||
setBottomSheetVisible={setBottomSheetVisible}
|
||||
/>
|
||||
</BottomSheet>
|
||||
</AccountContext.Provider>
|
||||
)
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import Button from '@components/Button'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const AccountInformationSwitch: React.FC = () => {
|
||||
const navigation = useNavigation()
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Button
|
||||
type='text'
|
||||
content='切换账号'
|
||||
content={t('meSwitch:heading')}
|
||||
onPress={() => navigation.navigate('Screen-Me-Switch')}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -92,5 +92,4 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
})
|
||||
|
||||
// export default React.memo(AccountNav, (_, next) => next.account === undefined)
|
||||
export default AccountNav
|
||||
export default React.memo(AccountNav, (_, next) => next.account === undefined)
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Dimensions,
|
||||
Image,
|
||||
|
@ -33,7 +33,7 @@ const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
|||
const { theme } = useTheme()
|
||||
const bottomTabBarHeight = useBottomTabBarHeight()
|
||||
const [index, setIndex] = useState(0)
|
||||
const { t, i18n } = useTranslation()
|
||||
const { t } = useTranslation('sharedAnnouncements')
|
||||
|
||||
const query = useAnnouncementQuery({
|
||||
showAll,
|
||||
|
@ -80,7 +80,10 @@ const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
|||
]}
|
||||
>
|
||||
<Text style={[styles.published, { color: theme.secondary }]}>
|
||||
发布于 <RelativeTime date={item.published_at} />
|
||||
<Trans
|
||||
i18nKey='sharedAnnouncements:content.published'
|
||||
components={[<RelativeTime date={item.published_at} />]}
|
||||
/>
|
||||
</Text>
|
||||
<ScrollView style={styles.scrollView} showsVerticalScrollIndicator>
|
||||
<ParseHTML
|
||||
|
@ -145,7 +148,9 @@ const ScreenSharedAnnouncements: React.FC<SharedAnnouncementsProp> = ({
|
|||
) : null}
|
||||
<Button
|
||||
type='text'
|
||||
content={item.read ? '已读' : '标记阅读'}
|
||||
content={
|
||||
item.read ? t('content.button.read') : t('content.button.unread')
|
||||
}
|
||||
loading={mutation.isLoading}
|
||||
disabled={item.read}
|
||||
onPress={() =>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { getLocalAccount } from '@utils/slices/instancesSlice'
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useEffect, useReducer, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Alert,
|
||||
Keyboard,
|
||||
|
@ -35,6 +36,7 @@ const Compose: React.FC<SharedComposeProp> = ({
|
|||
route: { params },
|
||||
navigation
|
||||
}) => {
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
|
@ -114,17 +116,11 @@ const Compose: React.FC<SharedComposeProp> = ({
|
|||
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
|
||||
composeState.text.count
|
||||
|
||||
const postButtonText = {
|
||||
conversation: '回复私信',
|
||||
reply: '发布回复',
|
||||
edit: '发嘟嘟'
|
||||
}
|
||||
|
||||
const headerLeft = useCallback(
|
||||
() => (
|
||||
<HeaderLeft
|
||||
type='text'
|
||||
content='退出编辑'
|
||||
content={t('heading.left.button')}
|
||||
onPress={() => {
|
||||
if (
|
||||
totalTextCount === 0 &&
|
||||
|
@ -134,13 +130,16 @@ const Compose: React.FC<SharedComposeProp> = ({
|
|||
navigation.goBack()
|
||||
return
|
||||
} else {
|
||||
Alert.alert('确认取消编辑?', '', [
|
||||
Alert.alert(t('heading.left.alert.title'), undefined, [
|
||||
{
|
||||
text: '退出编辑',
|
||||
text: t('heading.left.alert.buttons.exit'),
|
||||
style: 'destructive',
|
||||
onPress: () => navigation.goBack()
|
||||
},
|
||||
{ text: '继续编辑', style: 'cancel' }
|
||||
{
|
||||
text: t('heading.left.alert.buttons.continue'),
|
||||
style: 'cancel'
|
||||
}
|
||||
])
|
||||
}
|
||||
}}
|
||||
|
@ -168,7 +167,11 @@ const Compose: React.FC<SharedComposeProp> = ({
|
|||
() => (
|
||||
<HeaderRight
|
||||
type='text'
|
||||
content={params?.type ? postButtonText[params.type] : '发嘟嘟'}
|
||||
content={
|
||||
params?.type
|
||||
? t(`heading.right.button.${params.type}`)
|
||||
: t('heading.right.button.default')
|
||||
}
|
||||
onPress={() => {
|
||||
composeDispatch({ type: 'posting', payload: true })
|
||||
|
||||
|
@ -190,9 +193,9 @@ const Compose: React.FC<SharedComposeProp> = ({
|
|||
.catch(() => {
|
||||
haptics('Error')
|
||||
composeDispatch({ type: 'posting', payload: false })
|
||||
Alert.alert('发布失败', '', [
|
||||
Alert.alert(t('heading.right.alert.title'), undefined, [
|
||||
{
|
||||
text: '返回重试'
|
||||
text: t('heading.right.alert.button')
|
||||
}
|
||||
])
|
||||
})
|
||||
|
|
|
@ -8,6 +8,7 @@ import React, {
|
|||
useRef,
|
||||
useState
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Alert, KeyboardAvoidingView, Platform } from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||
|
@ -32,6 +33,7 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||
navigation
|
||||
}) => {
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const theAttachment = composeState.attachments.uploads[index].remote!
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
@ -71,7 +73,7 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||
() => (
|
||||
<HeaderLeft
|
||||
type='text'
|
||||
content='取消'
|
||||
content={t('content.editAttachment.header.left')}
|
||||
onPress={() => navigation.goBack()}
|
||||
/>
|
||||
),
|
||||
|
@ -81,7 +83,7 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||
() => (
|
||||
<HeaderRight
|
||||
type='text'
|
||||
content='应用'
|
||||
content={t('content.editAttachment.header.right.button')}
|
||||
loading={isSubmitting}
|
||||
onPress={() => {
|
||||
if (!altText && focus.current.x === 0 && focus.current.y === 0) {
|
||||
|
@ -105,24 +107,36 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||
})
|
||||
.then(() => {
|
||||
haptics('Success')
|
||||
Alert.alert('修改成功', '', [
|
||||
{
|
||||
text: '好的',
|
||||
onPress: () => {
|
||||
navigation.goBack()
|
||||
Alert.alert(
|
||||
t('content.editAttachment.header.right.succeed.title'),
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
text: t(
|
||||
'content.editAttachment.header.right.succeed.button'
|
||||
),
|
||||
onPress: () => {
|
||||
navigation.goBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
]
|
||||
)
|
||||
})
|
||||
.catch(() => {
|
||||
setIsSubmitting(false)
|
||||
haptics('Error')
|
||||
Alert.alert('修改失败', '', [
|
||||
{
|
||||
text: '返回重试',
|
||||
style: 'cancel'
|
||||
}
|
||||
])
|
||||
Alert.alert(
|
||||
t('content.editAttachment.header.right.failed.title'),
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
text: t(
|
||||
'content.editAttachment.header.right.failed.button'
|
||||
),
|
||||
style: 'cancel'
|
||||
}
|
||||
]
|
||||
)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { MutableRefObject, useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Dimensions, Image, StyleSheet, Text, View } from 'react-native'
|
||||
import { PanGestureHandler } from 'react-native-gesture-handler'
|
||||
import Animated, {
|
||||
|
@ -23,6 +24,7 @@ export interface Props {
|
|||
}
|
||||
|
||||
const ComposeEditAttachmentImage: React.FC<Props> = ({ index, focus }) => {
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
|
||||
const { composeState } = useContext(ComposeContext)
|
||||
|
@ -149,7 +151,7 @@ const ComposeEditAttachmentImage: React.FC<Props> = ({ index, focus }) => {
|
|||
</PanGestureHandler>
|
||||
</View>
|
||||
<Text style={[styles.imageFocusText, { color: theme.primary }]}>
|
||||
在预览图上拖动圆圈,以选择缩略图的焦点。
|
||||
{t('content.editAttachment.content.imageFocus')}
|
||||
</Text>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ import React, {
|
|||
useContext,
|
||||
useMemo
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'
|
||||
import ComposeContext from '../utils/createContext'
|
||||
import ComposeEditAttachmentImage from './Image'
|
||||
|
@ -28,6 +29,7 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({
|
|||
altText,
|
||||
setAltText
|
||||
}) => {
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
const { composeState } = useContext(ComposeContext)
|
||||
const theAttachment = composeState.attachments.uploads[index].remote!
|
||||
|
@ -41,6 +43,8 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({
|
|||
const video = composeState.attachments.uploads[index]
|
||||
return (
|
||||
<AttachmentVideo
|
||||
total={1}
|
||||
index={0}
|
||||
sensitiveShown={false}
|
||||
video={
|
||||
video.local
|
||||
|
@ -62,7 +66,7 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({
|
|||
{mediaDisplay}
|
||||
<View style={styles.altTextContainer}>
|
||||
<Text style={[styles.altTextInputHeading, { color: theme.primary }]}>
|
||||
为附件添加文字说明
|
||||
{t('content.editAttachment.content.altText.heading')}
|
||||
</Text>
|
||||
<TextInput
|
||||
style={[
|
||||
|
@ -74,9 +78,7 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({
|
|||
maxLength={1500}
|
||||
multiline
|
||||
onChangeText={e => setAltText(e)}
|
||||
placeholder={
|
||||
'你可以为附件添加文字说明,以便更多人可以查看他们(包括视力障碍或视力受损人士)。\n\n优质的描述应该简洁明了,但要准确地描述照片中的内容,以便用户理解其含义。'
|
||||
}
|
||||
placeholder={t('content.editAttachment.content.altText.placeholder')}
|
||||
placeholderTextColor={theme.secondary}
|
||||
scrollEnabled
|
||||
value={altText}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { forEach, groupBy, sortBy } from 'lodash'
|
|||
import React, { useCallback, useContext, useEffect, useMemo } from 'react'
|
||||
import { View, FlatList, StyleSheet } from 'react-native'
|
||||
import { Chase } from 'react-native-animated-spinkit'
|
||||
import ComposeActions from './Actions'
|
||||
import ComposeActions from './Root/Actions'
|
||||
import ComposePosting from './Posting'
|
||||
import ComposeRootFooter from './Root/Footer'
|
||||
import ComposeRootHeader from './Root/Header'
|
||||
|
|
|
@ -4,13 +4,15 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useContext, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Pressable, StyleSheet, View } from 'react-native'
|
||||
import addAttachment from './/addAttachment'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import ComposeContext from '../utils/createContext'
|
||||
import addAttachment from './Footer/addAttachment'
|
||||
|
||||
const ComposeActions: React.FC = () => {
|
||||
const { showActionSheetWithOptions } = useActionSheet()
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
|
||||
const attachmentColor = useMemo(() => {
|
||||
|
@ -71,7 +73,14 @@ const ComposeActions: React.FC = () => {
|
|||
if (!composeState.visibilityLock) {
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: ['公开', '不公开', '仅关注着', '私信', '取消'],
|
||||
title: t('content.root.actions.visibility.title'),
|
||||
options: [
|
||||
t('content.root.actions.visibility.options.public'),
|
||||
t('content.root.actions.visibility.options.unlisted'),
|
||||
t('content.root.actions.visibility.options.private'),
|
||||
t('content.root.actions.visibility.options.direct'),
|
||||
t('content.root.actions.visibility.options.cancel')
|
||||
],
|
||||
cancelButtonIndex: 4
|
||||
},
|
||||
buttonIndex => {
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useContext } from 'react'
|
||||
import ComposeAttachments from '@screens/Shared/Compose/Attachments'
|
||||
import ComposeEmojis from '@screens/Shared/Compose/Emojis'
|
||||
import ComposePoll from '@screens/Shared/Compose/Poll'
|
||||
import ComposeReply from '@screens/Shared/Compose/Reply'
|
||||
import ComposeAttachments from '@screens/Shared/Compose/Root/Footer/Attachments'
|
||||
import ComposeEmojis from '@screens/Shared/Compose/Root/Footer/Emojis'
|
||||
import ComposePoll from '@screens/Shared/Compose/Root/Footer/Poll'
|
||||
import ComposeReply from '@screens/Shared/Compose/Root/Footer/Reply'
|
||||
import ComposeContext from '@screens/Shared/Compose//utils/createContext'
|
||||
|
||||
const ComposeRootFooter: React.FC = () => {
|
||||
|
|
|
@ -13,6 +13,7 @@ import React, {
|
|||
useMemo,
|
||||
useRef
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
FlatList,
|
||||
Image,
|
||||
|
@ -22,15 +23,16 @@ import {
|
|||
View
|
||||
} from 'react-native'
|
||||
import { Chase } from 'react-native-animated-spinkit'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
import { ExtendedAttachment } from '../../utils/types'
|
||||
import addAttachment from './addAttachment'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import { ExtendedAttachment } from './utils/types'
|
||||
|
||||
const DEFAULT_HEIGHT = 200
|
||||
|
||||
const ComposeAttachments: React.FC = () => {
|
||||
const { showActionSheetWithOptions } = useActionSheet()
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
const navigation = useNavigation()
|
||||
|
||||
|
@ -234,7 +236,7 @@ const ComposeAttachments: React.FC = () => {
|
|||
color={theme.primary}
|
||||
/>
|
||||
<Text style={[styles.sensitiveText, { color: theme.primary }]}>
|
||||
标记媒体为敏感内容
|
||||
{t('content.root.footer.attachments.sensitive')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
<FlatList
|
|
@ -10,8 +10,8 @@ import {
|
|||
Text,
|
||||
View
|
||||
} from 'react-native'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import updateText from './updateText'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
import updateText from '../../updateText'
|
||||
|
||||
const SingleEmoji = ({ emoji }: { emoji: Mastodon.Emoji }) => {
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
|
@ -5,8 +5,9 @@ import { useActionSheet } from '@expo/react-native-action-sheet'
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, TextInput, View } from 'react-native'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
|
||||
const ComposePoll: React.FC = () => {
|
||||
const { showActionSheetWithOptions } = useActionSheet()
|
||||
|
@ -16,18 +17,9 @@ const ComposePoll: React.FC = () => {
|
|||
},
|
||||
composeDispatch
|
||||
} = useContext(ComposeContext)
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
|
||||
const expireMapping: { [key: string]: string } = {
|
||||
'300': '5分钟',
|
||||
'1800': '30分钟',
|
||||
'3600': '1小时',
|
||||
'21600': '6小时',
|
||||
'86400': '1天',
|
||||
'259200': '3天',
|
||||
'604800': '7天'
|
||||
}
|
||||
|
||||
const [firstRender, setFirstRender] = useState(true)
|
||||
useEffect(() => {
|
||||
setFirstRender(false)
|
||||
|
@ -63,7 +55,11 @@ const ComposePoll: React.FC = () => {
|
|||
color: hasConflict ? theme.red : theme.primary
|
||||
}
|
||||
]}
|
||||
placeholder={`选项`}
|
||||
placeholder={
|
||||
multiple
|
||||
? t('content.root.footer.poll.option.placeholder.multiple')
|
||||
: t('content.root.footer.poll.option.placeholder.single')
|
||||
}
|
||||
placeholderTextColor={theme.secondary}
|
||||
maxLength={50}
|
||||
// @ts-ignore
|
||||
|
@ -110,12 +106,20 @@ const ComposePoll: React.FC = () => {
|
|||
/>
|
||||
</View>
|
||||
<MenuRow
|
||||
title='可选项'
|
||||
content={multiple ? '多选' : '单选'}
|
||||
title={t('content.root.footer.poll.multiple.heading')}
|
||||
content={
|
||||
multiple
|
||||
? t('content.root.footer.poll.multiple.options.multiple')
|
||||
: t('content.root.footer.poll.multiple.options.single')
|
||||
}
|
||||
onPress={() =>
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: ['单选', '多选', '取消'],
|
||||
options: [
|
||||
t('content.root.footer.poll.multiple.options.single'),
|
||||
t('content.root.footer.poll.multiple.options.multiple'),
|
||||
t('content.root.footer.poll.multiple.options.cancel')
|
||||
],
|
||||
cancelButtonIndex: 2
|
||||
},
|
||||
index =>
|
||||
|
@ -129,22 +133,36 @@ const ComposePoll: React.FC = () => {
|
|||
iconBack='ChevronRight'
|
||||
/>
|
||||
<MenuRow
|
||||
title='有效期'
|
||||
content={expireMapping[expire]}
|
||||
onPress={() =>
|
||||
title={t('content.root.footer.poll.expiration.heading')}
|
||||
content={t(`content.root.footer.poll.expiration.options.${expire}`)}
|
||||
onPress={() => {
|
||||
const expirations: [
|
||||
'300',
|
||||
'1800',
|
||||
'3600',
|
||||
'21600',
|
||||
'86400',
|
||||
'259200',
|
||||
'604800'
|
||||
] = ['300', '1800', '3600', '21600', '86400', '259200', '604800']
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: [...Object.values(expireMapping), '取消'],
|
||||
options: [
|
||||
...expirations.map(e =>
|
||||
t(`content.root.footer.poll.expiration.options.${e}`)
|
||||
),
|
||||
t('content.root.footer.poll.expiration.options.cancel')
|
||||
],
|
||||
cancelButtonIndex: 7
|
||||
},
|
||||
index =>
|
||||
index < 7 &&
|
||||
composeDispatch({
|
||||
type: 'poll',
|
||||
payload: { expire: Object.keys(expireMapping)[index] }
|
||||
payload: { expire: expirations[index] }
|
||||
})
|
||||
)
|
||||
}
|
||||
}}
|
||||
iconBack='ChevronRight'
|
||||
/>
|
||||
</View>
|
|
@ -1,5 +1,5 @@
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
import React, { useContext } from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import TimelineDefault from '@root/components/Timelines/Timeline/Default'
|
||||
|
@ -12,12 +12,7 @@ const ComposeReply: React.FC = () => {
|
|||
|
||||
return (
|
||||
<View style={[styles.base, { borderTopColor: theme.border }]}>
|
||||
<TimelineDefault
|
||||
item={replyToStatus!}
|
||||
index={0}
|
||||
disableDetails
|
||||
disableOnPress
|
||||
/>
|
||||
<TimelineDefault item={replyToStatus!} disableDetails disableOnPress />
|
||||
</View>
|
||||
)
|
||||
}
|
|
@ -5,8 +5,9 @@ import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types'
|
|||
import * as VideoThumbnails from 'expo-video-thumbnails'
|
||||
import { Dispatch } from 'react'
|
||||
import { Alert, Linking } from 'react-native'
|
||||
import { ComposeAction } from './utils/types'
|
||||
import { ComposeAction } from '../../utils/types'
|
||||
import { ActionSheetOptions } from '@expo/react-native-action-sheet'
|
||||
import i18next from 'i18next'
|
||||
|
||||
export interface Props {
|
||||
composeDispatch: Dispatch<ComposeAction>
|
||||
|
@ -75,6 +76,27 @@ const addAttachment = async ({
|
|||
break
|
||||
}
|
||||
|
||||
const uploadFailed = () => {
|
||||
composeDispatch({
|
||||
type: 'attachment/upload/fail',
|
||||
payload: hash
|
||||
})
|
||||
Alert.alert(
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.failed.alert.title'
|
||||
),
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
text: i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.failed.alert.button'
|
||||
),
|
||||
onPress: () => {}
|
||||
}
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('file', {
|
||||
// @ts-ignore
|
||||
|
@ -96,35 +118,27 @@ const addAttachment = async ({
|
|||
payload: { remote: res, local: result }
|
||||
})
|
||||
} else {
|
||||
composeDispatch({
|
||||
type: 'attachment/upload/fail',
|
||||
payload: hash
|
||||
})
|
||||
Alert.alert('上传失败', '', [
|
||||
{
|
||||
text: '返回重试',
|
||||
onPress: () => {}
|
||||
}
|
||||
])
|
||||
uploadFailed()
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
composeDispatch({
|
||||
type: 'attachment/upload/fail',
|
||||
payload: hash
|
||||
})
|
||||
Alert.alert('上传失败', '', [
|
||||
{
|
||||
text: '返回重试',
|
||||
onPress: () => {}
|
||||
}
|
||||
])
|
||||
uploadFailed()
|
||||
})
|
||||
}
|
||||
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options: ['从相册选取', '现照', '取消'],
|
||||
options: [
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.options.library'
|
||||
),
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.options.photo'
|
||||
),
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.options.cancel'
|
||||
)
|
||||
],
|
||||
cancelButtonIndex: 2
|
||||
},
|
||||
async buttonIndex => {
|
||||
|
@ -133,18 +147,30 @@ const addAttachment = async ({
|
|||
status
|
||||
} = await ImagePicker.requestMediaLibraryPermissionsAsync()
|
||||
if (status !== 'granted') {
|
||||
Alert.alert('🈚️读取权限', '需要相片权限才能上传照片', [
|
||||
{
|
||||
text: '取消',
|
||||
style: 'cancel',
|
||||
onPress: () => {}
|
||||
},
|
||||
{
|
||||
text: '去系统设置',
|
||||
style: 'default',
|
||||
onPress: () => Linking.openURL('app-settings:')
|
||||
}
|
||||
])
|
||||
Alert.alert(
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.library.alert.title'
|
||||
),
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.library.alert.message'
|
||||
),
|
||||
[
|
||||
{
|
||||
text: i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.library.alert.buttons.cancel'
|
||||
),
|
||||
style: 'cancel',
|
||||
onPress: () => {}
|
||||
},
|
||||
{
|
||||
text: i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.library.alert.buttons.settings'
|
||||
),
|
||||
style: 'default',
|
||||
onPress: () => Linking.openURL('app-settings:')
|
||||
}
|
||||
]
|
||||
)
|
||||
} else {
|
||||
const result = await ImagePicker.launchImageLibraryAsync({
|
||||
mediaTypes: ImagePicker.MediaTypeOptions.All,
|
||||
|
@ -158,18 +184,30 @@ const addAttachment = async ({
|
|||
} else if (buttonIndex === 1) {
|
||||
const { status } = await ImagePicker.requestCameraPermissionsAsync()
|
||||
if (status !== 'granted') {
|
||||
Alert.alert('🈚️读取权限', '需要相机权限才能上传照片', [
|
||||
{
|
||||
text: '取消',
|
||||
style: 'cancel',
|
||||
onPress: () => {}
|
||||
},
|
||||
{
|
||||
text: '去系统设置',
|
||||
style: 'default',
|
||||
onPress: () => Linking.openURL('app-settings:')
|
||||
}
|
||||
])
|
||||
Alert.alert(
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.photo.alert.title'
|
||||
),
|
||||
i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.photo.alert.message'
|
||||
),
|
||||
[
|
||||
{
|
||||
text: i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.photo.alert.buttons.cancel'
|
||||
),
|
||||
style: 'cancel',
|
||||
onPress: () => {}
|
||||
},
|
||||
{
|
||||
text: i18next.t(
|
||||
'sharedCompose:content.root.actions.attachment.actions.photo.alert.buttons.settings'
|
||||
),
|
||||
style: 'default',
|
||||
onPress: () => Linking.openURL('app-settings:')
|
||||
}
|
||||
]
|
||||
)
|
||||
} else {
|
||||
const result = await ImagePicker.launchCameraAsync({
|
||||
mediaTypes: ImagePicker.MediaTypeOptions.All,
|
|
@ -1,45 +1,15 @@
|
|||
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||
import {
|
||||
getLocalActiveIndex,
|
||||
getLocalInstances,
|
||||
InstanceLocal
|
||||
getLocalInstances
|
||||
} from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext } from 'react'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { Chase } from 'react-native-animated-spinkit'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import ComposeSpoilerInput from '../SpoilerInput'
|
||||
import ComposeTextInput from '../TextInput'
|
||||
import ComposeContext from '../utils/createContext'
|
||||
|
||||
const PostingAs: React.FC<{
|
||||
id: Mastodon.Account['id']
|
||||
domain: InstanceLocal['url']
|
||||
}> = ({ id, domain }) => {
|
||||
const { theme } = useTheme()
|
||||
|
||||
const { data, status } = useAccountQuery({ id })
|
||||
|
||||
switch (status) {
|
||||
case 'loading':
|
||||
return (
|
||||
<Chase
|
||||
size={StyleConstants.Font.LineHeight.M - 2}
|
||||
color={theme.secondary}
|
||||
/>
|
||||
)
|
||||
case 'success':
|
||||
return (
|
||||
<Text style={[styles.postingAsText, { color: theme.secondary }]}>
|
||||
用 @{data?.acct}@{domain} 发布
|
||||
</Text>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
import PostingAs from './Header/PostingAs'
|
||||
import ComposeSpoilerInput from './Header/SpoilerInput'
|
||||
import ComposeTextInput from './Header/TextInput'
|
||||
|
||||
const ComposeRootHeader: React.FC = () => {
|
||||
const { composeState } = useContext(ComposeContext)
|
||||
|
@ -68,9 +38,6 @@ const styles = StyleSheet.create({
|
|||
postingAs: {
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginTop: StyleConstants.Spacing.S
|
||||
},
|
||||
postingAsText: {
|
||||
...StyleConstants.FontStyle.S
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||
import { InstanceLocal } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text } from 'react-native'
|
||||
import { Chase } from 'react-native-animated-spinkit'
|
||||
|
||||
const ComposePostingAs: React.FC<{
|
||||
id: Mastodon.Account['id']
|
||||
domain: InstanceLocal['url']
|
||||
}> = ({ id, domain }) => {
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
|
||||
const { data, status } = useAccountQuery({ id })
|
||||
|
||||
switch (status) {
|
||||
case 'loading':
|
||||
return (
|
||||
<Chase
|
||||
size={StyleConstants.Font.LineHeight.M - 2}
|
||||
color={theme.secondary}
|
||||
/>
|
||||
)
|
||||
case 'success':
|
||||
return (
|
||||
<Text style={[styles.text, { color: theme.secondary }]}>
|
||||
{t('content.root.header.postingAs', { acct: data?.acct, domain })}
|
||||
</Text>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
...StyleConstants.FontStyle.S
|
||||
}
|
||||
})
|
||||
|
||||
export default ComposePostingAs
|
|
@ -1,12 +1,14 @@
|
|||
import React, { useContext } from 'react'
|
||||
import { StyleSheet, Text, TextInput } from 'react-native'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import formatText from './formatText'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text, TextInput } from 'react-native'
|
||||
import formatText from '../../formatText'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
|
||||
const ComposeSpoilerInput: React.FC = () => {
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
|
@ -23,7 +25,7 @@ const ComposeSpoilerInput: React.FC = () => {
|
|||
autoFocus
|
||||
enablesReturnKeyAutomatically
|
||||
multiline
|
||||
placeholder='折叠部分的警告信息'
|
||||
placeholder={t('content.root.header.spoilerInput.placeholder')}
|
||||
placeholderTextColor={theme.secondary}
|
||||
onChangeText={content =>
|
||||
formatText({
|
|
@ -1,12 +1,14 @@
|
|||
import React, { useContext } from 'react'
|
||||
import { StyleSheet, Text, TextInput } from 'react-native'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import formatText from './formatText'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import React, { useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text, TextInput } from 'react-native'
|
||||
import formatText from '../../formatText'
|
||||
import ComposeContext from '../../utils/createContext'
|
||||
|
||||
const ComposeTextInput: React.FC = () => {
|
||||
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||
const { t } = useTranslation('sharedCompose')
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
|
@ -23,7 +25,7 @@ const ComposeTextInput: React.FC = () => {
|
|||
autoFocus
|
||||
enablesReturnKeyAutomatically
|
||||
multiline
|
||||
placeholder='想说点什么'
|
||||
placeholder={t('content.root.header.textInput.placeholder')}
|
||||
placeholderTextColor={theme.secondary}
|
||||
onChangeText={content =>
|
||||
formatText({
|
|
@ -1,6 +1,6 @@
|
|||
import ComponentAccount from '@components/Account'
|
||||
import haptics from '@components/haptics'
|
||||
import ComponentHashtag from '@components/Timelines/Hashtag'
|
||||
import ComponentHashtag from '@components/Hashtag'
|
||||
import React, { Dispatch, useCallback } from 'react'
|
||||
import updateText from '../updateText'
|
||||
import { ComposeAction, ComposeState } from '../utils/types'
|
||||
|
|
|
@ -39,15 +39,7 @@ export type ComposeState = {
|
|||
'3': string | undefined
|
||||
}
|
||||
multiple: boolean
|
||||
expire:
|
||||
| '300'
|
||||
| '1800'
|
||||
| '3600'
|
||||
| '21600'
|
||||
| '86400'
|
||||
| '259200'
|
||||
| '604800'
|
||||
| string
|
||||
expire: '300' | '1800' | '3600' | '21600' | '86400' | '259200' | '604800'
|
||||
}
|
||||
attachments: {
|
||||
sensitive: boolean
|
||||
|
|
|
@ -2,6 +2,7 @@ import SegmentedControl from '@react-native-community/segmented-control'
|
|||
import { useNavigation } from '@react-navigation/native'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Dimensions, StyleSheet, View } from 'react-native'
|
||||
import { TabView } from 'react-native-tab-view'
|
||||
import RelationshipsList from './Relationships/List'
|
||||
|
@ -12,6 +13,7 @@ const ScreenSharedRelationships: React.FC<SharedRelationshipsProp> = ({
|
|||
params: { account, initialType }
|
||||
}
|
||||
}) => {
|
||||
const { t } = useTranslation('sharedRelationships')
|
||||
const { mode } = useTheme()
|
||||
const navigation = useNavigation()
|
||||
|
||||
|
@ -23,7 +25,7 @@ const ScreenSharedRelationships: React.FC<SharedRelationshipsProp> = ({
|
|||
<View style={styles.segmentsContainer}>
|
||||
<SegmentedControl
|
||||
appearance={mode}
|
||||
values={['关注中', '关注者']}
|
||||
values={[t('heading.segments.left'), t('heading.segments.right')]}
|
||||
selectedIndex={segment}
|
||||
onChange={({ nativeEvent }) =>
|
||||
setSegment(nativeEvent.selectedSegmentIndex)
|
||||
|
|
|
@ -2,7 +2,7 @@ import ComponentAccount from '@components/Account'
|
|||
import ComponentSeparator from '@components/Separator'
|
||||
import TimelineEmpty from '@components/Timelines/Timeline/Empty'
|
||||
import TimelineEnd from '@components/Timelines/Timeline/End'
|
||||
import { useScrollToTop } from '@react-navigation/native'
|
||||
import { useNavigation, useScrollToTop } from '@react-navigation/native'
|
||||
import { useRelationshipsQuery } from '@utils/queryHooks/relationships'
|
||||
import React, { useCallback, useMemo, useRef } from 'react'
|
||||
import { RefreshControl, StyleSheet } from 'react-native'
|
||||
|
@ -14,6 +14,7 @@ export interface Props {
|
|||
}
|
||||
|
||||
const RelationshipsList: React.FC<Props> = ({ id, type }) => {
|
||||
const navigation = useNavigation()
|
||||
const {
|
||||
status,
|
||||
data,
|
||||
|
@ -42,7 +43,14 @@ const RelationshipsList: React.FC<Props> = ({ id, type }) => {
|
|||
|
||||
const keyExtractor = useCallback(({ id }) => id, [])
|
||||
const renderItem = useCallback(
|
||||
({ item }) => <ComponentAccount account={item} />,
|
||||
({ item }) => (
|
||||
<ComponentAccount
|
||||
account={item}
|
||||
onPress={() =>
|
||||
navigation.push('Screen-Shared-Account', { account: item })
|
||||
}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
)
|
||||
const flItemEmptyComponent = useMemo(
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import ComponentHashtag from '@components/Timelines/Hashtag'
|
||||
import ComponentAccount from '@components/Account'
|
||||
import ComponentHashtag from '@components/Hashtag'
|
||||
import ComponentSeparator from '@components/Separator'
|
||||
import TimelineDefault from '@components/Timelines/Timeline/Default'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import ComponentAccount from '@root/components/Account'
|
||||
import ComponentSeparator from '@root/components/Separator'
|
||||
import TimelineDefault from '@root/components/Timelines/Timeline/Default'
|
||||
import { useSearchQuery } from '@utils/queryHooks/search'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
|
@ -22,6 +23,7 @@ export interface Props {
|
|||
}
|
||||
|
||||
const ScreenSharedSearch: React.FC<Props> = ({ searchTerm }) => {
|
||||
const { t } = useTranslation('sharedSearch')
|
||||
const navigation = useNavigation()
|
||||
const { theme } = useTheme()
|
||||
const { status, data, refetch } = useSearchQuery({
|
||||
|
@ -32,6 +34,11 @@ const ScreenSharedSearch: React.FC<Props> = ({ searchTerm }) => {
|
|||
const [setctionData, setSectionData] = useState<
|
||||
{ title: string; data: any }[]
|
||||
>([])
|
||||
const mapKeyToTranslations = {
|
||||
accounts: t('content.sections.accounts'),
|
||||
hashtags: t('content.sections.hashtags'),
|
||||
statuses: t('content.sections.statuses')
|
||||
}
|
||||
useEffect(
|
||||
() =>
|
||||
data &&
|
||||
|
@ -40,6 +47,8 @@ const ScreenSharedSearch: React.FC<Props> = ({ searchTerm }) => {
|
|||
.map(key => ({
|
||||
title: key,
|
||||
// @ts-ignore
|
||||
translation: mapKeyToTranslations[key],
|
||||
// @ts-ignore
|
||||
data: data[key]
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
|
@ -63,8 +72,8 @@ const ScreenSharedSearch: React.FC<Props> = ({ searchTerm }) => {
|
|||
}
|
||||
}, [searchTerm])
|
||||
|
||||
const listEmpty = useMemo(
|
||||
() => (
|
||||
const listEmpty = useMemo(() => {
|
||||
return (
|
||||
<View style={styles.emptyBase}>
|
||||
<View>
|
||||
{status === 'loading' ? (
|
||||
|
@ -83,67 +92,70 @@ const ScreenSharedSearch: React.FC<Props> = ({ searchTerm }) => {
|
|||
{ color: theme.primary }
|
||||
]}
|
||||
>
|
||||
输入关键词搜索<Text style={styles.emptyFontBold}>用户</Text>、
|
||||
<Text style={styles.emptyFontBold}>话题标签</Text>或者
|
||||
<Text style={styles.emptyFontBold}>嘟文</Text>
|
||||
<Trans
|
||||
i18nKey='sharedSearch:content.empty.general'
|
||||
components={{ bold: <Text style={styles.emptyFontBold} /> }}
|
||||
/>
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
高级搜索格式
|
||||
{t('content.empty.advanced.header')}
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>@username@domain</Text>
|
||||
{' '}
|
||||
搜索用户
|
||||
{t('content.empty.advanced.example.account')}
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>#example</Text>
|
||||
{' '}搜索话题标签
|
||||
{' '}
|
||||
{t('content.empty.advanced.example.hashtag')}
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定嘟文
|
||||
{' '}
|
||||
{t('content.empty.advanced.example.statusLink')}
|
||||
</Text>
|
||||
<Text style={[styles.emptyAdvanced, { color: theme.primary }]}>
|
||||
<Text style={{ color: theme.secondary }}>URL</Text>
|
||||
{' '}搜索指定用户
|
||||
{' '}
|
||||
{t('content.empty.advanced.example.accountLink')}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
[status]
|
||||
)
|
||||
)
|
||||
}, [status])
|
||||
const sectionHeader = useCallback(
|
||||
({ section: { title } }) => (
|
||||
({ section: { translation } }) => (
|
||||
<View
|
||||
style={[styles.sectionHeader, { backgroundColor: theme.background }]}
|
||||
>
|
||||
<Text style={[styles.sectionHeaderText, { color: theme.primary }]}>
|
||||
{title}
|
||||
{translation}
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
[]
|
||||
)
|
||||
const sectionFooter = useCallback(
|
||||
({ section: { data, title } }) =>
|
||||
({ section: { data, translation } }) =>
|
||||
!data.length ? (
|
||||
<View
|
||||
style={[styles.sectionFooter, { backgroundColor: theme.background }]}
|
||||
>
|
||||
<Text style={[styles.sectionFooterText, { color: theme.secondary }]}>
|
||||
找不到{' '}
|
||||
<Text style={{ fontWeight: StyleConstants.Font.Weight.Bold }}>
|
||||
{searchTerm}
|
||||
</Text>{' '}
|
||||
相关的{title}
|
||||
<Trans
|
||||
i18nKey='sharedSearch:content.notFound'
|
||||
values={{ searchTerm, type: translation }}
|
||||
components={{ bold: <Text style={styles.emptyFontBold} /> }}
|
||||
/>
|
||||
</Text>
|
||||
</View>
|
||||
) : null,
|
||||
[searchTerm]
|
||||
)
|
||||
const listItem = useCallback(({ item, section, index }) => {
|
||||
const listItem = useCallback(({ item, section }) => {
|
||||
switch (section.title) {
|
||||
case 'accounts':
|
||||
return (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HeaderLeft } from '@components/Header'
|
||||
import { HeaderCenter, HeaderLeft } from '@components/Header'
|
||||
import { ParseEmojis } from '@components/Parse'
|
||||
import { StackNavigationState, TypedNavigator } from '@react-navigation/native'
|
||||
import { StackScreenProps } from '@react-navigation/stack'
|
||||
|
@ -15,7 +15,7 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||
import { debounce } from 'lodash'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { Platform, StyleSheet, Text, View } from 'react-native'
|
||||
import { TextInput } from 'react-native-gesture-handler'
|
||||
import { NativeStackNavigationOptions } from 'react-native-screens/lib/typescript'
|
||||
import {
|
||||
|
@ -211,7 +211,7 @@ const sharedScreens = (
|
|||
color: theme.primary
|
||||
}
|
||||
]}
|
||||
children='搜索'
|
||||
children={t('sharedSearch:content.header.prefix')}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
@ -233,7 +233,7 @@ const sharedScreens = (
|
|||
onSubmitEditing={({ nativeEvent: { text } }) =>
|
||||
setSearchTerm(text)
|
||||
}
|
||||
placeholder={'些什么'}
|
||||
placeholder={t('sharedSearch:content.header.placeholder')}
|
||||
placeholderTextColor={theme.secondary}
|
||||
returnKeyType='go'
|
||||
/>
|
||||
|
@ -248,7 +248,10 @@ const sharedScreens = (
|
|||
name='Screen-Shared-Toot'
|
||||
component={ScreenSharedToot}
|
||||
options={({ navigation }: SharedTootProp) => ({
|
||||
title: t('sharedToot:heading'),
|
||||
headerTitle: t('sharedToot:heading'),
|
||||
...(Platform.OS === 'android' && {
|
||||
headerCenter: () => <HeaderCenter content={t('sharedToot:heading')} />
|
||||
}),
|
||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
|
||||
})}
|
||||
/>
|
||||
|
|
Loading…
Reference in New Issue