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

Merge branch 'main' into candidate

This commit is contained in:
xmflsct
2022-12-24 02:25:48 +01:00
184 changed files with 14775 additions and 15948 deletions

11
.gitignore vendored
View File

@ -66,4 +66,13 @@ buck-out/
web-build/ web-build/
dist/ dist/
# @end expo-cli # @end expo-cli
# yarn 3
.pnp.*
.yarn/*
!.yarn/patches
# !.yarn/plugins
# !.yarn/releases
!.yarn/sdks
!.yarn/versions

View File

@ -1,7 +1,7 @@
diff --git a/node_modules/@types/react-native-share-menu/index.d.ts b/node_modules/@types/react-native-share-menu/index.d.ts diff --git a/index.d.ts b/index.d.ts
index f52822c..ee98565 100755 index f52822c8bed928f387baf90fdb7342c7416a775a..6d9d480d18342832c4b07af2b10f4a63ff538e7b 100755
--- a/node_modules/@types/react-native-share-menu/index.d.ts --- a/index.d.ts
+++ b/node_modules/@types/react-native-share-menu/index.d.ts +++ b/index.d.ts
@@ -5,11 +5,9 @@ @@ -5,11 +5,9 @@
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// Minimum TypeScript Version: 3.7 // Minimum TypeScript Version: 3.7
@ -17,12 +17,18 @@ index f52822c..ee98565 100755
export type ShareCallback = (share?: ShareData) => void; export type ShareCallback = (share?: ShareData) => void;
@@ -28,7 +26,7 @@ interface ShareMenuReactView { @@ -25,10 +23,10 @@ interface ShareMenu {
dismissExtension(error?: string): void; }
openApp(): void;
continueInApp(extraData?: object): void; interface ShareMenuReactView {
- dismissExtension(error?: string): void;
- openApp(): void;
- continueInApp(extraData?: object): void;
- data(): Promise<{mimeType: string, data: string}>; - data(): Promise<{mimeType: string, data: string}>;
+ data(): Promise<{data: {mimeType: string; data: string}[]}>; + dismissExtension(error?: string): void
+ openApp(): void
+ continueInApp(extraData?: object): void
+ data(): Promise<{ data: { mimeType: string; data: string }[] }>
} }
export const ShareMenuReactView: ShareMenuReactView; export const ShareMenuReactView: ShareMenuReactView;

View File

@ -0,0 +1,13 @@
diff --git a/ios/EXAV/EXAudioSessionManager.m b/ios/EXAV/EXAudioSessionManager.m
index 81dce13366c3947b12c863f7b39c0237882a6c36..fa27e0a354d48a994ca46e19642a5e224d42d9a8 100644
--- a/ios/EXAV/EXAudioSessionManager.m
+++ b/ios/EXAV/EXAudioSessionManager.m
@@ -170,7 +170,7 @@ - (void)moduleDidBackground:(id)backgroundingModule
[_foregroundedModules compact];
// Any possible failures are silent
- [self _updateSessionConfiguration];
+ // [self _updateSessionConfiguration];
}
- (void)moduleDidForeground:(id)module

View File

@ -1,7 +1,7 @@
diff --git a/node_modules/react-native-fast-image/RNFastImage.podspec b/node_modules/react-native-fast-image/RNFastImage.podspec diff --git a/RNFastImage.podspec b/RNFastImage.podspec
index db0fada..b23cd91 100644 index db0fada63fc06191f8620d336d244edde6c3dba3..b23cd91a9d70bdb8abd2f4ba1417c2c35e5a1d4c 100644
--- a/node_modules/react-native-fast-image/RNFastImage.podspec --- a/RNFastImage.podspec
+++ b/node_modules/react-native-fast-image/RNFastImage.podspec +++ b/RNFastImage.podspec
@@ -16,6 +16,6 @@ Pod::Spec.new do |s| @@ -16,6 +16,6 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m}" s.source_files = "ios/**/*.{h,m}"
@ -11,10 +11,10 @@ index db0fada..b23cd91 100644
+ s.dependency 'SDWebImage', '~> 5.14.2' + s.dependency 'SDWebImage', '~> 5.14.2'
+ s.dependency 'SDWebImageWebPCoder', '~> 0.9.1' + s.dependency 'SDWebImageWebPCoder', '~> 0.9.1'
end end
diff --git a/node_modules/react-native-fast-image/android/build.gradle b/node_modules/react-native-fast-image/android/build.gradle diff --git a/android/build.gradle b/android/build.gradle
index 5b21cd5..19d82f8 100644 index 5b21cd59c40a5754f5d19c77e2a0eb0229925911..19d82f826e88125c5e6d87ee7c348fac621f548c 100644
--- a/node_modules/react-native-fast-image/android/build.gradle --- a/android/build.gradle
+++ b/node_modules/react-native-fast-image/android/build.gradle +++ b/android/build.gradle
@@ -65,4 +65,5 @@ dependencies { @@ -65,4 +65,5 @@ dependencies {
implementation "com.github.bumptech.glide:glide:${glideVersion}" implementation "com.github.bumptech.glide:glide:${glideVersion}"
implementation "com.github.bumptech.glide:okhttp3-integration:${glideVersion}" implementation "com.github.bumptech.glide:okhttp3-integration:${glideVersion}"

View File

@ -1,7 +1,7 @@
diff --git a/node_modules/react-native-htmlview/HTMLView.js b/node_modules/react-native-htmlview/HTMLView.js diff --git a/HTMLView.js b/HTMLView.js
index 43f8b7e..728112b 100644 index 43f8b7eb552d9a44b5feef74ec2ae7d7ddbc2fca..334d144f1fb96067726bca199ff37267c2fa0fb2 100644
--- a/node_modules/react-native-htmlview/HTMLView.js --- a/HTMLView.js
+++ b/node_modules/react-native-htmlview/HTMLView.js +++ b/HTMLView.js
@@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import React, {PureComponent} from 'react'; import React, {PureComponent} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -16,7 +16,7 @@ index 43f8b7e..728112b 100644
RootComponent: PropTypes.func, RootComponent: PropTypes.func,
rootComponentProps: PropTypes.object, rootComponentProps: PropTypes.object,
- style: ViewPropTypes.style, - style: ViewPropTypes.style,
+ style: PropTypes.any, + style: PropTypes.object,
stylesheet: PropTypes.object, stylesheet: PropTypes.object,
TextComponent: PropTypes.func, TextComponent: PropTypes.func,
textComponentProps: PropTypes.object, textComponentProps: PropTypes.object,

View File

@ -1,7 +1,7 @@
diff --git a/node_modules/react-native-share-menu/android/build.gradle b/node_modules/react-native-share-menu/android/build.gradle diff --git a/android/build.gradle b/android/build.gradle
index 9557fdb..ebdeb6f 100644 index 9557fdbf2fbf97b7f7aeaf7ce86d301a8ced213d..ebdeb6f4de7846d3241101001755595c52a4b05e 100644
--- a/node_modules/react-native-share-menu/android/build.gradle --- a/android/build.gradle
+++ b/node_modules/react-native-share-menu/android/build.gradle +++ b/android/build.gradle
@@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
@ -19,10 +19,10 @@ index 9557fdb..ebdeb6f 100644
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
ndk { ndk {
diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift diff --git a/ios/ReactShareViewController.swift b/ios/ReactShareViewController.swift
index f42bce6..ee36062 100644 index f42bce6ce7e3f48a7ddc83f3366b68fd0664b1a0..ee360622b1d03cc9661c78c6f210b84c3b19a725 100644
--- a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift --- a/ios/ReactShareViewController.swift
+++ b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift +++ b/ios/ReactShareViewController.swift
@@ -13,7 +13,7 @@ class ReactShareViewController: ShareViewController, RCTBridgeDelegate, ReactSha @@ -13,7 +13,7 @@ class ReactShareViewController: ShareViewController, RCTBridgeDelegate, ReactSha
func sourceURL(for bridge: RCTBridge!) -> URL! { func sourceURL(for bridge: RCTBridge!) -> URL! {
#if DEBUG #if DEBUG
@ -32,10 +32,10 @@ index f42bce6..ee36062 100644
#else #else
return Bundle.main.url(forResource: "main", withExtension: "jsbundle") return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif #endif
diff --git a/node_modules/react-native-share-menu/ios/ShareViewController.swift b/node_modules/react-native-share-menu/ios/ShareViewController.swift diff --git a/ios/ShareViewController.swift b/ios/ShareViewController.swift
index 12d8c92..64aa72b 100644 index 12d8c92dda20fabd9e7b55fec57b3d867414063c..8a1db0de285b18a9358a10b2ca8293a8c7d56a8e 100644
--- a/node_modules/react-native-share-menu/ios/ShareViewController.swift --- a/ios/ShareViewController.swift
+++ b/node_modules/react-native-share-menu/ios/ShareViewController.swift +++ b/ios/ShareViewController.swift
@@ -19,8 +19,8 @@ class ShareViewController: SLComposeServiceViewController { @@ -19,8 +19,8 @@ class ShareViewController: SLComposeServiceViewController {
var hostAppUrlScheme: String? var hostAppUrlScheme: String?
var sharedItems: [Any] = [] var sharedItems: [Any] = []
@ -78,7 +78,7 @@ index 12d8c92..64aa72b 100644
override func configurationItems() -> [Any]! { override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return [] return []
@@ -238,11 +235,10 @@ class ShareViewController: SLComposeServiceViewController { @@ -238,7 +235,7 @@ class ShareViewController: SLComposeServiceViewController {
func completeRequest() { func completeRequest() {
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context. // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
@ -87,7 +87,3 @@ index 12d8c92..64aa72b 100644
} }
func cancelRequest() { func cancelRequest() {
extensionContext!.cancelRequest(withError: NSError())
}
-
}

7
.yarnrc.yml Normal file
View File

@ -0,0 +1,7 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript"
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"

View File

@ -1,47 +1,30 @@
module.exports = function (api) { module.exports = function (api) {
api.cache(true) api.cache(false)
const plugins = [ if (process.env.NODE_ENV === 'production' || process.env.BABEL_ENV === 'production') {
'@babel/plugin-proposal-optional-chaining',
[
'module-resolver',
{
root: ['./'],
alias: {
'@assets': './assets',
'@root': './src',
'@api': './src/api',
'@helpers': './src/helpers',
'@components': './src/components',
'@screens': './src/screens',
'@utils': './src/utils'
}
}
],
'react-native-reanimated/plugin'
]
if (
process.env.NODE_ENV === 'production' ||
process.env.BABEL_ENV === 'production'
) {
plugins.push('transform-remove-console') plugins.push('transform-remove-console')
} }
return { return {
presets: [ presets: ['babel-preset-expo'],
'babel-preset-expo', plugins: [
'@babel/plugin-proposal-optional-chaining',
[ [
'@babel/preset-react', 'module-resolver',
{ {
importSource: '@welldone-software/why-did-you-render', root: ['./'],
runtime: 'automatic', alias: {
development: '@assets': './assets',
process.env.NODE_ENV === 'development' || '@root': './src',
process.env.BABEL_ENV === 'development' '@api': './src/api',
'@helpers': './src/helpers',
'@components': './src/components',
'@screens': './src/screens',
'@utils': './src/utils'
}
} }
] ],
], 'react-native-reanimated/plugin'
plugins ]
} }
} }

View File

@ -1,3 +1,3 @@
Enjoy toooting! This version includes following improvements and fixes: Enjoy toooting! This version includes following improvements and fixes:
- Fixed wrongly update notification - Allowing adding more context of reports
- Fix some random crashes - Option to disable autoplay gif

View File

@ -1,3 +1,3 @@
toooting愉快此版本包括以下改进和修复 toooting愉快此版本包括以下改进和修复
- 修复错误的升级通知 - 可添加举报细节
- 修复部分应用崩溃 - 新增暂停自动播放gif动画选项

View File

@ -16,7 +16,7 @@ PODS:
- ExpoModulesCore - ExpoModulesCore
- EXNotifications (0.17.0): - EXNotifications (0.17.0):
- ExpoModulesCore - ExpoModulesCore
- Expo (47.0.8): - Expo (47.0.9):
- ExpoModulesCore - ExpoModulesCore
- ExpoCrypto (12.0.0): - ExpoCrypto (12.0.0):
- ExpoModulesCore - ExpoModulesCore
@ -26,7 +26,7 @@ PODS:
- ExpoModulesCore - ExpoModulesCore
- ExpoLocalization (14.0.0): - ExpoLocalization (14.0.0):
- ExpoModulesCore - ExpoModulesCore
- ExpoModulesCore (1.0.3): - ExpoModulesCore (1.0.4):
- React-Core - React-Core
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- ExpoRandom (13.0.0): - ExpoRandom (13.0.0):
@ -297,7 +297,7 @@ PODS:
- React-Core - React-Core
- react-native-cameraroll (5.2.0): - react-native-cameraroll (5.2.0):
- React-Core - React-Core
- react-native-image-picker (4.10.2): - react-native-image-picker (4.10.3):
- React-Core - React-Core
- react-native-ios-context-menu (1.15.1): - react-native-ios-context-menu (1.15.1):
- React-Core - React-Core
@ -694,12 +694,12 @@ SPEC CHECKSUMS:
EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6 EXFileSystem: 60602b6eefa6873f97172c684b7537c9760b50d6
EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80 EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80
EXNotifications: babce2a87b7922051354fcfe7a74dd279b7e272a EXNotifications: babce2a87b7922051354fcfe7a74dd279b7e272a
Expo: 36b5f625d36728adbdd1934d4d57182f319ab832 Expo: 5523a4835730104a301caeac1e68dad095a9dfff
ExpoCrypto: 51e7662c7f5bfeab25b7909b8a5d545ec15d4877 ExpoCrypto: 51e7662c7f5bfeab25b7909b8a5d545ec15d4877
ExpoHaptics: 5a56d30a87ea213dd00b09566dc4b441a4dff97f ExpoHaptics: 5a56d30a87ea213dd00b09566dc4b441a4dff97f
ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318 ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318
ExpoLocalization: e202d1e2a4950df17ac8d0889d65a1ffd7532d7e ExpoLocalization: e202d1e2a4950df17ac8d0889d65a1ffd7532d7e
ExpoModulesCore: b5d21c8880afda6fb6ee95469f9ac2ec9b98e995 ExpoModulesCore: 8211c89bcc3ba86e2d02c54b1d31f1fa81acb6de
ExpoRandom: 58b7e0a5fe1adf1cb6dc1cbe503a6fe9524f36ce ExpoRandom: 58b7e0a5fe1adf1cb6dc1cbe503a6fe9524f36ce
ExpoStoreReview: ff6d631f2949eb7e4b2d14146ef6af25a16d770d ExpoStoreReview: ff6d631f2949eb7e4b2d14146ef6af25a16d770d
ExpoWebBrowser: 073e50f16669d498fb49063b9b7fe780b24f7fda ExpoWebBrowser: 073e50f16669d498fb49063b9b7fe780b24f7fda
@ -732,7 +732,7 @@ SPEC CHECKSUMS:
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3 react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7 react-native-blurhash: add4df9a937b4e021a24bc67a0714f13e0bd40b7
react-native-cameraroll: 0ff04cc4e0ff5f19a94ff4313e5c8bc4503cd86d react-native-cameraroll: 0ff04cc4e0ff5f19a94ff4313e5c8bc4503cd86d
react-native-image-picker: bf34f3f516d139ed3e24c5f5a381a91819e349ea react-native-image-picker: 60f4246eb5bb7187fc15638a8c1f13abd3820695
react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac react-native-ios-context-menu: b170594b4448c0cd10c79e13432216bac99de1ac
react-native-language-detection: f414937fa715108ab50a6269a3de0bcb95e4ceb0 react-native-language-detection: f414937fa715108ab50a6269a3de0bcb95e4ceb0
react-native-menu: 8e172cfcf0e42e92f028e7781eddf84d430cae24 react-native-menu: 8e172cfcf0e42e92f028e7781eddf84d430cae24

View File

@ -1,6 +1,6 @@
{ {
"name": "tooot", "name": "tooot",
"version": "4.7.2", "version": "4.8.0",
"description": "tooot for Mastodon", "description": "tooot for Mastodon",
"author": "xmflsct <me@xmflsct.com>", "author": "xmflsct <me@xmflsct.com>",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
@ -14,8 +14,7 @@
"iphone": "react-native run-ios --simulator 'iPhone 14 Pro'", "iphone": "react-native run-ios --simulator 'iPhone 14 Pro'",
"ipad": "react-native run-ios --simulator 'iPad Pro (11-inch) (4th generation)'", "ipad": "react-native run-ios --simulator 'iPad Pro (11-inch) (4th generation)'",
"app:build": "bundle exec fastlane", "app:build": "bundle exec fastlane",
"clean": "react-native-clean-project", "clean": "react-native-clean-project"
"postinstall": "patch-package"
}, },
"dependencies": { "dependencies": {
"@expo/react-native-action-sheet": "^4.0.1", "@expo/react-native-action-sheet": "^4.0.1",
@ -34,24 +33,24 @@
"@react-native-community/netinfo": "9.3.7", "@react-native-community/netinfo": "9.3.7",
"@react-native-community/segmented-control": "^2.2.2", "@react-native-community/segmented-control": "^2.2.2",
"@react-native-menu/menu": "^0.7.2", "@react-native-menu/menu": "^0.7.2",
"@react-navigation/bottom-tabs": "^6.5.1", "@react-navigation/bottom-tabs": "^6.5.2",
"@react-navigation/native": "^6.1.1", "@react-navigation/native": "^6.1.1",
"@react-navigation/native-stack": "^6.9.6", "@react-navigation/native-stack": "^6.9.7",
"@react-navigation/stack": "^6.3.9", "@react-navigation/stack": "^6.3.10",
"@reduxjs/toolkit": "^1.9.1", "@reduxjs/toolkit": "^1.9.1",
"@sentry/react-native": "4.12.0", "@sentry/react-native": "4.12.0",
"@sharcoux/slider": "^6.1.1", "@sharcoux/slider": "^6.1.1",
"@tanstack/react-query": "^4.20.4", "@tanstack/react-query": "^4.20.4",
"axios": "^1.2.1", "axios": "^1.2.1",
"diff": "^5.1.0", "diff": "^5.1.0",
"expo": "^47.0.8", "expo": "^47.0.9",
"expo-auth-session": "^3.7.3", "expo-auth-session": "^3.8.0",
"expo-av": "^13.0.2", "expo-av": "^13.0.2",
"expo-constants": "^14.0.2", "expo-constants": "^14.0.2",
"expo-crypto": "^12.0.0", "expo-crypto": "^12.0.0",
"expo-file-system": "^15.1.1", "expo-file-system": "^15.1.1",
"expo-haptics": "^12.0.1", "expo-haptics": "^12.0.1",
"expo-linking": "^3.2.3", "expo-linking": "^3.3.0",
"expo-localization": "^14.0.0", "expo-localization": "^14.0.0",
"expo-notifications": "^0.17.0", "expo-notifications": "^0.17.0",
"expo-random": "^13.0.0", "expo-random": "^13.0.0",
@ -61,14 +60,14 @@
"expo-store-review": "^6.0.0", "expo-store-review": "^6.0.0",
"expo-video-thumbnails": "^7.0.0", "expo-video-thumbnails": "^7.0.0",
"expo-web-browser": "~12.0.0", "expo-web-browser": "~12.0.0",
"i18next": "^22.4.5", "i18next": "^22.4.6",
"linkify-it": "^4.0.1", "linkify-it": "^4.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^12.1.1", "react-i18next": "^12.1.1",
"react-intl": "^6.2.5", "react-intl": "^6.2.5",
"react-native": "0.70.6", "react-native": "^0.70.6",
"react-native-animated-spinkit": "^1.5.2", "react-native-animated-spinkit": "^1.5.2",
"react-native-base64": "^0.2.1", "react-native-base64": "^0.2.1",
"react-native-blurhash": "^1.1.10", "react-native-blurhash": "^1.1.10",
@ -77,7 +76,7 @@
"react-native-flash-message": "^0.3.1", "react-native-flash-message": "^0.3.1",
"react-native-gesture-handler": "~2.8.0", "react-native-gesture-handler": "~2.8.0",
"react-native-htmlview": "^0.16.0", "react-native-htmlview": "^0.16.0",
"react-native-image-picker": "^4.10.2", "react-native-image-picker": "^4.10.3",
"react-native-ios-context-menu": "^1.15.1", "react-native-ios-context-menu": "^1.15.1",
"react-native-language-detection": "^0.2.2", "react-native-language-detection": "^0.2.2",
"react-native-pager-view": "^6.1.2", "react-native-pager-view": "^6.1.2",
@ -94,33 +93,35 @@
"rn-placeholder": "^3.0.3", "rn-placeholder": "^3.0.3",
"rtl-detect": "^1.0.4", "rtl-detect": "^1.0.4",
"valid-url": "^1.0.9", "valid-url": "^1.0.9",
"zeego": "^0.5.0" "zeego": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.5", "@babel/core": "^7.20.7",
"@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-optional-chaining": "^7.20.7",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.18.6", "@babel/preset-typescript": "^7.18.6",
"@expo/config": "^7.0.3", "@expo/config": "^7.0.3",
"@types/diff": "^5.0.2", "@types/diff": "^5.0.2",
"@types/linkify-it": "^3.0.2", "@types/linkify-it": "^3.0.2",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/react": "~18.0.26", "@types/react": "^18.0.26",
"@types/react-dom": "~18.0.9", "@types/react-dom": "^18.0.10",
"@types/react-native": "~0.70.8", "@types/react-native": "^0.70.8",
"@types/react-native-base64": "^0.2.0", "@types/react-native-base64": "^0.2.0",
"@types/react-native-share-menu": "^5.0.2", "@types/react-native-share-menu": "^5.0.2",
"@types/react-timeago": "^4.1.3",
"@types/valid-url": "^1.0.3", "@types/valid-url": "^1.0.3",
"@welldone-software/why-did-you-render": "^7.0.1",
"babel-plugin-module-resolver": "^4.1.0", "babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-transform-remove-console": "^6.9.4", "babel-plugin-transform-remove-console": "^6.9.4",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"expo-cli": "^6.0.8",
"patch-package": "^6.5.0",
"postinstall-postinstall": "^2.1.0",
"react-native-clean-project": "^4.0.1", "react-native-clean-project": "^4.0.1",
"typescript": "^4.9.4" "typescript": "^4.9.4"
},
"packageManager": "yarn@3.3.1",
"resolutions": {
"react-native-fast-image@^8.6.3": "patch:react-native-fast-image@npm%3A8.6.3#./.yarn/patches/react-native-fast-image-npm-8.6.3-03ee2d23c0.patch",
"expo-av@^13.0.2": "patch:expo-av@npm%3A13.0.2#./.yarn/patches/expo-av-npm-13.0.2-7a651776f1.patch",
"react-native-htmlview@^0.16.0": "patch:react-native-htmlview@npm%3A0.16.0#./.yarn/patches/react-native-htmlview-npm-0.16.0-501f1b89ba.patch",
"react-native-share-menu@^6.0.0": "patch:react-native-share-menu@npm%3A6.0.0#./.yarn/patches/react-native-share-menu-npm-6.0.0-f1094c3204.patch",
"@types/react-native-share-menu@^5.0.2": "patch:@types/react-native-share-menu@npm%3A5.0.2#./.yarn/patches/@types-react-native-share-menu-npm-5.0.2-373df17ecc.patch"
} }
} }

View File

@ -1,14 +0,0 @@
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,8 +1,46 @@
import 'i18next' 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' { declare module 'i18next' {
interface CustomTypeOptions { 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 returnNull: false
returnEmptyString: false
} }
} }

View File

@ -470,6 +470,11 @@ declare namespace Mastodon {
updated_at: string updated_at: string
} }
type Rule = {
id: string
text: string
}
type Status = { type Status = {
// Base // Base
id: string id: string

View File

@ -4,7 +4,6 @@ import queryClient from '@helpers/queryClient'
import i18n from '@root/i18n/i18n' import i18n from '@root/i18n/i18n'
import Screens from '@root/Screens' import Screens from '@root/Screens'
import audio from '@root/startup/audio' import audio from '@root/startup/audio'
import dev from '@root/startup/dev'
import log from '@root/startup/log' import log from '@root/startup/log'
import netInfo from '@root/startup/netInfo' import netInfo from '@root/startup/netInfo'
import push from '@root/startup/push' import push from '@root/startup/push'
@ -31,7 +30,6 @@ Platform.select({
android: LogBox.ignoreLogs(['Setting a timer for a long period of time']) android: LogBox.ignoreLogs(['Setting a timer for a long period of time'])
}) })
dev()
sentry() sentry()
audio() audio()
push() push()

View File

@ -9,7 +9,6 @@ import ScreenAnnouncements from '@screens/Announcements'
import ScreenCompose from '@screens/Compose' import ScreenCompose from '@screens/Compose'
import ScreenImagesViewer from '@screens/ImagesViewer' import ScreenImagesViewer from '@screens/ImagesViewer'
import ScreenTabs from '@screens/Tabs' import ScreenTabs from '@screens/Tabs'
import * as Sentry from '@sentry/react-native'
import initQuery from '@utils/initQuery' import initQuery from '@utils/initQuery'
import { RootStackParamList } from '@utils/navigation/navigators' import { RootStackParamList } from '@utils/navigation/navigators'
import pushUseConnect from '@utils/push/useConnect' import pushUseConnect from '@utils/push/useConnect'
@ -25,11 +24,12 @@ import { useTheme } from '@utils/styles/ThemeManager'
import { themes } from '@utils/styles/themes' import { themes } from '@utils/styles/themes'
import * as Linking from 'expo-linking' import * as Linking from 'expo-linking'
import { addScreenshotListener } from 'expo-screen-capture' import { addScreenshotListener } from 'expo-screen-capture'
import React, { useCallback, useEffect, useRef, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Alert, Platform, StatusBar } from 'react-native' import { Alert, Platform, StatusBar } from 'react-native'
import ShareMenu from 'react-native-share-menu' import ShareMenu from 'react-native-share-menu'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { routingInstrumentation } from './startup/sentry'
import { useAppDispatch } from './store' import { useAppDispatch } from './store'
const Stack = createNativeStackNavigator<RootStackParamList>() const Stack = createNativeStackNavigator<RootStackParamList>()
@ -39,13 +39,16 @@ export interface Props {
} }
const Screens: React.FC<Props> = ({ localCorrupt }) => { const Screens: React.FC<Props> = ({ localCorrupt }) => {
const { t } = useTranslation('screens') const { t } = useTranslation([
'common',
'screens',
'screenAnnouncements',
'screenAccountSelection'
])
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const instanceActive = useSelector(getInstanceActive) const instanceActive = useSelector(getInstanceActive)
const { colors, theme } = useTheme() const { colors, theme } = useTheme()
const routeRef = useRef<{ name?: string; params?: {} }>()
// Push hooks // Push hooks
const instances = useSelector(getInstances, (prev, next) => prev.length === next.length) const instances = useSelector(getInstances, (prev, next) => prev.length === next.length)
pushUseConnect() pushUseConnect()
@ -55,7 +58,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
// Prevent screenshot alert // Prevent screenshot alert
useEffect(() => { useEffect(() => {
const screenshotListener = addScreenshotListener(() => 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' } { text: t('common:buttons.confirm'), style: 'destructive' }
]) ])
) )
@ -68,7 +71,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
const showLocalCorrect = () => { const showLocalCorrect = () => {
if (localCorrupt) { if (localCorrupt) {
displayMessage({ displayMessage({
message: t('localCorrupt.message'), message: t('screens:localCorrupt.message'),
description: localCorrupt.length ? localCorrupt : undefined, description: localCorrupt.length ? localCorrupt : undefined,
type: 'danger' type: 'danger'
}) })
@ -92,15 +95,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
}, [instanceActive]) }, [instanceActive])
// Callbacks // Callbacks
const navigationContainerOnReady = useCallback(() => {
const currentRoute = navigationRef.getCurrentRoute()
routeRef.current = {
name: currentRoute?.name,
params: currentRoute?.params ? JSON.stringify(currentRoute.params) : undefined
}
}, [])
const navigationContainerOnStateChange = useCallback(() => { const navigationContainerOnStateChange = useCallback(() => {
const previousRoute = routeRef.current
const currentRoute = navigationRef.getCurrentRoute() const currentRoute = navigationRef.getCurrentRoute()
const matchTabName = currentRoute?.name?.match(/(Tab-.*)-Root/) const matchTabName = currentRoute?.name?.match(/(Tab-.*)-Root/)
@ -108,15 +103,6 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
//@ts-ignore //@ts-ignore
dispatch(updatePreviousTab(matchTabName[1])) dispatch(updatePreviousTab(matchTabName[1]))
} }
if (previousRoute?.name !== currentRoute?.name) {
Sentry.setContext('page', {
previous: previousRoute,
current: currentRoute
})
}
routeRef.current = currentRoute
}, []) }, [])
// Deep linking for compose // Deep linking for compose
@ -176,7 +162,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
if (!typesImage.includes(mime.split('/')[1])) { if (!typesImage.includes(mime.split('/')[1])) {
console.warn('Image type not supported:', mime.split('/')[1]) console.warn('Image type not supported:', mime.split('/')[1])
displayMessage({ displayMessage({
message: t('shareError.imageNotSupported', { message: t('screens:shareError.imageNotSupported', {
type: mime.split('/')[1] type: mime.split('/')[1]
}), }),
type: 'danger' type: 'danger'
@ -188,7 +174,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
if (!typesVideo.includes(mime.split('/')[1])) { if (!typesVideo.includes(mime.split('/')[1])) {
console.warn('Video type not supported:', mime.split('/')[1]) console.warn('Video type not supported:', mime.split('/')[1])
displayMessage({ displayMessage({
message: t('shareError.videoNotSupported', { message: t('screens:shareError.videoNotSupported', {
type: mime.split('/')[1] type: mime.split('/')[1]
}), }),
type: 'danger' type: 'danger'
@ -274,7 +260,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
<NavigationContainer <NavigationContainer
ref={navigationRef} ref={navigationRef}
theme={themes[theme]} theme={themes[theme]}
onReady={navigationContainerOnReady} onReady={() => routingInstrumentation.registerNavigationContainer(navigationRef)}
onStateChange={navigationContainerOnStateChange} onStateChange={navigationContainerOnStateChange}
> >
<Stack.Navigator initialRouteName='Screen-Tabs'> <Stack.Navigator initialRouteName='Screen-Tabs'>
@ -345,4 +331,4 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
) )
} }
export default React.memo(Screens, () => true) export default Screens

View File

@ -11,7 +11,7 @@ import Icon from './Icon'
import CustomText from './Text' import CustomText from './Text'
export interface Props { export interface Props {
account: Mastodon.Account account: Partial<Mastodon.Account> & Pick<Mastodon.Account, 'id' | 'acct' | 'username'>
props?: PressableProps props?: PressableProps
} }
@ -42,11 +42,11 @@ const ComponentAccount: React.FC<PropsWithChildren & Props> = ({ account, props,
style={{ style={{
width: StyleConstants.Avatar.S, width: StyleConstants.Avatar.S,
height: StyleConstants.Avatar.S, height: StyleConstants.Avatar.S,
borderRadius: 6, borderRadius: 8,
marginRight: StyleConstants.Spacing.S marginRight: StyleConstants.Spacing.S
}} }}
/> />
<View> <View style={{ flex: 1 }}>
<CustomText numberOfLines={1}> <CustomText numberOfLines={1}>
<ParseEmojis <ParseEmojis
content={account.display_name || account.username} content={account.display_name || account.username}

View File

@ -7,7 +7,6 @@ import { chunk, forEach, groupBy, sortBy } from 'lodash'
import React, { createRef, PropsWithChildren, useEffect, useReducer, useState } from 'react' import React, { createRef, PropsWithChildren, useEffect, useReducer, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Keyboard, KeyboardAvoidingView, View } from 'react-native' 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 { Edge, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import EmojisContext, { Emojis, emojisReducer, EmojisState } from './Emojis/helpers/EmojisContext' import EmojisContext, { Emojis, emojisReducer, EmojisState } from './Emojis/helpers/EmojisContext'
@ -35,7 +34,7 @@ const ComponentEmojis: React.FC<Props & PropsWithChildren> = ({
emojisDispatch({ type: 'input', payload: inputProps }) emojisDispatch({ type: 'input', payload: inputProps })
}, [inputProps]) }, [inputProps])
const { t } = useTranslation() const { t } = useTranslation(['componentEmojis'])
const { data } = useEmojisQuery({}) const { data } = useEmojisQuery({})
const frequentEmojis = useSelector(getInstanceFrequentEmojis, () => true) const frequentEmojis = useSelector(getInstanceFrequentEmojis, () => true)
useEffect(() => { useEffect(() => {

View File

@ -25,10 +25,10 @@ import EmojisContext from './helpers/EmojisContext'
const EmojisList = () => { const EmojisList = () => {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { reduceMotionEnabled } = useAccessibility() const { reduceMotionEnabled } = useAccessibility()
const { t } = useTranslation() const { t } = useTranslation(['common', 'screenCompose'])
const { emojisState, emojisDispatch } = useContext(EmojisContext) const { emojisState, emojisDispatch } = useContext(EmojisContext)
const { colors, mode } = useTheme() const { colors } = useTheme()
const addEmoji = (shortcode: string) => { const addEmoji = (shortcode: string) => {
if (emojisState.targetIndex === -1) { if (emojisState.targetIndex === -1) {
@ -158,7 +158,6 @@ const EmojisList = () => {
onChangeText={setSearch} onChangeText={setSearch}
autoCapitalize='none' autoCapitalize='none'
clearButtonMode='always' clearButtonMode='always'
keyboardAppearance={mode}
autoCorrect={false} autoCorrect={false}
spellCheck={false} spellCheck={false}
/> />

View File

@ -18,6 +18,7 @@ export interface Props {
loading?: boolean loading?: boolean
disabled?: boolean disabled?: boolean
destructive?: boolean
onPress: () => void onPress: () => void
} }
@ -34,6 +35,7 @@ const HeaderRight: React.FC<Props> = ({
background = false, background = false,
loading, loading,
disabled, disabled,
destructive = false,
onPress onPress
}) => { }) => {
const { colors, theme } = useTheme() const { colors, theme } = useTheme()
@ -41,10 +43,7 @@ const HeaderRight: React.FC<Props> = ({
const loadingSpinkit = useMemo( const loadingSpinkit = useMemo(
() => ( () => (
<View style={{ position: 'absolute' }}> <View style={{ position: 'absolute' }}>
<Flow <Flow size={StyleConstants.Font.Size.M * 1.25} color={colors.secondary} />
size={StyleConstants.Font.Size.M * 1.25}
color={colors.secondary}
/>
</View> </View>
), ),
[theme] [theme]
@ -59,7 +58,7 @@ const HeaderRight: React.FC<Props> = ({
name={content} name={content}
style={{ opacity: loading ? 0 : 1 }} style={{ opacity: loading ? 0 : 1 }}
size={StyleConstants.Spacing.M * 1.25} size={StyleConstants.Spacing.M * 1.25}
color={disabled ? colors.secondary : colors.primaryDefault} color={disabled ? colors.secondary : destructive ? colors.red : colors.primaryDefault}
/> />
{loading && loadingSpinkit} {loading && loadingSpinkit}
</> </>
@ -69,8 +68,13 @@ const HeaderRight: React.FC<Props> = ({
<> <>
<CustomText <CustomText
fontStyle='M' fontStyle='M'
fontWeight={destructive ? 'Bold' : 'Normal'}
style={{ style={{
color: disabled ? colors.secondary : colors.primaryDefault, color: disabled
? colors.secondary
: destructive
? colors.red
: colors.primaryDefault,
opacity: loading ? 0 : 1 opacity: loading ? 0 : 1
}} }}
children={content} children={content}
@ -94,14 +98,10 @@ const HeaderRight: React.FC<Props> = ({
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
backgroundColor: background backgroundColor: background ? colors.backgroundOverlayDefault : undefined,
? colors.backgroundOverlayDefault
: undefined,
minHeight: 44, minHeight: 44,
minWidth: 44, minWidth: 44,
marginRight: native marginRight: native ? -StyleConstants.Spacing.S : StyleConstants.Spacing.S,
? -StyleConstants.Spacing.S
: StyleConstants.Spacing.S,
...(type === 'icon' && { ...(type === 'icon' && {
borderRadius: 100 borderRadius: 100
}), }),

View File

@ -85,7 +85,6 @@ const ComponentInput = forwardRef(
multiline, multiline,
numberOfLines: Platform.OS === 'android' ? 5 : undefined numberOfLines: Platform.OS === 'android' ? 5 : undefined
})} })}
keyboardAppearance={mode}
textAlignVertical='top' textAlignVertical='top'
{...props} {...props}
/> />

View File

@ -12,55 +12,44 @@ export interface Props {
potentialWidth?: number potentialWidth?: number
} }
const InstanceInfo = React.memo( const InstanceInfo: React.FC<Props> = ({ style, header, content, potentialWidth }) => {
({ style, header, content, potentialWidth }: Props) => { const { colors } = useTheme()
const { colors } = useTheme()
return ( return (
<View <View
style={[ style={[
{ {
flex: 1, flex: 1,
marginTop: StyleConstants.Spacing.M, marginTop: StyleConstants.Spacing.M,
paddingLeft: StyleConstants.Spacing.Global.PagePadding, paddingLeft: StyleConstants.Spacing.Global.PagePadding,
paddingRight: StyleConstants.Spacing.Global.PagePadding paddingRight: StyleConstants.Spacing.Global.PagePadding
}, },
style style
]} ]}
accessible accessible
> >
<CustomText <CustomText
fontStyle='S' fontStyle='S'
style={{ style={{
marginBottom: StyleConstants.Spacing.XS, marginBottom: StyleConstants.Spacing.XS,
color: colors.primaryDefault color: colors.primaryDefault
}} }}
fontWeight='Bold' fontWeight='Bold'
children={header} children={header}
/>
{content ? (
<CustomText fontStyle='M' style={{ color: colors.primaryDefault }} children={content} />
) : (
<PlaceholderLine
width={potentialWidth ? potentialWidth * StyleConstants.Font.Size.M : undefined}
height={StyleConstants.Font.LineHeight.M}
color={colors.shimmerDefault}
noMargin
style={{ borderRadius: 0 }}
/> />
{content ? ( )}
<CustomText </View>
fontStyle='M' )
style={{ color: colors.primaryDefault }} }
children={content}
/>
) : (
<PlaceholderLine
width={
potentialWidth
? potentialWidth * StyleConstants.Font.Size.M
: undefined
}
height={StyleConstants.Font.LineHeight.M}
color={colors.shimmerDefault}
noMargin
style={{ borderRadius: 0 }}
/>
)}
</View>
)
},
(prev, next) => prev.content === next.content
)
export default InstanceInfo export default InstanceInfo

View File

@ -35,7 +35,7 @@ const ComponentInstance: React.FC<Props> = ({
disableHeaderImage, disableHeaderImage,
goBack = false goBack = false
}) => { }) => {
const { t } = useTranslation('componentInstance') const { t } = useTranslation(['common', 'componentInstance'])
const { colors, mode } = useTheme() const { colors, mode } = useTheme()
const navigation = useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>() const navigation = useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
@ -113,16 +113,20 @@ const ComponentInstance: React.FC<Props> = ({
const processUpdate = useCallback(() => { const processUpdate = useCallback(() => {
if (domain) { if (domain) {
if (instances && instances.filter(instance => instance.url === domain).length) { if (instances && instances.filter(instance => instance.url === domain).length) {
Alert.alert(t('update.alert.title'), t('update.alert.message'), [ Alert.alert(
{ t('componentInstance:update.alert.title'),
text: t('common:buttons.cancel'), t('componentInstance:update.alert.message'),
style: 'cancel' [
}, {
{ text: t('common:buttons.cancel'),
text: t('common:buttons.continue'), style: 'cancel'
onPress: () => appsMutation.mutate({ domain }) },
} {
]) text: t('common:buttons.continue'),
onPress: () => appsMutation.mutate({ domain })
}
]
)
} else { } else {
appsMutation.mutate({ domain }) appsMutation.mutate({ domain })
} }
@ -209,7 +213,7 @@ const ComponentInstance: React.FC<Props> = ({
processUpdate() processUpdate()
} }
}} }}
placeholder={' ' + t('server.textInput.placeholder')} placeholder={' ' + t('componentInstance:server.textInput.placeholder')}
placeholderTextColor={colors.secondary} placeholderTextColor={colors.secondary}
returnKeyType='go' returnKeyType='go'
keyboardAppearance={mode} keyboardAppearance={mode}
@ -222,7 +226,7 @@ const ComponentInstance: React.FC<Props> = ({
/> />
<Button <Button
type='text' type='text'
content={t('server.button')} content={t('componentInstance:server.button')}
onPress={processUpdate} onPress={processUpdate}
disabled={!instanceQuery.data?.uri && !whitelisted} disabled={!instanceQuery.data?.uri && !whitelisted}
loading={instanceQuery.isFetching || appsMutation.isLoading} loading={instanceQuery.isFetching || appsMutation.isLoading}
@ -239,31 +243,31 @@ const ComponentInstance: React.FC<Props> = ({
paddingTop: StyleConstants.Spacing.XS paddingTop: StyleConstants.Spacing.XS
}} }}
> >
{t('server.whitelisted')} {t('componentInstance:server.whitelisted')}
</CustomText> </CustomText>
) : ( ) : (
<Placeholder> <Placeholder>
<InstanceInfo <InstanceInfo
header={t('server.information.name')} header={t('componentInstance:server.information.name')}
content={instanceQuery.data?.title || undefined} content={instanceQuery.data?.title || undefined}
potentialWidth={2} potentialWidth={2}
/> />
<View style={{ flex: 1, flexDirection: 'row' }}> <View style={{ flex: 1, flexDirection: 'row' }}>
<InstanceInfo <InstanceInfo
style={{ alignItems: 'flex-start' }} style={{ alignItems: 'flex-start' }}
header={t('server.information.accounts')} header={t('componentInstance:server.information.accounts')}
content={instanceQuery.data?.stats?.user_count?.toString() || undefined} content={instanceQuery.data?.stats?.user_count?.toString() || undefined}
potentialWidth={4} potentialWidth={4}
/> />
<InstanceInfo <InstanceInfo
style={{ alignItems: 'center' }} style={{ alignItems: 'center' }}
header={t('server.information.statuses')} header={t('componentInstance:server.information.statuses')}
content={instanceQuery.data?.stats?.status_count?.toString() || undefined} content={instanceQuery.data?.stats?.status_count?.toString() || undefined}
potentialWidth={4} potentialWidth={4}
/> />
<InstanceInfo <InstanceInfo
style={{ alignItems: 'flex-end' }} style={{ alignItems: 'flex-end' }}
header={t('server.information.domains')} header={t('componentInstance:server.information.domains')}
content={instanceQuery.data?.stats?.domain_count?.toString() || undefined} content={instanceQuery.data?.stats?.domain_count?.toString() || undefined}
potentialWidth={4} potentialWidth={4}
/> />
@ -287,7 +291,7 @@ const ComponentInstance: React.FC<Props> = ({
}} }}
/> />
<CustomText fontStyle='S' style={{ flex: 1, color: colors.secondary }}> <CustomText fontStyle='S' style={{ flex: 1, color: colors.secondary }}>
{t('server.disclaimer.base')} {t('componentInstance:server.disclaimer.base')}
</CustomText> </CustomText>
</View> </View>
<View <View
@ -312,7 +316,8 @@ const ComponentInstance: React.FC<Props> = ({
accessibilityRole='link' accessibilityRole='link'
> >
<Trans <Trans
i18nKey='componentInstance:server.terms.base' ns='componentInstance'
i18nKey='server.terms.base'
components={[ components={[
<CustomText <CustomText
accessible accessible

View File

@ -13,7 +13,7 @@ import validUrl from 'valid-url'
const regexEmoji = new RegExp(/(:[A-Za-z0-9_]+:)/) const regexEmoji = new RegExp(/(:[A-Za-z0-9_]+:)/)
export interface Props { export interface Props {
content: string content?: string
emojis?: Mastodon.Emoji[] emojis?: Mastodon.Emoji[]
size?: 'S' | 'M' | 'L' size?: 'S' | 'M' | 'L'
adaptiveSize?: boolean adaptiveSize?: boolean
@ -23,6 +23,8 @@ export interface Props {
const ParseEmojis = React.memo( const ParseEmojis = React.memo(
({ content, emojis, size = 'M', adaptiveSize = false, fontBold = false, style }: Props) => { ({ content, emojis, size = 'M', adaptiveSize = false, fontBold = false, style }: Props) => {
if (!content) return null
const { reduceMotionEnabled } = useAccessibility() const { reduceMotionEnabled } = useAccessibility()
const adaptiveFontsize = useSelector(getSettingsFontsize) const adaptiveFontsize = useSelector(getSettingsFontsize)
@ -93,7 +95,7 @@ const ParseEmojis = React.memo(
</CustomText> </CustomText>
) )
}, },
(prev, next) => prev.content === next.content (prev, next) => prev.content === next.content && prev.style?.color === next.style?.color
) )
export default ParseEmojis export default ParseEmojis

View File

@ -2,6 +2,7 @@ import Icon from '@components/Icon'
import openLink from '@components/openLink' import openLink from '@components/openLink'
import ParseEmojis from '@components/Parse/Emojis' import ParseEmojis from '@components/Parse/Emojis'
import CustomText from '@components/Text' import CustomText from '@components/Text'
import { getHost } from '@helpers/urlMatcher'
import { useNavigation, useRoute } from '@react-navigation/native' import { useNavigation, useRoute } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack' import { StackNavigationProp } from '@react-navigation/stack'
import { TabLocalStackParamList } from '@utils/navigation/navigators' import { TabLocalStackParamList } from '@utils/navigation/navigators'
@ -13,7 +14,7 @@ import { useTheme } from '@utils/styles/ThemeManager'
import { isEqual } from 'lodash' import { isEqual } from 'lodash'
import React, { useCallback, useState } from 'react' import React, { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Platform, Pressable, TextStyleIOS, View } from 'react-native' import { Pressable, TextStyleIOS, View } from 'react-native'
import HTMLView from 'react-native-htmlview' import HTMLView from 'react-native-htmlview'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
@ -102,7 +103,7 @@ const renderNode = ({
) )
} }
} else { } else {
const domain = href?.split(new RegExp(/:\/\/(.[^\/]+\/.{3})/)) const host = getHost(href)
// Need example here // Need example here
const content = node.children && node.children[0] && node.children[0].data const content = node.children && node.children[0] && node.children[0].data
const shouldBeTag = tags && tags.filter(tag => `#${tag.name}` === content).length > 0 const shouldBeTag = tags && tags.filter(tag => `#${tag.name}` === content).length > 0
@ -127,8 +128,8 @@ const renderNode = ({
} }
}} }}
> >
{content && content !== href ? content : showFullLink ? href : domain?.[1]} {content && content !== href ? content : showFullLink ? href : host}
{!shouldBeTag ? '...' : null} {!shouldBeTag ? '/...' : null}
</CustomText> </CustomText>
) )
} }

View File

@ -16,7 +16,7 @@ export interface Props {
const RelationshipIncoming: React.FC<Props> = ({ id }) => { const RelationshipIncoming: React.FC<Props> = ({ id }) => {
const { theme } = useTheme() const { theme } = useTheme()
const { t } = useTranslation() const { t } = useTranslation(['common', 'componentRelationship'])
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }] const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }]
const queryKeyNotification: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }] const queryKeyNotification: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
@ -33,7 +33,7 @@ const RelationshipIncoming: React.FC<Props> = ({ id }) => {
type: 'error', type: 'error',
theme, theme,
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`relationship:${type}.function`) function: t(`componentRelationship:${type}.function` as any)
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&

View File

@ -22,7 +22,7 @@ export interface Props {
const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => { const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
const { theme } = useTheme() const { theme } = useTheme()
const { t } = useTranslation('componentRelationship') const { t } = useTranslation(['common', 'componentRelationship'])
const canFollowNotify = useSelector(checkInstanceFeature('account_follow_notify')) const canFollowNotify = useSelector(checkInstanceFeature('account_follow_notify'))
@ -44,7 +44,7 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
theme, theme,
type: 'error', type: 'error',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`${action}.function`) function: t(`componentRelationship:${action}.function` as any)
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -61,15 +61,15 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
let onPress: () => void let onPress: () => void
if (query.isError) { if (query.isError) {
content = t('button.error') content = t('componentRelationship:button.error')
onPress = () => {} onPress = () => {}
} else { } else {
if (query.data?.blocked_by) { if (query.data?.blocked_by) {
content = t('button.blocked_by') content = t('componentRelationship:button.blocked_by')
onPress = () => {} onPress = () => {}
} else { } else {
if (query.data?.blocking) { if (query.data?.blocking) {
content = t('button.blocking') content = t('componentRelationship:button.blocking')
onPress = () => { onPress = () => {
mutation.mutate({ mutation.mutate({
id, id,
@ -82,7 +82,7 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
} }
} else { } else {
if (query.data?.following) { if (query.data?.following) {
content = t('button.following') content = t('componentRelationship:button.following')
onPress = () => { onPress = () => {
mutation.mutate({ mutation.mutate({
id, id,
@ -95,7 +95,7 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
} }
} else { } else {
if (query.data?.requested) { if (query.data?.requested) {
content = t('button.requested') content = t('componentRelationship:button.requested')
onPress = () => { onPress = () => {
mutation.mutate({ mutation.mutate({
id, id,
@ -107,7 +107,7 @@ const RelationshipOutgoing: React.FC<Props> = ({ id }: Props) => {
}) })
} }
} else { } else {
content = t('button.default') content = t('componentRelationship:button.default')
onPress = () => { onPress = () => {
mutation.mutate({ mutation.mutate({
id, id,

View File

@ -11,9 +11,15 @@ export interface Props {
multiple?: boolean multiple?: boolean
options: { selected: boolean; content: string }[] options: { selected: boolean; content: string }[]
setOptions: React.Dispatch<React.SetStateAction<{ selected: boolean; content: string }[]>> setOptions: React.Dispatch<React.SetStateAction<{ selected: boolean; content: string }[]>>
disabled?: boolean
} }
const Selections: React.FC<Props> = ({ multiple = false, options, setOptions }) => { const Selections: React.FC<Props> = ({
multiple = false,
options,
setOptions,
disabled = false
}) => {
const { colors } = useTheme() const { colors } = useTheme()
const isSelected = (index: number): string => const isSelected = (index: number): string =>
@ -22,10 +28,11 @@ const Selections: React.FC<Props> = ({ multiple = false, options, setOptions })
: `${multiple ? 'Square' : 'Circle'}` : `${multiple ? 'Square' : 'Circle'}`
return ( return (
<> <View>
{options.map((option, index) => ( {options.map((option, index) => (
<Pressable <Pressable
key={index} key={index}
disabled={disabled}
style={{ flex: 1, paddingVertical: StyleConstants.Spacing.S }} style={{ flex: 1, paddingVertical: StyleConstants.Spacing.S }}
onPress={() => { onPress={() => {
if (multiple) { if (multiple) {
@ -56,15 +63,18 @@ const Selections: React.FC<Props> = ({ multiple = false, options, setOptions })
}} }}
name={isSelected(index)} name={isSelected(index)}
size={StyleConstants.Font.Size.M} size={StyleConstants.Font.Size.M}
color={colors.primaryDefault} color={disabled ? colors.disabled : colors.primaryDefault}
/> />
<CustomText style={{ flex: 1 }}> <CustomText style={{ flex: 1 }}>
<ParseEmojis content={option.content} /> <ParseEmojis
content={option.content}
style={{ color: disabled ? colors.disabled : colors.primaryDefault }}
/>
</CustomText> </CustomText>
</View> </View>
</Pressable> </Pressable>
))} ))}
</> </View>
) )
} }

View File

@ -196,37 +196,19 @@ const TimelineDefault: React.FC<Props> = ({
</ContextMenu.Trigger> </ContextMenu.Trigger>
<ContextMenu.Content> <ContextMenu.Content>
{mShare.map((mGroup, index) => ( {[mShare, mStatus, mInstance].map(type => (
<ContextMenu.Group key={index}> <>
{mGroup.map(menu => ( {type.map((mGroup, index) => (
<ContextMenu.Item key={menu.key} {...menu.item}> <ContextMenu.Group key={index}>
<ContextMenu.ItemTitle children={menu.title} /> {mGroup.map(menu => (
<ContextMenu.ItemIcon iosIconName={menu.icon} /> <ContextMenu.Item key={menu.key} {...menu.item}>
</ContextMenu.Item> <ContextMenu.ItemTitle children={menu.title} />
<ContextMenu.ItemIcon ios={{ name: menu.icon }} />
</ContextMenu.Item>
))}
</ContextMenu.Group>
))} ))}
</ContextMenu.Group> </>
))}
{mStatus.map((mGroup, index) => (
<ContextMenu.Group key={index}>
{mGroup.map(menu => (
<ContextMenu.Item key={menu.key} {...menu.item}>
<ContextMenu.ItemTitle children={menu.title} />
<ContextMenu.ItemIcon iosIconName={menu.icon} />
</ContextMenu.Item>
))}
</ContextMenu.Group>
))}
{mInstance.map((mGroup, index) => (
<ContextMenu.Group key={index}>
{mGroup.map(menu => (
<ContextMenu.Item key={menu.key} {...menu.item}>
<ContextMenu.ItemTitle children={menu.title} />
<ContextMenu.ItemIcon iosIconName={menu.icon} />
</ContextMenu.Item>
))}
</ContextMenu.Group>
))} ))}
</ContextMenu.Content> </ContextMenu.Content>
</ContextMenu.Root> </ContextMenu.Root>

View File

@ -13,74 +13,71 @@ export interface Props {
queryKey: QueryKeyTimeline queryKey: QueryKeyTimeline
} }
const TimelineEmpty = React.memo( const TimelineEmpty: React.FC<Props> = ({ queryKey }) => {
({ queryKey }: Props) => { const { status, refetch } = useTimelineQuery({
const { status, refetch } = useTimelineQuery({ ...queryKey[1],
...queryKey[1], options: { notifyOnChangeProps: ['status'] }
options: { notifyOnChangeProps: ['status'] } })
})
const { colors } = useTheme() const { colors } = useTheme()
const { t } = useTranslation('componentTimeline') const { t } = useTranslation('componentTimeline')
const children = () => { const children = () => {
switch (status) { switch (status) {
case 'loading': case 'loading':
return <Circle size={StyleConstants.Font.Size.L} color={colors.secondary} /> return <Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
case 'error': case 'error':
return ( return (
<> <>
<Icon name='Frown' size={StyleConstants.Font.Size.L} color={colors.primaryDefault} /> <Icon name='Frown' size={StyleConstants.Font.Size.L} color={colors.primaryDefault} />
<CustomText <CustomText
fontStyle='M' fontStyle='M'
style={{ style={{
marginTop: StyleConstants.Spacing.S, marginTop: StyleConstants.Spacing.S,
marginBottom: StyleConstants.Spacing.L, marginBottom: StyleConstants.Spacing.L,
color: colors.primaryDefault color: colors.primaryDefault
}} }}
> >
{t('empty.error.message')} {t('empty.error.message')}
</CustomText> </CustomText>
<Button type='text' content={t('empty.error.button')} onPress={() => refetch()} /> <Button type='text' content={t('empty.error.button')} onPress={() => refetch()} />
</> </>
) )
case 'success': case 'success':
return ( return (
<> <>
<Icon <Icon
name='Smartphone' name='Smartphone'
size={StyleConstants.Font.Size.L} size={StyleConstants.Font.Size.L}
color={colors.primaryDefault} color={colors.primaryDefault}
/> />
<CustomText <CustomText
fontStyle='M' fontStyle='M'
style={{ style={{
marginTop: StyleConstants.Spacing.S, marginTop: StyleConstants.Spacing.S,
marginBottom: StyleConstants.Spacing.L, marginBottom: StyleConstants.Spacing.L,
color: colors.secondary color: colors.secondary
}} }}
> >
{t('empty.success.message')} {t('empty.success.message')}
</CustomText> </CustomText>
</> </>
) )
}
} }
return ( }
<View return (
style={{ <View
flex: 1, style={{
minHeight: '100%', flex: 1,
justifyContent: 'center', minHeight: '100%',
alignItems: 'center', justifyContent: 'center',
backgroundColor: colors.backgroundDefault alignItems: 'center',
}} backgroundColor: colors.backgroundDefault
> }}
{children()} >
</View> {children()}
) </View>
}, )
() => true }
)
export default TimelineEmpty export default TimelineEmpty

View File

@ -13,49 +13,47 @@ export interface Props {
disableInfinity: boolean disableInfinity: boolean
} }
const TimelineFooter = React.memo( const TimelineFooter: React.FC<Props> = ({ queryKey, disableInfinity }) => {
({ queryKey, disableInfinity }: Props) => { const { hasNextPage } = useTimelineQuery({
const { hasNextPage } = useTimelineQuery({ ...queryKey[1],
...queryKey[1], options: {
options: { enabled: !disableInfinity,
enabled: !disableInfinity, notifyOnChangeProps: ['hasNextPage'],
notifyOnChangeProps: ['hasNextPage'], getNextPageParam: lastPage =>
getNextPageParam: lastPage => lastPage?.links?.next && {
lastPage?.links?.next && { ...(lastPage.links.next.isOffset
...(lastPage.links.next.isOffset ? { offset: lastPage.links.next.id }
? { offset: lastPage.links.next.id } : { max_id: lastPage.links.next.id })
: { max_id: lastPage.links.next.id }) }
} }
} })
})
const { colors } = useTheme() const { colors } = useTheme()
return ( return (
<View <View
style={{ style={{
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'center', justifyContent: 'center',
padding: StyleConstants.Spacing.M padding: StyleConstants.Spacing.M
}} }}
> >
{!disableInfinity && hasNextPage ? ( {!disableInfinity && hasNextPage ? (
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} /> <Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
) : ( ) : (
<CustomText fontStyle='S' style={{ color: colors.secondary }}> <CustomText fontStyle='S' style={{ color: colors.secondary }}>
<Trans <Trans
i18nKey='componentTimeline:end.message' ns='componentTimeline'
components={[ i18nKey='end.message'
<Icon name='Coffee' size={StyleConstants.Font.Size.S} color={colors.secondary} /> components={[
]} <Icon name='Coffee' size={StyleConstants.Font.Size.S} color={colors.secondary} />
/> ]}
</CustomText> />
)} </CustomText>
</View> )}
) </View>
}, )
() => true }
)
export default TimelineFooter export default TimelineFooter

View File

@ -164,37 +164,19 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
</ContextMenu.Trigger> </ContextMenu.Trigger>
<ContextMenu.Content> <ContextMenu.Content>
{mShare.map((mGroup, index) => ( {[mShare, mStatus, mInstance].map(type => (
<ContextMenu.Group key={index}> <>
{mGroup.map(menu => ( {type.map((mGroup, index) => (
<ContextMenu.Item key={menu.key} {...menu.item}> <ContextMenu.Group key={index}>
<ContextMenu.ItemTitle children={menu.title} /> {mGroup.map(menu => (
<ContextMenu.ItemIcon iosIconName={menu.icon} /> <ContextMenu.Item key={menu.key} {...menu.item}>
</ContextMenu.Item> <ContextMenu.ItemTitle children={menu.title} />
<ContextMenu.ItemIcon ios={{ name: menu.icon }} />
</ContextMenu.Item>
))}
</ContextMenu.Group>
))} ))}
</ContextMenu.Group> </>
))}
{mStatus.map((mGroup, index) => (
<ContextMenu.Group key={index}>
{mGroup.map(menu => (
<ContextMenu.Item key={menu.key} {...menu.item}>
<ContextMenu.ItemTitle children={menu.title} />
<ContextMenu.ItemIcon iosIconName={menu.icon} />
</ContextMenu.Item>
))}
</ContextMenu.Group>
))}
{mInstance.map((mGroup, index) => (
<ContextMenu.Group key={index}>
{mGroup.map(menu => (
<ContextMenu.Item key={menu.key} {...menu.item}>
<ContextMenu.ItemTitle children={menu.title} />
<ContextMenu.ItemIcon iosIconName={menu.icon} />
</ContextMenu.Item>
))}
</ContextMenu.Group>
))} ))}
</ContextMenu.Content> </ContextMenu.Content>
</ContextMenu.Root> </ContextMenu.Root>

View File

@ -28,7 +28,7 @@ const TimelineActions: React.FC = () => {
if (!queryKey || !status || disableDetails) return null if (!queryKey || !status || disableDetails) return null
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>() const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
const { t } = useTranslation('componentTimeline') const { t } = useTranslation(['common', 'componentTimeline'])
const { colors, theme } = useTheme() const { colors, theme } = useTheme()
const iconColor = colors.secondary const iconColor = colors.secondary
@ -54,13 +54,15 @@ const TimelineActions: React.FC = () => {
queryClient.invalidateQueries(tempQueryKey) queryClient.invalidateQueries(tempQueryKey)
} }
}, },
onError: (err: any, params, oldData) => { onError: (err: any, params) => {
const correctParam = params as MutationVarsTimelineUpdateStatusProperty const correctParam = params as MutationVarsTimelineUpdateStatusProperty
displayMessage({ displayMessage({
theme, theme,
type: 'error', type: 'error',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`shared.actions.${correctParam.payload.property}.function`) function: t(
`componentTimeline:shared.actions.${correctParam.payload.property}.function` as any
)
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -94,10 +96,10 @@ const TimelineActions: React.FC = () => {
if (!status.reblogged) { if (!status.reblogged) {
showActionSheetWithOptions( showActionSheetWithOptions(
{ {
title: t('shared.actions.reblogged.options.title'), title: t('componentTimeline:shared.actions.reblogged.options.title'),
options: [ options: [
t('shared.actions.reblogged.options.public'), t('componentTimeline:shared.actions.reblogged.options.public'),
t('shared.actions.reblogged.options.unlisted'), t('componentTimeline:shared.actions.reblogged.options.unlisted'),
t('common:buttons.cancel') t('common:buttons.cancel')
], ],
cancelButtonIndex: 2, cancelButtonIndex: 2,
@ -209,15 +211,14 @@ const TimelineActions: React.FC = () => {
) )
const childrenReblog = useMemo(() => { const childrenReblog = useMemo(() => {
const color = (state: boolean) => (state ? colors.green : colors.secondary) const color = (state: boolean) => (state ? colors.green : colors.secondary)
const disabled =
status.visibility === 'direct' || (status.visibility === 'private' && !ownAccount)
return ( return (
<> <>
<Icon <Icon
name='Repeat' name='Repeat'
color={ color={disabled ? colors.disabled : color(status.reblogged)}
status.visibility === 'direct' || (status.visibility === 'private' && !ownAccount) crossOut={disabled}
? colors.disabled
: color(status.reblogged)
}
size={StyleConstants.Font.Size.L} size={StyleConstants.Font.Size.L}
/> />
{status.reblogs_count > 0 ? ( {status.reblogs_count > 0 ? (
@ -269,7 +270,7 @@ const TimelineActions: React.FC = () => {
<Pressable <Pressable
{...(highlighted {...(highlighted
? { ? {
accessibilityLabel: t('shared.actions.reply.accessibilityLabel'), accessibilityLabel: t('componentTimeline:shared.actions.reply.accessibilityLabel'),
accessibilityRole: 'button' accessibilityRole: 'button'
} }
: { accessibilityLabel: '' })} : { accessibilityLabel: '' })}
@ -281,7 +282,9 @@ const TimelineActions: React.FC = () => {
<Pressable <Pressable
{...(highlighted {...(highlighted
? { ? {
accessibilityLabel: t('shared.actions.reblogged.accessibilityLabel'), accessibilityLabel: t(
'componentTimeline:shared.actions.reblogged.accessibilityLabel'
),
accessibilityRole: 'button' accessibilityRole: 'button'
} }
: { accessibilityLabel: '' })} : { accessibilityLabel: '' })}
@ -296,7 +299,9 @@ const TimelineActions: React.FC = () => {
<Pressable <Pressable
{...(highlighted {...(highlighted
? { ? {
accessibilityLabel: t('shared.actions.favourited.accessibilityLabel'), accessibilityLabel: t(
'componentTimeline:shared.actions.favourited.accessibilityLabel'
),
accessibilityRole: 'button' accessibilityRole: 'button'
} }
: { accessibilityLabel: '' })} : { accessibilityLabel: '' })}
@ -308,7 +313,9 @@ const TimelineActions: React.FC = () => {
<Pressable <Pressable
{...(highlighted {...(highlighted
? { ? {
accessibilityLabel: t('shared.actions.bookmarked.accessibilityLabel'), accessibilityLabel: t(
'componentTimeline:shared.actions.bookmarked.accessibilityLabel'
),
accessibilityRole: 'button' accessibilityRole: 'button'
} }
: { accessibilityLabel: '' })} : { accessibilityLabel: '' })}

View File

@ -8,6 +8,8 @@ import AttachmentAltText from './AltText'
import { Platform } from 'expo-modules-core' import { Platform } from 'expo-modules-core'
import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
import { aspectRatio } from './dimensions' import { aspectRatio } from './dimensions'
import { useSelector } from 'react-redux'
import { getSettingsAutoplayGifv } from '@utils/slices/settingsSlice'
export interface Props { export interface Props {
total: number total: number
@ -25,6 +27,7 @@ const AttachmentVideo: React.FC<Props> = ({
gifv = false gifv = false
}) => { }) => {
const { reduceMotionEnabled } = useAccessibility() const { reduceMotionEnabled } = useAccessibility()
const autoplayGifv = useSelector(getSettingsAutoplayGifv)
const videoPlayer = useRef<Video>(null) const videoPlayer = useRef<Video>(null)
const [videoLoading, setVideoLoading] = useState(false) const [videoLoading, setVideoLoading] = useState(false)
@ -60,7 +63,7 @@ const AttachmentVideo: React.FC<Props> = ({
resizeMode={videoResizeMode} resizeMode={videoResizeMode}
{...(gifv {...(gifv
? { ? {
shouldPlay: reduceMotionEnabled ? false : true, shouldPlay: reduceMotionEnabled || !autoplayGifv ? false : true,
isMuted: true, isMuted: true,
isLooping: true, isLooping: true,
source: { uri: video.url } source: { uri: video.url }
@ -73,7 +76,7 @@ const AttachmentVideo: React.FC<Props> = ({
onFullscreenUpdate={event => { onFullscreenUpdate={event => {
if (event.fullscreenUpdate === VideoFullscreenUpdate.PLAYER_DID_DISMISS) { if (event.fullscreenUpdate === VideoFullscreenUpdate.PLAYER_DID_DISMISS) {
Platform.OS === 'android' && setVideoResizeMode(ResizeMode.COVER) Platform.OS === 'android' && setVideoResizeMode(ResizeMode.COVER)
if (gifv && !reduceMotionEnabled) { if (gifv && !reduceMotionEnabled && autoplayGifv) {
videoPlayer.current?.playAsync() videoPlayer.current?.playAsync()
} else { } else {
videoPlayer.current?.pauseAsync() videoPlayer.current?.pauseAsync()
@ -106,7 +109,7 @@ const AttachmentVideo: React.FC<Props> = ({
video.blurhash ? ( video.blurhash ? (
<Blurhash blurhash={video.blurhash} style={{ width: '100%', height: '100%' }} /> <Blurhash blurhash={video.blurhash} style={{ width: '100%', height: '100%' }} />
) : null ) : null
) : !gifv || (gifv && reduceMotionEnabled) ? ( ) : !gifv || (gifv && (reduceMotionEnabled || !autoplayGifv)) ? (
<Button <Button
round round
overlay overlay
@ -119,6 +122,21 @@ const AttachmentVideo: React.FC<Props> = ({
) : null} ) : null}
<AttachmentAltText sensitiveShown={sensitiveShown} text={video.description} /> <AttachmentAltText sensitiveShown={sensitiveShown} text={video.description} />
</Pressable> </Pressable>
{gifv && !autoplayGifv ? (
<Button
style={{
position: 'absolute',
left: StyleConstants.Spacing.S,
bottom: StyleConstants.Spacing.S
}}
overlay
size='S'
type='text'
content='GIF'
fontBold
onPress={() => {}}
/>
) : null}
</View> </View>
) )
} }

View File

@ -5,8 +5,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Platform, StyleSheet, View } from 'react-native' import { Platform } from 'react-native'
import { Path, Svg } from 'react-native-svg'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { isRtlLang } from 'rtl-detect' import { isRtlLang } from 'rtl-detect'
import StatusContext from './Context' import StatusContext from './Context'
@ -45,7 +44,14 @@ const TimelineContent: React.FC<Props> = ({ notificationOwnToot = false, setSpoi
} }
/> />
{inThread ? ( {inThread ? (
<CustomText fontStyle='S' style={{ textAlign: 'center', color: colors.secondary, paddingVertical: StyleConstants.Spacing.XS }}> <CustomText
fontStyle='S'
style={{
textAlign: 'center',
color: colors.secondary,
paddingVertical: StyleConstants.Spacing.XS
}}
>
{t('shared.content.expandHint')} {t('shared.content.expandHint')}
</CustomText> </CustomText>
) : null} ) : null}

View File

@ -15,7 +15,7 @@ export interface FilteredProps {
const TimelineFiltered: React.FC<FilteredProps> = ({ filterResults }) => { const TimelineFiltered: React.FC<FilteredProps> = ({ filterResults }) => {
const { colors } = useTheme() const { colors } = useTheme()
const { t } = useTranslation('componentTimeline') const { t } = useTranslation(['common', 'componentTimeline'])
const main = () => { const main = () => {
if (!filterResults?.length) { if (!filterResults?.length) {
@ -23,18 +23,27 @@ const TimelineFiltered: React.FC<FilteredProps> = ({ filterResults }) => {
} }
switch (typeof filterResults[0]) { switch (typeof filterResults[0]) {
case 'string': // v1 filter case 'string': // v1 filter
return <>{t('shared.filtered.match', { context: 'v1', phrase: filterResults[0] })}</> return (
<>
{t('componentTimeline:shared.filtered.match', {
defaultValue: 'v1',
context: 'v1',
phrase: filterResults[0]
})}
</>
)
default: default:
return ( return (
<> <>
{t('shared.filtered.match', { {t('componentTimeline:shared.filtered.match', {
defaultValue: 'v2',
context: 'v2', context: 'v2',
count: filterResults.length, count: filterResults.length,
filters: filterResults.map(result => result.title).join(t('common:separator')) filters: filterResults.map(result => result.title).join(t('common:separator'))
})} })}
<CustomText <CustomText
style={{ color: colors.blue }} style={{ color: colors.blue }}
children={`\n${t('shared.filtered.reveal')}`} children={`\n${t('componentTimeline:shared.filtered.reveal')}`}
/> />
</> </>
) )

View File

@ -28,6 +28,7 @@ const TimelineHeaderAndroid: React.FC = () => {
type: 'status', type: 'status',
openChange, openChange,
account: status.account, account: status.account,
...(status && { status }),
queryKey queryKey
}) })
const mStatus = menuStatus({ status, queryKey, rootQueryKey }) const mStatus = menuStatus({ status, queryKey, rootQueryKey })
@ -52,34 +53,19 @@ const TimelineHeaderAndroid: React.FC = () => {
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Content> <DropdownMenu.Content>
{mShare.map((mGroup, index) => ( {[mShare, mAccount, mStatus].map(type => (
<DropdownMenu.Group key={index}> <>
{mGroup.map(menu => ( {type.map((mGroup, index) => (
<DropdownMenu.Item key={menu.key} {...menu.item}> <DropdownMenu.Group key={index}>
<DropdownMenu.ItemTitle children={menu.title} /> {mGroup.map(menu => (
</DropdownMenu.Item> <DropdownMenu.Item key={menu.key} {...menu.item}>
<DropdownMenu.ItemTitle children={menu.title} />
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
</DropdownMenu.Item>
))}
</DropdownMenu.Group>
))} ))}
</DropdownMenu.Group> </>
))}
{mAccount.map((mGroup, index) => (
<DropdownMenu.Group key={index}>
{mGroup.map(menu => (
<DropdownMenu.Item key={menu.key} {...menu.item}>
<DropdownMenu.ItemTitle children={menu.title} />
</DropdownMenu.Item>
))}
</DropdownMenu.Group>
))}
{mStatus.map((mGroup, index) => (
<DropdownMenu.Group key={index}>
{mGroup.map(menu => (
<DropdownMenu.Item key={menu.key} {...menu.item}>
<DropdownMenu.ItemTitle children={menu.title} />
</DropdownMenu.Item>
))}
</DropdownMenu.Group>
))} ))}
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Root> </DropdownMenu.Root>

View File

@ -22,7 +22,7 @@ const HeaderConversation = ({ conversation }: Props) => {
if (!queryKey) return null if (!queryKey) return null
const { colors, theme } = useTheme() const { colors, theme } = useTheme()
const { t } = useTranslation('componentTimeline') const { t } = useTranslation(['common', 'componentTimeline'])
const queryClient = useQueryClient() const queryClient = useQueryClient()
const mutation = useTimelineMutation({ const mutation = useTimelineMutation({
@ -32,7 +32,7 @@ const HeaderConversation = ({ conversation }: Props) => {
theme, theme,
type: 'error', type: 'error',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`shared.header.conversation.delete.function`) function: t(`componentTimeline:shared.header.conversation.delete.function`)
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -53,7 +53,7 @@ const HeaderConversation = ({ conversation }: Props) => {
numberOfLines={1} numberOfLines={1}
style={{ ...StyleConstants.FontStyle.M, color: colors.secondary }} style={{ ...StyleConstants.FontStyle.M, color: colors.secondary }}
> >
<CustomText>{t('shared.header.conversation.withAccounts')}</CustomText> <CustomText>{t('componentTimeline:shared.header.conversation.withAccounts')}</CustomText>
{conversation.accounts.map((account, index) => ( {conversation.accounts.map((account, index) => (
<CustomText key={account.id} numberOfLines={1}> <CustomText key={account.id} numberOfLines={1}>
{index !== 0 ? t('common:separator') : undefined} {index !== 0 ? t('common:separator') : undefined}

View File

@ -34,6 +34,7 @@ const TimelineHeaderDefault: React.FC = () => {
type: 'status', type: 'status',
openChange, openChange,
account: status.account, account: status.account,
...(status && { status }),
queryKey queryKey
}) })
const mStatus = menuStatus({ status, queryKey, rootQueryKey }) const mStatus = menuStatus({ status, queryKey, rootQueryKey })
@ -82,37 +83,19 @@ const TimelineHeaderDefault: React.FC = () => {
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Content> <DropdownMenu.Content>
{mShare.map((mGroup, index) => ( {[mShare, mAccount, mStatus].map(type => (
<DropdownMenu.Group key={index}> <>
{mGroup.map(menu => ( {type.map((mGroup, index) => (
<DropdownMenu.Item key={menu.key} {...menu.item}> <DropdownMenu.Group key={index}>
<DropdownMenu.ItemTitle children={menu.title} /> {mGroup.map(menu => (
<DropdownMenu.ItemIcon iosIconName={menu.icon} /> <DropdownMenu.Item key={menu.key} {...menu.item}>
</DropdownMenu.Item> <DropdownMenu.ItemTitle children={menu.title} />
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
</DropdownMenu.Item>
))}
</DropdownMenu.Group>
))} ))}
</DropdownMenu.Group> </>
))}
{mAccount.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>
))}
{mStatus.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.Content>
</DropdownMenu.Root> </DropdownMenu.Root>

View File

@ -42,6 +42,7 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
type: 'status', type: 'status',
openChange, openChange,
account: status?.account, account: status?.account,
...(status && { status }),
queryKey queryKey
}) })
const mStatus = menuStatus({ status, queryKey }) const mStatus = menuStatus({ status, queryKey })
@ -74,7 +75,7 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
/> />
) )
default: default:
if (status) { if (status && Platform.OS !== 'android') {
return ( return (
<Pressable <Pressable
style={{ flex: 1, alignItems: 'center' }} style={{ flex: 1, alignItems: 'center' }}
@ -89,48 +90,19 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Content> <DropdownMenu.Content>
{mShare.map((mGroup, index) => ( {[mShare, mStatus, mAccount, mInstance].map(type => (
<DropdownMenu.Group key={index}> <>
{mGroup.map(menu => ( {type.map((mGroup, index) => (
<DropdownMenu.Item key={menu.key} {...menu.item}> <DropdownMenu.Group key={index}>
<DropdownMenu.ItemTitle children={menu.title} /> {mGroup.map(menu => (
<DropdownMenu.ItemIcon iosIconName={menu.icon} /> <DropdownMenu.Item key={menu.key} {...menu.item}>
</DropdownMenu.Item> <DropdownMenu.ItemTitle children={menu.title} />
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
</DropdownMenu.Item>
))}
</DropdownMenu.Group>
))} ))}
</DropdownMenu.Group> </>
))}
{mAccount.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>
))}
{mStatus.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>
))}
{mInstance.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.Content>
</DropdownMenu.Root> </DropdownMenu.Root>
@ -187,19 +159,17 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
</View> </View>
</View> </View>
{Platform.OS !== 'android' ? ( <View
<View style={[
style={[ { marginLeft: StyleConstants.Spacing.M },
{ marginLeft: StyleConstants.Spacing.M }, notification.type === 'follow' ||
notification.type === 'follow' || notification.type === 'follow_request' ||
notification.type === 'follow_request' || notification.type === 'admin.report'
notification.type === 'admin.report' ? { flexShrink: 1 }
? { flexShrink: 1 } : { flex: 1 }
: { flex: 1 } ]}
]} children={actions()}
children={actions()} />
/>
) : null}
</View> </View>
) )
} }

View File

@ -9,31 +9,28 @@ export interface Props {
application?: Mastodon.Application application?: Mastodon.Application
} }
const HeaderSharedApplication = React.memo( const HeaderSharedApplication: React.FC<Props> = ({ application }) => {
({ application }: Props) => { const { colors } = useTheme()
const { colors } = useTheme() const { t } = useTranslation('componentTimeline')
const { t } = useTranslation('componentTimeline')
return application && application.name !== 'Web' ? ( return application && application.name !== 'Web' ? (
<CustomText <CustomText
fontStyle='S' fontStyle='S'
accessibilityRole='link' accessibilityRole='link'
onPress={async () => { onPress={async () => {
application.website && (await openLink(application.website)) application.website && (await openLink(application.website))
}} }}
style={{ style={{
marginLeft: StyleConstants.Spacing.S, marginLeft: StyleConstants.Spacing.S,
color: colors.secondary color: colors.secondary
}} }}
numberOfLines={1} numberOfLines={1}
> >
{t('shared.header.shared.application', { {t('shared.header.shared.application', {
application: application.name application: application.name
})} })}
</CustomText> </CustomText>
) : null ) : null
}, }
() => true
)
export default HeaderSharedApplication export default HeaderSharedApplication

View File

@ -13,43 +13,34 @@ export interface Props {
highlighted?: boolean highlighted?: boolean
} }
const HeaderSharedCreated = React.memo( const HeaderSharedCreated: React.FC<Props> = ({ created_at, edited_at, highlighted = false }) => {
({ created_at, edited_at, highlighted = false }: Props) => { const { t } = useTranslation('componentTimeline')
const { t } = useTranslation('componentTimeline') const { colors } = useTheme()
const { colors } = useTheme()
const actualTime = edited_at || created_at const actualTime = edited_at || created_at
return ( return (
<> <>
<CustomText fontStyle='S' style={{ color: colors.secondary }}> <CustomText fontStyle='S' style={{ color: colors.secondary }}>
{highlighted ? ( {highlighted ? (
<> <>
<FormattedDate <FormattedDate value={new Date(actualTime)} dateStyle='medium' timeStyle='short' />
value={new Date(actualTime)} </>
dateStyle='medium' ) : (
timeStyle='short' <RelativeTime time={actualTime} />
/> )}
</> </CustomText>
) : ( {edited_at ? (
<RelativeTime time={actualTime} /> <Icon
)} accessibilityLabel={t('shared.header.shared.edited.accessibilityLabel')}
</CustomText> name='Edit'
{edited_at ? ( size={StyleConstants.Font.Size.S}
<Icon color={colors.secondary}
accessibilityLabel={t( style={{ marginLeft: StyleConstants.Spacing.S }}
'shared.header.shared.edited.accessibilityLabel' />
)} ) : null}
name='Edit' </>
size={StyleConstants.Font.Size.S} )
color={colors.secondary} }
style={{ marginLeft: StyleConstants.Spacing.S }}
/>
) : null}
</>
)
},
(prev, next) => prev.edited_at === next.edited_at
)
export default HeaderSharedCreated export default HeaderSharedCreated

View File

@ -3,34 +3,24 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { StyleSheet } from 'react-native'
export interface Props { export interface Props {
muted?: Mastodon.Status['muted'] muted?: Mastodon.Status['muted']
} }
const HeaderSharedMuted = React.memo( const HeaderSharedMuted: React.FC<Props> = ({ muted }) => {
({ muted }: Props) => { const { t } = useTranslation('componentTimeline')
const { t } = useTranslation('componentTimeline') const { colors } = useTheme()
const { colors } = useTheme()
return muted ? ( return muted ? (
<Icon <Icon
accessibilityLabel={t('shared.header.shared.muted.accessibilityLabel')} accessibilityLabel={t('shared.header.shared.muted.accessibilityLabel')}
name='VolumeX' name='VolumeX'
size={StyleConstants.Font.Size.S} size={StyleConstants.Font.Size.M}
color={colors.secondary} color={colors.secondary}
style={styles.visibility} style={{ marginLeft: StyleConstants.Spacing.S }}
/> />
) : null ) : null
}, }
() => true
)
const styles = StyleSheet.create({
visibility: {
marginLeft: StyleConstants.Spacing.S
}
})
export default HeaderSharedMuted export default HeaderSharedMuted

View File

@ -9,48 +9,45 @@ export interface Props {
visibility: Mastodon.Status['visibility'] visibility: Mastodon.Status['visibility']
} }
const HeaderSharedVisibility = React.memo( const HeaderSharedVisibility: React.FC<Props> = ({ visibility }) => {
({ visibility }: Props) => { const { t } = useTranslation('componentTimeline')
const { t } = useTranslation('componentTimeline') const { colors } = useTheme()
const { colors } = useTheme()
switch (visibility) { switch (visibility) {
case 'unlisted': case 'unlisted':
return ( return (
<Icon <Icon
accessibilityLabel={t('shared.header.shared.visibility.private.accessibilityLabel')} accessibilityLabel={t('shared.header.shared.visibility.private.accessibilityLabel')}
name='Unlock' name='Unlock'
size={StyleConstants.Font.Size.S} size={StyleConstants.Font.Size.S}
color={colors.secondary} color={colors.secondary}
style={styles.visibility} style={styles.visibility}
/> />
) )
case 'private': case 'private':
return ( return (
<Icon <Icon
accessibilityLabel={t('shared.header.shared.visibility.private.accessibilityLabel')} accessibilityLabel={t('shared.header.shared.visibility.private.accessibilityLabel')}
name='Lock' name='Lock'
size={StyleConstants.Font.Size.S} size={StyleConstants.Font.Size.S}
color={colors.secondary} color={colors.secondary}
style={styles.visibility} style={styles.visibility}
/> />
) )
case 'direct': case 'direct':
return ( return (
<Icon <Icon
accessibilityLabel={t('shared.header.shared.visibility.direct.accessibilityLabel')} accessibilityLabel={t('shared.header.shared.visibility.direct.accessibilityLabel')}
name='Mail' name='Mail'
size={StyleConstants.Font.Size.S} size={StyleConstants.Font.Size.S}
color={colors.secondary} color={colors.secondary}
style={styles.visibility} style={styles.visibility}
/> />
) )
default: default:
return null return null
} }
}, }
() => true
)
const styles = StyleSheet.create({ const styles = StyleSheet.create({
visibility: { visibility: {

View File

@ -33,7 +33,7 @@ const TimelinePoll: React.FC = () => {
const poll = status.poll const poll = status.poll
const { colors, theme } = useTheme() const { colors, theme } = useTheme()
const { t } = useTranslation('componentTimeline') const { t } = useTranslation(['common', 'componentTimeline'])
const [allOptions, setAllOptions] = useState(new Array(status.poll.options.length).fill(false)) const [allOptions, setAllOptions] = useState(new Array(status.poll.options.length).fill(false))
@ -58,8 +58,7 @@ const TimelinePoll: React.FC = () => {
theme, theme,
type: 'error', type: 'error',
message: t('common:message.error.message', { message: t('common:message.error.message', {
// @ts-ignore function: t(`componentTimeline:shared.poll.meta.button.${theParams.payload.type}` as any)
function: t(`shared.poll.meta.button.${theParams.payload.type}`)
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -95,7 +94,7 @@ const TimelinePoll: React.FC = () => {
}) })
} }
type='text' type='text'
content={t('shared.poll.meta.button.vote')} content={t('componentTimeline:shared.poll.meta.button.vote')}
loading={mutation.isLoading} loading={mutation.isLoading}
disabled={allOptions.filter(o => o !== false).length === 0} disabled={allOptions.filter(o => o !== false).length === 0}
/> />
@ -120,7 +119,7 @@ const TimelinePoll: React.FC = () => {
}) })
} }
type='text' type='text'
content={t('shared.poll.meta.button.refresh')} content={t('componentTimeline:shared.poll.meta.button.refresh')}
loading={mutation.isLoading} loading={mutation.isLoading}
/> />
</View> </View>
@ -233,20 +232,25 @@ const TimelinePoll: React.FC = () => {
const pollVoteCounts = () => { const pollVoteCounts = () => {
if (poll.voters_count !== null) { if (poll.voters_count !== null) {
return t('shared.poll.meta.count.voters', { count: poll.voters_count }) + ' • ' return (
t('componentTimeline:shared.poll.meta.count.voters', { count: poll.voters_count }) + ' • '
)
} else if (poll.votes_count !== null) { } else if (poll.votes_count !== null) {
return t('shared.poll.meta.count.votes', { count: poll.votes_count }) + ' • ' return (
t('componentTimeline:shared.poll.meta.count.votes', { count: poll.votes_count }) + ' • '
)
} }
} }
const pollExpiration = () => { const pollExpiration = () => {
if (poll.expired) { if (poll.expired) {
return t('shared.poll.meta.expiration.expired') return t('componentTimeline:shared.poll.meta.expiration.expired')
} else { } else {
if (poll.expires_at) { if (poll.expires_at) {
return ( return (
<Trans <Trans
i18nKey='componentTimeline:shared.poll.meta.expiration.until' ns='componentTimeline'
i18nKey='shared.poll.meta.expiration.until'
components={[<RelativeTime time={poll.expires_at} />]} components={[<RelativeTime time={poll.expires_at} />]}
/> />
) )

View File

@ -16,7 +16,7 @@ const TimelineTranslate = () => {
const { status, highlighted, rawContent, detectedLanguage } = useContext(StatusContext) const { status, highlighted, rawContent, detectedLanguage } = useContext(StatusContext)
if (!status || !highlighted || !rawContent?.current.length) return null if (!status || !highlighted || !rawContent?.current.length) return null
const { t } = useTranslation('componentTimeline') const { t } = useTranslation(['componentTimeline'])
const { colors } = useTheme() const { colors } = useTheme()
const [detected, setDetected] = useState<{ const [detected, setDetected] = useState<{
@ -101,15 +101,15 @@ const TimelineTranslate = () => {
}} }}
> >
{isError {isError
? t('shared.translate.failed') ? t('componentTimeline:shared.translate.failed')
: isSuccess : isSuccess
? typeof data?.error === 'string' ? typeof data?.error === 'string'
? t(`shared.translate.${data.error}`) ? t(`componentTimeline:shared.translate.${data.error}` as any)
: t('shared.translate.succeed', { : t('componentTimeline:shared.translate.succeed', {
provider: data?.provider, provider: data?.provider,
source: data?.sourceLanguage source: data?.sourceLanguage
}) })
: t('shared.translate.default')} : t('componentTimeline:shared.translate.default')}
</CustomText> </CustomText>
{isFetching ? ( {isFetching ? (
<Circle <Circle

View File

@ -24,12 +24,14 @@ const menuAccount = ({
type, type,
openChange, openChange,
account, account,
status,
queryKey, queryKey,
rootQueryKey rootQueryKey
}: { }: {
type: 'status' | 'account' // Where the action is coming from type: 'status' | 'account' // Where the action is coming from
openChange: boolean openChange: boolean
account?: Pick<Mastodon.Account, 'id' | 'username'> account?: Partial<Mastodon.Account> & Pick<Mastodon.Account, 'id' | 'username' | 'acct'>
status?: Mastodon.Status
queryKey?: QueryKeyTimeline queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline
}): ContextMenu[][] => { }): ContextMenu[][] => {
@ -37,7 +39,7 @@ const menuAccount = ({
const navigation = const navigation =
useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>() useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>()
const { t } = useTranslation('componentContextMenu') const { t } = useTranslation(['common', 'componentContextMenu', 'componentRelationship'])
const menus: ContextMenu[][] = [[]] const menus: ContextMenu[][] = [[]]
@ -60,11 +62,15 @@ const menuAccount = ({
displayMessage({ displayMessage({
type: 'success', type: 'success',
message: t('common:message.success.message', { message: t('common:message.success.message', {
function: t(`account.${theParams.payload.property}.action`, { function: t(
...(theParams.payload.property !== 'reports' && { `componentContextMenu:account.${theParams.payload.property}.action`,
context: (theParams.payload.currentValue || false).toString() theParams.payload.property !== 'reports'
}) ? {
}) defaultValue: 'false',
context: (theParams.payload.currentValue || false).toString()
}
: { defaultValue: 'false' }
)
}) })
}) })
}, },
@ -73,11 +79,15 @@ const menuAccount = ({
displayMessage({ displayMessage({
type: 'danger', type: 'danger',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`account.${theParams.payload.property}.action`, { function: t(
...(theParams.payload.property !== 'reports' && { `componentContextMenu:account.${theParams.payload.property}.action`,
context: (theParams.payload.currentValue || false).toString() theParams.payload.property !== 'reports'
}) ? {
}) defaultValue: 'false',
context: (theParams.payload.currentValue || false).toString()
}
: { defaultValue: 'false' }
)
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -107,7 +117,7 @@ const menuAccount = ({
displayMessage({ displayMessage({
type: 'danger', type: 'danger',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`${action}.function`) function: t(`componentContextMenu:${action}.function` as any)
}), }),
...(err.status && ...(err.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -136,7 +146,8 @@ const menuAccount = ({
hidden: false hidden: false
}, },
title: !data?.requested title: !data?.requested
? t('account.following.action', { ? t('componentContextMenu:account.following.action', {
defaultValue: 'false',
context: (data?.following || false).toString() context: (data?.following || false).toString()
}) })
: t('componentRelationship:button.requested'), : t('componentRelationship:button.requested'),
@ -147,6 +158,7 @@ const menuAccount = ({
: 'person.badge.minus' : 'person.badge.minus'
}) })
} }
if (!ownAccount) { if (!ownAccount) {
menus[0].push({ menus[0].push({
key: 'account-list', key: 'account-list',
@ -156,9 +168,28 @@ const menuAccount = ({
destructive: false, destructive: false,
hidden: !isFetched || !data?.following hidden: !isFetched || !data?.following
}, },
title: t('account.inLists'), title: t('componentContextMenu:account.inLists'),
icon: 'checklist' icon: 'checklist'
}) })
menus[0].push({
key: 'account-show-boosts',
item: {
onSelect: () =>
relationshipMutation.mutate({
id: account.id,
type: 'outgoing',
payload: { action: 'follow', state: false, reblogs: !data?.showing_reblogs }
}),
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
destructive: false,
hidden: !isFetched || !data?.following
},
title: t('componentContextMenu:account.showBoosts.action', {
defaultValue: 'false',
context: (data?.showing_reblogs || false).toString()
}),
icon: data?.showing_reblogs ? 'rectangle.on.rectangle.slash' : 'rectangle.on.rectangle'
})
menus[0].push({ menus[0].push({
key: 'account-mute', key: 'account-mute',
item: { item: {
@ -173,40 +204,44 @@ const menuAccount = ({
destructive: false, destructive: false,
hidden: false hidden: false
}, },
title: t('account.mute.action', { title: t('componentContextMenu:account.mute.action', {
defaultValue: 'false',
context: (data?.muting || false).toString() context: (data?.muting || false).toString()
}), }),
icon: data?.muting ? 'eye' : 'eye.slash' icon: data?.muting ? 'eye' : 'eye.slash'
}) })
}
!ownAccount &&
menus.push([ menus.push([
{ {
key: 'account-block', key: 'account-block',
item: { item: {
onSelect: () => onSelect: () =>
Alert.alert(t('account.block.alert.title', { username: account.username }), undefined, [ Alert.alert(
{ t('componentContextMenu:account.block.alert.title', { username: account.username }),
text: t('common:buttons.confirm'), undefined,
style: 'destructive', [
onPress: () => {
timelineMutation.mutate({ text: t('common:buttons.confirm'),
type: 'updateAccountProperty', style: 'destructive',
queryKey, onPress: () =>
id: account.id, timelineMutation.mutate({
payload: { property: 'block', currentValue: data?.blocking } type: 'updateAccountProperty',
}) queryKey,
}, id: account.id,
{ payload: { property: 'block', currentValue: data?.blocking }
text: t('common:buttons.cancel') })
} },
]), {
text: t('common:buttons.cancel')
}
]
),
disabled: Platform.OS !== 'android' ? !data || !isFetched : false, disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
destructive: !data?.blocking, destructive: !data?.blocking,
hidden: false hidden: false
}, },
title: t('account.block.action', { title: t('componentContextMenu:account.block.action', {
defaultValue: 'false',
context: (data?.blocking || false).toString() context: (data?.blocking || false).toString()
}), }),
icon: data?.blocking ? 'checkmark.circle' : 'xmark.circle' icon: data?.blocking ? 'checkmark.circle' : 'xmark.circle'
@ -214,42 +249,16 @@ const menuAccount = ({
{ {
key: 'account-reports', key: 'account-reports',
item: { item: {
onSelect: () => onSelect: () => navigation.navigate('Tab-Shared-Report', { account, status }),
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')
}
]
),
disabled: false, disabled: false,
destructive: true, destructive: true,
hidden: false hidden: false
}, },
title: t('account.reports.action'), title: t('componentContextMenu:account.reports.action'),
icon: 'flag' icon: 'flag'
} }
]) ])
}
return menus return menus
} }

View File

@ -17,7 +17,7 @@ const menuInstance = ({
}): ContextMenu[][] => { }): ContextMenu[][] => {
if (!status || !queryKey) return [] if (!status || !queryKey) return []
const { t } = useTranslation('componentContextMenu') const { t } = useTranslation(['common', 'componentContextMenu'])
const queryClient = useQueryClient() const queryClient = useQueryClient()
const mutation = useTimelineMutation({ const mutation = useTimelineMutation({
@ -25,7 +25,7 @@ const menuInstance = ({
displayMessage({ displayMessage({
type: 'success', type: 'success',
message: t('common:message.success.message', { message: t('common:message.success.message', {
function: t(`instance.block.action`, { instance }) function: t(`componentContextMenu:instance.block.action`, { instance })
}) })
}) })
queryClient.invalidateQueries(queryKey) queryClient.invalidateQueries(queryKey)
@ -45,8 +45,8 @@ const menuInstance = ({
item: { item: {
onSelect: () => onSelect: () =>
Alert.alert( Alert.alert(
t('instance.block.alert.title', { instance }), t('componentContextMenu:instance.block.alert.title', { instance }),
t('instance.block.alert.message'), t('componentContextMenu:instance.block.alert.message'),
[ [
{ {
text: t('common:buttons.confirm'), text: t('common:buttons.confirm'),
@ -68,7 +68,7 @@ const menuInstance = ({
destructive: true, destructive: true,
hidden: false hidden: false
}, },
title: t('instance.block.action', { instance }), title: t('componentContextMenu:instance.block.action', { instance }),
icon: '' icon: ''
} }
]) ])

View File

@ -13,7 +13,7 @@ const menuShare = (
} }
| { | {
type: 'account' type: 'account'
url: string url?: string
} }
): ContextMenu[][] => { ): ContextMenu[][] => {
if (params.type === 'status' && params.visibility === 'direct') return [] if (params.type === 'status' && params.visibility === 'direct') return []

View File

@ -28,7 +28,7 @@ const menuStatus = ({
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Screen-Tabs'>>() const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Screen-Tabs'>>()
const { theme } = useTheme() const { theme } = useTheme()
const { t } = useTranslation('componentContextMenu') const { t } = useTranslation(['common', 'componentContextMenu'])
const queryClient = useQueryClient() const queryClient = useQueryClient()
const mutation = useTimelineMutation({ const mutation = useTimelineMutation({
@ -41,7 +41,7 @@ const menuStatus = ({
theme, theme,
type: 'error', type: 'error',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`status.${theFunction}.action`) function: t(`componentContextMenu:status.${theFunction}.action` as any)
}), }),
...(err?.status && ...(err?.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
@ -62,52 +62,54 @@ const menuStatus = ({
const canEditPost = useSelector(checkInstanceFeature('edit_post')) const canEditPost = useSelector(checkInstanceFeature('edit_post'))
if (ownAccount) { menus.push([
menus.push([ {
{ key: 'status-edit',
key: 'status-edit', item: {
item: { onSelect: async () => {
onSelect: async () => { let replyToStatus: Mastodon.Status | undefined = undefined
let replyToStatus: Mastodon.Status | undefined = undefined if (status.in_reply_to_id) {
if (status.in_reply_to_id) { replyToStatus = await apiInstance<Mastodon.Status>({
replyToStatus = await apiInstance<Mastodon.Status>({
method: 'get',
url: `statuses/${status.in_reply_to_id}`
}).then(res => res.body)
}
apiInstance<{
id: Mastodon.Status['id']
text: NonNullable<Mastodon.Status['text']>
spoiler_text: Mastodon.Status['spoiler_text']
}>({
method: 'get', method: 'get',
url: `statuses/${status.id}/source` url: `statuses/${status.in_reply_to_id}`
}).then(res => { }).then(res => res.body)
navigation.navigate('Screen-Compose', { }
type: 'edit', apiInstance<{
incomingStatus: { id: Mastodon.Status['id']
...status, text: NonNullable<Mastodon.Status['text']>
text: res.body.text, spoiler_text: Mastodon.Status['spoiler_text']
spoiler_text: res.body.spoiler_text }>({
}, method: 'get',
...(replyToStatus && { replyToStatus }), url: `statuses/${status.id}/source`
queryKey, }).then(res => {
rootQueryKey navigation.navigate('Screen-Compose', {
}) type: 'edit',
incomingStatus: {
...status,
text: res.body.text,
spoiler_text: res.body.spoiler_text
},
...(replyToStatus && { replyToStatus }),
queryKey,
rootQueryKey
}) })
}, })
disabled: false,
destructive: false,
hidden: !canEditPost
}, },
title: t('status.edit.action'), disabled: false,
icon: 'square.and.pencil' destructive: false,
hidden: !ownAccount || !canEditPost
}, },
{ title: t('componentContextMenu:status.edit.action'),
key: 'status-delete-edit', icon: 'square.and.pencil'
item: { },
onSelect: () => {
Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [ key: 'status-delete-edit',
item: {
onSelect: () =>
Alert.alert(
t('componentContextMenu:status.deleteEdit.alert.title'),
t('componentContextMenu:status.deleteEdit.alert.message'),
[
{ {
text: t('common:buttons.confirm'), text: t('common:buttons.confirm'),
style: 'destructive', style: 'destructive',
@ -139,19 +141,23 @@ const menuStatus = ({
{ {
text: t('common:buttons.cancel') text: t('common:buttons.cancel')
} }
]), ]
disabled: false, ),
destructive: true, disabled: false,
hidden: false destructive: true,
}, hidden: !ownAccount
title: t('status.deleteEdit.action'),
icon: 'pencil.and.outline'
}, },
{ title: t('componentContextMenu:status.deleteEdit.action'),
key: 'status-delete', icon: 'pencil.and.outline'
item: { },
onSelect: () => {
Alert.alert(t('status.delete.alert.title'), t('status.delete.alert.message'), [ key: 'status-delete',
item: {
onSelect: () =>
Alert.alert(
t('componentContextMenu:status.delete.alert.title'),
t('componentContextMenu:status.delete.alert.message'),
[
{ {
text: t('common:buttons.confirm'), text: t('common:buttons.confirm'),
style: 'destructive', style: 'destructive',
@ -169,70 +175,72 @@ const menuStatus = ({
text: t('common:buttons.cancel'), text: t('common:buttons.cancel'),
style: 'default' style: 'default'
} }
]), ]
disabled: false, ),
destructive: true, disabled: false,
hidden: false destructive: true,
}, hidden: !ownAccount
title: t('status.delete.action'),
icon: 'trash'
}
])
menus.push([
{
key: 'status-mute',
item: {
onSelect: () =>
mutation.mutate({
type: 'updateStatusProperty',
queryKey,
rootQueryKey,
id: status.id,
payload: {
property: 'muted',
currentValue: status.muted,
propertyCount: undefined,
countValue: undefined
}
}),
disabled: false,
destructive: false,
hidden: false
},
title: t('status.mute.action', {
context: (status.muted || false).toString()
}),
icon: status.muted ? 'speaker' : 'speaker.slash'
}, },
{ title: t('componentContextMenu:status.delete.action'),
key: 'status-pin', icon: 'trash'
item: { }
onSelect: () => ])
// Also note that reblogs cannot be pinned.
mutation.mutate({ menus.push([
type: 'updateStatusProperty', {
queryKey, key: 'status-mute',
rootQueryKey, item: {
id: status.id, onSelect: () =>
payload: { mutation.mutate({
property: 'pinned', type: 'updateStatusProperty',
currentValue: status.pinned, queryKey,
propertyCount: undefined, rootQueryKey,
countValue: undefined id: status.id,
} payload: {
}), property: 'muted',
disabled: false, currentValue: status.muted
destructive: false, }
hidden: status.visibility !== 'public' && status.visibility !== 'unlisted' }),
}, disabled: false,
title: t('status.pin.action', { destructive: false,
context: (status.pinned || false).toString() hidden:
}), !ownAccount &&
icon: status.pinned ? 'pin.slash' : 'pin' !status.mentions.filter(mention => mention.id === instanceAccount.id).length
} },
]) title: t('componentContextMenu:status.mute.action', {
} defaultValue: 'false',
context: (status.muted || false).toString()
}),
icon: status.muted ? 'speaker' : 'speaker.slash'
},
{
key: 'status-pin',
item: {
onSelect: () =>
// Also note that reblogs cannot be pinned.
mutation.mutate({
type: 'updateStatusProperty',
queryKey,
rootQueryKey,
id: status.id,
payload: {
property: 'pinned',
currentValue: status.pinned,
propertyCount: undefined,
countValue: undefined
}
}),
disabled: false,
destructive: false,
hidden: !ownAccount || (status.visibility !== 'public' && status.visibility !== 'unlisted')
},
title: t('componentContextMenu:status.pin.action', {
defaultValue: 'false',
context: (status.pinned || false).toString()
}),
icon: status.pinned ? 'pin.slash' : 'pin'
}
])
return menus return menus
} }

View File

@ -1,6 +1,13 @@
import { store } from '@root/store' import { store } from '@root/store'
import { getInstanceUrl } from '@utils/slices/instancesSlice' import { getInstanceUrl } from '@utils/slices/instancesSlice'
const getHost = (url: unknown): string | void => {
if (typeof url !== 'string') return undefined
const matches = url.match(/^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/i)
return matches?.[1]
}
const matchStatus = ( const matchStatus = (
url: string url: string
): { id: string; style: 'default' | 'pretty'; sameInstance: boolean } | null => { ): { id: string; style: 'default' | 'pretty'; sameInstance: boolean } | null => {
@ -53,4 +60,4 @@ const matchAccount = (
return null return null
} }
export { matchStatus, matchAccount } export { getHost, matchStatus, matchAccount }

View File

@ -7,6 +7,10 @@
"action_true": "Deixa de seguir l'usuari" "action_true": "Deixa de seguir l'usuari"
}, },
"inLists": "Gestionar usuari de llistes", "inLists": "Gestionar usuari de llistes",
"showBoosts": {
"action_false": "Mostra els impulsos de l'usuari",
"action_true": "Oculta els impulsos de l'usuari"
},
"mute": { "mute": {
"action_false": "Silencia l'usuari", "action_false": "Silencia l'usuari",
"action_true": "Deixa de silenciar l'usuari" "action_true": "Deixa de silenciar l'usuari"
@ -15,13 +19,13 @@
"action_false": "Bloqueja l'usuari", "action_false": "Bloqueja l'usuari",
"action_true": "Deixa de bloquejar l'usuari", "action_true": "Deixa de bloquejar l'usuari",
"alert": { "alert": {
"title": "" "title": "Vols bloquejar a @{{username}}?"
} }
}, },
"reports": { "reports": {
"action": "Denuncia i bloqueja l'usuari", "action": "Denuncia i bloqueja l'usuari",
"alert": { "alert": {
"title": "" "title": "Vols denunciar i bloquejar a @{{username}}?"
} }
} }
}, },

View File

@ -32,7 +32,7 @@
}, },
"update": "L'impuls ha sigut editat", "update": "L'impuls ha sigut editat",
"admin.sign_up": "{{name}} s'ha unit a la instància", "admin.sign_up": "{{name}} s'ha unit a la instància",
"admin.report": "" "admin.report": "{{name}} ha reportat:"
}, },
"actions": { "actions": {
"reply": { "reply": {
@ -100,7 +100,7 @@
"fullConversation": "Llegeix conversacions", "fullConversation": "Llegeix conversacions",
"translate": { "translate": {
"default": "Tradueix", "default": "Tradueix",
"succeed": "Traduït per {{provider}} amb {{source}}", "succeed": "Traduït per {{provider}} des de l'idioma {{source}}",
"failed": "Error al traduir", "failed": "Error al traduir",
"source_not_supported": "L'idioma de la publicació no està suportada", "source_not_supported": "L'idioma de la publicació no està suportada",
"target_not_supported": "Aquest idioma no està suportat" "target_not_supported": "Aquest idioma no està suportat"

View File

@ -4,7 +4,7 @@
"published": "S'ha publicat <0 />", "published": "S'ha publicat <0 />",
"button": { "button": {
"read": "Llegit", "read": "Llegit",
"unread": "Marca com a llegit" "unread": "Marca-ho com a llegit"
} }
} }
} }

View File

@ -2,10 +2,10 @@
"heading": { "heading": {
"left": { "left": {
"alert": { "alert": {
"title": "Voleu cancel·lar l'edició?", "title": "Vols cancel·lar l'edició?",
"buttons": { "buttons": {
"save": "Desa l'esborrany", "save": "Desa'l",
"delete": "Esborra l'esborrany" "delete": "Esborra'l"
} }
} }
}, },
@ -13,9 +13,9 @@
"button": { "button": {
"default": "Publica", "default": "Publica",
"conversation": "Envia un missatge directe", "conversation": "Envia un missatge directe",
"reply": "Publica la resposta", "reply": "Respon",
"deleteEdit": "Publicació", "deleteEdit": "Torna-ho a publicar",
"edit": "Publica l'edició", "edit": "Edita",
"share": "Publicació" "share": "Publicació"
}, },
"alert": { "alert": {
@ -42,8 +42,7 @@
"placeholder": "Què et passa pel cap?", "placeholder": "Què et passa pel cap?",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "S'ha arribat al nombre màxim d'adjunts", "title": "S'ha arribat al nombre màxim d'adjunts"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -3,7 +3,7 @@
"local": { "local": {
"name": "Seguint", "name": "Seguint",
"options": { "options": {
"showBoosts": "Mostra les publicacions", "showBoosts": "Mostra els impulsos",
"showReplies": "Mostra les respostes" "showReplies": "Mostra les respostes"
} }
}, },
@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filtra", "accessibilityLabel": "Filtra",
"accessibilityHint": "Filtra els tipus de notificacions", "accessibilityHint": "Filtra els tipus de notificacions",
"title": "Mostra les notificacions", "title": "Mostra les notificacions"
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Sol·licituds de seguiment",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Publicacions d'usuaris subscrits",
"update": "Edicions d'impulsos",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -133,7 +121,7 @@
"listDelete": { "listDelete": {
"heading": "Esborra la llista", "heading": "Esborra la llista",
"confirm": { "confirm": {
"title": "Vol esborrar la llista \"{{list}}\"?", "title": "Vols esborrar la llista \"{{list}}\"?",
"message": "Aquesta acció no es pot desfer." "message": "Aquesta acció no es pot desfer."
} }
}, },
@ -254,13 +242,10 @@
"content_true": "Habilitat", "content_true": "Habilitat",
"content_false": "Deshabilitat" "content_false": "Deshabilitat"
}, },
"update": {
"title": "Actualitza a la última versió"
},
"logout": { "logout": {
"button": "Tanca la sessió", "button": "Tanca la sessió",
"alert": { "alert": {
"title": "Vol tancar la sessió?", "title": "Vols tancar la sessió?",
"message": "Després de tancar la sessió, hauràs de tornar a iniciar la sessió", "message": "Després de tancar la sessió, hauràs de tornar a iniciar la sessió",
"buttons": { "buttons": {
"logout": "Tanca la sessió" "logout": "Tanca la sessió"
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Aspecte", "heading": "Aspecte",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "Utilitza el navegador del sistema" "external": "Utilitza el navegador del sistema"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "Utilitza emojis estàtics", "heading": "Reprodueix els GIF automàticament"
"description": "Si l'aplicació falla freqüentment en visualitzar la llista d'emojis, pots intentar fer servir els emojis estàtics."
}, },
"feedback": { "feedback": {
"heading": "Peticions de característiques" "heading": "Peticions de característiques"
@ -335,9 +306,7 @@
"moved": "S'ha traslladat", "moved": "S'ha traslladat",
"created_at": "Es va unir el dia {{date}}", "created_at": "Es va unir el dia {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} publicacions", "statuses_count": "{{count}} publicacions"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Publicacions", "default": "Publicacions",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "Edita l'historial" "name": "Edita l'historial"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": "Envia anònimament al servidor remot {{instance}}"
},
"reasons": {
"heading": "Què està passant amb aquest compte?",
"spam": "És contingut brossa",
"other": "És una altra cosa",
"violation": "Incompleix les regles del servidor"
},
"comment": {
"heading": "Vols afegir alguna altra cosa?"
},
"violatedRules": {
"heading": "Ha incomplert les regles del servidor"
}
},
"search": { "search": {
"header": { "header": {
"prefix": "Cercant", "prefix": "Cercant",

View File

@ -7,6 +7,10 @@
"action_true": "" "action_true": ""
}, },
"inLists": "", "inLists": "",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "", "action_false": "",
"action_true": "" "action_true": ""

View File

@ -42,8 +42,7 @@
"placeholder": "", "placeholder": "",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "", "title": ""
"OK": ""
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "", "accessibilityLabel": "",
"accessibilityHint": "", "accessibilityHint": "",
"title": "", "title": ""
"options": {
"follow": "",
"follow_request": "",
"favourite": "",
"reblog": "",
"mention": "",
"poll": "",
"status": "",
"update": "",
"admin.sign_up": "",
"admin.report": ""
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "", "content_true": "",
"content_false": "" "content_false": ""
}, },
"update": {
"title": ""
},
"logout": { "logout": {
"button": "", "button": "",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "",
"content": {
"S": "",
"M": "",
"L": "",
"XL": "",
"XXL": ""
}
},
"language": {
"heading": ""
},
"theme": { "theme": {
"heading": "", "heading": "",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "" "external": ""
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "", "heading": ""
"description": ""
}, },
"feedback": { "feedback": {
"heading": "" "heading": ""
@ -335,9 +306,7 @@
"moved": "", "moved": "",
"created_at": "", "created_at": "",
"summary": { "summary": {
"statuses_count": "", "statuses_count": ""
"following_count": "",
"followers_count": ""
}, },
"toots": { "toots": {
"default": "", "default": "",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "" "name": ""
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "", "prefix": "",

View File

@ -7,6 +7,10 @@
"action_true": "Nutzer entfolgen" "action_true": "Nutzer entfolgen"
}, },
"inLists": "Konten in Listen verwalten", "inLists": "Konten in Listen verwalten",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "Profil stummschalten", "action_false": "Profil stummschalten",
"action_true": "Stummschaltung des Nutzers aufheben" "action_true": "Stummschaltung des Nutzers aufheben"
@ -15,13 +19,13 @@
"action_false": "Nutzer blockieren", "action_false": "Nutzer blockieren",
"action_true": "User entblocken", "action_true": "User entblocken",
"alert": { "alert": {
"title": "" "title": "{{username}} wirklich blockieren?"
} }
}, },
"reports": { "reports": {
"action": "Nutzer melden und blockieren", "action": "Nutzer melden und blockieren",
"alert": { "alert": {
"title": "" "title": "{{username}} wirklich blockieren und melden?"
} }
} }
}, },

View File

@ -42,8 +42,7 @@
"placeholder": "Was geht in dir vor", "placeholder": "Was geht in dir vor",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "Maximale Anzahl von Anhängen erreicht", "title": "Maximale Anzahl von Anhängen erreicht"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filter", "accessibilityLabel": "Filter",
"accessibilityHint": "Angezeigte Benachrichtigungstypen filtern", "accessibilityHint": "Angezeigte Benachrichtigungstypen filtern",
"title": "Benachrichtigungen anzeigen", "title": "Benachrichtigungen anzeigen"
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Followeranfrage",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Tröt eines abonnierten Nutzers",
"update": "Boost wurde bearbeitet",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "Aktiviert", "content_true": "Aktiviert",
"content_false": "Deaktiviert" "content_false": "Deaktiviert"
}, },
"update": {
"title": "Auf neueste Version aktualisiert"
},
"logout": { "logout": {
"button": "Abmelden", "button": "Abmelden",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Erscheinungsbild", "heading": "Erscheinungsbild",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "Im Systembrowser öffnen" "external": "Im Systembrowser öffnen"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "System-Emojis verwenden", "heading": "GIFs in der Timeline automatisch abspielen"
"description": "Wenn du beim Betrachten der Emoji-Liste häufige App-Abstürze feststellst, kannst du stattdessen versuchen, statische Emoji zu verwenden."
}, },
"feedback": { "feedback": {
"heading": "Neue Funktion vorschlagen" "heading": "Neue Funktion vorschlagen"
@ -335,9 +306,7 @@
"moved": "Benutzer umgezogen", "moved": "Benutzer umgezogen",
"created_at": "Registriert am: {{date}}", "created_at": "Registriert am: {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} Tröts", "statuses_count": "{{count}} Tröts"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Tröts", "default": "Tröts",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "Bearbeitungsverlauf" "name": "Bearbeitungsverlauf"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "Suche", "prefix": "Suche",

View File

@ -7,6 +7,10 @@
"action_true": "Unfollow user" "action_true": "Unfollow user"
}, },
"inLists": "Manage user of lists", "inLists": "Manage user of lists",
"showBoosts": {
"action_false": "Show user's boosts",
"action_true": "Hide users's boosts"
},
"mute": { "mute": {
"action_false": "Mute user", "action_false": "Mute user",
"action_true": "Unmute user" "action_true": "Unmute user"

View File

@ -42,8 +42,7 @@
"placeholder": "What's on your mind", "placeholder": "What's on your mind",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "Maximum attachments amount reached", "title": "Maximum attachments amount reached"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filter", "accessibilityLabel": "Filter",
"accessibilityHint": "Filter shown notifications' types", "accessibilityHint": "Filter shown notifications' types",
"title": "Show notifications", "title": "Show notifications"
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Follow request",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Toot from subscribed users",
"update": "Reblog has been edited",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -266,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Appearance", "heading": "Appearance",
"options": { "options": {
@ -301,9 +276,8 @@
"external": "Use system browser" "external": "Use system browser"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "Use static emoji", "heading": "Autoplay GIF in timeline"
"description": "If you encounter frequent app crash when viewing emoji list, you can try to use static emoji instead."
}, },
"feedback": { "feedback": {
"heading": "Feature Requests" "heading": "Feature Requests"
@ -332,9 +306,7 @@
"moved": "User moved", "moved": "User moved",
"created_at": "Joined: {{date}}", "created_at": "Joined: {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} toots", "statuses_count": "{{count}} toots"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Toots", "default": "Toots",
@ -357,6 +329,25 @@
"history": { "history": {
"name": "Edit 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": { "search": {
"header": { "header": {
"prefix": "Searching", "prefix": "Searching",

View File

@ -1,6 +1,6 @@
{ {
"buttons": { "buttons": {
"OK": "OK", "OK": "De acuerdo",
"apply": "Aplicar", "apply": "Aplicar",
"cancel": "Cancelar", "cancel": "Cancelar",
"discard": "Descartar", "discard": "Descartar",

View File

@ -7,6 +7,10 @@
"action_true": "Dejar de seguir usuario" "action_true": "Dejar de seguir usuario"
}, },
"inLists": "Administrar usuario de listas", "inLists": "Administrar usuario de listas",
"showBoosts": {
"action_false": "Mostrar los impulsos del usuario",
"action_true": "Ocultar los impulsos del usuario"
},
"mute": { "mute": {
"action_false": "Silenciar usuario", "action_false": "Silenciar usuario",
"action_true": "Dejar de silenciar al usuario" "action_true": "Dejar de silenciar al usuario"
@ -15,13 +19,13 @@
"action_false": "Bloquear usuario", "action_false": "Bloquear usuario",
"action_true": "Desbloquear usuario", "action_true": "Desbloquear usuario",
"alert": { "alert": {
"title": "" "title": "¿Quieres bloquear a @{{username}}?"
} }
}, },
"reports": { "reports": {
"action": "Reportar y bloquear usuario", "action": "Reportar y bloquear usuario",
"alert": { "alert": {
"title": "" "title": "¿Quieres denunciar y bloquear a @{{username}}?"
} }
} }
}, },
@ -38,7 +42,7 @@
"block": { "block": {
"action": "Bloquear instancia {{instance}}", "action": "Bloquear instancia {{instance}}",
"alert": { "alert": {
"title": "¿Confirmar bloqueo de la instancia {{instance}}?", "title": "¿Quieres bloquear la instancia {{instance}}?",
"message": "Puedes silenciar o bloquear a un usuario.\n\nTras bloquear una instancia, todo su contenido junto con sus seguidores se eliminarán." "message": "Puedes silenciar o bloquear a un usuario.\n\nTras bloquear una instancia, todo su contenido junto con sus seguidores se eliminarán."
} }
} }
@ -59,14 +63,14 @@
"delete": { "delete": {
"action": "Eliminar toot", "action": "Eliminar toot",
"alert": { "alert": {
"title": "¿Confirmar eliminación?", "title": "¿Quieres eliminar?",
"message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas." "message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas."
} }
}, },
"deleteEdit": { "deleteEdit": {
"action": "Eliminar toot y volver a publicar", "action": "Eliminar toot y volver a publicar",
"alert": { "alert": {
"title": "¿Confirmar eliminación y volver a publicar?", "title": "¿Quieres eliminar y volver a publicar?",
"message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas." "message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas."
} }
}, },

View File

@ -32,7 +32,7 @@
}, },
"update": "El impulso ha sido editado", "update": "El impulso ha sido editado",
"admin.sign_up": "{{name}} se unió a la instancia", "admin.sign_up": "{{name}} se unió a la instancia",
"admin.report": "" "admin.report": "{{name}} reportó:"
}, },
"actions": { "actions": {
"reply": { "reply": {
@ -145,7 +145,7 @@
"refresh": "Actualizar" "refresh": "Actualizar"
}, },
"count": { "count": {
"voters_one": "{{count}} usuarios ha votado", "voters_one": "{{count}} usuario ha votado",
"voters_other": "{{count}} usuarios han votado", "voters_other": "{{count}} usuarios han votado",
"votes_one": "{{count}} voto", "votes_one": "{{count}} voto",
"votes_other": "{{count}} votos" "votes_other": "{{count}} votos"

View File

@ -2,7 +2,7 @@
"heading": { "heading": {
"left": { "left": {
"alert": { "alert": {
"title": "¿Cancelar edición?", "title": "¿Quieres cancelar la edición?",
"buttons": { "buttons": {
"save": "Guardar borrador", "save": "Guardar borrador",
"delete": "Eliminar borrador" "delete": "Eliminar borrador"
@ -13,9 +13,9 @@
"button": { "button": {
"default": "Toot", "default": "Toot",
"conversation": "Mensaje privado", "conversation": "Mensaje privado",
"reply": "Respuesta al toot", "reply": "Responde",
"deleteEdit": "Toot", "deleteEdit": "Vuelve a publicar",
"edit": "Edita el toot", "edit": "Edita",
"share": "Toot" "share": "Toot"
}, },
"alert": { "alert": {
@ -39,11 +39,10 @@
"placeholder": "Mensaje de aviso de spoiler" "placeholder": "Mensaje de aviso de spoiler"
}, },
"textInput": { "textInput": {
"placeholder": "Qué está pasando", "placeholder": "¿Qué está pasando?",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "Número máximo de adjuntos alcanzado", "title": "Número máximo de adjuntos alcanzado"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -11,7 +11,7 @@
"segments": { "segments": {
"federated": "Federado", "federated": "Federado",
"local": "Local", "local": "Local",
"trending": "En tendencia" "trending": "Tendencia"
} }
}, },
"notifications": { "notifications": {
@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filtrar", "accessibilityLabel": "Filtrar",
"accessibilityHint": "Filtrar tipos de notificación", "accessibilityHint": "Filtrar tipos de notificación",
"title": "Mostrar las notificaciones", "title": "Mostrar las notificaciones"
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Solicitud de seguimiento",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Toot de usuarios suscritos",
"update": "El impulso ha sido editado",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -133,7 +121,7 @@
"listDelete": { "listDelete": {
"heading": "Borrar lista", "heading": "Borrar lista",
"confirm": { "confirm": {
"title": "¿Eliminar lista \"{{list}}\"?", "title": "¿Quieres eliminar la lista \"{{list}}\"?",
"message": "Esta acción no se podrá deshacer." "message": "Esta acción no se podrá deshacer."
} }
}, },
@ -254,9 +242,6 @@
"content_true": "Habilitado", "content_true": "Habilitado",
"content_false": "Deshabilitado" "content_false": "Deshabilitado"
}, },
"update": {
"title": "Actualizar a la última versión"
},
"logout": { "logout": {
"button": "Cerrar sesión", "button": "Cerrar sesión",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Apariencia", "heading": "Apariencia",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "Usar navegador" "external": "Usar navegador"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "Usar emojis estáticos", "heading": "Reproduce automáticamente los GIF"
"description": "Si la aplicación falla al visualizar la lista de emojis, puedes intentar usar emojis estáticos en su lugar."
}, },
"feedback": { "feedback": {
"heading": "Petición de funciones" "heading": "Petición de funciones"
@ -335,9 +306,7 @@
"moved": "Se ha trasladado", "moved": "Se ha trasladado",
"created_at": "Se unió el {{date}}", "created_at": "Se unió el {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} toots", "statuses_count": "{{count}} toots"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Toots", "default": "Toots",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "Historial de ediciones" "name": "Historial de ediciones"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": "Enviar anónimamente al servidor remoto {{instance}}"
},
"reasons": {
"heading": "¿Qué está pasando con esta cuenta?",
"spam": "Es spam",
"other": "Es otra cosa",
"violation": "Incumple las reglas del servidor"
},
"comment": {
"heading": "¿Hay algo más que quieras añadir?"
},
"violatedRules": {
"heading": "Ha incumplido las reglas del servidor"
}
},
"search": { "search": {
"header": { "header": {
"prefix": "Buscando", "prefix": "Buscando",

View File

@ -7,6 +7,10 @@
"action_true": "Ne plus suivre l'utilisateur" "action_true": "Ne plus suivre l'utilisateur"
}, },
"inLists": "Gérer l'utilisateur des listes", "inLists": "Gérer l'utilisateur des listes",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "Rendre muet l'utilisateur", "action_false": "Rendre muet l'utilisateur",
"action_true": "Rendre la parole" "action_true": "Rendre la parole"

View File

@ -42,8 +42,7 @@
"placeholder": "Quavez-vous en tête", "placeholder": "Quavez-vous en tête",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "Nombre maximum de pièces jointes atteint", "title": "Nombre maximum de pièces jointes atteint"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filtrer", "accessibilityLabel": "Filtrer",
"accessibilityHint": "Filtrer les types de notifications affichés", "accessibilityHint": "Filtrer les types de notifications affichés",
"title": "Afficher les notifications", "title": "Afficher les notifications"
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Demande d'abonnement",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Pouet des utilisateurs inscrits",
"update": "Le reblog a été modifié",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "Activé", "content_true": "Activé",
"content_false": "Désactivé" "content_false": "Désactivé"
}, },
"update": {
"title": "Mettre à jour vers la dernière version"
},
"logout": { "logout": {
"button": "Se déconnecter", "button": "Se déconnecter",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Apparence", "heading": "Apparence",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "Ouvrir dans le navigateur système" "external": "Ouvrir dans le navigateur système"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "Utiliser des émojis statiques", "heading": ""
"description": "Si vous rencontrez des plantages fréquents de l'application lors de l'affichage de la liste d'émojis, vous pouvez essayer d'utiliser des émojis statiques."
}, },
"feedback": { "feedback": {
"heading": "Demande de fonctionnalités" "heading": "Demande de fonctionnalités"
@ -335,9 +306,7 @@
"moved": "Utilisateur déplacé", "moved": "Utilisateur déplacé",
"created_at": "Inscrit le: {{date}}", "created_at": "Inscrit le: {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} pouets", "statuses_count": "{{count}} pouets"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Pouets", "default": "Pouets",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "Modifier l'historique" "name": "Modifier l'historique"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "Recherche en cours", "prefix": "Recherche en cours",

View File

@ -112,6 +112,7 @@ i18n.use(initReactI18next).init({
'zh-Hans': zh_Hans, 'zh-Hans': zh_Hans,
'zh-Hant': zh_Hant 'zh-Hant': zh_Hant
}, },
returnNull: false,
returnEmptyString: false, returnEmptyString: false,
saveMissing: true, saveMissing: true,

View File

@ -7,6 +7,10 @@
"action_true": "" "action_true": ""
}, },
"inLists": "", "inLists": "",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "Muta utente", "action_false": "Muta utente",
"action_true": "Riattiva utente" "action_true": "Riattiva utente"

View File

@ -42,8 +42,7 @@
"placeholder": "A cosa stai pensando?", "placeholder": "A cosa stai pensando?",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "Hai raggiunto il numero massimo di allegati permessi", "title": "Hai raggiunto il numero massimo di allegati permessi"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filtri", "accessibilityLabel": "Filtri",
"accessibilityHint": "Filtra le notifiche mostrate per tipo", "accessibilityHint": "Filtra le notifiche mostrate per tipo",
"title": "", "title": ""
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Richiesta di seguirti",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Toot da utenti seguiti",
"update": "Il link è stato modificato",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "Abilitato", "content_true": "Abilitato",
"content_false": "Disabilitato" "content_false": "Disabilitato"
}, },
"update": {
"title": "Aggiorna all'ultima versione"
},
"logout": { "logout": {
"button": "Esci dall'account", "button": "Esci dall'account",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Tema", "heading": "Tema",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "Nel browser di sistema" "external": "Nel browser di sistema"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "Usa emoji statiche", "heading": ""
"description": "Se la app crasha spesso quando all'apertura della lista di emoji, puoi provare ad attivare le emoji statiche."
}, },
"feedback": { "feedback": {
"heading": "Richiedi funzionalità" "heading": "Richiedi funzionalità"
@ -335,9 +306,7 @@
"moved": "Profilo trasferito", "moved": "Profilo trasferito",
"created_at": "Account creato il: {{date}}", "created_at": "Account creato il: {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} toot", "statuses_count": "{{count}} toot"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Toot", "default": "Toot",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "Cronologia delle modifiche" "name": "Cronologia delle modifiche"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "Cerca", "prefix": "Cerca",

View File

@ -7,6 +7,10 @@
"action_true": "ユーザーをフォロー解除" "action_true": "ユーザーをフォロー解除"
}, },
"inLists": "リストのユーザーを管理", "inLists": "リストのユーザーを管理",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "ユーザーをミュート", "action_false": "ユーザーをミュート",
"action_true": "ユーザーのミュートを解除" "action_true": "ユーザーのミュートを解除"

View File

@ -42,8 +42,7 @@
"placeholder": "今なにかんがえてるの?", "placeholder": "今なにかんがえてるの?",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "アップロードできるメディアの数の上限に達しました", "title": "アップロードできるメディアの数の上限に達しました"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "フィルター", "accessibilityLabel": "フィルター",
"accessibilityHint": "表示される通知の種類をフィルターする", "accessibilityHint": "表示される通知の種類をフィルターする",
"title": "", "title": ""
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "フォローリクエスト",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "購読したユーザーのトゥート",
"update": "ブーストしたトゥートが編集されました",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "有効", "content_true": "有効",
"content_false": "無効" "content_false": "無効"
}, },
"update": {
"title": "最新バージョンへのアップデート"
},
"logout": { "logout": {
"button": "ログアウト", "button": "ログアウト",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "外観", "heading": "外観",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "システムブラウザを使用する" "external": "システムブラウザを使用する"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "静的な絵文字リスト", "heading": ""
"description": "絵文字リストを表示しているときにアプリが頻繁にクラッシュする場合、代わりにアニメーションが無効化された絵文字リストを使用してみてください。"
}, },
"feedback": { "feedback": {
"heading": "機能リクエスト" "heading": "機能リクエスト"
@ -335,9 +306,7 @@
"moved": "ユーザーは引っ越ししました", "moved": "ユーザーは引っ越ししました",
"created_at": "登録日: {{date}}", "created_at": "登録日: {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} 投稿", "statuses_count": "{{count}} 投稿"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "投稿", "default": "投稿",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "編集履歴" "name": "編集履歴"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "検索", "prefix": "検索",

View File

@ -7,6 +7,10 @@
"action_true": "사용자 팔로우 해제" "action_true": "사용자 팔로우 해제"
}, },
"inLists": "리스트의 사용자 관리", "inLists": "리스트의 사용자 관리",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "사용자 뮤트", "action_false": "사용자 뮤트",
"action_true": "사용자 뮤트 해제" "action_true": "사용자 뮤트 해제"

View File

@ -42,8 +42,7 @@
"placeholder": "무엇을 생각하고 있나요?", "placeholder": "무엇을 생각하고 있나요?",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "최대 첨부 파일 개수를 초과함", "title": "최대 첨부 파일 개수를 초과함"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "필터", "accessibilityLabel": "필터",
"accessibilityHint": "받는 알림 종류 선택", "accessibilityHint": "받는 알림 종류 선택",
"title": "알림 표시", "title": "알림 표시"
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "팔로우 요청",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "구독한 사용자의 툿",
"update": "부스트한 툿이 수정됨",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "활성화됨", "content_true": "활성화됨",
"content_false": "비활성화됨" "content_false": "비활성화됨"
}, },
"update": {
"title": "최신 버전으로 업데이트"
},
"logout": { "logout": {
"button": "로그아웃", "button": "로그아웃",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "테마", "heading": "테마",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "시스템 브라우저 사용" "external": "시스템 브라우저 사용"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "정적인 에모지 사용", "heading": ""
"description": "에모지 목록 화면에서 자주 앱이 강제 종료된다면, 정적인 에모지를 사용해보세요."
}, },
"feedback": { "feedback": {
"heading": "기능 제안" "heading": "기능 제안"
@ -335,9 +306,7 @@
"moved": "유저가 이동함", "moved": "유저가 이동함",
"created_at": "등록된 날: {{date}}", "created_at": "등록된 날: {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} 툿", "statuses_count": "{{count}} 툿"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "툿", "default": "툿",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "수정 이력" "name": "수정 이력"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "검색할", "prefix": "검색할",

View File

@ -7,6 +7,10 @@
"action_true": "Ontvolg" "action_true": "Ontvolg"
}, },
"inLists": "Gebruiker op lijsten beheren", "inLists": "Gebruiker op lijsten beheren",
"showBoosts": {
"action_false": "Boosts van gebruiker weergeven",
"action_true": "Boosts van gebruiker verbergen"
},
"mute": { "mute": {
"action_false": "Gebruiker dempen", "action_false": "Gebruiker dempen",
"action_true": "Dempen opheffen voor gebruiker" "action_true": "Dempen opheffen voor gebruiker"

View File

@ -42,8 +42,7 @@
"placeholder": "Wat wil je kwijt", "placeholder": "Wat wil je kwijt",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "Maximum aantal bijlagen bereikt", "title": "Maximum aantal bijlagen bereikt"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filter", "accessibilityLabel": "Filter",
"accessibilityHint": "Filter getoonde meldingstypes", "accessibilityHint": "Filter getoonde meldingstypes",
"title": "Notificaties weergeven", "title": "Notificaties weergeven"
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Volgverzoek",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Toot van geabonneerde gebruikers",
"update": "De reblog is bewerkt",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "Ingeschakeld", "content_true": "Ingeschakeld",
"content_false": "Uitgeschakeld" "content_false": "Uitgeschakeld"
}, },
"update": {
"title": "Bijwerken naar de laatste versie"
},
"logout": { "logout": {
"button": "Uitloggen", "button": "Uitloggen",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Uiterlijk", "heading": "Uiterlijk",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "Systeembrowser gebruiken" "external": "Systeembrowser gebruiken"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "Statische emoji gebruiken", "heading": "GIF automatisch afspelen op tijdlijn"
"description": "Als je vaak een app crash tegenkomt bij het bekijken van de emoji-lijst, kun je proberen om statische emojis te gebruiken."
}, },
"feedback": { "feedback": {
"heading": "Feature aanvragen" "heading": "Feature aanvragen"
@ -335,9 +306,7 @@
"moved": "Gebruiker is verplaatst", "moved": "Gebruiker is verplaatst",
"created_at": "Lid sinds {{date}}", "created_at": "Lid sinds {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} toots", "statuses_count": "{{count}} toots"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Toots", "default": "Toots",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "Geschiedenis bewerken" "name": "Geschiedenis bewerken"
}, },
"report": {
"name": "Rapporteer {{acct}}",
"report": "Rapporteer",
"forward": {
"heading": "Anoniem doorsturen naar externe server {{instance}}"
},
"reasons": {
"heading": "Wat is er aan de hand met dit account?",
"spam": "Het is spam",
"other": "Het is iets anders",
"violation": "Het schendt de serverregels"
},
"comment": {
"heading": "Wil je nog iets anders toevoegen?"
},
"violatedRules": {
"heading": "Het is in strijd met de serverregels"
}
},
"search": { "search": {
"header": { "header": {
"prefix": "Zoeken", "prefix": "Zoeken",

View File

@ -5,10 +5,10 @@
"cancel": "Anuluj", "cancel": "Anuluj",
"discard": "Anuluj", "discard": "Anuluj",
"continue": "Dalej", "continue": "Dalej",
"create": "", "create": "Utwórz",
"delete": "Usuń", "delete": "Usuń",
"done": "", "done": "Gotowe",
"confirm": "" "confirm": "Potwierdź"
}, },
"customEmoji": { "customEmoji": {
"accessibilityLabel": "Własne emoji {{emoji}}" "accessibilityLabel": "Własne emoji {{emoji}}"

View File

@ -1,12 +1,16 @@
{ {
"accessibilityHint": "", "accessibilityHint": "Akcje dla tego tootka, takie jak jego konto lub sam tootek",
"account": { "account": {
"title": "", "title": "Działania użytkownika",
"following": { "following": {
"action_false": "Obserwuj", "action_false": "Obserwuj",
"action_true": "Przestań obserwować" "action_true": "Przestań obserwować"
}, },
"inLists": "", "inLists": "Zarządzaj kontem list",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "Wycisz użytkownika", "action_false": "Wycisz użytkownika",
"action_true": "Wyłącz wyciszenie" "action_true": "Wyłącz wyciszenie"
@ -15,13 +19,13 @@
"action_false": "Zablokuj użytkownika", "action_false": "Zablokuj użytkownika",
"action_true": "Odblokuj użytkownika", "action_true": "Odblokuj użytkownika",
"alert": { "alert": {
"title": "" "title": "Na pewno zablokować {{username}}?"
} }
}, },
"reports": { "reports": {
"action": "Zgłoś i zablokuj", "action": "Zgłoś i zablokuj",
"alert": { "alert": {
"title": "" "title": "Potwierdzić zgłoszenie i zablokować {{username}}?"
} }
} }
}, },
@ -34,7 +38,7 @@
"succeed": "Skopiowano" "succeed": "Skopiowano"
}, },
"instance": { "instance": {
"title": "", "title": "Opcje instancji",
"block": { "block": {
"action": "Zablokuj instancję {{instance}}", "action": "Zablokuj instancję {{instance}}",
"alert": { "alert": {
@ -52,7 +56,7 @@
} }
}, },
"status": { "status": {
"title": "", "title": "Opcje tootka",
"edit": { "edit": {
"action": "Edytuj wpis" "action": "Edytuj wpis"
}, },
@ -64,15 +68,15 @@
} }
}, },
"deleteEdit": { "deleteEdit": {
"action": "", "action": "Skasuj tootka i opublikuj ponownie",
"alert": { "alert": {
"title": "", "title": "Potwierdzasz usunięcie i ponowną publikację?",
"message": "" "message": "Wszystkie podbicia i lajki zostaną usunięte, łącznie ze wszystkimi odpowiedziami."
} }
}, },
"mute": { "mute": {
"action_false": "", "action_false": "Wycisz tootka i odpowiedzi na niego",
"action_true": "" "action_true": "Wyłącz wyciszenie tootka i odpowiedzi na niego"
}, },
"pin": { "pin": {
"action_false": "Przypnij wpis", "action_false": "Przypnij wpis",

View File

@ -1,27 +1,27 @@
{ {
"server": { "server": {
"textInput": { "textInput": {
"placeholder": "" "placeholder": "Domena instancji"
}, },
"whitelisted": "", "whitelisted": "To może być godna zaufania instancja, z której tooot nie może pobrać danych przed zalogowaniem.",
"button": "", "button": "Zaloguj",
"information": { "information": {
"name": "", "name": "Imię i nazwisko",
"accounts": "", "accounts": "Użytkownicy",
"statuses": "", "statuses": "Tootki",
"domains": "" "domains": "Uniwersa"
}, },
"disclaimer": { "disclaimer": {
"base": "" "base": "Logowanie odbywa się przy użyciu przeglądarki systemowej, która nie będzie widoczna dla aplikacji."
}, },
"terms": { "terms": {
"base": "" "base": "Logując się, akceptujesz <0>politykę prywatności</0> i <1>regulamin usługi</1>."
} }
}, },
"update": { "update": {
"alert": { "alert": {
"title": "", "title": "Zalogowano do tej instancji",
"message": "" "message": "Możesz zalogować się na inne konto, zachowując już zalogowane konto"
} }
} }
} }

View File

@ -1,8 +1,8 @@
{ {
"HTML": { "HTML": {
"accessibilityHint": "", "accessibilityHint": "Dotknij, by rozwinąć lub zwinąć zawartość",
"expanded": "", "expanded": "{{hint}}{{moreLines}}",
"moreLines": "", "moreLines": " ({{count}} więcej linii tekstu)",
"defaultHint": "" "defaultHint": "Tootek o znacznej długości"
} }
} }

View File

@ -1,16 +1,16 @@
{ {
"follow": { "follow": {
"function": "" "function": "Obserwuj użytkownika"
}, },
"block": { "block": {
"function": "" "function": "Zablokuj użytkownika"
}, },
"button": { "button": {
"error": "", "error": "Błąd wczytywania",
"blocked_by": "", "blocked_by": "Zablokowany przez użytkownika",
"blocking": "", "blocking": "Odblokuj",
"following": "", "following": "Przestań obserwować",
"requested": "", "requested": "Wycofaj żądanie",
"default": "" "default": "Obserwuj"
} }
} }

View File

@ -9,51 +9,51 @@
} }
}, },
"end": { "end": {
"message": "" "message": "Koniec! Co powiesz na kubek <0 />?"
}, },
"lookback": { "lookback": {
"message": "" "message": "Ostatnio czytane o"
}, },
"refresh": { "refresh": {
"fetchPreviousPage": "", "fetchPreviousPage": "Nowsze",
"refetch": "" "refetch": "Do najnowszych"
}, },
"shared": { "shared": {
"actioned": { "actioned": {
"pinned": "", "pinned": "Przypięte",
"favourite": "", "favourite": "{{name}} polubił Twojego tootka",
"status": "", "status": "{{name}} właśnie coś opublikował(a)",
"follow": "", "follow": "{{name}} obserwuje Cię",
"follow_request": "", "follow_request": "{{name}} pragnie Cię obserwować",
"poll": "", "poll": "Ankieta w której brałeś(-aś) udział zakończyło się",
"reblog": { "reblog": {
"default": "", "default": "{{name}} podbił(a)",
"notification": "" "notification": "{{name}} podbił(a) Twojego tootka"
}, },
"update": "", "update": "Podbity tootek został edytowany",
"admin.sign_up": "{{name}} dołącza do instancji", "admin.sign_up": "{{name}} dołącza do instancji",
"admin.report": "" "admin.report": "{{name}} zgłosił(a):"
}, },
"actions": { "actions": {
"reply": { "reply": {
"accessibilityLabel": "" "accessibilityLabel": "Odpowiedz na tego tootka"
}, },
"reblogged": { "reblogged": {
"accessibilityLabel": "", "accessibilityLabel": "Podbij tego tootka",
"function": "", "function": "Podbij tootka",
"options": { "options": {
"title": "", "title": "Wybierz widoczność podbicia",
"public": "", "public": "Podbicie publiczne",
"unlisted": "" "unlisted": "Niewidoczne podbicie"
} }
}, },
"favourited": { "favourited": {
"accessibilityLabel": "", "accessibilityLabel": "Dodaj do ulubionych",
"function": "" "function": "Polubiony tootek"
}, },
"bookmarked": { "bookmarked": {
"accessibilityLabel": "", "accessibilityLabel": "Dodaj tego tootka do ulubionych",
"function": "" "function": "Zapisz tootka"
}, },
"openReport": "" "openReport": ""
}, },

View File

@ -1,10 +1,10 @@
{ {
"heading": "", "heading": "Ważne ogłoszenia",
"content": { "content": {
"published": "", "published": "Opublikowano <0 />",
"button": { "button": {
"read": "", "read": "Przeczytane",
"unread": "" "unread": "Oznacz jako przeczytane"
} }
} }
} }

View File

@ -2,31 +2,31 @@
"heading": { "heading": {
"left": { "left": {
"alert": { "alert": {
"title": "", "title": "Porzucić zmiany?",
"buttons": { "buttons": {
"save": "", "save": "Zapisz wersję roboczą",
"delete": "" "delete": "Usuń wersję roboczą"
} }
} }
}, },
"right": { "right": {
"button": { "button": {
"default": "", "default": "Trąbnij tootka",
"conversation": "", "conversation": "Wyślij prywatnego tootka",
"reply": "", "reply": "Odpowiedz",
"deleteEdit": "", "deleteEdit": "Trąbnij tootka",
"edit": "", "edit": "Trąbnij tootka",
"share": "" "share": "Trąbnij tootka"
}, },
"alert": { "alert": {
"default": { "default": {
"title": "", "title": "Nie udało się trąbnąć tootka",
"button": "" "button": "Spróbuj jeszcze raz"
}, },
"removeReply": { "removeReply": {
"title": "", "title": "Nie znaleziono odpowiedzi",
"description": "", "description": "Odpowiedź mogła zostać usunięta. Czy chcesz usunąć ją z odnośnika?",
"confirm": "" "confirm": "Usuń odnośnik"
} }
} }
} }
@ -34,82 +34,81 @@
"content": { "content": {
"root": { "root": {
"header": { "header": {
"postingAs": "", "postingAs": "Trąbię tootka jako @{{acct}}@{{domain}}",
"spoilerInput": { "spoilerInput": {
"placeholder": "" "placeholder": "Ostrzeżenie przed spoilerami"
}, },
"textInput": { "textInput": {
"placeholder": "", "placeholder": "Co Ci chodzi po głowie?",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "", "title": "Osiągnięto maksymalną ilość załączników"
"OK": ""
} }
} }
} }
}, },
"footer": { "footer": {
"attachments": { "attachments": {
"sensitive": "", "sensitive": "Oznacz załączniki jako kontrowersyjne",
"remove": { "remove": {
"accessibilityLabel": "" "accessibilityLabel": "Usuń przesłany załącznik, numer {{attachment}}"
}, },
"edit": { "edit": {
"accessibilityLabel": "" "accessibilityLabel": "Edytuj przesłany załącznik, numer {{attachment}}"
}, },
"upload": { "upload": {
"accessibilityLabel": "" "accessibilityLabel": "Prześlij więcej załączników"
} }
}, },
"emojis": { "emojis": {
"accessibilityHint": "" "accessibilityHint": "Dotknij, by dodać emoji do tootka"
}, },
"poll": { "poll": {
"option": { "option": {
"placeholder": { "placeholder": {
"accessibilityLabel": "", "accessibilityLabel": "Opcje ankiety {{index}}",
"single": "", "single": "Pojedynczy wybór",
"multiple": "" "multiple": "Wielokrotny wybór"
} }
}, },
"quantity": { "quantity": {
"reduce": { "reduce": {
"accessibilityLabel": "", "accessibilityLabel": "Zredukuj opcje ankiety do {{amount}}",
"accessibilityHint": "" "accessibilityHint": "Osiągnięto minimalną ilość opcji ankiety, obecnie posiada {{amount}}"
}, },
"increase": { "increase": {
"accessibilityLabel": "", "accessibilityLabel": "Zwiększ ilość opcji ankiety do {{amount}}",
"accessibilityHint": "" "accessibilityHint": "Osiągnięto maksymalną ilość opcji ankiety, obecnie {{amount}}"
} }
}, },
"multiple": { "multiple": {
"heading": "", "heading": "Rodzaj wyboru",
"options": { "options": {
"single": "", "single": "Pojedynczy wybór",
"multiple": "" "multiple": "Wielokrotny wybór"
} }
}, },
"expiration": { "expiration": {
"heading": "", "heading": "Ważność",
"options": { "options": {
"300": "", "300": "5 minut",
"1800": "", "1800": "30 minut",
"3600": "", "3600": "1 godzina",
"21600": "", "21600": "6 godzin",
"86400": "", "86400": "1 dzień",
"259200": "", "259200": "3 dni",
"604800": "" "604800": "7 dni"
} }
} }
} }
}, },
"actions": { "actions": {
"attachment": { "attachment": {
"accessibilityLabel": "", "accessibilityLabel": "Prześlij załącznik",
"accessibilityHint": "", "accessibilityHint": "Funkcja ankiety zostanie wyłączona jeśli pojawi się jakiś załącznik",
"failed": { "failed": {
"alert": { "alert": {
"title": "", "title": "Błąd przesyłania",
"button": "" "button": ""
} }
} }

View File

@ -10,7 +10,7 @@
}, },
"save": { "save": {
"succeed": "", "succeed": "",
"failed": "" "failed": "Zapisywanie obrazka nie powiodło się"
} }
} }
} }

View File

@ -28,89 +28,77 @@
"filters": { "filters": {
"accessibilityLabel": "", "accessibilityLabel": "",
"accessibilityHint": "", "accessibilityHint": "",
"title": "", "title": ""
"options": {
"follow": "",
"follow_request": "",
"favourite": "",
"reblog": "",
"mention": "",
"poll": "",
"status": "",
"update": "",
"admin.sign_up": "",
"admin.report": ""
}
} }
}, },
"me": { "me": {
"stacks": { "stacks": {
"bookmarks": { "bookmarks": {
"name": "" "name": "Zalajkowane"
}, },
"conversations": { "conversations": {
"name": "" "name": "Prywatne tootki"
}, },
"favourites": { "favourites": {
"name": "" "name": "Ulubione"
}, },
"followedTags": { "followedTags": {
"name": "" "name": "Obserwowane hasztagi"
}, },
"fontSize": { "fontSize": {
"name": "" "name": "Rozmiar czcionki"
}, },
"language": { "language": {
"name": "" "name": "Język"
}, },
"list": { "list": {
"name": "" "name": "Lista: {{list}}"
}, },
"listAccounts": { "listAccounts": {
"name": "Użytkownicy: {{list}}" "name": "Użytkownicy: {{list}}"
}, },
"listAdd": { "listAdd": {
"name": "" "name": "Utwórz listę"
}, },
"listEdit": { "listEdit": {
"name": "Edycja listy" "name": "Edycja listy"
}, },
"lists": { "lists": {
"name": "" "name": "Listy"
}, },
"push": { "push": {
"name": "" "name": "Powiadomienia Push"
}, },
"profile": { "profile": {
"name": "" "name": "Edytuj profil"
}, },
"profileName": { "profileName": {
"name": "" "name": "Edytuj nazwę użytkownika"
}, },
"profileNote": { "profileNote": {
"name": "" "name": "Edytuj opis"
}, },
"profileFields": { "profileFields": {
"name": "" "name": "Edytuj metadane"
}, },
"settings": { "settings": {
"name": "" "name": "Ustawienia"
}, },
"webSettings": { "webSettings": {
"name": "" "name": "Ustawienia konta"
}, },
"switch": { "switch": {
"name": "" "name": "Przełącz konto"
} }
}, },
"fontSize": { "fontSize": {
"demo": "", "demo": "<p>Oto przykładowy tootek 😊 Wybierz opcje wielkości czcionki z listy poniżej.<br /><br />To ustawienie wpłynie jedynie na zawartość Twoich tootków, ale nie na rozmiar innych komunikatów z apki.</p>",
"sizes": { "sizes": {
"S": "", "S": "S",
"M": "", "M": "M (Domyślna)",
"L": "", "L": "L",
"XL": "", "XL": "XL",
"XXL": "" "XXL": "XXL"
} }
}, },
"listAccounts": { "listAccounts": {
@ -139,55 +127,55 @@
}, },
"profile": { "profile": {
"feedback": { "feedback": {
"succeed": "", "succeed": "Zaktualizowano {{type}}",
"failed": "" "failed": "Aktualizacja {{type}} nie powiodła się, prosimy spróbować ponownie"
}, },
"root": { "root": {
"name": { "name": {
"title": "" "title": "Wyświetlana nazwa"
}, },
"avatar": { "avatar": {
"title": "", "title": "Zdjęcie profilowe",
"description": "" "description": "Zostanie zmniejszone do 400x400 px"
}, },
"header": { "header": {
"title": "", "title": "Zdjęcie w tle",
"description": "" "description": "Zostanie zmniejszony do 1500x500 px"
}, },
"note": { "note": {
"title": "" "title": "Opis"
}, },
"fields": { "fields": {
"title": "", "title": "Metadane",
"total_one": "", "total_one": "Pole {{count}}",
"total_other": "" "total_other": "{{count}} pola"
}, },
"visibility": { "visibility": {
"title": "", "title": "Domyślna widoczność tootków",
"options": { "options": {
"public": "", "public": "Publiczne",
"unlisted": "", "unlisted": "Niepubliczne",
"private": "" "private": "Tylko dla obserwujących"
} }
}, },
"sensitive": { "sensitive": {
"title": "" "title": "Publikuję rzeczy 18+"
}, },
"lock": { "lock": {
"title": "", "title": "Zablokuj konto",
"description": "" "description": "Wymaga ręcznego zatwierdzania obserwujących"
}, },
"bot": { "bot": {
"title": "", "title": "Konto bota",
"description": "" "description": "To konto podejmuje głównie zautomatyzowane działania i może nie być nadzorowane"
} }
}, },
"fields": { "fields": {
"group": "", "group": "Grupa {{index}}",
"label": "", "label": "Nazwa",
"content": "" "content": "Link"
}, },
"mediaSelectionFailed": "" "mediaSelectionFailed": "Nie udało się przetworzyć obrazka. Prosimy, spróbuj raz jeszcze"
}, },
"push": { "push": {
"notAvailable": "", "notAvailable": "",
@ -254,9 +242,6 @@
"content_true": "", "content_true": "",
"content_false": "" "content_false": ""
}, },
"update": {
"title": ""
},
"logout": { "logout": {
"button": "", "button": "",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "",
"content": {
"S": "",
"M": "",
"L": "",
"XL": "",
"XXL": ""
}
},
"language": {
"heading": ""
},
"theme": { "theme": {
"heading": "", "heading": "",
"options": { "options": {
@ -304,21 +276,20 @@
"external": "" "external": ""
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "", "heading": ""
"description": ""
}, },
"feedback": { "feedback": {
"heading": "" "heading": ""
}, },
"support": { "support": {
"heading": "" "heading": "Wesprzyj tooot!"
}, },
"contact": { "contact": {
"heading": "" "heading": "Skontaktuj się z nami"
}, },
"version": "", "version": "Wersja: v{{version}}",
"instanceVersion": "" "instanceVersion": "Wersja Mastodona: v{{version}}"
}, },
"switch": { "switch": {
"existing": "", "existing": "",
@ -335,9 +306,7 @@
"moved": "", "moved": "",
"created_at": "", "created_at": "",
"summary": { "summary": {
"statuses_count": "", "statuses_count": ""
"following_count": "",
"followers_count": ""
}, },
"toots": { "toots": {
"default": "", "default": "",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "" "name": ""
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "", "prefix": "",

View File

@ -7,6 +7,10 @@
"action_true": "" "action_true": ""
}, },
"inLists": "", "inLists": "",
"showBoosts": {
"action_false": "",
"action_true": ""
},
"mute": { "mute": {
"action_false": "Silenciar usuário", "action_false": "Silenciar usuário",
"action_true": "Desativar o silêncio do usuário" "action_true": "Desativar o silêncio do usuário"

View File

@ -42,8 +42,7 @@
"placeholder": "No que você está pensando", "placeholder": "No que você está pensando",
"keyboardImage": { "keyboardImage": {
"exceedMaximum": { "exceedMaximum": {
"title": "Quantidade máxima de anexos atingida", "title": "Quantidade máxima de anexos atingida"
"OK": "$t(common:buttons.OK)"
} }
} }
} }

View File

@ -28,19 +28,7 @@
"filters": { "filters": {
"accessibilityLabel": "Filtro", "accessibilityLabel": "Filtro",
"accessibilityHint": "Filtrar notificações por tipos", "accessibilityHint": "Filtrar notificações por tipos",
"title": "", "title": ""
"options": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"follow_request": "Solicitações de seguidores pendentes",
"favourite": "$t(screenTabs:me.push.favourite.heading)",
"reblog": "$t(screenTabs:me.push.reblog.heading)",
"mention": "$t(screenTabs:me.push.mention.heading)",
"poll": "$t(screenTabs:me.push.poll.heading)",
"status": "Toot de usuários inscritos",
"update": "Toot foi editado",
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
}
} }
}, },
"me": { "me": {
@ -254,9 +242,6 @@
"content_true": "Habilitado", "content_true": "Habilitado",
"content_false": "Desabilitado" "content_false": "Desabilitado"
}, },
"update": {
"title": "Atualize para a versão mais recente"
},
"logout": { "logout": {
"button": "Sair", "button": "Sair",
"alert": { "alert": {
@ -269,19 +254,6 @@
} }
}, },
"settings": { "settings": {
"fontsize": {
"heading": "$t(me.stacks.fontSize.name)",
"content": {
"S": "$t(me.fontSize.sizes.S)",
"M": "$t(me.fontSize.sizes.M)",
"L": "$t(me.fontSize.sizes.L)",
"XL": "$t(me.fontSize.sizes.XL)",
"XXL": "$t(me.fontSize.sizes.XXL)"
}
},
"language": {
"heading": "$t(me.stacks.language.name)"
},
"theme": { "theme": {
"heading": "Aparência", "heading": "Aparência",
"options": { "options": {
@ -304,9 +276,8 @@
"external": "Usar navegador do sistema" "external": "Usar navegador do sistema"
} }
}, },
"staticEmoji": { "autoplayGifv": {
"heading": "Usar emojis estáticos", "heading": ""
"description": "Se você encontrar falhas frequentes de apps ao visualizar a lista de emojis, você pode tentar usar emojis estáticos."
}, },
"feedback": { "feedback": {
"heading": "Pedidos de Funcionalidades" "heading": "Pedidos de Funcionalidades"
@ -335,9 +306,7 @@
"moved": "Usuário movido", "moved": "Usuário movido",
"created_at": "Registrado em: {{date}}", "created_at": "Registrado em: {{date}}",
"summary": { "summary": {
"statuses_count": "{{count}} toots", "statuses_count": "{{count}} toots"
"following_count": "$t(shared.users.accounts.following)",
"followers_count": "$t(shared.users.accounts.followers)"
}, },
"toots": { "toots": {
"default": "Toots", "default": "Toots",
@ -360,6 +329,25 @@
"history": { "history": {
"name": "Histórico de Edição" "name": "Histórico de Edição"
}, },
"report": {
"name": "",
"report": "",
"forward": {
"heading": ""
},
"reasons": {
"heading": "",
"spam": "",
"other": "",
"violation": ""
},
"comment": {
"heading": ""
},
"violatedRules": {
"heading": ""
}
},
"search": { "search": {
"header": { "header": {
"prefix": "Procurando", "prefix": "Procurando",

Some files were not shown because too many files have changed in this diff Show More