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

Merge pull request #508 from tooot-app/main

Test audio playback
This commit is contained in:
xmflsct
2022-12-04 14:53:30 +01:00
committed by GitHub
48 changed files with 339 additions and 231 deletions

View 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

View File

@ -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'

View File

@ -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

View File

@ -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 }

View File

@ -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'

View File

@ -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)
})

View File

@ -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
/>
)
}

View File

@ -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}

View File

@ -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()
}
}
}}

View 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

View File

@ -19,6 +19,10 @@
"action": "Denuncia i bloqueja l'usuari"
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "Copia la publicació",
"succeed": "Copiat"

View File

@ -19,6 +19,10 @@
"action": ""
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "",
"succeed": ""

View File

@ -19,6 +19,10 @@
"action": "Nutzer melden und blockieren"
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "Tröt kopieren",
"succeed": "Kopiert"

View File

@ -352,7 +352,7 @@
}
},
"trending": {
"tags": ""
"tags": "Angesagte Tags"
}
},
"sections": {

View File

@ -19,6 +19,10 @@
"action": "Report and block user"
}
},
"at": {
"direct": "Direct message",
"public": "Public message"
},
"copy": {
"action": "Copy toot",
"succeed": "Copied"

View File

@ -19,6 +19,10 @@
"action": "Reportar y bloquear usuario"
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "Copiar toot",
"succeed": "Copiado"

View File

@ -19,6 +19,10 @@
"action": ""
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "Copier le Pouet",
"succeed": "Copié"

View File

@ -19,6 +19,10 @@
"action": ""
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "",
"succeed": "Copiato"

View File

@ -19,6 +19,10 @@
"action": "ユーザーの報告とブロック"
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "トゥートをコピー",
"succeed": "コピー完了"

View File

@ -352,7 +352,7 @@
}
},
"trending": {
"tags": ""
"tags": "トレンドタグ"
}
},
"sections": {

View File

@ -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": "변경 사항이 저장되지 않았습니다. 작업 내용 저장을 취소할까요?"
}
}

View File

@ -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": "복사됨"

View File

@ -1,7 +1,7 @@
{
"screenshot": {
"title": "개인정보 보호",
"message": "다른 사용자의 사용자 이름이나, 아바타 등의 정보를 유출하지 말아주세요. 고마워요!",
"message": "다른 사용자의 이름이나, 프로필 사진 등의 정보를 유출하지 말아주세요. 고마워요!",
"button": "확인"
},
"localCorrupt": {

View File

@ -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}} 즐겨찾기"
}
}
}

View File

@ -19,6 +19,10 @@
"action": "Rapporteren en blokkeren"
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "Toot kopiëren",
"succeed": "Gekopieerd"

View File

@ -19,6 +19,10 @@
"action": ""
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "",
"succeed": ""

View File

@ -19,6 +19,10 @@
"action": ""
}
},
"at": {
"direct": "",
"public": ""
},
"copy": {
"action": "",
"succeed": "Copiado"

View File

@ -19,6 +19,10 @@
"action": "Rapportera och blockera användare"
}
},
"at": {
"direct": "Direktmeddelande",
"public": "Offentligt meddelande"
},
"copy": {
"action": "Kopiera inlägg",
"succeed": "Kopierat"

View File

@ -352,7 +352,7 @@
}
},
"trending": {
"tags": ""
"tags": "Trendande hashtaggar"
}
},
"sections": {

View File

@ -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"

View File

@ -19,6 +19,10 @@
"action": "举报并屏蔽用户"
}
},
"at": {
"direct": "私信",
"public": "公开信息"
},
"copy": {
"action": "复制嘟文",
"succeed": "已复制"

View File

@ -19,6 +19,10 @@
"action": "檢舉並封鎖使用者"
}
},
"at": {
"direct": "私訊",
"public": "公開訊息"
},
"copy": {
"action": "複製嘟文",
"succeed": "已複製"

View File

@ -352,7 +352,7 @@
}
},
"trending": {
"tags": ""
"tags": "熱門標籤"
}
},
"sections": {

View File

@ -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, [

View File

@ -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) =>

View File

@ -35,8 +35,6 @@ const ComposeSpoilerInput: React.FC = () => {
fontSize: adaptedFontsize,
lineHeight: adaptedLineheight
}}
autoCapitalize='none'
autoCorrect={false}
autoFocus
enablesReturnKeyAutomatically
multiline

View File

@ -92,7 +92,7 @@ const composeParseState = (
...composeInitialState,
dirty: true,
timestamp: Date.now(),
...assignVisibility('direct')
...assignVisibility(params.visibility || 'direct')
}
}
}

View File

@ -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={

View File

@ -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}` +

View File

@ -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

View File

@ -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>
)

View File

@ -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
}
| {

View File

@ -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()

View File

@ -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
})