1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Merge branch 'main' into l10n_main

This commit is contained in:
xmflsct
2022-05-08 23:57:29 +02:00
committed by GitHub
75 changed files with 3278 additions and 3242 deletions

View File

@ -1,4 +1,31 @@
import { ActionSheetProvider } from '@expo/react-native-action-sheet'
import '@formatjs/intl-getcanonicallocales/polyfill'
import '@formatjs/intl-locale/polyfill'
import '@formatjs/intl-pluralrules/polyfill'
import '@formatjs/intl-pluralrules/locale-data/de'
import '@formatjs/intl-pluralrules/locale-data/en'
import '@formatjs/intl-pluralrules/locale-data/ko'
import '@formatjs/intl-pluralrules/locale-data/vi'
import '@formatjs/intl-pluralrules/locale-data/zh'
import '@formatjs/intl-numberformat/polyfill'
import '@formatjs/intl-numberformat/locale-data/de'
import '@formatjs/intl-numberformat/locale-data/en'
import '@formatjs/intl-numberformat/locale-data/ko'
import '@formatjs/intl-numberformat/locale-data/vi'
import '@formatjs/intl-numberformat/locale-data/zh'
import '@formatjs/intl-datetimeformat/polyfill'
import '@formatjs/intl-datetimeformat/locale-data/de'
import '@formatjs/intl-datetimeformat/locale-data/en'
import '@formatjs/intl-datetimeformat/locale-data/ko'
import '@formatjs/intl-datetimeformat/locale-data/vi'
import '@formatjs/intl-datetimeformat/locale-data/zh'
import '@formatjs/intl-datetimeformat/add-all-tz'
import '@formatjs/intl-relativetimeformat/polyfill'
import '@formatjs/intl-relativetimeformat/locale-data/de'
import '@formatjs/intl-relativetimeformat/locale-data/en'
import '@formatjs/intl-relativetimeformat/locale-data/ko'
import '@formatjs/intl-relativetimeformat/locale-data/vi'
import '@formatjs/intl-relativetimeformat/locale-data/zh'
import queryClient from '@helpers/queryClient'
import i18n from '@root/i18n/i18n'
import Screens from '@root/Screens'
@ -6,7 +33,9 @@ import audio from '@root/startup/audio'
import dev from '@root/startup/dev'
import log from '@root/startup/log'
import netInfo from '@root/startup/netInfo'
import push from '@root/startup/push'
import sentry from '@root/startup/sentry'
import timezone from '@root/startup/timezone'
import { persistor, store } from '@root/store'
import AccessibilityManager from '@utils/accessibility/AccessibilityManager'
import {
@ -19,11 +48,12 @@ import * as SplashScreen from 'expo-splash-screen'
import React, { useCallback, useEffect, useState } from 'react'
import { AppState, LogBox, Platform } from 'react-native'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
import 'react-native-image-keyboard'
import { enableFreeze } from 'react-native-screens'
import { QueryClientProvider } from 'react-query'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import push from './startup/push'
import { IntlProvider } from 'react-intl'
Platform.select({
android: LogBox.ignoreLogs(['Setting a timer for a long period of time'])
@ -33,6 +63,7 @@ dev()
sentry()
audio()
push()
timezone()
enableFreeze(true)
const App: React.FC = () => {
@ -91,13 +122,18 @@ const App: React.FC = () => {
const language = getSettingsLanguage(store.getState())
if (!language) {
store.dispatch(changeLanguage('en'))
i18n.changeLanguage('en')
} else {
i18n.changeLanguage(language)
}
i18n.changeLanguage(language)
return (
<ActionSheetProvider>
<AccessibilityManager>
<ThemeManager>
<Screens localCorrupt={localCorrupt} />
<IntlProvider locale={language}>
<Screens localCorrupt={localCorrupt} />
</IntlProvider>
</ThemeManager>
</AccessibilityManager>
</ActionSheetProvider>

View File

@ -1,24 +0,0 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Text } from 'react-native'
import TimeAgo from 'react-timeago'
// @ts-ignore
import buildFormatter from 'react-timeago/lib/formatters/buildFormatter'
export interface Props {
date: string | number
}
const RelativeTime: React.FC<Props> = ({ date }) => {
const { t } = useTranslation('componentRelativeTime')
return (
<TimeAgo
date={date}
component={Text}
formatter={buildFormatter(t('strings', { returnObjects: true }))}
/>
)
}
export default RelativeTime

View File

@ -102,6 +102,7 @@ const TimelineDefault = React.memo(
queryKey={disableOnPress ? undefined : queryKey}
rootQueryKey={disableOnPress ? undefined : rootQueryKey}
status={actualStatus}
highlighted={highlighted}
/>
</View>

View File

@ -1,6 +1,6 @@
import Button from '@components/Button'
import { StyleConstants } from '@utils/styles/constants'
import { Video } from 'expo-av'
import { ResizeMode, Video, VideoFullscreenUpdate } from 'expo-av'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import {
AppState,
@ -110,15 +110,14 @@ const AttachmentVideo: React.FC<Props> = ({
source: { uri: video.url }
}
: {
resizeMode: 'cover',
resizeMode: ResizeMode.COVER,
posterSource: { uri: video.preview_url },
posterStyle: { resizeMode: 'cover' }
posterStyle: { resizeMode: ResizeMode.COVER }
})}
useNativeControls={false}
onFullscreenUpdate={async event => {
if (
event.fullscreenUpdate ===
Video.FULLSCREEN_UPDATE_PLAYER_DID_DISMISS
event.fullscreenUpdate === VideoFullscreenUpdate.PLAYER_DID_DISMISS
) {
if (gifv) {
await videoPlayer.current?.pauseAsync()

View File

@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
export interface Props {
status: Pick<
Mastodon.Status,
'content' | 'spoiler_text' | 'emojis' | 'mentions' | 'tags'
>
status: Pick<Mastodon.Status, 'content' | 'spoiler_text' | 'emojis'> & {
mentions?: Mastodon.Status['mentions']
tags?: Mastodon.Status['tags']
}
numberOfLines?: number
highlighted?: boolean
disableDetails?: boolean

View File

@ -18,9 +18,15 @@ export interface Props {
queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
status: Mastodon.Status
highlighted: boolean
}
const TimelineHeaderDefault = ({ queryKey, rootQueryKey, status }: Props) => {
const TimelineHeaderDefault = ({
queryKey,
rootQueryKey,
status,
highlighted
}: Props) => {
const { t } = useTranslation('componentTimeline')
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
const { colors } = useTheme()
@ -40,6 +46,7 @@ const TimelineHeaderDefault = ({ queryKey, rootQueryKey, status }: Props) => {
<HeaderSharedCreated
created_at={status.created_at}
edited_at={status.edited_at}
highlighted={highlighted}
/>
<HeaderSharedVisibility visibility={status.visibility} />
<HeaderSharedMuted muted={status.muted} />

View File

@ -1,25 +1,43 @@
import Icon from '@components/Icon'
import RelativeTime from '@components/RelativeTime'
import CustomText from '@components/Text'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { FormattedDate, FormattedRelativeTime, FormattedTime } from 'react-intl'
export interface Props {
created_at: Mastodon.Status['created_at'] | number
edited_at?: Mastodon.Status['edited_at']
highlighted?: boolean
}
const HeaderSharedCreated = React.memo(
({ created_at, edited_at }: Props) => {
({ created_at, edited_at, highlighted = false }: Props) => {
const { t } = useTranslation('componentTimeline')
const { colors } = useTheme()
const actualTime = edited_at || created_at
return (
<>
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
<RelativeTime date={edited_at || created_at} />
{highlighted ? (
<>
<FormattedDate
value={new Date(actualTime)}
dateStyle='medium'
timeStyle='short'
/>
</>
) : (
<FormattedRelativeTime
value={
-(new Date().getTime() - new Date(actualTime).getTime()) / 1000
}
updateIntervalInSeconds={1}
/>
)}
</CustomText>
{edited_at ? (
<Icon

View File

@ -15,6 +15,30 @@ const HeaderSharedVisibility = React.memo(
const { colors } = useTheme()
switch (visibility) {
case 'public':
return (
<Icon
accessibilityLabel={t(
'shared.header.shared.visibility.private.accessibilityLabel'
)}
name='Globe'
size={StyleConstants.Font.Size.S}
color={colors.secondary}
style={styles.visibility}
/>
)
case 'unlisted':
return (
<Icon
accessibilityLabel={t(
'shared.header.shared.visibility.private.accessibilityLabel'
)}
name='Unlock'
size={StyleConstants.Font.Size.S}
color={colors.secondary}
style={styles.visibility}
/>
)
case 'private':
return (
<Icon

View File

@ -4,7 +4,6 @@ import haptics from '@components/haptics'
import Icon from '@components/Icon'
import { displayMessage } from '@components/Message'
import { ParseEmojis } from '@components/Parse'
import RelativeTime from '@components/RelativeTime'
import CustomText from '@components/Text'
import {
MutationVarsTimelineUpdateStatusProperty,
@ -17,6 +16,7 @@ import { useTheme } from '@utils/styles/ThemeManager'
import { maxBy } from 'lodash'
import React, { useCallback, useMemo, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { FormattedRelativeTime } from 'react-intl'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { useQueryClient } from 'react-query'
@ -158,7 +158,16 @@ const TimelinePoll: React.FC<Props> = ({
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
<Trans
i18nKey='componentTimeline:shared.poll.meta.expiration.until'
components={[<RelativeTime date={poll.expires_at} />]}
components={[
<FormattedRelativeTime
value={
(new Date(poll.expires_at).getTime() -
new Date().getTime()) /
1000
}
updateIntervalInSeconds={1}
/>
]}
/>
</CustomText>
)

View File

@ -2,7 +2,10 @@ import analytics from '@components/analytics'
import { ActionSheetOptions } from '@expo/react-native-action-sheet'
import * as ImageManipulator from 'expo-image-manipulator'
import * as ImagePicker from 'expo-image-picker'
import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types'
import {
ImageInfo,
UIImagePickerPresentationStyle
} from 'expo-image-picker/build/ImagePicker.types'
import i18next from 'i18next'
import { Alert, Linking, Platform } from 'react-native'
@ -39,7 +42,7 @@ const mediaSelector = async ({
{ resize }
])
}
resolve(newResult)
resolve({ ...newResult, cancelled: false })
} else {
resolve(result)
}
@ -94,8 +97,8 @@ const mediaSelector = async ({
exif: false,
presentationStyle:
Platform.OS === 'ios' && parseInt(Platform.Version) < 13
? 0
: -2
? UIImagePickerPresentationStyle.FULL_SCREEN
: UIImagePickerPresentationStyle.AUTOMATIC
})
if (!result.cancelled) {

View File

@ -34,6 +34,7 @@ const openLink = async (url: string, navigation?: any) => {
// @ts-ignore
navigation.push(page, options)
} else {
// @ts-ignore
navigationRef.navigate(page, options)
}
}

View File

@ -1,20 +0,0 @@
{
"strings": {
"prefixAgo": "",
"prefixFromNow": "",
"suffixAgo": "her",
"suffixFromNow": "",
"seconds": "%d Sekunden",
"minute": "etwa eine Minute",
"minutes": "%d Minuten",
"hour": "etwa eine Stunde",
"hours": "etwa %d Stunden",
"day": "1 Tag",
"days": "%d Tage",
"month": "etwa 1 Monat",
"months": "%d Monate",
"year": "etwa 1 Jahr",
"years": "%d Jahre",
"wordSeparator": ""
}
}

View File

@ -62,8 +62,8 @@
"history": {
"accessibilityLabel": "Dieser Tröt wurde {{count}} mal bearbeitet",
"accessibilityHint": "Für den vollständigen Verlauf auswählen",
"text": "{{count}} bearbeitet",
"text_plural": "{{count}} mal bearbeitet"
"text_one": "{{count}} bearbeitet",
"text_other": "{{count}} mal bearbeitet"
}
},
"attachment": {
@ -219,10 +219,10 @@
"refresh": "Aktualisieren"
},
"count": {
"voters": "{{count}} Benutzer haben abgestimmt",
"voters_plural": "{{count}} Benutzer haben abgestimmt",
"votes": "{{count}} Stimmen",
"votes_plural": "{{count}} Stimmen"
"voters_one": "{{count}} Benutzer haben abgestimmt",
"voters_other": "{{count}} Benutzer haben abgestimmt",
"votes_one": "{{count}} Stimmen",
"votes_other": "{{count}} Stimmen"
},
"expiration": {
"expired": "Abstimmung abgelaufen",

View File

@ -13,6 +13,5 @@ export default {
componentMediaSelector: require('./components/mediaSelector'),
componentParse: require('./components/parse'),
componentRelationship: require('./components/relationship'),
componentRelativeTime: require('./components/relativeTime'),
componentTimeline: require('./components/timeline')
}

View File

@ -1,5 +1,6 @@
{
"buttons": {
"OK": "OK",
"apply": "Apply",
"cancel": "Cancel"
},

View File

@ -1,20 +0,0 @@
{
"strings": {
"prefixAgo": "",
"prefixFromNow": "",
"suffixAgo": "ago",
"suffixFromNow": "",
"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": " "
}
}

View File

@ -62,8 +62,8 @@
"history": {
"accessibilityLabel": "This toot has been edited {{count}} times",
"accessibilityHint": "Tap to view the full edit history",
"text": "{{count}} edit",
"text_plural": "{{count}} edits"
"text_one": "{{count}} edit",
"text_other": "{{count}} edits"
}
},
"attachment": {
@ -219,10 +219,10 @@
"refresh": "Refresh"
},
"count": {
"voters": "{{count}} user voted",
"voters_plural": "{{count}} users voted",
"votes": "{{count}} vote",
"votes_plural": "{{count}} votes"
"voters_one": "{{count}} user voted",
"voters_other": "{{count}} users voted",
"votes_one": "{{count}} vote",
"votes_other": "{{count}} votes"
},
"expiration": {
"expired": "Vote expired",

View File

@ -42,7 +42,13 @@
"placeholder": "Spoiler warning message"
},
"textInput": {
"placeholder": "What's on your mind"
"placeholder": "What's on your mind",
"keyboardImage": {
"exceedMaximum": {
"title": "Maximum attachments amount reached",
"OK": "$t(common:buttons.OK)"
}
}
}
},
"footer": {
@ -136,8 +142,8 @@
"accessibilityHint": "Open emoji selection panel, swipe horizontally to change page"
}
},
"drafts": "Draft ({{count}})",
"drafts_plural": "Drafts ({{count}})"
"drafts_one": "Draft ({{count}})",
"drafts_other": "Drafts ({{count}})"
},
"editAttachment": {
"header": {

View File

@ -116,8 +116,8 @@
},
"fields": {
"title": "Metadata",
"total": "{{count}} field",
"total_plural": "{{count}} fields"
"total_one": "{{count}} field",
"total_other": "{{count}} fields"
},
"visibility": {
"title": "Posting Visibility",
@ -281,6 +281,7 @@
"accessibilityLabel": "Actions for user {{user}}",
"accessibilityHint": "You can mute, block, report or share this user"
},
"followed_by": " is following you",
"moved": "User moved",
"created_at": "Registered on: {{date}}",
"summary": {

View File

@ -13,6 +13,5 @@ export default {
componentMediaSelector: require('./components/mediaSelector'),
componentParse: require('./components/parse'),
componentRelationship: require('./components/relationship'),
componentRelativeTime: require('./components/relativeTime'),
componentTimeline: require('./components/timeline')
}

View File

@ -1,20 +0,0 @@
{
"strings": {
"prefixAgo": "",
"prefixFromNow": "",
"suffixAgo": "전",
"suffixFromNow": "",
"seconds": "%d초",
"minute": "약 1분",
"minutes": "%d분",
"hour": "약 1시간",
"hours": "약 %d시간",
"day": "하루",
"days": "%d일",
"month": "약 1달",
"months": "%d달",
"year": "약 1년",
"years": "%d년",
"wordSeparator": ""
}
}

View File

@ -196,10 +196,10 @@
"refresh": "새로고침"
},
"count": {
"voters": "{{count}}명의 사용자가 투표",
"voters_plural": "{{count}}명의 사용자가 투표",
"votes": "{{count}} 투표",
"votes_plural": "{{count}} 투표"
"voters_one": "{{count}}명의 사용자가 투표",
"voters_other": "{{count}}명의 사용자가 투표",
"votes_one": "{{count}} 투표",
"votes_other": "{{count}} 투표"
},
"expiration": {
"expired": "투표 종료됨",

View File

@ -134,8 +134,8 @@
"accessibilityHint": "이모지 선택 패널 열기, 가로로 스와이프해서 페이지를 바꿀 수 있어요"
}
},
"drafts": "초안 ({{count}})",
"drafts_plural": "초안 ({{count}})"
"drafts_one": "초안 ({{count}})",
"drafts_other": "초안 ({{count}})"
},
"editAttachment": {
"header": {

View File

@ -113,8 +113,8 @@
},
"fields": {
"title": "메타데이터",
"total": "{{count}}개 필드",
"total_plural": "{{count}}개 필드"
"total_one": "{{count}}개 필드",
"total_other": "{{count}}개 필드"
},
"visibility": {
"title": "공개 범위",

View File

@ -13,6 +13,5 @@ export default {
componentMediaSelector: require('./components/mediaSelector'),
componentParse: require('./components/parse'),
componentRelationship: require('./components/relationship'),
componentRelativeTime: require('./components/relativeTime'),
componentTimeline: require('./components/timeline')
}

View File

@ -1,20 +0,0 @@
{
"strings": {
"prefixAgo": "",
"prefixFromNow": "",
"suffixAgo": " trước",
"suffixFromNow": "",
"seconds": "%d giây",
"minute": "một phút",
"minutes": "%d phút",
"hour": "một giờ",
"hours": "khoảng %d giờ",
"day": "một ngày",
"days": "%d ngày",
"month": "khoảng một tháng",
"months": "%d tháng",
"year": "khoảng một năm",
"years": "%d năm",
"wordSeparator": ""
}
}

View File

@ -13,6 +13,5 @@ export default {
componentMediaSelector: require('./components/mediaSelector'),
componentParse: require('./components/parse'),
componentRelationship: require('./components/relationship'),
componentRelativeTime: require('./components/relativeTime'),
componentTimeline: require('./components/timeline')
}

View File

@ -1,20 +0,0 @@
{
"strings": {
"prefixAgo": "",
"prefixFromNow": "",
"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": ""
}
}

View File

@ -1,20 +0,0 @@
{
"strings": {
"prefixAgo": "",
"prefixFromNow": "",
"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": ""
}
}

View File

@ -2,7 +2,6 @@ import analytics from '@components/analytics'
import Button from '@components/Button'
import haptics from '@components/haptics'
import { ParseHTML } from '@components/Parse'
import RelativeTime from '@components/RelativeTime'
import CustomText from '@components/Text'
import { BlurView } from '@react-native-community/blur'
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
@ -15,6 +14,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useEffect, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { FormattedRelativeTime } from 'react-intl'
import { Dimensions, Platform, Pressable, StyleSheet, View } from 'react-native'
import { Circle } from 'react-native-animated-spinkit'
import FastImage from 'react-native-fast-image'
@ -91,7 +91,17 @@ const ScreenAnnouncements: React.FC<
>
<Trans
i18nKey='screenAnnouncements:content.published'
components={[<RelativeTime date={item.published_at} />]}
components={[
<FormattedRelativeTime
value={
-(
new Date().getTime() -
new Date(item.published_at).getTime()
) / 1000
}
updateIntervalInSeconds={1}
/>
]}
/>
</CustomText>
<ScrollView

View File

@ -1,4 +1,3 @@
import apiInstance from '@api/instance'
import analytics from '@components/analytics'
import { HeaderLeft, HeaderRight } from '@components/Header'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

View File

@ -5,6 +5,7 @@ import Icon from '@components/Icon'
import CustomText from '@components/Text'
import { useActionSheet } from '@expo/react-native-action-sheet'
import { useNavigation } from '@react-navigation/native'
import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import layoutAnimation from '@utils/styles/layoutAnimation'
import { useTheme } from '@utils/styles/ThemeManager'
@ -17,8 +18,10 @@ import React, {
useRef
} from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, Image, Pressable, StyleSheet, View } from 'react-native'
import { FlatList, Pressable, StyleSheet, View } from 'react-native'
import { Circle } from 'react-native-animated-spinkit'
import FastImage from 'react-native-fast-image'
import { useSelector } from 'react-redux'
import ComposeContext from '../../utils/createContext'
import { ExtendedAttachment } from '../../utils/types'
import chooseAndUploadAttachment from './addAttachment'
@ -33,9 +36,14 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
const { showActionSheetWithOptions } = useActionSheet()
const { composeState, composeDispatch } = useContext(ComposeContext)
const { t } = useTranslation('screenCompose')
const { colors, mode } = useTheme()
const { colors } = useTheme()
const navigation = useNavigation<any>()
const maxAttachments = useSelector(
getInstanceConfigurationStatusMaxAttachments,
() => true
)
const flatListRef = useRef<FlatList>(null)
const sensitiveOnPress = useCallback(() => {
@ -124,7 +132,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
width: calculateWidth(item)
}}
>
<Image
<FastImage
style={{ width: '100%', height: '100%' }}
source={{
uri: item.local?.local_thumbnail || item.remote?.preview_url
@ -320,7 +328,9 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
item.local?.uri || item.remote?.url || Math.random().toString()
}
ListFooterComponent={
composeState.attachments.uploads.length < 4 ? listFooter : null
composeState.attachments.uploads.length < maxAttachments
? listFooter
: null
}
/>
</View>

View File

@ -1,17 +1,25 @@
import CustomText from '@components/Text'
import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { TextInput } from 'react-native'
import { Alert, TextInput } from 'react-native'
import { useSelector } from 'react-redux'
import formatText from '../../formatText'
import ComposeContext from '../../utils/createContext'
import { uploadAttachment } from '../Footer/addAttachment'
const ComposeTextInput: React.FC = () => {
const { composeState, composeDispatch } = useContext(ComposeContext)
const { t } = useTranslation('screenCompose')
const { colors, mode } = useTheme()
const maxAttachments = useSelector(
getInstanceConfigurationStatusMaxAttachments,
() => true
)
return (
<TextInput
keyboardAppearance={mode}
@ -54,6 +62,36 @@ const ComposeTextInput: React.FC = () => {
}}
ref={composeState.textInputFocus.refs.text}
scrollEnabled={false}
onImageChange={({ nativeEvent }) => {
if (composeState.attachments.uploads.length >= maxAttachments) {
Alert.alert(
t(
'content.root.header.textInput.keyboardImage.exceedMaximum.title'
),
undefined,
[
{
text: t(
'content.root.header.textInput.keyboardImage.exceedMaximum.OK'
),
style: 'default'
}
]
)
return
}
if (nativeEvent.linkUri) {
uploadAttachment({
composeDispatch,
imageInfo: {
uri: nativeEvent.linkUri,
type: 'image',
width: 100,
height: 100
}
})
}
}}
>
<CustomText>{composeState.text.formatted}</CustomText>
</TextInput>

View File

@ -7,6 +7,7 @@
*/
import GracefullyImage from '@components/GracefullyImage'
import { RootStackParamList } from '@utils/navigation/navigators'
import React, { useState, useCallback } from 'react'
import { Animated, Dimensions, StyleSheet } from 'react-native'
import usePanResponder from '../hooks/usePanResponder'
@ -17,11 +18,11 @@ const SCREEN_WIDTH = SCREEN.width
const SCREEN_HEIGHT = SCREEN.height
type Props = {
imageSrc: Nav.RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
imageSrc: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
onRequestClose: () => void
onZoom: (isZoomed: boolean) => void
onLongPress: (
image: Nav.RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
image: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
) => void
delayLongPress: number
swipeToCloseEnabled?: boolean

View File

@ -7,6 +7,7 @@
*/
import GracefullyImage from '@components/GracefullyImage'
import { RootStackParamList } from '@utils/navigation/navigators'
import React, { createRef, useCallback, useRef, useState } from 'react'
import {
Animated,
@ -31,11 +32,11 @@ const SCREEN_WIDTH = SCREEN.width
const SCREEN_HEIGHT = SCREEN.height
type Props = {
imageSrc: Nav.RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
imageSrc: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
onRequestClose: () => void
onZoom: (scaled: boolean) => void
onLongPress: (
image: Nav.RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
image: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
) => void
swipeToCloseEnabled?: boolean
}

View File

@ -1,5 +1,6 @@
import Icon from '@components/Icon'
import CustomText from '@components/Text'
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
import {
getInstanceAccount,
getInstanceUri
@ -7,6 +8,7 @@ import {
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { View } from 'react-native'
import { useSelector } from 'react-redux'
import { PlaceholderLine } from 'rn-placeholder'
@ -20,6 +22,7 @@ const AccountInformationAccount: React.FC<Props> = ({
account,
localInstance
}) => {
const { t } = useTranslation('screenTabs')
const { colors } = useTheme()
const instanceAccount = useSelector(
getInstanceAccount,
@ -27,6 +30,11 @@ const AccountInformationAccount: React.FC<Props> = ({
)
const instanceUri = useSelector(getInstanceUri)
const { data: relationship } = useRelationshipQuery({
id: account?.id || '',
options: { enabled: account !== undefined }
})
const movedContent = useMemo(() => {
if (account?.moved) {
return (
@ -65,6 +73,11 @@ const AccountInformationAccount: React.FC<Props> = ({
@{localInstance ? instanceAccount?.acct : account?.acct}
{localInstance ? `@${instanceUri}` : null}
</CustomText>
{relationship?.followed_by ? (
<CustomText fontStyle='M' style={{ color: colors.secondary }}>
{t('shared.account.followed_by')}
</CustomText>
) : null}
{movedContent}
{account?.locked ? (
<Icon

View File

@ -1,12 +1,12 @@
import { Audio } from 'expo-av'
import { Audio, InterruptionModeAndroid, InterruptionModeIOS } from 'expo-av'
import log from './log'
const audio = () => {
log('log', 'audio', 'setting audio playback default options')
Audio.setAudioModeAsync({
playsInSilentModeIOS: true,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DUCK_OTHERS,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS
interruptionModeIOS: InterruptionModeIOS.DuckOthers,
interruptionModeAndroid: InterruptionModeAndroid.DuckOthers
})
}

12
src/startup/timezone.ts Normal file
View File

@ -0,0 +1,12 @@
import * as Localization from 'expo-localization'
import log from './log'
const timezone = () => {
log('log', 'Timezone', Localization.timezone)
if ('__setDefaultTimeZone' in Intl.DateTimeFormat) {
// @ts-ignore
Intl.DateTimeFormat.__setDefaultTimeZone(Localization.timezone)
}
}
export default timezone