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