mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
14
patches/expo-av+13.0.2.patch
Normal file
14
patches/expo-av+13.0.2.patch
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
diff --git a/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m b/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m
|
||||||
|
index 81dce13..8664b90 100644
|
||||||
|
--- a/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m
|
||||||
|
+++ b/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m
|
||||||
|
@@ -168,9 +168,6 @@ - (void)moduleDidBackground:(id)backgroundingModule
|
||||||
|
// compact doesn't work, that's why we need the `|| !pointer` above
|
||||||
|
// http://www.openradar.me/15396578
|
||||||
|
[_foregroundedModules compact];
|
||||||
|
-
|
||||||
|
- // Any possible failures are silent
|
||||||
|
- [self _updateSessionConfiguration];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)moduleDidForeground:(id)module
|
@ -1,6 +1,5 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import handleError, { ctx } from './handleError'
|
import { ctx, handleError, userAgent } from './helpers'
|
||||||
import { userAgent } from './helpers'
|
|
||||||
|
|
||||||
export type Params = {
|
export type Params = {
|
||||||
method: 'get' | 'post' | 'put' | 'delete'
|
method: 'get' | 'post' | 'put' | 'delete'
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import chalk from 'chalk'
|
|
||||||
|
|
||||||
export const ctx = new chalk.Instance({ level: 3 })
|
|
||||||
|
|
||||||
const handleError = (error: any) => {
|
|
||||||
if (error?.response) {
|
|
||||||
// The request was made and the server responded with a status code
|
|
||||||
// that falls out of the range of 2xx
|
|
||||||
console.error(
|
|
||||||
ctx.bold(' API '),
|
|
||||||
ctx.bold('response'),
|
|
||||||
error.response.status,
|
|
||||||
error?.response.data?.error || error?.response.message || 'Unknown error'
|
|
||||||
)
|
|
||||||
return Promise.reject({
|
|
||||||
status: error?.response.status,
|
|
||||||
message: error?.response.data?.error || error?.response.message || 'Unknown error'
|
|
||||||
})
|
|
||||||
} else if (error?.request) {
|
|
||||||
// The request was made but no response was received
|
|
||||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
|
||||||
// http.ClientRequest in node.js
|
|
||||||
console.error(ctx.bold(' API '), ctx.bold('request'), error)
|
|
||||||
return Promise.reject()
|
|
||||||
} else {
|
|
||||||
console.error(ctx.bold(' API '), ctx.bold('internal'), error?.message)
|
|
||||||
return Promise.reject()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handleError
|
|
@ -1,6 +1,36 @@
|
|||||||
import Constants from "expo-constants"
|
import chalk from 'chalk'
|
||||||
import { Platform } from "react-native"
|
import Constants from 'expo-constants'
|
||||||
|
import { Platform } from 'react-native'
|
||||||
|
|
||||||
const userAgent = { 'User-Agent': `tooot/${Constants.expoConfig?.version} ${Platform.OS}/${Platform.Version}` }
|
const userAgent = {
|
||||||
|
'User-Agent': `tooot/${Constants.expoConfig?.version} ${Platform.OS}/${Platform.Version}`
|
||||||
|
}
|
||||||
|
|
||||||
export { userAgent }
|
const ctx = new chalk.Instance({ level: 3 })
|
||||||
|
const handleError = (error: any) => {
|
||||||
|
if (error?.response) {
|
||||||
|
// The request was made and the server responded with a status code
|
||||||
|
// that falls out of the range of 2xx
|
||||||
|
console.error(
|
||||||
|
ctx.bold(' API '),
|
||||||
|
ctx.bold('response'),
|
||||||
|
error.response.status,
|
||||||
|
error?.response.data?.error || error?.response.message || 'Unknown error'
|
||||||
|
)
|
||||||
|
return Promise.reject({
|
||||||
|
status: error?.response.status,
|
||||||
|
message: error?.response.data?.error || error?.response.message || 'Unknown error'
|
||||||
|
})
|
||||||
|
} else if (error?.request) {
|
||||||
|
// The request was made but no response was received
|
||||||
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||||
|
// http.ClientRequest in node.js
|
||||||
|
console.error(ctx.bold(' API '), ctx.bold('request'), error)
|
||||||
|
return Promise.reject()
|
||||||
|
} else {
|
||||||
|
console.error(ctx.bold(' API '), ctx.bold('internal'), error?.message)
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ctx, handleError, userAgent }
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import axios, { AxiosRequestConfig } from 'axios'
|
import axios, { AxiosRequestConfig } from 'axios'
|
||||||
import li from 'li'
|
import li from 'li'
|
||||||
import handleError, { ctx } from './handleError'
|
import { ctx, handleError, userAgent } from './helpers'
|
||||||
import { userAgent } from './helpers'
|
|
||||||
|
|
||||||
export type Params = {
|
export type Params = {
|
||||||
method: 'get' | 'post' | 'put' | 'delete' | 'patch'
|
method: 'get' | 'post' | 'put' | 'delete' | 'patch'
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import * as Sentry from '@sentry/react-native'
|
import * as Sentry from '@sentry/react-native'
|
||||||
import { mapEnvironment } from '@utils/checkEnvironment'
|
import { mapEnvironment } from '@utils/checkEnvironment'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import handleError, { ctx } from './handleError'
|
import { ctx, handleError, userAgent } from './helpers'
|
||||||
import { userAgent } from './helpers'
|
|
||||||
|
|
||||||
export type Params = {
|
export type Params = {
|
||||||
method: 'get' | 'post' | 'put' | 'delete'
|
method: 'get' | 'post' | 'put' | 'delete'
|
||||||
@ -57,14 +56,12 @@ const apiTooot = async <T = unknown>({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Sentry.setExtras({
|
Sentry.setContext('API request', { url, params, body })
|
||||||
API: 'tooot',
|
Sentry.setContext('Error response', {
|
||||||
request: { url, params, body },
|
...(error?.response && { response: error.response?._response })
|
||||||
...(error?.response && { response: error.response })
|
|
||||||
})
|
|
||||||
Sentry.captureMessage('API error', {
|
|
||||||
contexts: { errorObject: error }
|
|
||||||
})
|
})
|
||||||
|
Sentry.setContext('Error object', { error })
|
||||||
|
Sentry.captureMessage('API error')
|
||||||
|
|
||||||
return handleError(error)
|
return handleError(error)
|
||||||
})
|
})
|
||||||
|
@ -5,22 +5,17 @@ import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
|||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { PropsWithChildren } from 'react'
|
import React, { PropsWithChildren } from 'react'
|
||||||
import { Pressable, View } from 'react-native'
|
import { Pressable, PressableProps, View } from 'react-native'
|
||||||
import GracefullyImage from './GracefullyImage'
|
import GracefullyImage from './GracefullyImage'
|
||||||
|
import Icon from './Icon'
|
||||||
import CustomText from './Text'
|
import CustomText from './Text'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
account: Mastodon.Account
|
account: Mastodon.Account
|
||||||
Component?: typeof View | typeof Pressable
|
props?: PressableProps
|
||||||
props?: {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComponentAccount: React.FC<PropsWithChildren & Props> = ({
|
const ComponentAccount: React.FC<PropsWithChildren & Props> = ({ account, props, children }) => {
|
||||||
account,
|
|
||||||
Component,
|
|
||||||
props,
|
|
||||||
children
|
|
||||||
}) => {
|
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
@ -28,50 +23,62 @@ const ComponentAccount: React.FC<PropsWithChildren & Props> = ({
|
|||||||
props = { onPress: () => navigation.push('Tab-Shared-Account', { account }) }
|
props = { onPress: () => navigation.push('Tab-Shared-Account', { account }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return React.createElement(
|
return (
|
||||||
Component || Pressable,
|
<Pressable
|
||||||
{
|
{...props}
|
||||||
...props,
|
style={{
|
||||||
style: {
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||||
paddingVertical: StyleConstants.Spacing.M,
|
paddingVertical: StyleConstants.Spacing.M,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
children={
|
||||||
|
<>
|
||||||
|
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
|
||||||
|
<GracefullyImage
|
||||||
|
uri={{ original: account.avatar, static: account.avatar_static }}
|
||||||
|
style={{
|
||||||
|
width: StyleConstants.Avatar.S,
|
||||||
|
height: StyleConstants.Avatar.S,
|
||||||
|
borderRadius: 6,
|
||||||
|
marginRight: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View>
|
||||||
|
<CustomText numberOfLines={1}>
|
||||||
|
<ParseEmojis
|
||||||
|
content={account.display_name || account.username}
|
||||||
|
emojis={account.emojis}
|
||||||
|
size='S'
|
||||||
|
fontBold
|
||||||
|
/>
|
||||||
|
</CustomText>
|
||||||
|
<CustomText
|
||||||
|
numberOfLines={1}
|
||||||
|
style={{
|
||||||
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
|
color: colors.secondary
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
@{account.acct}
|
||||||
|
</CustomText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{props.onPress && !props.disabled ? (
|
||||||
|
<Icon
|
||||||
|
name='ChevronRight'
|
||||||
|
size={StyleConstants.Font.Size.L}
|
||||||
|
color={colors.secondary}
|
||||||
|
style={{ marginLeft: 8 }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
children || null
|
||||||
|
)}
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
},
|
/>
|
||||||
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
|
|
||||||
<GracefullyImage
|
|
||||||
uri={{ original: account.avatar, static: account.avatar_static }}
|
|
||||||
style={{
|
|
||||||
width: StyleConstants.Avatar.S,
|
|
||||||
height: StyleConstants.Avatar.S,
|
|
||||||
borderRadius: 6,
|
|
||||||
marginRight: StyleConstants.Spacing.S
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<View>
|
|
||||||
<CustomText numberOfLines={1}>
|
|
||||||
<ParseEmojis
|
|
||||||
content={account.display_name || account.username}
|
|
||||||
emojis={account.emojis}
|
|
||||||
size='S'
|
|
||||||
fontBold
|
|
||||||
/>
|
|
||||||
</CustomText>
|
|
||||||
<CustomText
|
|
||||||
numberOfLines={1}
|
|
||||||
style={{
|
|
||||||
marginTop: StyleConstants.Spacing.XS,
|
|
||||||
color: colors.secondary
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
@{account.acct}
|
|
||||||
</CustomText>
|
|
||||||
</View>
|
|
||||||
</View>,
|
|
||||||
children
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import {
|
import { AccessibilityProps, Pressable, StyleProp, View, ViewStyle } from 'react-native'
|
||||||
AccessibilityProps,
|
|
||||||
Pressable,
|
|
||||||
StyleProp,
|
|
||||||
View,
|
|
||||||
ViewStyle
|
|
||||||
} from 'react-native'
|
|
||||||
import { Flow } from 'react-native-animated-spinkit'
|
import { Flow } from 'react-native-animated-spinkit'
|
||||||
import CustomText from './Text'
|
import CustomText from './Text'
|
||||||
|
|
||||||
@ -57,15 +50,6 @@ const Button: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
|
|
||||||
const mounted = useRef(false)
|
|
||||||
useEffect(() => {
|
|
||||||
if (mounted.current) {
|
|
||||||
layoutAnimation()
|
|
||||||
} else {
|
|
||||||
mounted.current = true
|
|
||||||
}
|
|
||||||
}, [content, loading, disabled])
|
|
||||||
|
|
||||||
const loadingSpinkit = useMemo(
|
const loadingSpinkit = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<View style={{ position: 'absolute' }}>
|
<View style={{ position: 'absolute' }}>
|
||||||
@ -120,8 +104,7 @@ const Button: React.FC<Props> = ({
|
|||||||
<CustomText
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
color: mainColor,
|
color: mainColor,
|
||||||
fontSize:
|
fontSize: StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
||||||
StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
|
||||||
opacity: loading ? 0 : 1
|
opacity: loading ? 0 : 1
|
||||||
}}
|
}}
|
||||||
fontWeight={fontBold ? 'Bold' : 'Normal'}
|
fontWeight={fontBold ? 'Bold' : 'Normal'}
|
||||||
@ -156,15 +139,13 @@ const Button: React.FC<Props> = ({
|
|||||||
borderColor: mainColor,
|
borderColor: mainColor,
|
||||||
backgroundColor: colorBackground,
|
backgroundColor: colorBackground,
|
||||||
paddingVertical: StyleConstants.Spacing[spacing],
|
paddingVertical: StyleConstants.Spacing[spacing],
|
||||||
paddingHorizontal:
|
paddingHorizontal: StyleConstants.Spacing[spacing] + StyleConstants.Spacing.XS,
|
||||||
StyleConstants.Spacing[spacing] + StyleConstants.Spacing.XS,
|
|
||||||
width: round && layoutHeight ? layoutHeight : undefined
|
width: round && layoutHeight ? layoutHeight : undefined
|
||||||
},
|
},
|
||||||
customStyle
|
customStyle
|
||||||
]}
|
]}
|
||||||
{...(round && {
|
{...(round && {
|
||||||
onLayout: ({ nativeEvent }) =>
|
onLayout: ({ nativeEvent }) => setLayoutHeight(nativeEvent.layout.height)
|
||||||
setLayoutHeight(nativeEvent.layout.height)
|
|
||||||
})}
|
})}
|
||||||
testID='base'
|
testID='base'
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
|
@ -26,7 +26,6 @@ const AttachmentVideo: React.FC<Props> = ({
|
|||||||
const videoPlayer = useRef<Video>(null)
|
const videoPlayer = useRef<Video>(null)
|
||||||
const [videoLoading, setVideoLoading] = useState(false)
|
const [videoLoading, setVideoLoading] = useState(false)
|
||||||
const [videoLoaded, setVideoLoaded] = useState(false)
|
const [videoLoaded, setVideoLoaded] = useState(false)
|
||||||
const [videoPosition, setVideoPosition] = useState<number>(0)
|
|
||||||
const [videoResizeMode, setVideoResizeMode] = useState<ResizeMode>(ResizeMode.COVER)
|
const [videoResizeMode, setVideoResizeMode] = useState<ResizeMode>(ResizeMode.COVER)
|
||||||
const playOnPress = useCallback(async () => {
|
const playOnPress = useCallback(async () => {
|
||||||
setVideoLoading(true)
|
setVideoLoading(true)
|
||||||
@ -34,19 +33,15 @@ const AttachmentVideo: React.FC<Props> = ({
|
|||||||
await videoPlayer.current?.loadAsync({ uri: video.url })
|
await videoPlayer.current?.loadAsync({ uri: video.url })
|
||||||
}
|
}
|
||||||
Platform.OS === 'android' && setVideoResizeMode(ResizeMode.CONTAIN)
|
Platform.OS === 'android' && setVideoResizeMode(ResizeMode.CONTAIN)
|
||||||
await videoPlayer.current?.setPositionAsync(videoPosition)
|
|
||||||
await videoPlayer.current?.presentFullscreenPlayer()
|
await videoPlayer.current?.presentFullscreenPlayer()
|
||||||
videoPlayer.current?.playAsync()
|
videoPlayer.current?.playAsync()
|
||||||
setVideoLoading(false)
|
setVideoLoading(false)
|
||||||
videoPlayer.current?.setOnPlaybackStatusUpdate(props => {
|
videoPlayer.current?.setOnPlaybackStatusUpdate(props => {
|
||||||
if (props.isLoaded) {
|
if (props.isLoaded) {
|
||||||
setVideoLoaded(true)
|
setVideoLoaded(true)
|
||||||
if (props.positionMillis) {
|
|
||||||
setVideoPosition(props.positionMillis)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [videoLoaded, videoPosition])
|
}, [videoLoaded])
|
||||||
|
|
||||||
const appState = useRef(AppState.currentState)
|
const appState = useRef(AppState.currentState)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -107,7 +102,7 @@ const AttachmentVideo: React.FC<Props> = ({
|
|||||||
if (event.fullscreenUpdate === VideoFullscreenUpdate.PLAYER_DID_DISMISS) {
|
if (event.fullscreenUpdate === VideoFullscreenUpdate.PLAYER_DID_DISMISS) {
|
||||||
Platform.OS === 'android' && setVideoResizeMode(ResizeMode.COVER)
|
Platform.OS === 'android' && setVideoResizeMode(ResizeMode.COVER)
|
||||||
if (!gifv) {
|
if (!gifv) {
|
||||||
await videoPlayer.current?.pauseAsync()
|
await videoPlayer.current?.stopAsync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
50
src/components/contextMenu/at.ts
Normal file
50
src/components/contextMenu/at.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
const menuAt = ({ account }: { account: Mastodon.Account }): ContextMenu[][] => {
|
||||||
|
const { t } = useTranslation('componentContextMenu')
|
||||||
|
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>()
|
||||||
|
|
||||||
|
const menus: ContextMenu[][] = []
|
||||||
|
|
||||||
|
menus.push([
|
||||||
|
{
|
||||||
|
key: 'at-direct',
|
||||||
|
item: {
|
||||||
|
onSelect: () =>
|
||||||
|
navigation.navigate('Screen-Compose', {
|
||||||
|
type: 'conversation',
|
||||||
|
accts: [account.acct],
|
||||||
|
visibility: 'direct'
|
||||||
|
}),
|
||||||
|
disabled: false,
|
||||||
|
destructive: false,
|
||||||
|
hidden: false
|
||||||
|
},
|
||||||
|
title: t('at.direct'),
|
||||||
|
icon: 'envelope'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'at-public',
|
||||||
|
item: {
|
||||||
|
onSelect: () =>
|
||||||
|
navigation.navigate('Screen-Compose', {
|
||||||
|
type: 'conversation',
|
||||||
|
accts: [account.acct],
|
||||||
|
visibility: 'public'
|
||||||
|
}),
|
||||||
|
disabled: false,
|
||||||
|
destructive: false,
|
||||||
|
hidden: false
|
||||||
|
},
|
||||||
|
title: t('at.public'),
|
||||||
|
icon: 'at'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
return menus
|
||||||
|
}
|
||||||
|
|
||||||
|
export default menuAt
|
@ -19,6 +19,10 @@
|
|||||||
"action": "Denuncia i bloqueja l'usuari"
|
"action": "Denuncia i bloqueja l'usuari"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Copia la publicació",
|
"action": "Copia la publicació",
|
||||||
"succeed": "Copiat"
|
"succeed": "Copiat"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": ""
|
"action": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "",
|
"action": "",
|
||||||
"succeed": ""
|
"succeed": ""
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "Nutzer melden und blockieren"
|
"action": "Nutzer melden und blockieren"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Tröt kopieren",
|
"action": "Tröt kopieren",
|
||||||
"succeed": "Kopiert"
|
"succeed": "Kopiert"
|
||||||
|
@ -352,7 +352,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trending": {
|
"trending": {
|
||||||
"tags": ""
|
"tags": "Angesagte Tags"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sections": {
|
"sections": {
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "Report and block user"
|
"action": "Report and block user"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "Direct message",
|
||||||
|
"public": "Public message"
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Copy toot",
|
"action": "Copy toot",
|
||||||
"succeed": "Copied"
|
"succeed": "Copied"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "Reportar y bloquear usuario"
|
"action": "Reportar y bloquear usuario"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Copiar toot",
|
"action": "Copiar toot",
|
||||||
"succeed": "Copiado"
|
"succeed": "Copiado"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": ""
|
"action": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Copier le Pouet",
|
"action": "Copier le Pouet",
|
||||||
"succeed": "Copié"
|
"succeed": "Copié"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": ""
|
"action": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "",
|
"action": "",
|
||||||
"succeed": "Copiato"
|
"succeed": "Copiato"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "ユーザーの報告とブロック"
|
"action": "ユーザーの報告とブロック"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "トゥートをコピー",
|
"action": "トゥートをコピー",
|
||||||
"succeed": "コピー完了"
|
"succeed": "コピー完了"
|
||||||
|
@ -352,7 +352,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trending": {
|
"trending": {
|
||||||
"tags": ""
|
"tags": "トレンドタグ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sections": {
|
"sections": {
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
"OK": "확인",
|
"OK": "확인",
|
||||||
"apply": "적용",
|
"apply": "적용",
|
||||||
"cancel": "취소",
|
"cancel": "취소",
|
||||||
"discard": "",
|
"discard": "취소",
|
||||||
"continue": "",
|
"continue": "계속",
|
||||||
"delete": "",
|
"delete": "삭제",
|
||||||
"done": ""
|
"done": "완료"
|
||||||
},
|
},
|
||||||
"customEmoji": {
|
"customEmoji": {
|
||||||
"accessibilityLabel": "커스텀 이모지 {{emoji}}"
|
"accessibilityLabel": "커스텀 에모지 {{emoji}}"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"success": {
|
"success": {
|
||||||
@ -24,7 +24,7 @@
|
|||||||
},
|
},
|
||||||
"separator": ", ",
|
"separator": ", ",
|
||||||
"discard": {
|
"discard": {
|
||||||
"title": "",
|
"title": "변경 사항이 저장되지 않음",
|
||||||
"message": ""
|
"message": "변경 사항이 저장되지 않았습니다. 작업 내용 저장을 취소할까요?"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,10 +3,10 @@
|
|||||||
"account": {
|
"account": {
|
||||||
"title": "사용자 동작",
|
"title": "사용자 동작",
|
||||||
"following": {
|
"following": {
|
||||||
"action_false": "",
|
"action_false": "사용자 팔로우",
|
||||||
"action_true": ""
|
"action_true": "사용자 팔로우 해제"
|
||||||
},
|
},
|
||||||
"inLists": "",
|
"inLists": "리스트의 사용자 관리",
|
||||||
"mute": {
|
"mute": {
|
||||||
"action_false": "사용자 뮤트",
|
"action_false": "사용자 뮤트",
|
||||||
"action_true": "사용자 뮤트 해제"
|
"action_true": "사용자 뮤트 해제"
|
||||||
@ -16,9 +16,13 @@
|
|||||||
"action_true": "사용자 차단 해제"
|
"action_true": "사용자 차단 해제"
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"action": ""
|
"action": "사용자 신고 및 차단"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "툿 복사",
|
"action": "툿 복사",
|
||||||
"succeed": "복사됨"
|
"succeed": "복사됨"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"screenshot": {
|
"screenshot": {
|
||||||
"title": "개인정보 보호",
|
"title": "개인정보 보호",
|
||||||
"message": "다른 사용자의 사용자 이름이나, 아바타 등의 정보를 유출하지 말아주세요. 고마워요!",
|
"message": "다른 사용자의 이름이나, 프로필 사진 등의 정보를 유출하지 말아주세요. 고마워요!",
|
||||||
"button": "확인"
|
"button": "확인"
|
||||||
},
|
},
|
||||||
"localCorrupt": {
|
"localCorrupt": {
|
||||||
|
@ -50,13 +50,13 @@
|
|||||||
"name": "목록: {{list}}"
|
"name": "목록: {{list}}"
|
||||||
},
|
},
|
||||||
"listAccounts": {
|
"listAccounts": {
|
||||||
"name": ""
|
"name": "{{list}} 리스트의 사용자"
|
||||||
},
|
},
|
||||||
"listAdd": {
|
"listAdd": {
|
||||||
"name": ""
|
"name": "리스트에 추가"
|
||||||
},
|
},
|
||||||
"listEdit": {
|
"listEdit": {
|
||||||
"name": ""
|
"name": "리스트 상세 편집"
|
||||||
},
|
},
|
||||||
"lists": {
|
"lists": {
|
||||||
"name": "목록"
|
"name": "목록"
|
||||||
@ -97,27 +97,27 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"listAccounts": {
|
"listAccounts": {
|
||||||
"heading": "",
|
"heading": "사용자 관리",
|
||||||
"error": "",
|
"error": "사용자를 리스트에서 제거",
|
||||||
"empty": ""
|
"empty": "이 리스트에 추가된 사용자가 없어요"
|
||||||
},
|
},
|
||||||
"listEdit": {
|
"listEdit": {
|
||||||
"heading": "",
|
"heading": "리스트 상세 편집",
|
||||||
"title": "",
|
"title": "이름",
|
||||||
"repliesPolicy": {
|
"repliesPolicy": {
|
||||||
"heading": "",
|
"heading": "답장을 표시할 대상:",
|
||||||
"options": {
|
"options": {
|
||||||
"none": "",
|
"none": "없음",
|
||||||
"list": "",
|
"list": "리스트의 사용자",
|
||||||
"followed": ""
|
"followed": "모든 팔로우 중인 사용자"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"listDelete": {
|
"listDelete": {
|
||||||
"heading": "",
|
"heading": "리스트 삭제",
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "",
|
"title": "리스트 \"{{list}}\"를 삭제할까요?",
|
||||||
"message": ""
|
"message": "이 작업은 되돌릴 수 없습니다."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
@ -127,18 +127,18 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"name": {
|
"name": {
|
||||||
"title": "표시 이름"
|
"title": "사옹자 이름"
|
||||||
},
|
},
|
||||||
"avatar": {
|
"avatar": {
|
||||||
"title": "아바타",
|
"title": "아바타",
|
||||||
"description": "400x400px으로 다운스케일되어요"
|
"description": "400x400px 크기로 조정돼요"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"title": "배너",
|
"title": "배너",
|
||||||
"description": "1500x500px으로 다운스케일되어요"
|
"description": "1500x500px 크기로 조정돼요"
|
||||||
},
|
},
|
||||||
"note": {
|
"note": {
|
||||||
"title": "설명"
|
"title": "자기소개"
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"title": "메타데이터",
|
"title": "메타데이터",
|
||||||
@ -154,15 +154,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sensitive": {
|
"sensitive": {
|
||||||
"title": "미디어 민감함으로 포스트"
|
"title": "미디어를 민감함 표시 후 게시"
|
||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
"title": "계정 잠그기",
|
"title": "계정 잠그기",
|
||||||
"description": "내가 직접 팔로워를 수락해야해요"
|
"description": "직접 승인한 사람만 나를 팔로우 할 수 있어요"
|
||||||
},
|
},
|
||||||
"bot": {
|
"bot": {
|
||||||
"title": "봇 계정",
|
"title": "봇 계정",
|
||||||
"description": "이 계정이 대부분 자동으로 작업을 수행하고 잘 확인하지 않는다는 것을 알려요."
|
"description": "이 계정이 대부분 자동으로 작업을 수행하고 잘 확인하지 않는다는 것을 알려요"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
@ -180,17 +180,17 @@
|
|||||||
},
|
},
|
||||||
"global": {
|
"global": {
|
||||||
"heading": "{{acct}} 활성화",
|
"heading": "{{acct}} 활성화",
|
||||||
"description": "메시지는 tooot의 서버를 거쳐 라우트되어요"
|
"description": "메시지는 tooot의 서버를 거쳐 전달돼요"
|
||||||
},
|
},
|
||||||
"decode": {
|
"decode": {
|
||||||
"heading": "메시지 세부 정보",
|
"heading": "메시지 세부 정보",
|
||||||
"description": "tooot의 서버를 거치는 메시지는 암호화되지만, 메시지를 서버에서 복호화하도록 설정할 수 있습니다. 서버의 소스는 오픈 소스이고, 로그하지 않습니다."
|
"description": "tooot의 서버를 거치는 메시지는 암호화되어 있지만, 서버에서 이를 복호화하도록 선택할 수 있어요. 서버의 소스 코드는 오픈 소스로 관리되며 로그를 남기지 않습니다."
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"heading": "기본값"
|
"heading": "기본값"
|
||||||
},
|
},
|
||||||
"follow": {
|
"follow": {
|
||||||
"heading": "새 팔로워"
|
"heading": "새로운 팔로워"
|
||||||
},
|
},
|
||||||
"follow_request": {
|
"follow_request": {
|
||||||
"heading": "팔로우 요청"
|
"heading": "팔로우 요청"
|
||||||
@ -202,7 +202,7 @@
|
|||||||
"heading": "부스트됨"
|
"heading": "부스트됨"
|
||||||
},
|
},
|
||||||
"mention": {
|
"mention": {
|
||||||
"heading": "멘션했어요"
|
"heading": "멘션됨"
|
||||||
},
|
},
|
||||||
"poll": {
|
"poll": {
|
||||||
"heading": "투표 업데이트"
|
"heading": "투표 업데이트"
|
||||||
@ -210,7 +210,7 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"heading": "구독한 사용자의 툿"
|
"heading": "구독한 사용자의 툿"
|
||||||
},
|
},
|
||||||
"howitworks": "라우팅 방법 알아보기"
|
"howitworks": "메시지 라우팅 방식 더 알아보기"
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"announcements": {
|
"announcements": {
|
||||||
@ -255,7 +255,7 @@
|
|||||||
"heading": "$t(me.stacks.language.name)"
|
"heading": "$t(me.stacks.language.name)"
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"heading": "모양",
|
"heading": "테마",
|
||||||
"options": {
|
"options": {
|
||||||
"auto": "시스템과 동일",
|
"auto": "시스템과 동일",
|
||||||
"light": "밝은 모드",
|
"light": "밝은 모드",
|
||||||
@ -296,7 +296,7 @@
|
|||||||
"instanceVersion": "마스토돈 버전 v{{version}}"
|
"instanceVersion": "마스토돈 버전 v{{version}}"
|
||||||
},
|
},
|
||||||
"switch": {
|
"switch": {
|
||||||
"existing": "로그인된 것 중 선택",
|
"existing": "로그인 한 계정 선택",
|
||||||
"new": "인스턴스에 로그인"
|
"new": "인스턴스에 로그인"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -318,15 +318,15 @@
|
|||||||
"default": "툿",
|
"default": "툿",
|
||||||
"all": "툿과 답장"
|
"all": "툿과 답장"
|
||||||
},
|
},
|
||||||
"suspended": "계정이 서버 관리자에 의해 정지되었어요."
|
"suspended": "계정이 서버 관리자에 의해 정지되었어요"
|
||||||
},
|
},
|
||||||
"accountInLists": {
|
"accountInLists": {
|
||||||
"name": "",
|
"name": "@{{username}}의 리스트",
|
||||||
"inLists": "",
|
"inLists": "포함된 리스트",
|
||||||
"notInLists": ""
|
"notInLists": "다른 리스트"
|
||||||
},
|
},
|
||||||
"attachments": {
|
"attachments": {
|
||||||
"name": "<0 /><1>\"의 미디어</1>"
|
"name": "<0 /><1>의 미디어</1>"
|
||||||
},
|
},
|
||||||
"hashtag": {
|
"hashtag": {
|
||||||
"follow": "팔로우",
|
"follow": "팔로우",
|
||||||
@ -337,11 +337,11 @@
|
|||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"header": {
|
"header": {
|
||||||
"prefix": "무엇을",
|
"prefix": "검색할",
|
||||||
"placeholder": "검색할까요..."
|
"placeholder": "내용을 입력..."
|
||||||
},
|
},
|
||||||
"empty": {
|
"empty": {
|
||||||
"general": "키워드를 입력해 <bold>$t(screenTabs:shared.search.sections.accounts)</bold>, <bold>$t(screenTabs:shared.search.sections.hashtags)</bold>이나 <bold>$t(screenTabs:shared.search.sections.statuses)</bold>를 검색할 수 있어요",
|
"general": "키워드를 입력해 <bold>$t(screenTabs:shared.search.sections.accounts)</bold>, <bold>$t(screenTabs:shared.search.sections.hashtags)</bold>, 또는 <bold>$t(screenTabs:shared.search.sections.statuses)</bold> 등을 검색할 수 있어요",
|
||||||
"advanced": {
|
"advanced": {
|
||||||
"header": "고급 검색",
|
"header": "고급 검색",
|
||||||
"example": {
|
"example": {
|
||||||
@ -352,7 +352,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trending": {
|
"trending": {
|
||||||
"tags": ""
|
"tags": "유행하는 태그"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sections": {
|
"sections": {
|
||||||
@ -367,12 +367,12 @@
|
|||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"following": "팔로잉 {{count}}",
|
"following": "{{count}} 팔로잉",
|
||||||
"followers": "{{count}} 팔로워"
|
"followers": "{{count}} 팔로워"
|
||||||
},
|
},
|
||||||
"statuses": {
|
"statuses": {
|
||||||
"reblogged_by": "{{count}} 부스트함",
|
"reblogged_by": "{{count}} 부스트",
|
||||||
"favourited_by": "{{count}} 즐겨찾기함"
|
"favourited_by": "{{count}} 즐겨찾기"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "Rapporteren en blokkeren"
|
"action": "Rapporteren en blokkeren"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Toot kopiëren",
|
"action": "Toot kopiëren",
|
||||||
"succeed": "Gekopieerd"
|
"succeed": "Gekopieerd"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": ""
|
"action": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "",
|
"action": "",
|
||||||
"succeed": ""
|
"succeed": ""
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": ""
|
"action": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "",
|
"action": "",
|
||||||
"succeed": "Copiado"
|
"succeed": "Copiado"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "Rapportera och blockera användare"
|
"action": "Rapportera och blockera användare"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "Direktmeddelande",
|
||||||
|
"public": "Offentligt meddelande"
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Kopiera inlägg",
|
"action": "Kopiera inlägg",
|
||||||
"succeed": "Kopierat"
|
"succeed": "Kopierat"
|
||||||
|
@ -352,7 +352,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trending": {
|
"trending": {
|
||||||
"tags": ""
|
"tags": "Trendande hashtaggar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sections": {
|
"sections": {
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "Báo cáo và chặn"
|
"action": "Báo cáo và chặn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "",
|
||||||
|
"public": ""
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "Sao chép tút",
|
"action": "Sao chép tút",
|
||||||
"succeed": "Đã sao chép"
|
"succeed": "Đã sao chép"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "举报并屏蔽用户"
|
"action": "举报并屏蔽用户"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "私信",
|
||||||
|
"public": "公开信息"
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "复制嘟文",
|
"action": "复制嘟文",
|
||||||
"succeed": "已复制"
|
"succeed": "已复制"
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
"action": "檢舉並封鎖使用者"
|
"action": "檢舉並封鎖使用者"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"at": {
|
||||||
|
"direct": "私訊",
|
||||||
|
"public": "公開訊息"
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"action": "複製嘟文",
|
"action": "複製嘟文",
|
||||||
"succeed": "已複製"
|
"succeed": "已複製"
|
||||||
|
@ -352,7 +352,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trending": {
|
"trending": {
|
||||||
"tags": ""
|
"tags": "熱門標籤"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sections": {
|
"sections": {
|
||||||
|
@ -259,7 +259,9 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
type='text'
|
type='text'
|
||||||
content={
|
content={
|
||||||
params?.type
|
params?.type
|
||||||
? t(`heading.right.button.${params.type}`)
|
? params.type === 'conversation' && params.visibility === 'direct'
|
||||||
|
? t(`heading.right.button.${params.type}`)
|
||||||
|
: t('heading.right.button.default')
|
||||||
: t('heading.right.button.default')
|
: t('heading.right.button.default')
|
||||||
}
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@ -317,9 +319,8 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Sentry.captureMessage('Compose posting', {
|
Sentry.setContext('Error object', { error })
|
||||||
contexts: { errorObject: error }
|
Sentry.captureMessage('Posting error')
|
||||||
})
|
|
||||||
haptics('Error')
|
haptics('Error')
|
||||||
composeDispatch({ type: 'posting', payload: false })
|
composeDispatch({ type: 'posting', payload: false })
|
||||||
Alert.alert(t('heading.right.alert.default.title'), undefined, [
|
Alert.alert(t('heading.right.alert.default.title'), undefined, [
|
||||||
|
@ -73,8 +73,6 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
|
|||||||
color: colors.primaryDefault
|
color: colors.primaryDefault
|
||||||
}}
|
}}
|
||||||
onFocus={() => scrollViewRef.current?.scrollToEnd()}
|
onFocus={() => scrollViewRef.current?.scrollToEnd()}
|
||||||
autoCapitalize='none'
|
|
||||||
autoCorrect={false}
|
|
||||||
maxLength={1500}
|
maxLength={1500}
|
||||||
multiline
|
multiline
|
||||||
onChangeText={(e) =>
|
onChangeText={(e) =>
|
||||||
|
@ -35,8 +35,6 @@ const ComposeSpoilerInput: React.FC = () => {
|
|||||||
fontSize: adaptedFontsize,
|
fontSize: adaptedFontsize,
|
||||||
lineHeight: adaptedLineheight
|
lineHeight: adaptedLineheight
|
||||||
}}
|
}}
|
||||||
autoCapitalize='none'
|
|
||||||
autoCorrect={false}
|
|
||||||
autoFocus
|
autoFocus
|
||||||
enablesReturnKeyAutomatically
|
enablesReturnKeyAutomatically
|
||||||
multiline
|
multiline
|
||||||
|
@ -92,7 +92,7 @@ const composeParseState = (
|
|||||||
...composeInitialState,
|
...composeInitialState,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
...assignVisibility('direct')
|
...assignVisibility(params.visibility || 'direct')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ const TabMeListAccounts: React.FC<TabMeStackScreenProps<'Tab-Me-List-Accounts'>>
|
|||||||
<ComponentAccount
|
<ComponentAccount
|
||||||
key={index}
|
key={index}
|
||||||
account={item}
|
account={item}
|
||||||
Component={View}
|
|
||||||
children={
|
children={
|
||||||
<Button
|
<Button
|
||||||
type='icon'
|
type='icon'
|
||||||
@ -68,6 +67,7 @@ const TabMeListAccounts: React.FC<TabMeStackScreenProps<'Tab-Me-List-Accounts'>>
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
props={{ disabled: true }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
|
@ -58,6 +58,7 @@ const SettingsTooot: React.FC = () => {
|
|||||||
navigation.navigate('Screen-Compose', {
|
navigation.navigate('Screen-Compose', {
|
||||||
type: 'conversation',
|
type: 'conversation',
|
||||||
accts: ['tooot@xmflsct.com'],
|
accts: ['tooot@xmflsct.com'],
|
||||||
|
visibility: 'direct',
|
||||||
text:
|
text:
|
||||||
'[' +
|
'[' +
|
||||||
`${Platform.OS}/${Platform.Version}` +
|
`${Platform.OS}/${Platform.Version}` +
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
|
import menuAt from '@components/contextMenu/at'
|
||||||
import { RelationshipOutgoing } from '@components/Relationship'
|
import { RelationshipOutgoing } from '@components/Relationship'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
||||||
@ -8,32 +9,13 @@ import React from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
account: Mastodon.Account | undefined
|
account: Mastodon.Account | undefined
|
||||||
myInfo?: boolean
|
myInfo?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Conversation = ({ account }: { account: Mastodon.Account }) => {
|
|
||||||
const navigation = useNavigation<any>()
|
|
||||||
const query = useRelationshipQuery({ id: account.id })
|
|
||||||
|
|
||||||
return query.data && !query.data.blocked_by ? (
|
|
||||||
<Button
|
|
||||||
round
|
|
||||||
type='icon'
|
|
||||||
content='Mail'
|
|
||||||
style={styles.actionLeft}
|
|
||||||
onPress={() =>
|
|
||||||
navigation.navigate('Screen-Compose', {
|
|
||||||
type: 'conversation',
|
|
||||||
accts: [account.acct]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
const AccountInformationActions: React.FC<Props> = ({ account, myInfo }) => {
|
const AccountInformationActions: React.FC<Props> = ({ account, myInfo }) => {
|
||||||
if (!account || account.suspended) {
|
if (!account || account.suspended) {
|
||||||
return null
|
return null
|
||||||
@ -71,10 +53,38 @@ const AccountInformationActions: React.FC<Props> = ({ account, myInfo }) => {
|
|||||||
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
||||||
const ownAccount = account?.id === instanceAccount?.id && account?.acct === instanceAccount?.acct
|
const ownAccount = account?.id === instanceAccount?.id && account?.acct === instanceAccount?.acct
|
||||||
|
|
||||||
|
const query = useRelationshipQuery({ id: account.id })
|
||||||
|
const mAt = menuAt({ account })
|
||||||
|
|
||||||
if (!ownAccount && account) {
|
if (!ownAccount && account) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
<Conversation account={account} />
|
{query.data && !query.data.blocked_by ? (
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger>
|
||||||
|
<Button
|
||||||
|
round
|
||||||
|
type='icon'
|
||||||
|
content='AtSign'
|
||||||
|
style={{ marginRight: StyleConstants.Spacing.S }}
|
||||||
|
onPress={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
|
<DropdownMenu.Content>
|
||||||
|
{mAt.map((mGroup, index) => (
|
||||||
|
<DropdownMenu.Group key={index}>
|
||||||
|
{mGroup.map(menu => (
|
||||||
|
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||||
|
<DropdownMenu.ItemTitle children={menu.title} />
|
||||||
|
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
) : null}
|
||||||
<RelationshipOutgoing id={account.id} />
|
<RelationshipOutgoing id={account.id} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@ -86,9 +96,9 @@ const AccountInformationActions: React.FC<Props> = ({ account, myInfo }) => {
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
base: {
|
base: {
|
||||||
alignSelf: 'flex-end',
|
alignSelf: 'flex-end',
|
||||||
flexDirection: 'row'
|
flexDirection: 'row',
|
||||||
},
|
alignItems: 'center'
|
||||||
actionLeft: { marginRight: StyleConstants.Spacing.S }
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default AccountInformationActions
|
export default AccountInformationActions
|
||||||
|
@ -71,12 +71,12 @@ const TabSharedSearch: React.FC<TabSharedStackScreenProps<'Tab-Shared-Search'>>
|
|||||||
})}
|
})}
|
||||||
autoCapitalize='none'
|
autoCapitalize='none'
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
clearButtonMode='never'
|
clearButtonMode='always'
|
||||||
keyboardType='web-search'
|
keyboardType='web-search'
|
||||||
onSubmitEditing={({ nativeEvent: { text } }) => navigation.setParams({ text })}
|
onSubmitEditing={({ nativeEvent: { text } }) => navigation.setParams({ text })}
|
||||||
placeholder={t('shared.search.header.placeholder')}
|
placeholder={t('shared.search.header.placeholder')}
|
||||||
placeholderTextColor={colors.secondary}
|
placeholderTextColor={colors.secondary}
|
||||||
returnKeyType='go'
|
returnKeyType='search'
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ import { BottomTabScreenProps } from '@react-navigation/bottom-tabs'
|
|||||||
import { NavigatorScreenParams } from '@react-navigation/native'
|
import { NavigatorScreenParams } from '@react-navigation/native'
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { ComposeState } from '@screens/Compose/utils/types'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
|
|
||||||
export type RootStackParamList = {
|
export type RootStackParamList = {
|
||||||
@ -38,6 +39,7 @@ export type RootStackParamList = {
|
|||||||
| {
|
| {
|
||||||
type: 'conversation'
|
type: 'conversation'
|
||||||
accts: Mastodon.Account['acct'][]
|
accts: Mastodon.Account['acct'][]
|
||||||
|
visibility: ComposeState['visibility']
|
||||||
text?: string // For contacting tooot only
|
text?: string // For contacting tooot only
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
@ -33,14 +33,11 @@ const pushUseConnect = () => {
|
|||||||
})
|
})
|
||||||
.then(() => Notifications.setBadgeCountAsync(0))
|
.then(() => Notifications.setBadgeCountAsync(0))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Sentry.setExtras({
|
Sentry.setContext('Error response', {
|
||||||
API: 'tooot',
|
...(error?.response && { response: error.response?._response })
|
||||||
expoToken,
|
|
||||||
...(error?.response && { response: error.response })
|
|
||||||
})
|
|
||||||
Sentry.captureMessage('Push connect error', {
|
|
||||||
contexts: { errorObject: error }
|
|
||||||
})
|
})
|
||||||
|
Sentry.setContext('Error object', { error })
|
||||||
|
Sentry.captureMessage('Push connect error')
|
||||||
Notifications.setBadgeCountAsync(0)
|
Notifications.setBadgeCountAsync(0)
|
||||||
if (error?.status == 404) {
|
if (error?.status == 404) {
|
||||||
displayMessage({
|
displayMessage({
|
||||||
@ -84,10 +81,7 @@ const pushUseConnect = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Sentry.setExtras({
|
Sentry.setContext('Push', { expoToken, pushEnabledCount: pushEnabled.length })
|
||||||
expoToken,
|
|
||||||
pushEnabledCount: pushEnabled
|
|
||||||
})
|
|
||||||
|
|
||||||
if (expoToken && pushEnabled.length) {
|
if (expoToken && pushEnabled.length) {
|
||||||
connect()
|
connect()
|
||||||
|
@ -74,8 +74,7 @@ const pushRegister = async (
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!res.body.server_key?.length) {
|
if (!res.body.server_key?.length) {
|
||||||
Sentry.setExtras({
|
Sentry.setContext('Push server key', {
|
||||||
API: 'tooot',
|
|
||||||
instance: instanceUri,
|
instance: instanceUri,
|
||||||
resBody: res.body
|
resBody: res.body
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user