mirror of
https://github.com/tooot-app/app
synced 2025-01-27 08:51:26 +01:00
Updates
This commit is contained in:
parent
813f6b57c4
commit
f977fdfa8b
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
BIN
assets/icon.png
BIN
assets/icon.png
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 33 KiB |
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 69 KiB |
@ -154,7 +154,7 @@ const GracefullyImage: React.FC<Props> = ({
|
||||
children={children}
|
||||
style={[style, dimension && { ...dimension }]}
|
||||
{...(onPress
|
||||
? !imageVisible
|
||||
? hidden
|
||||
? { disabled: true }
|
||||
: { onPress }
|
||||
: { disabled: true })}
|
||||
|
@ -244,6 +244,7 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
{type === 'local' && appData ? (
|
||||
<InstanceAuth
|
||||
instanceDomain={instanceDomain!}
|
||||
instanceUri={instanceQuery.data!.uri}
|
||||
appData={appData}
|
||||
goBack={goBack}
|
||||
/>
|
||||
|
@ -8,12 +8,14 @@ import { useDispatch } from 'react-redux'
|
||||
|
||||
export interface Props {
|
||||
instanceDomain: string
|
||||
// Domain can be different than uri
|
||||
instanceUri: Mastodon.Instance['uri']
|
||||
appData: InstanceLocal['appData']
|
||||
goBack?: boolean
|
||||
}
|
||||
|
||||
const InstanceAuth = React.memo(
|
||||
({ instanceDomain, appData, goBack }: Props) => {
|
||||
({ instanceDomain, instanceUri, appData, goBack }: Props) => {
|
||||
let redirectUri: string
|
||||
switch (Constants.manifest.releaseChannel) {
|
||||
case 'production':
|
||||
@ -70,6 +72,7 @@ const InstanceAuth = React.memo(
|
||||
localAddInstance({
|
||||
url: instanceDomain,
|
||||
token: accessToken,
|
||||
uri: instanceUri,
|
||||
appData
|
||||
})
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import { Image, StyleSheet, Text } from 'react-native'
|
||||
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'
|
||||
|
||||
@ -49,10 +50,7 @@ const ParseEmojis: React.FC<Props> = ({
|
||||
<Text key={i}>
|
||||
{/* When emoji starts a paragraph, lineHeight will break */}
|
||||
{i === 0 ? <Text> </Text> : null}
|
||||
<Image
|
||||
source={{ uri: emojis[emojiIndex].url }}
|
||||
style={[styles.image]}
|
||||
/>
|
||||
<Image uri={emojis[emojiIndex].url} style={[styles.image]} />
|
||||
</Text>
|
||||
)
|
||||
} else {
|
||||
|
@ -68,7 +68,7 @@ const renderNode = ({
|
||||
mention => mention.url === href
|
||||
)
|
||||
const differentAccount = routeParams?.account
|
||||
? routeParams.account.id !== mentions[accountIndex].id
|
||||
? routeParams.account.id !== mentions[accountIndex]?.id
|
||||
: true
|
||||
return (
|
||||
<Text
|
||||
|
@ -5,22 +5,20 @@ import TimeAgo from 'react-timeago'
|
||||
// @ts-ignore
|
||||
import buildFormatter from 'react-timeago/lib/formatters/buildFormatter'
|
||||
|
||||
import zh from '@root/i18n/zh/components/relativeTime'
|
||||
import en from '@root/i18n/en/components/relativeTime'
|
||||
|
||||
export interface Props {
|
||||
date: string
|
||||
}
|
||||
|
||||
const RelativeTime: React.FC<Props> = ({ date }) => {
|
||||
const { i18n } = useTranslation()
|
||||
const mapLanguageToTranslation: { [key: string]: Object } = {
|
||||
'zh-CN': zh,
|
||||
'en-US': en
|
||||
}
|
||||
const formatter = buildFormatter(mapLanguageToTranslation[i18n.language])
|
||||
const { t } = useTranslation('relativeTime')
|
||||
|
||||
return <TimeAgo date={date} formatter={formatter} component={Text} />
|
||||
return (
|
||||
<TimeAgo
|
||||
date={date}
|
||||
component={Text}
|
||||
formatter={buildFormatter(t('strings', { returnObjects: true }))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default RelativeTime
|
||||
|
@ -56,6 +56,7 @@ const Timeline: React.FC<Props> = ({
|
||||
refetch,
|
||||
isSuccess,
|
||||
isFetching,
|
||||
isLoading,
|
||||
hasPreviousPage,
|
||||
fetchPreviousPage,
|
||||
isFetchingPreviousPage,
|
||||
@ -170,7 +171,8 @@ const Timeline: React.FC<Props> = ({
|
||||
<RefreshControl
|
||||
{...(Platform.OS === 'android' && { enabled: true })}
|
||||
refreshing={
|
||||
isFetchingPreviousPage || (isFetching && !isFetchingNextPage)
|
||||
isFetchingPreviousPage ||
|
||||
(isFetching && !isFetchingNextPage && !isLoading)
|
||||
}
|
||||
onRefresh={() => {
|
||||
if (hasPreviousPage) {
|
||||
@ -192,7 +194,13 @@ const Timeline: React.FC<Props> = ({
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[hasPreviousPage, isFetchingPreviousPage, isFetching, isFetchingNextPage]
|
||||
[
|
||||
hasPreviousPage,
|
||||
isFetchingPreviousPage,
|
||||
isFetching,
|
||||
isFetchingNextPage,
|
||||
isLoading
|
||||
]
|
||||
)
|
||||
const onScrollToIndexFailed = useCallback(error => {
|
||||
const offset = error.averageItemLength * error.index
|
||||
@ -209,10 +217,10 @@ const Timeline: React.FC<Props> = ({
|
||||
return (
|
||||
<FlatList
|
||||
ref={flRef}
|
||||
windowSize={11}
|
||||
windowSize={8}
|
||||
data={flattenData}
|
||||
initialNumToRender={5}
|
||||
maxToRenderPerBatch={5}
|
||||
initialNumToRender={3}
|
||||
maxToRenderPerBatch={3}
|
||||
style={styles.flatList}
|
||||
renderItem={renderItem}
|
||||
onEndReached={onEndReached}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import Button from '@components/Button'
|
||||
import haptics from '@components/haptics'
|
||||
import Icon from '@components/Icon'
|
||||
import { ParseEmojis } from '@components/Parse'
|
||||
import RelativeTime from '@components/RelativeTime'
|
||||
import { ParseEmojis } from '@root/components/Parse'
|
||||
import { toast } from '@root/components/toast'
|
||||
import { toast } from '@components/toast'
|
||||
import {
|
||||
QueryKeyTimeline,
|
||||
useTimelineMutation
|
||||
@ -32,7 +32,7 @@ const TimelinePoll: React.FC<Props> = ({
|
||||
sameAccount
|
||||
}) => {
|
||||
const { mode, theme } = useTheme()
|
||||
const { t, i18n } = useTranslation('timeline')
|
||||
const { t } = useTranslation('timeline')
|
||||
|
||||
const [allOptions, setAllOptions] = useState(
|
||||
new Array(poll.options.length).fill(false)
|
||||
@ -220,7 +220,7 @@ const TimelinePoll: React.FC<Props> = ({
|
||||
<Icon
|
||||
style={styles.optionSelection}
|
||||
name={isSelected(index)}
|
||||
size={StyleConstants.Font.Size.L}
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={theme.primary}
|
||||
/>
|
||||
<Text style={styles.optionText}>
|
||||
@ -275,6 +275,7 @@ const styles = StyleSheet.create({
|
||||
flex: 1
|
||||
},
|
||||
optionSelection: {
|
||||
paddingTop: StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
},
|
||||
optionPercentage: {
|
||||
|
@ -1,3 +1,5 @@
|
||||
export default {
|
||||
common: require('./common').default
|
||||
common: require('./common').default,
|
||||
|
||||
relativeTime: require('./components/relativeTime').default
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
const strings = {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: 'ago',
|
||||
suffixFromNow: 'from now',
|
||||
seconds: '%d seconds',
|
||||
minute: 'about a minute',
|
||||
minutes: '%d minutes',
|
||||
hour: 'about an hour',
|
||||
hours: 'about %d hours',
|
||||
day: 'a day',
|
||||
days: '%d days',
|
||||
month: 'about a month',
|
||||
months: '%d months',
|
||||
year: 'about a year',
|
||||
years: '%d years',
|
||||
wordSeparator: ' '
|
||||
export default {
|
||||
strings: {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: 'ago',
|
||||
suffixFromNow: 'from now',
|
||||
seconds: '%d seconds',
|
||||
minute: 'about a minute',
|
||||
minutes: '%d minutes',
|
||||
hour: 'about an hour',
|
||||
hours: 'about %d hours',
|
||||
day: 'a day',
|
||||
days: '%d days',
|
||||
month: 'about a month',
|
||||
months: '%d months',
|
||||
year: 'about a year',
|
||||
years: '%d years',
|
||||
wordSeparator: ' '
|
||||
}
|
||||
}
|
||||
|
||||
export default strings
|
||||
|
@ -1,32 +1,20 @@
|
||||
import { store } from '@root/store'
|
||||
import { getSettingsLanguage, supportedLngs } from '@utils/slices/settingsSlice'
|
||||
import i18next from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
import * as Localization from 'expo-localization'
|
||||
|
||||
import zh from '@root/i18n/zh/_all'
|
||||
import en from '@root/i18n/en/_all'
|
||||
import {
|
||||
changeLanguage,
|
||||
getSettingsLanguage
|
||||
} from '@utils/slices/settingsSlice'
|
||||
import { store } from '@root/store'
|
||||
import zh_Hans from '@root/i18n/zh-Hans/_all'
|
||||
|
||||
if (!getSettingsLanguage(store.getState())) {
|
||||
const deviceLocal = Localization.locale
|
||||
if (deviceLocal.startsWith('zh')) {
|
||||
store.dispatch(changeLanguage('zh-CN'))
|
||||
} else {
|
||||
store.dispatch(changeLanguage('en-US'))
|
||||
}
|
||||
}
|
||||
i18next.use(initReactI18next).init({
|
||||
lng: 'zh-CN',
|
||||
fallbackLng: 'en-US',
|
||||
supportedLngs: ['zh-CN', 'en-US'],
|
||||
lng: getSettingsLanguage(store.getState()),
|
||||
fallbackLng: 'en',
|
||||
supportedLngs: supportedLngs,
|
||||
|
||||
ns: ['common'],
|
||||
defaultNS: 'common',
|
||||
|
||||
resources: { 'zh-CN': zh, 'en-US': en },
|
||||
resources: { 'zh-Hans': zh_Hans, en },
|
||||
|
||||
saveMissing: true,
|
||||
missingKeyHandler: (lng, ns, key, fallbackValue) => {
|
||||
|
@ -21,5 +21,6 @@ export default {
|
||||
sharedAnnouncements: require('./screens/sharedAnnouncements').default,
|
||||
|
||||
relationship: require('./components/relationship').default,
|
||||
relativeTime: require('./components/relativeTime').default,
|
||||
timeline: require('./components/timeline').default
|
||||
}
|
21
src/i18n/zh-Hans/components/relativeTime.ts
Normal file
21
src/i18n/zh-Hans/components/relativeTime.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
strings: {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: '之前',
|
||||
suffixFromNow: '之后',
|
||||
seconds: '%d秒',
|
||||
minute: '1分钟',
|
||||
minutes: '%d分钟',
|
||||
hour: '1小时',
|
||||
hours: '%d小时',
|
||||
day: '1天',
|
||||
days: '%d天',
|
||||
month: '1个月',
|
||||
months: '%d月',
|
||||
year: '大约1年',
|
||||
years: '%d年',
|
||||
|
||||
wordSeparator: ''
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ export default {
|
||||
language: {
|
||||
heading: '切换语言',
|
||||
options: {
|
||||
zh: '简体中文',
|
||||
en: 'English',
|
||||
'en': 'English',
|
||||
'zh-Hans': '简体中文',
|
||||
cancel: '$t(common:buttons.cancel)'
|
||||
}
|
||||
},
|
@ -1,21 +0,0 @@
|
||||
const strings = {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: '之前',
|
||||
suffixFromNow: '之后',
|
||||
seconds: '%d秒',
|
||||
minute: '1分钟',
|
||||
minutes: '%d分钟',
|
||||
hour: '1小时',
|
||||
hours: '%d小时',
|
||||
day: '1天',
|
||||
days: '%d天',
|
||||
month: '1个月',
|
||||
months: '%d月',
|
||||
year: '大约1年',
|
||||
years: '%d年',
|
||||
|
||||
wordSeparator: ''
|
||||
}
|
||||
|
||||
export default strings
|
@ -98,33 +98,28 @@ const ScreenMeSettings: React.FC = () => {
|
||||
title={t('content.language.heading')}
|
||||
content={t(`content.language.options.${settingsLanguage}`)}
|
||||
iconBack='ChevronRight'
|
||||
onPress={() =>
|
||||
onPress={() => {
|
||||
const availableLanguages = Object.keys(
|
||||
i18n.services.resourceStore.data
|
||||
)
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
title: t('content.language.heading'),
|
||||
options: [
|
||||
t('content.language.options.zh'),
|
||||
t('content.language.options.en'),
|
||||
...availableLanguages.map(language =>
|
||||
t(`content.language.options.${language}`)
|
||||
),
|
||||
t('content.language.options.cancel')
|
||||
],
|
||||
cancelButtonIndex: 2
|
||||
cancelButtonIndex: i18n.languages.length
|
||||
},
|
||||
buttonIndex => {
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
haptics('Success')
|
||||
dispatch(changeLanguage('zh-CN'))
|
||||
i18n.changeLanguage('zh-CN')
|
||||
break
|
||||
case 1:
|
||||
haptics('Success')
|
||||
dispatch(changeLanguage('en-US'))
|
||||
i18n.changeLanguage('en-US')
|
||||
break
|
||||
}
|
||||
haptics('Success')
|
||||
dispatch(changeLanguage(availableLanguages[buttonIndex]))
|
||||
i18n.changeLanguage(availableLanguages[buttonIndex])
|
||||
}
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<MenuRow
|
||||
title={t('content.theme.heading')}
|
||||
@ -229,7 +224,7 @@ const ScreenMeSettings: React.FC = () => {
|
||||
iconBack='ChevronRight'
|
||||
/>
|
||||
<Text style={[styles.version, { color: theme.secondary }]}>
|
||||
{t('content.version', { version: '1.0.0' })}
|
||||
{t('content.version', { version: Constants.manifest.version })}
|
||||
</Text>
|
||||
</MenuContainer>
|
||||
|
||||
|
@ -15,6 +15,7 @@ export interface Props {
|
||||
|
||||
const AccountInformationCreated = forwardRef<ShimmerPlaceholder, Props>(
|
||||
({ account }, ref) => {
|
||||
const { i18n } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation('sharedAccount')
|
||||
const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient)
|
||||
@ -26,7 +27,11 @@ const AccountInformationCreated = forwardRef<ShimmerPlaceholder, Props>(
|
||||
width={StyleConstants.Font.Size.S * 8}
|
||||
height={StyleConstants.Font.LineHeight.S}
|
||||
style={{ marginBottom: StyleConstants.Spacing.M }}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
shimmerColors={[
|
||||
theme.shimmerDefault,
|
||||
theme.shimmerHighlight,
|
||||
theme.shimmerDefault
|
||||
]}
|
||||
>
|
||||
<View style={styles.created}>
|
||||
<Icon
|
||||
@ -42,11 +47,14 @@ const AccountInformationCreated = forwardRef<ShimmerPlaceholder, Props>(
|
||||
}}
|
||||
>
|
||||
{t('content.created_at', {
|
||||
date: new Date(account?.created_at!).toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
date: new Date(account?.created_at!).toLocaleDateString(
|
||||
i18n.language,
|
||||
{
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
}
|
||||
)
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
|
@ -28,7 +28,7 @@ const AccountInformationFields = React.memo(
|
||||
size={'M'}
|
||||
emojis={account.emojis}
|
||||
showFullLink
|
||||
numberOfLines={3}
|
||||
numberOfLines={5}
|
||||
/>
|
||||
{field.verified_at ? (
|
||||
<Icon
|
||||
@ -45,7 +45,7 @@ const AccountInformationFields = React.memo(
|
||||
size={'M'}
|
||||
emojis={account.emojis}
|
||||
showFullLink
|
||||
numberOfLines={3}
|
||||
numberOfLines={5}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -123,19 +123,28 @@ const Compose: React.FC<SharedComposeProp> = ({
|
||||
<HeaderLeft
|
||||
type='text'
|
||||
content='退出编辑'
|
||||
onPress={() =>
|
||||
Alert.alert('确认取消编辑?', '', [
|
||||
{
|
||||
text: '退出编辑',
|
||||
style: 'destructive',
|
||||
onPress: () => navigation.goBack()
|
||||
},
|
||||
{ text: '继续编辑', style: 'cancel' }
|
||||
])
|
||||
}
|
||||
onPress={() => {
|
||||
if (
|
||||
totalTextCount === 0 &&
|
||||
composeState.attachments.uploads.length === 0 &&
|
||||
composeState.poll.active === false
|
||||
) {
|
||||
navigation.goBack()
|
||||
return
|
||||
} else {
|
||||
Alert.alert('确认取消编辑?', '', [
|
||||
{
|
||||
text: '退出编辑',
|
||||
style: 'destructive',
|
||||
onPress: () => navigation.goBack()
|
||||
},
|
||||
{ text: '继续编辑', style: 'cancel' }
|
||||
])
|
||||
}
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
[totalTextCount]
|
||||
)
|
||||
const headerCenter = useCallback(
|
||||
() => (
|
||||
|
@ -1,17 +1,77 @@
|
||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||
import {
|
||||
getLocalActiveIndex,
|
||||
getLocalInstances,
|
||||
InstanceLocal
|
||||
} from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext } from 'react'
|
||||
import ComposeSpoilerInput from '@screens/Shared/Compose/SpoilerInput'
|
||||
import ComposeTextInput from '@screens/Shared/Compose/TextInput'
|
||||
import ComposeContext from '@screens/Shared/Compose//utils/createContext'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { Chase } from 'react-native-animated-spinkit'
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
const ComposeRootHeader: React.FC = () => {
|
||||
const { composeState } = useContext(ComposeContext)
|
||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
||||
const localInstances = useSelector(getLocalInstances)
|
||||
|
||||
return (
|
||||
<>
|
||||
{localActiveIndex !== null &&
|
||||
localInstances.length &&
|
||||
localInstances.length > 1 && (
|
||||
<View style={styles.postingAs}>
|
||||
<PostingAs
|
||||
id={localInstances[localActiveIndex].account.id}
|
||||
domain={localInstances[localActiveIndex].uri}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{composeState.spoiler.active ? <ComposeSpoilerInput /> : null}
|
||||
<ComposeTextInput />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
postingAs: {
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginTop: StyleConstants.Spacing.S
|
||||
},
|
||||
postingAsText: {
|
||||
...StyleConstants.FontStyle.S
|
||||
}
|
||||
})
|
||||
|
||||
export default ComposeRootHeader
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
Image,
|
||||
Platform,
|
||||
Share,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
Text
|
||||
} from 'react-native'
|
||||
@ -57,20 +58,23 @@ const ScreenSharedImagesViewer: React.FC<SharedImagesViewerProp> = ({
|
||||
|
||||
const component = useCallback(
|
||||
() => (
|
||||
<ImageViewer
|
||||
index={initialIndex}
|
||||
imageUrls={imageUrls}
|
||||
pageAnimateTime={250}
|
||||
enableSwipeDown={true}
|
||||
useNativeDriver={true}
|
||||
swipeDownThreshold={100}
|
||||
renderIndicator={() => <></>}
|
||||
saveToLocalByLongPress={false}
|
||||
onSwipeDown={() => navigation.goBack()}
|
||||
style={{ flex: 1, marginBottom: 44 + safeAreaInsets.bottom }}
|
||||
onChange={index => index !== undefined && setCurrentIndex(index)}
|
||||
renderImage={props => <TheImage {...props} imageUrls={imageUrls} />}
|
||||
/>
|
||||
<>
|
||||
<StatusBar barStyle='light-content' />
|
||||
<ImageViewer
|
||||
index={initialIndex}
|
||||
imageUrls={imageUrls}
|
||||
pageAnimateTime={250}
|
||||
enableSwipeDown={true}
|
||||
useNativeDriver={true}
|
||||
swipeDownThreshold={100}
|
||||
renderIndicator={() => <></>}
|
||||
saveToLocalByLongPress={false}
|
||||
onSwipeDown={() => navigation.goBack()}
|
||||
style={{ flex: 1, marginBottom: 44 + safeAreaInsets.bottom }}
|
||||
onChange={index => index !== undefined && setCurrentIndex(index)}
|
||||
renderImage={props => <TheImage {...props} imageUrls={imageUrls} />}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
[]
|
||||
)
|
||||
@ -85,7 +89,9 @@ const ScreenSharedImagesViewer: React.FC<SharedImagesViewerProp> = ({
|
||||
}, [currentIndex])
|
||||
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}>
|
||||
<Stack.Navigator
|
||||
screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}
|
||||
>
|
||||
<Stack.Screen
|
||||
name='Screen-Shared-ImagesViewer-Root'
|
||||
component={component}
|
||||
|
@ -150,7 +150,7 @@ const ScreenSharedSearch: React.FC<Props> = ({ searchTerm }) => {
|
||||
<ComponentAccount
|
||||
account={item}
|
||||
onPress={() => {
|
||||
navigation.push('Screen-Shared-Account', { item })
|
||||
navigation.push('Screen-Shared-Account', { account: item })
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -201,16 +201,27 @@ const sharedScreens = (
|
||||
// https://github.com/react-navigation/react-navigation/issues/6746#issuecomment-583897436
|
||||
headerCenter: () => (
|
||||
<View style={styles.searchBar}>
|
||||
<Text
|
||||
style={{ ...StyleConstants.FontStyle.M, color: theme.primary }}
|
||||
>
|
||||
搜索
|
||||
</Text>
|
||||
<TextInput
|
||||
editable={false}
|
||||
children={
|
||||
<Text
|
||||
style={[
|
||||
styles.textInput,
|
||||
{
|
||||
color: theme.primary
|
||||
}
|
||||
]}
|
||||
children='搜索'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.textInput,
|
||||
{
|
||||
color: theme.primary
|
||||
flex: 1,
|
||||
color: theme.primary,
|
||||
paddingLeft: StyleConstants.Spacing.XS
|
||||
}
|
||||
]}
|
||||
autoFocus
|
||||
@ -251,10 +262,7 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center'
|
||||
},
|
||||
textInput: {
|
||||
...StyleConstants.FontStyle.M,
|
||||
paddingLeft: StyleConstants.Spacing.XS,
|
||||
marginBottom:
|
||||
(StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M) / 2
|
||||
fontSize: StyleConstants.Font.Size.M
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -3,6 +3,7 @@ import analytics from '@components/analytics'
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { RootState } from '@root/store'
|
||||
import * as AuthSession from 'expo-auth-session'
|
||||
import * as Localization from 'expo-localization'
|
||||
|
||||
export type InstanceLocal = {
|
||||
appData: {
|
||||
@ -11,6 +12,7 @@ export type InstanceLocal = {
|
||||
}
|
||||
url: string
|
||||
token: string
|
||||
uri: Mastodon.Instance['uri']
|
||||
account: {
|
||||
id: Mastodon.Account['id']
|
||||
preferences: Mastodon.Preferences
|
||||
@ -50,10 +52,12 @@ export const localAddInstance = createAsyncThunk(
|
||||
async ({
|
||||
url,
|
||||
token,
|
||||
uri,
|
||||
appData
|
||||
}: {
|
||||
url: InstanceLocal['url']
|
||||
token: InstanceLocal['token']
|
||||
uri: Mastodon.Instance['uri']
|
||||
appData: InstanceLocal['appData']
|
||||
}): Promise<{ type: 'add' | 'overwrite'; data: InstanceLocal }> => {
|
||||
const { store } = require('@root/store')
|
||||
@ -101,6 +105,7 @@ export const localAddInstance = createAsyncThunk(
|
||||
appData,
|
||||
url,
|
||||
token,
|
||||
uri,
|
||||
account: {
|
||||
id,
|
||||
preferences
|
||||
@ -159,7 +164,7 @@ export const instancesInitialState: InstancesState = {
|
||||
instances: []
|
||||
},
|
||||
remote: {
|
||||
url: 'm.cmx.im'
|
||||
url: Localization.locale.includes('zh') ? 'm.cmx.im' : 'mastodon.social'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,19 @@
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { RootState } from '@root/store'
|
||||
import * as Analytics from 'expo-firebase-analytics'
|
||||
import * as Localization from 'expo-localization'
|
||||
|
||||
export const supportedLngs = ['zh-Hans', 'en']
|
||||
|
||||
export type SettingsState = {
|
||||
language: 'zh-CN' | 'en-US' | undefined
|
||||
language: 'zh-Hans' | 'en'
|
||||
theme: 'light' | 'dark' | 'auto'
|
||||
browser: 'internal' | 'external'
|
||||
analytics: boolean
|
||||
}
|
||||
|
||||
export const settingsInitialState = {
|
||||
language: undefined,
|
||||
language: Localization.locale,
|
||||
theme: 'auto',
|
||||
browser: 'internal',
|
||||
analytics: true
|
||||
|
Loading…
x
Reference in New Issue
Block a user