From 39ab9059d9f97a8673d90ddcde4f49ccc27bbe7a Mon Sep 17 00:00:00 2001 From: xmflsct Date: Thu, 22 Dec 2022 01:21:51 +0100 Subject: [PATCH 01/93] Fix #600 --- fastlane/metadata/en-US/release_notes.txt | 3 +- fastlane/metadata/zh-Hans/release_notes.txt | 3 +- package.json | 2 +- src/@types/mastodon.d.ts | 5 + src/components/Account.tsx | 4 +- src/components/Emojis/List.tsx | 3 +- src/components/Header/Right.tsx | 24 +- src/components/Input.tsx | 1 - src/components/Parse/Emojis.tsx | 2 +- src/components/Selections.tsx | 20 +- src/components/Timeline/Shared/Content.tsx | 12 +- .../Timeline/Shared/HeaderAndroid.tsx | 1 + .../Timeline/Shared/HeaderDefault.tsx | 1 + .../Timeline/Shared/HeaderNotification.tsx | 1 + src/components/contextMenu/account.ts | 33 +-- src/i18n/en/screens/tabs.json | 19 ++ src/screens/Tabs/Shared/Report.tsx | 218 ++++++++++++++++++ src/screens/Tabs/Shared/index.tsx | 9 +- src/utils/navigation/navigators.ts | 3 +- src/utils/queryHooks/reports.ts | 18 ++ 20 files changed, 320 insertions(+), 62 deletions(-) create mode 100644 src/screens/Tabs/Shared/Report.tsx create mode 100644 src/utils/queryHooks/reports.ts diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index 9e267a57..568f648b 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -1,3 +1,2 @@ Enjoy toooting! This version includes following improvements and fixes: -- Fixed wrongly update notification -- Fix some random crashes +- Allowing adding more context of reports diff --git a/fastlane/metadata/zh-Hans/release_notes.txt b/fastlane/metadata/zh-Hans/release_notes.txt index 8ba28658..1f213bc2 100644 --- a/fastlane/metadata/zh-Hans/release_notes.txt +++ b/fastlane/metadata/zh-Hans/release_notes.txt @@ -1,3 +1,2 @@ toooting愉快!此版本包括以下改进和修复: -- 修复错误的升级通知 -- 修复部分应用崩溃 +- 可添加举报细节 diff --git a/package.json b/package.json index 1a0f20db..02186088 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.7.2", + "version": "4.7.3", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", diff --git a/src/@types/mastodon.d.ts b/src/@types/mastodon.d.ts index 102739ca..80b3e477 100644 --- a/src/@types/mastodon.d.ts +++ b/src/@types/mastodon.d.ts @@ -470,6 +470,11 @@ declare namespace Mastodon { updated_at: string } + type Rule = { + id: string + text: string + } + type Status = { // Base id: string diff --git a/src/components/Account.tsx b/src/components/Account.tsx index b2c7a058..7445ad7e 100644 --- a/src/components/Account.tsx +++ b/src/components/Account.tsx @@ -42,11 +42,11 @@ const ComponentAccount: React.FC = ({ account, props, style={{ width: StyleConstants.Avatar.S, height: StyleConstants.Avatar.S, - borderRadius: 6, + borderRadius: 8, marginRight: StyleConstants.Spacing.S }} /> - + { const { t } = useTranslation() const { emojisState, emojisDispatch } = useContext(EmojisContext) - const { colors, mode } = useTheme() + const { colors } = useTheme() const addEmoji = (shortcode: string) => { if (emojisState.targetIndex === -1) { @@ -158,7 +158,6 @@ const EmojisList = () => { onChangeText={setSearch} autoCapitalize='none' clearButtonMode='always' - keyboardAppearance={mode} autoCorrect={false} spellCheck={false} /> diff --git a/src/components/Header/Right.tsx b/src/components/Header/Right.tsx index 3c833c86..ade36587 100644 --- a/src/components/Header/Right.tsx +++ b/src/components/Header/Right.tsx @@ -18,6 +18,7 @@ export interface Props { loading?: boolean disabled?: boolean + destructive?: boolean onPress: () => void } @@ -34,6 +35,7 @@ const HeaderRight: React.FC = ({ background = false, loading, disabled, + destructive = false, onPress }) => { const { colors, theme } = useTheme() @@ -41,10 +43,7 @@ const HeaderRight: React.FC = ({ const loadingSpinkit = useMemo( () => ( - + ), [theme] @@ -59,7 +58,7 @@ const HeaderRight: React.FC = ({ name={content} style={{ opacity: loading ? 0 : 1 }} size={StyleConstants.Spacing.M * 1.25} - color={disabled ? colors.secondary : colors.primaryDefault} + color={disabled ? colors.secondary : destructive ? colors.red : colors.primaryDefault} /> {loading && loadingSpinkit} @@ -69,8 +68,13 @@ const HeaderRight: React.FC = ({ <> = ({ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', - backgroundColor: background - ? colors.backgroundOverlayDefault - : undefined, + backgroundColor: background ? colors.backgroundOverlayDefault : undefined, minHeight: 44, minWidth: 44, - marginRight: native - ? -StyleConstants.Spacing.S - : StyleConstants.Spacing.S, + marginRight: native ? -StyleConstants.Spacing.S : StyleConstants.Spacing.S, ...(type === 'icon' && { borderRadius: 100 }), diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 7354dc44..c6b3de63 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -85,7 +85,6 @@ const ComponentInput = forwardRef( multiline, numberOfLines: Platform.OS === 'android' ? 5 : undefined })} - keyboardAppearance={mode} textAlignVertical='top' {...props} /> diff --git a/src/components/Parse/Emojis.tsx b/src/components/Parse/Emojis.tsx index 7607f555..195c9893 100644 --- a/src/components/Parse/Emojis.tsx +++ b/src/components/Parse/Emojis.tsx @@ -93,7 +93,7 @@ const ParseEmojis = React.memo( ) }, - (prev, next) => prev.content === next.content + (prev, next) => prev.content === next.content && prev.style?.color === next.style?.color ) export default ParseEmojis diff --git a/src/components/Selections.tsx b/src/components/Selections.tsx index 3e39cdfd..9fa38501 100644 --- a/src/components/Selections.tsx +++ b/src/components/Selections.tsx @@ -11,9 +11,15 @@ export interface Props { multiple?: boolean options: { selected: boolean; content: string }[] setOptions: React.Dispatch> + disabled?: boolean } -const Selections: React.FC = ({ multiple = false, options, setOptions }) => { +const Selections: React.FC = ({ + multiple = false, + options, + setOptions, + disabled = false +}) => { const { colors } = useTheme() const isSelected = (index: number): string => @@ -22,10 +28,11 @@ const Selections: React.FC = ({ multiple = false, options, setOptions }) : `${multiple ? 'Square' : 'Circle'}` return ( - <> + {options.map((option, index) => ( { if (multiple) { @@ -56,15 +63,18 @@ const Selections: React.FC = ({ multiple = false, options, setOptions }) }} name={isSelected(index)} size={StyleConstants.Font.Size.M} - color={colors.primaryDefault} + color={disabled ? colors.disabled : colors.primaryDefault} /> - + ))} - + ) } diff --git a/src/components/Timeline/Shared/Content.tsx b/src/components/Timeline/Shared/Content.tsx index 5cbcde98..b250d56a 100644 --- a/src/components/Timeline/Shared/Content.tsx +++ b/src/components/Timeline/Shared/Content.tsx @@ -5,8 +5,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useContext } from 'react' import { useTranslation } from 'react-i18next' -import { Platform, StyleSheet, View } from 'react-native' -import { Path, Svg } from 'react-native-svg' +import { Platform } from 'react-native' import { useSelector } from 'react-redux' import { isRtlLang } from 'rtl-detect' import StatusContext from './Context' @@ -45,7 +44,14 @@ const TimelineContent: React.FC = ({ notificationOwnToot = false, setSpoi } /> {inThread ? ( - + {t('shared.content.expandHint')} ) : null} diff --git a/src/components/Timeline/Shared/HeaderAndroid.tsx b/src/components/Timeline/Shared/HeaderAndroid.tsx index ece4ef79..31836e4c 100644 --- a/src/components/Timeline/Shared/HeaderAndroid.tsx +++ b/src/components/Timeline/Shared/HeaderAndroid.tsx @@ -28,6 +28,7 @@ const TimelineHeaderAndroid: React.FC = () => { type: 'status', openChange, account: status.account, + ...(status && { status }), queryKey }) const mStatus = menuStatus({ status, queryKey, rootQueryKey }) diff --git a/src/components/Timeline/Shared/HeaderDefault.tsx b/src/components/Timeline/Shared/HeaderDefault.tsx index 44e28479..1a5f4d98 100644 --- a/src/components/Timeline/Shared/HeaderDefault.tsx +++ b/src/components/Timeline/Shared/HeaderDefault.tsx @@ -34,6 +34,7 @@ const TimelineHeaderDefault: React.FC = () => { type: 'status', openChange, account: status.account, + ...(status && { status }), queryKey }) const mStatus = menuStatus({ status, queryKey, rootQueryKey }) diff --git a/src/components/Timeline/Shared/HeaderNotification.tsx b/src/components/Timeline/Shared/HeaderNotification.tsx index 02cdef2f..171148ff 100644 --- a/src/components/Timeline/Shared/HeaderNotification.tsx +++ b/src/components/Timeline/Shared/HeaderNotification.tsx @@ -42,6 +42,7 @@ const TimelineHeaderNotification: React.FC = ({ notification }) => { type: 'status', openChange, account: status?.account, + ...(status && { status }), queryKey }) const mStatus = menuStatus({ status, queryKey }) diff --git a/src/components/contextMenu/account.ts b/src/components/contextMenu/account.ts index 1b26735a..21a5bed4 100644 --- a/src/components/contextMenu/account.ts +++ b/src/components/contextMenu/account.ts @@ -24,12 +24,14 @@ const menuAccount = ({ type, openChange, account, + status, queryKey, rootQueryKey }: { type: 'status' | 'account' // Where the action is coming from openChange: boolean - account?: Pick + account?: Mastodon.Account + status?: Mastodon.Status queryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline }): ContextMenu[][] => { @@ -214,34 +216,7 @@ const menuAccount = ({ { key: 'account-reports', item: { - onSelect: () => - Alert.alert( - t('account.reports.alert.title', { username: account.username }), - undefined, - [ - { - text: t('common:buttons.confirm'), - style: 'destructive', - onPress: () => { - timelineMutation.mutate({ - type: 'updateAccountProperty', - queryKey, - id: account.id, - payload: { property: 'reports' } - }) - timelineMutation.mutate({ - type: 'updateAccountProperty', - queryKey, - id: account.id, - payload: { property: 'block', currentValue: false } - }) - } - }, - { - text: t('common:buttons.cancel') - } - ] - ), + onSelect: () => navigation.navigate('Tab-Shared-Report', { account, status }), disabled: false, destructive: true, hidden: false diff --git a/src/i18n/en/screens/tabs.json b/src/i18n/en/screens/tabs.json index e8785d5d..75c975eb 100644 --- a/src/i18n/en/screens/tabs.json +++ b/src/i18n/en/screens/tabs.json @@ -357,6 +357,25 @@ "history": { "name": "Edit History" }, + "report": { + "name": "Report {{acct}}", + "report": "Report", + "forward": { + "heading": "Anonymously forward to remote server {{instance}}" + }, + "reasons": { + "heading": "What is going on with this account?", + "spam": "It is spam", + "other": "It is something else", + "violation": "It violates server rules" + }, + "comment": { + "heading": "Anything else you want to add?" + }, + "violatedRules": { + "heading": "Violated server rules" + } + }, "search": { "header": { "prefix": "Searching", diff --git a/src/screens/Tabs/Shared/Report.tsx b/src/screens/Tabs/Shared/Report.tsx new file mode 100644 index 00000000..97e92920 --- /dev/null +++ b/src/screens/Tabs/Shared/Report.tsx @@ -0,0 +1,218 @@ +import apiInstance from '@api/instance' +import ComponentAccount from '@components/Account' +import { HeaderLeft, HeaderRight } from '@components/Header' +import Selections from '@components/Selections' +import CustomText from '@components/Text' +import { TabSharedStackScreenProps } from '@utils/navigation/navigators' +import { useRulesQuery } from '@utils/queryHooks/reports' +import { getInstanceUri } from '@utils/slices/instancesSlice' +import { StyleConstants } from '@utils/styles/constants' +import { useTheme } from '@utils/styles/ThemeManager' +import React, { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { Platform, ScrollView, TextInput, View } from 'react-native' +import { Switch } from 'react-native-gesture-handler' +import { useSelector } from 'react-redux' + +const TabSharedReport: React.FC> = ({ + navigation, + route: { + params: { account, status } + } +}) => { + const { colors } = useTheme() + const { t } = useTranslation('screenTabs') + + const [categories, setCategories] = useState< + { selected: boolean; content: string; type: 'spam' | 'other' | 'violation' }[] + >([ + { selected: true, content: t('shared.report.reasons.spam'), type: 'spam' }, + { selected: false, content: t('shared.report.reasons.other'), type: 'other' }, + { selected: false, content: t('shared.report.reasons.violation'), type: 'violation' } + ]) + const [rules, setRules] = useState<{ selected: boolean; content: string; id: string }[]>([]) + const [forward, setForward] = useState(true) + const [comment, setComment] = useState('') + + const [isReporting, setIsReporting] = useState(false) + useEffect(() => { + navigation.setOptions({ + title: t('shared.report.name', { acct: `@${account.acct}` }), + headerLeft: () => ( + navigation.goBack()} + /> + ), + headerRight: () => ( + { + const body = new FormData() + body.append('account_id', account.id) + status && body.append('status_ids[]', status.id) + comment.length && body.append('comment', comment) + body.append('forward', forward.toString()) + body.append('category', categories.find(category => category.selected)?.type || 'other') + rules.filter(rule => rule.selected).forEach(rule => body.append('rule_ids[]', rule.id)) + + apiInstance({ method: 'post', url: 'reports', body }) + .then(() => { + setIsReporting(false) + navigation.pop(1) + }) + .catch(() => { + setIsReporting(false) + }) + }} + loading={isReporting} + /> + ) + }) + }, [isReporting, comment, forward, categories, rules]) + + const instanceUri = useSelector(getInstanceUri) + const localInstance = account?.acct.includes('@') + ? account?.acct.includes(`@${instanceUri}`) + : true + + const rulesQuery = useRulesQuery() + useEffect(() => { + if (rulesQuery.data) { + setRules(rulesQuery.data.map(rule => ({ ...rule, selected: false, content: rule.text }))) + } + }, [rulesQuery.data]) + + return ( + + + + + + {!localInstance ? ( + + + {t('shared.report.forward.heading', { instance: account.acct.match(/@(.*)/)?.[1] })} + + + + ) : null} + + + {t('shared.report.reasons.heading')} + + + + + + {categories[1].selected || comment.length ? ( + <> + + {t('shared.report.comment.heading')} + + + + + + + {comment.length} / 1000 + + + + + ) : null} + + {rules.length ? ( + <> + + {t('shared.report.violatedRules.heading')} + + + + + + ) : null} + + + ) +} + +export default TabSharedReport diff --git a/src/screens/Tabs/Shared/index.tsx b/src/screens/Tabs/Shared/index.tsx index dfc67d4a..7ae62d84 100644 --- a/src/screens/Tabs/Shared/index.tsx +++ b/src/screens/Tabs/Shared/index.tsx @@ -1,13 +1,14 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack' import TabSharedAccount from '@screens/Tabs/Shared/Account' +import TabSharedAccountInLists from '@screens/Tabs/Shared/AccountInLists' import TabSharedAttachments from '@screens/Tabs/Shared/Attachments' import TabSharedHashtag from '@screens/Tabs/Shared/Hashtag' import TabSharedHistory from '@screens/Tabs/Shared/History' +import TabSharedReport from '@screens/Tabs/Shared/Report' import TabSharedSearch from '@screens/Tabs/Shared/Search' import TabSharedToot from '@screens/Tabs/Shared/Toot' import TabSharedUsers from '@screens/Tabs/Shared/Users' import React from 'react' -import TabSharedAccountInLists from './AccountInLists' const TabShared = ({ Stack }: { Stack: ReturnType }) => { return ( @@ -37,6 +38,12 @@ const TabShared = ({ Stack }: { Stack: ReturnType + diff --git a/src/utils/navigation/navigators.ts b/src/utils/navigation/navigators.ts index 95f752d8..42095ac2 100644 --- a/src/utils/navigation/navigators.ts +++ b/src/utils/navigation/navigators.ts @@ -92,7 +92,7 @@ export type ScreenTabsScreenProps = Bo export type TabSharedStackParamList = { 'Tab-Shared-Account': { - account: Mastodon.Account | Mastodon.Mention + account: Partial & Pick } 'Tab-Shared-Account-In-Lists': { account: Pick @@ -105,6 +105,7 @@ export type TabSharedStackParamList = { id: Mastodon.Status['id'] detectedLanguage: string } + 'Tab-Shared-Report': { account: Mastodon.Account; status?: Pick } 'Tab-Shared-Search': undefined 'Tab-Shared-Toot': { toot: Mastodon.Status diff --git a/src/utils/queryHooks/reports.ts b/src/utils/queryHooks/reports.ts new file mode 100644 index 00000000..9b33fcf9 --- /dev/null +++ b/src/utils/queryHooks/reports.ts @@ -0,0 +1,18 @@ +import apiInstance from '@api/instance' +import { AxiosError } from 'axios' +import { useQuery, UseQueryOptions } from '@tanstack/react-query' + +export type QueryKeyRules = ['Rules'] + +const queryFunction = () => + apiInstance({ + method: 'get', + url: 'instance/rules' + }).then(res => res.body) + +const useRulesQuery = (params?: { options?: UseQueryOptions }) => { + const queryKey: QueryKeyRules = ['Rules'] + return useQuery(queryKey, queryFunction, params?.options) +} + +export { useRulesQuery } From b388853429ae509632bc3d5bc1be7df123192327 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Thu, 22 Dec 2022 18:38:04 +0100 Subject: [PATCH 02/93] Refine types for #600 --- src/components/Account.tsx | 2 +- src/components/Parse/Emojis.tsx | 4 +++- src/components/contextMenu/account.ts | 2 +- src/components/contextMenu/share.ts | 2 +- src/utils/navigation/navigators.ts | 7 +++++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/Account.tsx b/src/components/Account.tsx index 7445ad7e..7153973a 100644 --- a/src/components/Account.tsx +++ b/src/components/Account.tsx @@ -11,7 +11,7 @@ import Icon from './Icon' import CustomText from './Text' export interface Props { - account: Mastodon.Account + account: Partial & Pick props?: PressableProps } diff --git a/src/components/Parse/Emojis.tsx b/src/components/Parse/Emojis.tsx index 195c9893..a43835a1 100644 --- a/src/components/Parse/Emojis.tsx +++ b/src/components/Parse/Emojis.tsx @@ -13,7 +13,7 @@ import validUrl from 'valid-url' const regexEmoji = new RegExp(/(:[A-Za-z0-9_]+:)/) export interface Props { - content: string + content?: string emojis?: Mastodon.Emoji[] size?: 'S' | 'M' | 'L' adaptiveSize?: boolean @@ -23,6 +23,8 @@ export interface Props { const ParseEmojis = React.memo( ({ content, emojis, size = 'M', adaptiveSize = false, fontBold = false, style }: Props) => { + if (!content) return null + const { reduceMotionEnabled } = useAccessibility() const adaptiveFontsize = useSelector(getSettingsFontsize) diff --git a/src/components/contextMenu/account.ts b/src/components/contextMenu/account.ts index 21a5bed4..128745b3 100644 --- a/src/components/contextMenu/account.ts +++ b/src/components/contextMenu/account.ts @@ -30,7 +30,7 @@ const menuAccount = ({ }: { type: 'status' | 'account' // Where the action is coming from openChange: boolean - account?: Mastodon.Account + account?: Partial & Pick status?: Mastodon.Status queryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline diff --git a/src/components/contextMenu/share.ts b/src/components/contextMenu/share.ts index 1f6d63d6..fa99dd89 100644 --- a/src/components/contextMenu/share.ts +++ b/src/components/contextMenu/share.ts @@ -13,7 +13,7 @@ const menuShare = ( } | { type: 'account' - url: string + url?: string } ): ContextMenu[][] => { if (params.type === 'status' && params.visibility === 'direct') return [] diff --git a/src/utils/navigation/navigators.ts b/src/utils/navigation/navigators.ts index 42095ac2..3403c2fb 100644 --- a/src/utils/navigation/navigators.ts +++ b/src/utils/navigation/navigators.ts @@ -92,7 +92,7 @@ export type ScreenTabsScreenProps = Bo export type TabSharedStackParamList = { 'Tab-Shared-Account': { - account: Partial & Pick + account: Partial & Pick } 'Tab-Shared-Account-In-Lists': { account: Pick @@ -105,7 +105,10 @@ export type TabSharedStackParamList = { id: Mastodon.Status['id'] detectedLanguage: string } - 'Tab-Shared-Report': { account: Mastodon.Account; status?: Pick } + 'Tab-Shared-Report': { + account: Partial & Pick + status?: Pick + } 'Tab-Shared-Search': undefined 'Tab-Shared-Toot': { toot: Mastodon.Status From e32125ad170f9eaa78c608ab7bc5eac7946a2918 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Fri, 23 Dec 2022 15:53:40 +0100 Subject: [PATCH 03/93] Update i18next type --- ios/Podfile.lock | 12 +- src/@types/i18next.d.ts | 40 +- src/Screens.tsx | 15 +- src/components/Emojis.tsx | 3 +- src/components/Emojis/List.tsx | 2 +- src/components/Instance/index.tsx | 45 +- src/components/Relationship/Incoming.tsx | 4 +- src/components/Relationship/Outgoing.tsx | 16 +- src/components/Timeline/Footer.tsx | 3 +- src/components/Timeline/Shared/Actions.tsx | 26 +- src/components/Timeline/Shared/Filtered.tsx | 17 +- .../Timeline/Shared/HeaderConversation.tsx | 6 +- src/components/Timeline/Shared/Poll.tsx | 22 +- src/components/Timeline/Shared/Translate.tsx | 10 +- src/components/contextMenu/account.ts | 81 +- src/components/contextMenu/instance.ts | 10 +- src/components/contextMenu/status.ts | 122 +-- src/i18n/i18n.ts | 1 + src/screens/Actions/AltText.tsx | 8 +- src/screens/Announcements.tsx | 3 +- src/screens/Compose.tsx | 22 +- src/screens/Compose/Root/Actions.tsx | 28 +- src/screens/Compose/Root/Footer/Poll.tsx | 39 +- src/screens/ImagesViewer.tsx | 14 +- src/screens/Tabs/Me/FollowedTags.tsx | 8 +- src/screens/Tabs/Me/List/Accounts.tsx | 6 +- src/screens/Tabs/Me/List/Edit.tsx | 21 +- src/screens/Tabs/Me/List/List.tsx | 2 +- src/screens/Tabs/Me/List/index.tsx | 4 +- src/screens/Tabs/Me/Profile/Fields.tsx | 36 +- src/screens/Tabs/Me/Profile/Name.tsx | 28 +- src/screens/Tabs/Me/Profile/Note.tsx | 28 +- src/screens/Tabs/Me/Profile/Root.tsx | 120 +-- .../Tabs/Me/Profile/Root/AvatarHeader.tsx | 6 +- src/screens/Tabs/Me/Root/Collections.tsx | 25 +- src/screens/Tabs/Me/Root/Logout.tsx | 38 +- src/screens/Tabs/Me/Settings/App.tsx | 46 +- src/screens/Tabs/Notifications/Filters.tsx | 8 +- src/screens/Tabs/Shared/AccountInLists.tsx | 12 +- src/screens/Tabs/Shared/Attachments.tsx | 3 +- src/screens/Tabs/Shared/Hashtag.tsx | 12 +- src/screens/Tabs/Shared/Report.tsx | 22 +- src/screens/Tabs/Shared/Search/Empty.tsx | 3 +- src/screens/Tabs/Shared/Search/index.tsx | 3 +- src/screens/Tabs/Shared/Users.tsx | 4 +- src/startup/sentry.ts | 8 +- src/utils/queryHooks/profile.ts | 4 +- src/utils/slices/instances/push/utils.ts | 2 +- yarn.lock | 952 +++++++++--------- 49 files changed, 1054 insertions(+), 896 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1ad566ac..e148c49d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -16,7 +16,7 @@ PODS: - ExpoModulesCore - EXNotifications (0.17.0): - ExpoModulesCore - - Expo (47.0.8): + - Expo (47.0.9): - ExpoModulesCore - ExpoCrypto (12.0.0): - ExpoModulesCore @@ -26,7 +26,7 @@ PODS: - ExpoModulesCore - ExpoLocalization (14.0.0): - ExpoModulesCore - - ExpoModulesCore (1.0.3): + - ExpoModulesCore (1.0.4): - React-Core - ReactCommon/turbomodule/core - ExpoRandom (13.0.0): @@ -297,7 +297,7 @@ PODS: - React-Core - react-native-cameraroll (5.2.0): - React-Core - - react-native-image-picker (4.10.2): + - react-native-image-picker (4.10.3): - React-Core - react-native-ios-context-menu (1.15.1): - React-Core @@ -694,12 +694,12 @@ SPEC CHECKSUMS: EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6 EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80 EXNotifications: babce2a87b7922051354fcfe7a74dd279b7e272a - Expo: 36b5f625d36728adbdd1934d4d57182f319ab832 + Expo: 5523a4835730104a301caeac1e68dad095a9dfff ExpoCrypto: 51e7662c7f5bfeab25b7909b8a5d545ec15d4877 ExpoHaptics: 5a56d30a87ea213dd00b09566dc4b441a4dff97f ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318 ExpoLocalization: e202d1e2a4950df17ac8d0889d65a1ffd7532d7e - ExpoModulesCore: b5d21c8880afda6fb6ee95469f9ac2ec9b98e995 + ExpoModulesCore: 8211c89bcc3ba86e2d02c54b1d31f1fa81acb6de ExpoRandom: 58b7e0a5fe1adf1cb6dc1cbe503a6fe9524f36ce ExpoStoreReview: ff6d631f2949eb7e4b2d14146ef6af25a16d770d ExpoWebBrowser: 073e50f16669d498fb49063b9b7fe780b24f7fda @@ -732,7 +732,7 @@ SPEC CHECKSUMS: react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3 react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7 react-native-cameraroll: 0ff04cc4e0ff5f19a94ff4313e5c8bc4503cd86d - react-native-image-picker: bf34f3f516d139ed3e24c5f5a381a91819e349ea + react-native-image-picker: 60f4246eb5bb7187fc15638a8c1f13abd3820695 react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac react-native-language-detection: f414937fa715108ab50a6269a3de0bcb95e4ceb0 react-native-menu: 8e172cfcf0e42e92f028e7781eddf84d430cae24 diff --git a/src/@types/i18next.d.ts b/src/@types/i18next.d.ts index a1693a61..0eebc410 100644 --- a/src/@types/i18next.d.ts +++ b/src/@types/i18next.d.ts @@ -1,8 +1,46 @@ import 'i18next' +import common from '../i18n/en/common.json' +import screens from '../i18n/en/screens.json' + +import screenAccountSelection from '../i18n/en/screens/accountSelection.json' +import screenActions from '../i18n/en/screens/actions.json' +import screenAnnouncements from '../i18n/en/screens/announcements.json' +import screenCompose from '../i18n/en/screens/compose.json' +import screenImageViewer from '../i18n/en/screens/imageViewer.json' +import screenTabs from '../i18n/en/screens/tabs.json' + +import componentContextMenu from '../i18n/en/components/contextMenu.json' +import componentEmojis from '../i18n/en/components/emojis.json' +import componentInstance from '../i18n/en/components/instance.json' +import componentMediaSelector from '../i18n/en/components/mediaSelector.json' +import componentParse from '../i18n/en/components/parse.json' +import componentRelationship from '../i18n/en/components/relationship.json' +import componentTimeline from '../i18n/en/components/timeline.json' + declare module 'i18next' { interface CustomTypeOptions { - defaultNS: 'common', + defaultNS: 'common' + resources: { + common: typeof common + screens: typeof screens + + screenAccountSelection: typeof screenAccountSelection + screenActions: typeof screenActions + screenAnnouncements: typeof screenAnnouncements + screenCompose: typeof screenCompose + screenImageViewer: typeof screenImageViewer + screenTabs: typeof screenTabs + + componentContextMenu: typeof componentContextMenu + componentEmojis: typeof componentEmojis + componentInstance: typeof componentInstance + componentMediaSelector: typeof componentMediaSelector + componentParse: typeof componentParse + componentRelationship: typeof componentRelationship + componentTimeline: typeof componentTimeline + } returnNull: false + returnEmptyString: false } } diff --git a/src/Screens.tsx b/src/Screens.tsx index 6febfbde..cc16181e 100644 --- a/src/Screens.tsx +++ b/src/Screens.tsx @@ -39,7 +39,12 @@ export interface Props { } const Screens: React.FC = ({ localCorrupt }) => { - const { t } = useTranslation('screens') + const { t } = useTranslation([ + 'common', + 'screens', + 'screenAnnouncements', + 'screenAccountSelection' + ]) const dispatch = useAppDispatch() const instanceActive = useSelector(getInstanceActive) const { colors, theme } = useTheme() @@ -55,7 +60,7 @@ const Screens: React.FC = ({ localCorrupt }) => { // Prevent screenshot alert useEffect(() => { const screenshotListener = addScreenshotListener(() => - Alert.alert(t('screenshot.title'), t('screenshot.message'), [ + Alert.alert(t('screens:screenshot.title'), t('screens:screenshot.message'), [ { text: t('common:buttons.confirm'), style: 'destructive' } ]) ) @@ -68,7 +73,7 @@ const Screens: React.FC = ({ localCorrupt }) => { const showLocalCorrect = () => { if (localCorrupt) { displayMessage({ - message: t('localCorrupt.message'), + message: t('screens:localCorrupt.message'), description: localCorrupt.length ? localCorrupt : undefined, type: 'danger' }) @@ -176,7 +181,7 @@ const Screens: React.FC = ({ localCorrupt }) => { if (!typesImage.includes(mime.split('/')[1])) { console.warn('Image type not supported:', mime.split('/')[1]) displayMessage({ - message: t('shareError.imageNotSupported', { + message: t('screens:shareError.imageNotSupported', { type: mime.split('/')[1] }), type: 'danger' @@ -188,7 +193,7 @@ const Screens: React.FC = ({ localCorrupt }) => { if (!typesVideo.includes(mime.split('/')[1])) { console.warn('Video type not supported:', mime.split('/')[1]) displayMessage({ - message: t('shareError.videoNotSupported', { + message: t('screens:shareError.videoNotSupported', { type: mime.split('/')[1] }), type: 'danger' diff --git a/src/components/Emojis.tsx b/src/components/Emojis.tsx index ca744678..5b876f2e 100644 --- a/src/components/Emojis.tsx +++ b/src/components/Emojis.tsx @@ -7,7 +7,6 @@ import { chunk, forEach, groupBy, sortBy } from 'lodash' import React, { createRef, PropsWithChildren, useEffect, useReducer, useState } from 'react' import { useTranslation } from 'react-i18next' import { Keyboard, KeyboardAvoidingView, View } from 'react-native' -import FastImage from 'react-native-fast-image' import { Edge, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context' import { useSelector } from 'react-redux' import EmojisContext, { Emojis, emojisReducer, EmojisState } from './Emojis/helpers/EmojisContext' @@ -35,7 +34,7 @@ const ComponentEmojis: React.FC = ({ emojisDispatch({ type: 'input', payload: inputProps }) }, [inputProps]) - const { t } = useTranslation() + const { t } = useTranslation(['componentEmojis']) const { data } = useEmojisQuery({}) const frequentEmojis = useSelector(getInstanceFrequentEmojis, () => true) useEffect(() => { diff --git a/src/components/Emojis/List.tsx b/src/components/Emojis/List.tsx index 8b2c243d..983316e0 100644 --- a/src/components/Emojis/List.tsx +++ b/src/components/Emojis/List.tsx @@ -25,7 +25,7 @@ import EmojisContext from './helpers/EmojisContext' const EmojisList = () => { const dispatch = useAppDispatch() const { reduceMotionEnabled } = useAccessibility() - const { t } = useTranslation() + const { t } = useTranslation(['common', 'screenCompose']) const { emojisState, emojisDispatch } = useContext(EmojisContext) const { colors } = useTheme() diff --git a/src/components/Instance/index.tsx b/src/components/Instance/index.tsx index f0fe1b3a..8ebfea24 100644 --- a/src/components/Instance/index.tsx +++ b/src/components/Instance/index.tsx @@ -35,7 +35,7 @@ const ComponentInstance: React.FC = ({ disableHeaderImage, goBack = false }) => { - const { t } = useTranslation('componentInstance') + const { t } = useTranslation(['common', 'componentInstance']) const { colors, mode } = useTheme() const navigation = useNavigation>() @@ -113,16 +113,20 @@ const ComponentInstance: React.FC = ({ const processUpdate = useCallback(() => { if (domain) { if (instances && instances.filter(instance => instance.url === domain).length) { - Alert.alert(t('update.alert.title'), t('update.alert.message'), [ - { - text: t('common:buttons.cancel'), - style: 'cancel' - }, - { - text: t('common:buttons.continue'), - onPress: () => appsMutation.mutate({ domain }) - } - ]) + Alert.alert( + t('componentInstance:update.alert.title'), + t('componentInstance:update.alert.message'), + [ + { + text: t('common:buttons.cancel'), + style: 'cancel' + }, + { + text: t('common:buttons.continue'), + onPress: () => appsMutation.mutate({ domain }) + } + ] + ) } else { appsMutation.mutate({ domain }) } @@ -209,7 +213,7 @@ const ComponentInstance: React.FC = ({ processUpdate() } }} - placeholder={' ' + t('server.textInput.placeholder')} + placeholder={' ' + t('componentInstance:server.textInput.placeholder')} placeholderTextColor={colors.secondary} returnKeyType='go' keyboardAppearance={mode} @@ -222,7 +226,7 @@ const ComponentInstance: React.FC = ({ />