diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f43af7b4b..6ab0888f4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,3 @@ * @krille-chan +pubspec.* @dependabot lib/l10n/*.arb @weblate \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 485bfd8c5..d241cf918 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -34,6 +34,28 @@ body: placeholder: "e.g. 1.12.0" validations: required: true + - type: dropdown + id: platform + attributes: + label: "Platform" + description: "Select the platform where the bug occurs." + options: + - Android (PlayStore) + - Android (F-Droid) + - Android (Other) + - iOS (iPhone) + - iOS (iPad) + - Web (Chrome(ium)) + - Web (Firefox) + - Web (Safari) + - Linux (Snap) + - Linux (Flatpak) + - Linux (Other) + - macOS (iOS/iPadOS version) + - macOS (Self-compiled) + - Windows (Self-compiled) + validations: + required: true - type: input id: platform-info attributes: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 780f692f7..ddcf8edba 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: 👬 FluffyChat Community - url: https://matrix.to/#/#fluffychat:matrix.org + url: https://matrix.to/#/#fluffy-space:matrix.org about: Please ask and answer questions here. diff --git a/.github/actions/free_up_space/action.yaml b/.github/actions/free_up_space/action.yaml new file mode 100644 index 000000000..abe2c4b55 --- /dev/null +++ b/.github/actions/free_up_space/action.yaml @@ -0,0 +1,50 @@ +name: "Free up space" + +inputs: + target: + required: true + +runs: + using: "composite" + steps: + - name: Free up space + shell: bash + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/share/boost + sudo rm -rf /usr/local/share/chromium + sudo rm -rf /usr/local/share/powershell + sudo rm -rf /usr/local/share/vcpkg + sudo rm -rf /usr/local/share/miniconda + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo rm -rf /opt/hostedtoolcache/go + sudo rm -rf /opt/hostedtoolcache/Python + sudo rm -rf /opt/hostedtoolcache/node + sudo rm -rf /opt/hostedtoolcache/R + sudo rm -rf /opt/hostedtoolcache/Java + sudo rm -rf /opt/hostedtoolcache/LLVM + sudo rm -rf /opt/hostedtoolcache/Swift + sudo rm -rf /opt/hostedtoolcache/Php + sudo rm -rf /opt/hostedtoolcache/Perl + sudo rm -rf /opt/hostedtoolcache/Scala + sudo rm -rf /opt/hostedtoolcache/Julia + sudo rm -rf /opt/hostedtoolcache/Mono + sudo rm -rf /opt/hostedtoolcache/PowerShell + sudo rm -rf /opt/hostedtoolcache/Crystal + sudo rm -rf /opt/hostedtoolcache/Elixir + sudo rm -rf /opt/hostedtoolcache/Erlang + sudo rm -rf /opt/hostedtoolcache/FSharp + sudo rm -rf /opt/hostedtoolcache/Haskell + sudo rm -rf /opt/hostedtoolcache/OCaml + sudo rm -rf /opt/hostedtoolcache/Sbt + sudo rm -rf /opt/hostedtoolcache/Solidity + sudo rm -rf /opt/hostedtoolcache/VisualStudio + sudo rm -rf /opt/hostedtoolcache/WinAppDriver + sudo rm -rf /opt/hostedtoolcache/Xamarin + sudo rm -rf /opt/hostedtoolcache/Yarn + sudo rm -rf /opt/hostedtoolcache/Zephyr + sudo rm -rf /opt/hostedtoolcache/zig + sudo rm -rf /opt/hostedtoolcache/zulu + sudo rm -rf /opt/hostedtoolcache/azcopy + echo "export CARGO_BUILD_JOBS=1" >> $GITHUB_ENV diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7e75afa95..1ac76ab0e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,4 +1,3 @@ -# #Pangea # version: 2 # updates: # - package-ecosystem: "pub" @@ -10,6 +9,9 @@ # commit-message: # prefix: "build: " # include: "scope" +# assignees: +# - "krille-chan" +# open-pull-requests-limit: 5 # - package-ecosystem: "github-actions" # directory: "/" # schedule: @@ -19,4 +21,3 @@ # commit-message: # prefix: "build: " # include: "scope" -# Pangea# diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c92536427..7f4e66ec9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,13 +1,6 @@ *Thank you so much for your contribution to FluffyChat ❤️❤️❤️* -Please make sure that your Pull Request meet the following **acceptance criteria**: - -- [ ] Code formatting and import sorting has been done with `dart format lib/ test/` and `dart run import_sorter:main --no-comments` -- [ ] The commit message uses the format of [Conventional Commits](https://www.conventionalcommits.org) -- [ ] The commit message describes what has been changed, why it has been changed and how it has been changed -- [ ] Every new feature or change of the design/GUI is linked to an approved design proposal in an issue -- [ ] Every new feature in the app or the build system has a strategy how this will be tested and maintained from now on for every release, e.g. a volunteer who takes over maintainership - +- [ ] I have read and understood the [contributing guidelines](https://github.com/krille-chan/fluffychat/blob/main/CONTRIBUTING.md). ### Pull Request has been tested on: diff --git a/.github/workflows/check_duplicates.yaml b/.github/workflows/check_duplicates.yaml index ff88e8768..8a17c409f 100644 --- a/.github/workflows/check_duplicates.yaml +++ b/.github/workflows/check_duplicates.yaml @@ -1,10 +1,6 @@ -# #Pangea # name: Check duplicates on: - - -# on: # issues: # types: [opened] @@ -17,10 +13,11 @@ on: # number: ${{ github.event.issue.number }} # GH_TOKEN: ${{ github.token }} # steps: -# - uses: actions/checkout@v4 +# - uses: actions/checkout@v6 # - name: Check duplicates # run: | -# issues=$(gh issue list --search '${{ env.title }}' --json number,title,url) +# title=$(printf %q "${{ env.title }}") +# issues=$(gh issue list --search '${{ title }}' --json number,title,url) # number=${{ env.number }} # issues_filtered=$(echo "$issues" | jq --arg num "$number" 'map(select(.number != ($num | tonumber)))') # if [ "$(echo "$issues_filtered" | jq length)" -eq 0 ]; then @@ -29,5 +26,4 @@ on: # issues_markdown=$(echo "$issues_filtered" | jq -r '.[] | "- [" + .title + "](" + .url + ")"') # formatted_body=$(echo -e "@${{ env.author }}\nPossible duplication of:\n$issues_markdown") # gh issue comment ${{ github.event.issue.number }} --body "$formatted_body" -# fi -# Pangea# \ No newline at end of file +# fi \ No newline at end of file diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index 40f746865..7cc825c9d 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -8,7 +8,7 @@ jobs: code_tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - run: ./scripts/generate-locale-config.sh - run: git diff --exit-code - run: cat .github/workflows/versions.env >> $GITHUB_ENV @@ -25,7 +25,7 @@ jobs: - name: Check license compliance run: dart run license_checker check-licenses -c licenses.yaml --problematic - run: flutter analyze - - name: Add Firebase Messaging # Add android and analyze again + - name: Add Firebase Messaging run: ./scripts/add-firebase-messaging.sh - run: flutter analyze - run: flutter test @@ -33,7 +33,7 @@ jobs: build_debug_apk: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - uses: actions/setup-java@v5 with: @@ -43,6 +43,7 @@ jobs: with: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true + - uses: ./.github/actions/free_up_space - uses: moonrepo/setup-rust@v1 - name: Add Firebase Messaging run: ./scripts/add-firebase-messaging.sh @@ -51,7 +52,7 @@ jobs: build_debug_web: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - uses: subosito/flutter-action@v2 with: @@ -67,10 +68,10 @@ jobs: build_debug_linux: strategy: matrix: - arch: [ arm64 ] # Pangea Disabled x64 + arch: [ x64, arm64 ] runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest'}} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - name: Install dependencies run: sudo apt-get update && sudo apt-get install git wget curl libcurl4-openssl-dev clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 libssl-dev libwebkit2gtk-4.1-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev -y @@ -85,7 +86,7 @@ jobs: build_debug_ios: runs-on: macos-15 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - uses: subosito/flutter-action@v2 with: diff --git a/.github/workflows/main_deploy.yaml b/.github/workflows/main_deploy.yaml index 9c447c21f..dad9f5972 100644 --- a/.github/workflows/main_deploy.yaml +++ b/.github/workflows/main_deploy.yaml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest environment: staging steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - uses: subosito/flutter-action@v2 with: @@ -80,7 +80,7 @@ jobs: SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }} SENTRY_ORG: ${{ vars.SENTRY_ORG }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - uses: subosito/flutter-action@v2 with: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e083f2590..e9c6e7dd9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest environment: production steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - uses: subosito/flutter-action@v2 with: @@ -64,7 +64,7 @@ jobs: - name: Create archive run: tar -czf pangeachat-web.tar.gz build/web/ - name: Upload Web Build - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Web Build path: pangeachat-web.tar.gz @@ -90,9 +90,9 @@ jobs: env: WEB_APP_ENV: ${{ vars.WEB_APP_ENV }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: cat .github/workflows/versions.env >> $GITHUB_ENV - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: ${{ env.JAVA_VERSION }} distribution: 'zulu' @@ -131,7 +131,7 @@ jobs: PLAY_STORE_KEYSTORE_KEY_PASSWORD: ${{ secrets.PLAY_STORE_KEYSTORE_KEY_PASSWORD }} PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }} run: ./scripts/prepare-android-release.sh - - run: flutter build apk --release + - run: flutter build apk --release --target-platform android-arm,android-arm64 - name: Upload to release uses: actions/upload-release-asset@v1 env: diff --git a/.github/workflows/versions.env b/.github/workflows/versions.env index 4d5a9ba26..26c8c0750 100644 --- a/.github/workflows/versions.env +++ b/.github/workflows/versions.env @@ -1,2 +1,2 @@ -FLUTTER_VERSION=3.35.3 +FLUTTER_VERSION=3.38.7 JAVA_VERSION=17 diff --git a/.gitignore b/.gitignore index ff817a6d8..77d0c82a5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,8 @@ assets/.env.local_choreo *.env.prod envs.json # libolm package -/assets/js/package +web/Imaging.js +web/Imaging.wasm # IntelliJ related *.iml @@ -81,1855 +82,13 @@ olm needed-translations.txt .venv -# Generated files from find_unused_intl_keys.py -scripts/unused_intl_keys_report.txt -scripts/unused_intl_keys.json +docs/node_modules +rust -docs/node_modules/.package-lock.json -docs/node_modules/.bin/detect-libc -docs/node_modules/.bin/jiti -docs/node_modules/.bin/tailwindcss -docs/node_modules/@parcel/watcher/binding.gyp -docs/node_modules/@parcel/watcher/index.d.ts -docs/node_modules/@parcel/watcher/index.js -docs/node_modules/@parcel/watcher/index.js.flow -docs/node_modules/@parcel/watcher/LICENSE -docs/node_modules/@parcel/watcher/package.json -docs/node_modules/@parcel/watcher/README.md -docs/node_modules/@parcel/watcher/wrapper.js -docs/node_modules/@parcel/watcher/scripts/build-from-source.js -docs/node_modules/@parcel/watcher/src/Backend.cc -docs/node_modules/@parcel/watcher/src/Backend.hh -docs/node_modules/@parcel/watcher/src/binding.cc -docs/node_modules/@parcel/watcher/src/Debounce.cc -docs/node_modules/@parcel/watcher/src/Debounce.hh -docs/node_modules/@parcel/watcher/src/DirTree.cc -docs/node_modules/@parcel/watcher/src/DirTree.hh -docs/node_modules/@parcel/watcher/src/Event.hh -docs/node_modules/@parcel/watcher/src/Glob.cc -docs/node_modules/@parcel/watcher/src/Glob.hh -docs/node_modules/@parcel/watcher/src/PromiseRunner.hh -docs/node_modules/@parcel/watcher/src/Signal.hh -docs/node_modules/@parcel/watcher/src/Watcher.cc -docs/node_modules/@parcel/watcher/src/Watcher.hh -docs/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.cc -docs/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.hh -docs/node_modules/@parcel/watcher/src/linux/InotifyBackend.cc -docs/node_modules/@parcel/watcher/src/linux/InotifyBackend.hh -docs/node_modules/@parcel/watcher/src/macos/FSEventsBackend.cc -docs/node_modules/@parcel/watcher/src/macos/FSEventsBackend.hh -docs/node_modules/@parcel/watcher/src/shared/BruteForceBackend.cc -docs/node_modules/@parcel/watcher/src/shared/BruteForceBackend.hh -docs/node_modules/@parcel/watcher/src/unix/fts.cc -docs/node_modules/@parcel/watcher/src/unix/legacy.cc -docs/node_modules/@parcel/watcher/src/wasm/include.h -docs/node_modules/@parcel/watcher/src/wasm/WasmBackend.cc -docs/node_modules/@parcel/watcher/src/wasm/WasmBackend.hh -docs/node_modules/@parcel/watcher/src/watchman/BSER.cc -docs/node_modules/@parcel/watcher/src/watchman/BSER.hh -docs/node_modules/@parcel/watcher/src/watchman/IPC.hh -docs/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.cc -docs/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.hh -docs/node_modules/@parcel/watcher/src/windows/win_utils.cc -docs/node_modules/@parcel/watcher/src/windows/win_utils.hh -docs/node_modules/@parcel/watcher/src/windows/WindowsBackend.cc -docs/node_modules/@parcel/watcher/src/windows/WindowsBackend.hh -docs/node_modules/@parcel/watcher-linux-x64-glibc/LICENSE -docs/node_modules/@parcel/watcher-linux-x64-glibc/package.json -docs/node_modules/@parcel/watcher-linux-x64-glibc/README.md -docs/node_modules/@parcel/watcher-linux-x64-glibc/watcher.node -docs/node_modules/@tailwindcss/cli/LICENSE -docs/node_modules/@tailwindcss/cli/package.json -docs/node_modules/@tailwindcss/cli/README.md -docs/node_modules/@tailwindcss/cli/dist/index.mjs -docs/node_modules/@tailwindcss/node/LICENSE -docs/node_modules/@tailwindcss/node/package.json -docs/node_modules/@tailwindcss/node/README.md -docs/node_modules/@tailwindcss/node/dist/esm-cache.loader.d.mts -docs/node_modules/@tailwindcss/node/dist/esm-cache.loader.mjs -docs/node_modules/@tailwindcss/node/dist/index.d.mts -docs/node_modules/@tailwindcss/node/dist/index.d.ts -docs/node_modules/@tailwindcss/node/dist/index.js -docs/node_modules/@tailwindcss/node/dist/index.mjs -docs/node_modules/@tailwindcss/node/dist/require-cache.d.ts -docs/node_modules/@tailwindcss/node/dist/require-cache.js -docs/node_modules/@tailwindcss/oxide/index.d.ts -docs/node_modules/@tailwindcss/oxide/index.js -docs/node_modules/@tailwindcss/oxide/LICENSE -docs/node_modules/@tailwindcss/oxide/package.json -docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/LICENSE -docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/package.json -docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/README.md -docs/node_modules/@tailwindcss/oxide-linux-x64-gnu/tailwindcss-oxide.linux-x64-gnu.node -docs/node_modules/braces/index.js -docs/node_modules/braces/LICENSE -docs/node_modules/braces/package.json -docs/node_modules/braces/README.md -docs/node_modules/braces/lib/compile.js -docs/node_modules/braces/lib/constants.js -docs/node_modules/braces/lib/expand.js -docs/node_modules/braces/lib/parse.js -docs/node_modules/braces/lib/stringify.js -docs/node_modules/braces/lib/utils.js -docs/node_modules/detect-libc/.npmignore -docs/node_modules/detect-libc/LICENSE -docs/node_modules/detect-libc/package.json -docs/node_modules/detect-libc/README.md -docs/node_modules/detect-libc/bin/detect-libc.js -docs/node_modules/detect-libc/lib/detect-libc.js -docs/node_modules/enhanced-resolve/LICENSE -docs/node_modules/enhanced-resolve/package.json -docs/node_modules/enhanced-resolve/README.md -docs/node_modules/enhanced-resolve/types.d.ts -docs/node_modules/enhanced-resolve/lib/AliasFieldPlugin.js -docs/node_modules/enhanced-resolve/lib/AliasPlugin.js -docs/node_modules/enhanced-resolve/lib/AppendPlugin.js -docs/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js -docs/node_modules/enhanced-resolve/lib/CloneBasenamePlugin.js -docs/node_modules/enhanced-resolve/lib/ConditionalPlugin.js -docs/node_modules/enhanced-resolve/lib/createInnerContext.js -docs/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js -docs/node_modules/enhanced-resolve/lib/DescriptionFileUtils.js -docs/node_modules/enhanced-resolve/lib/DirectoryExistsPlugin.js -docs/node_modules/enhanced-resolve/lib/ExportsFieldPlugin.js -docs/node_modules/enhanced-resolve/lib/ExtensionAliasPlugin.js -docs/node_modules/enhanced-resolve/lib/FileExistsPlugin.js -docs/node_modules/enhanced-resolve/lib/forEachBail.js -docs/node_modules/enhanced-resolve/lib/getInnerRequest.js -docs/node_modules/enhanced-resolve/lib/getPaths.js -docs/node_modules/enhanced-resolve/lib/ImportsFieldPlugin.js -docs/node_modules/enhanced-resolve/lib/index.js -docs/node_modules/enhanced-resolve/lib/JoinRequestPartPlugin.js -docs/node_modules/enhanced-resolve/lib/JoinRequestPlugin.js -docs/node_modules/enhanced-resolve/lib/LogInfoPlugin.js -docs/node_modules/enhanced-resolve/lib/MainFieldPlugin.js -docs/node_modules/enhanced-resolve/lib/ModulesInHierachicDirectoriesPlugin.js -docs/node_modules/enhanced-resolve/lib/ModulesInHierarchicalDirectoriesPlugin.js -docs/node_modules/enhanced-resolve/lib/ModulesInRootPlugin.js -docs/node_modules/enhanced-resolve/lib/NextPlugin.js -docs/node_modules/enhanced-resolve/lib/ParsePlugin.js -docs/node_modules/enhanced-resolve/lib/PnpPlugin.js -docs/node_modules/enhanced-resolve/lib/Resolver.js -docs/node_modules/enhanced-resolve/lib/ResolverFactory.js -docs/node_modules/enhanced-resolve/lib/RestrictionsPlugin.js -docs/node_modules/enhanced-resolve/lib/ResultPlugin.js -docs/node_modules/enhanced-resolve/lib/RootsPlugin.js -docs/node_modules/enhanced-resolve/lib/SelfReferencePlugin.js -docs/node_modules/enhanced-resolve/lib/SymlinkPlugin.js -docs/node_modules/enhanced-resolve/lib/SyncAsyncFileSystemDecorator.js -docs/node_modules/enhanced-resolve/lib/TryNextPlugin.js -docs/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js -docs/node_modules/enhanced-resolve/lib/UseFilePlugin.js -docs/node_modules/enhanced-resolve/lib/util/entrypoints.js -docs/node_modules/enhanced-resolve/lib/util/identifier.js -docs/node_modules/enhanced-resolve/lib/util/module-browser.js -docs/node_modules/enhanced-resolve/lib/util/path.js -docs/node_modules/enhanced-resolve/lib/util/process-browser.js -docs/node_modules/fill-range/index.js -docs/node_modules/fill-range/LICENSE -docs/node_modules/fill-range/package.json -docs/node_modules/fill-range/README.md -docs/node_modules/graceful-fs/clone.js -docs/node_modules/graceful-fs/graceful-fs.js -docs/node_modules/graceful-fs/legacy-streams.js -docs/node_modules/graceful-fs/LICENSE -docs/node_modules/graceful-fs/package.json -docs/node_modules/graceful-fs/polyfills.js -docs/node_modules/graceful-fs/README.md -docs/node_modules/is-extglob/index.js -docs/node_modules/is-extglob/LICENSE -docs/node_modules/is-extglob/package.json -docs/node_modules/is-extglob/README.md -docs/node_modules/is-glob/index.js -docs/node_modules/is-glob/LICENSE -docs/node_modules/is-glob/package.json -docs/node_modules/is-glob/README.md -docs/node_modules/is-number/index.js -docs/node_modules/is-number/LICENSE -docs/node_modules/is-number/package.json -docs/node_modules/is-number/README.md -docs/node_modules/jiti/LICENSE -docs/node_modules/jiti/package.json -docs/node_modules/jiti/README.md -docs/node_modules/jiti/dist/babel.cjs -docs/node_modules/jiti/dist/jiti.cjs -docs/node_modules/jiti/lib/jiti-cli.mjs -docs/node_modules/jiti/lib/jiti-hooks.mjs -docs/node_modules/jiti/lib/jiti-native.mjs -docs/node_modules/jiti/lib/jiti-register.d.mts -docs/node_modules/jiti/lib/jiti-register.mjs -docs/node_modules/jiti/lib/jiti.cjs -docs/node_modules/jiti/lib/jiti.d.cts -docs/node_modules/jiti/lib/jiti.d.mts -docs/node_modules/jiti/lib/jiti.mjs -docs/node_modules/jiti/lib/types.d.ts -docs/node_modules/lightningcss/LICENSE -docs/node_modules/lightningcss/package.json -docs/node_modules/lightningcss/README.md -docs/node_modules/lightningcss/node/ast.d.ts -docs/node_modules/lightningcss/node/ast.js.flow -docs/node_modules/lightningcss/node/browserslistToTargets.js -docs/node_modules/lightningcss/node/composeVisitors.js -docs/node_modules/lightningcss/node/flags.js -docs/node_modules/lightningcss/node/index.d.ts -docs/node_modules/lightningcss/node/index.js -docs/node_modules/lightningcss/node/index.js.flow -docs/node_modules/lightningcss/node/index.mjs -docs/node_modules/lightningcss/node/targets.d.ts -docs/node_modules/lightningcss/node/targets.js.flow -docs/node_modules/lightningcss-linux-x64-gnu/LICENSE -docs/node_modules/lightningcss-linux-x64-gnu/lightningcss.linux-x64-gnu.node -docs/node_modules/lightningcss-linux-x64-gnu/package.json -docs/node_modules/lightningcss-linux-x64-gnu/README.md -docs/node_modules/micromatch/index.js -docs/node_modules/micromatch/LICENSE -docs/node_modules/micromatch/package.json -docs/node_modules/micromatch/README.md -docs/node_modules/mri/index.d.ts -docs/node_modules/mri/license.md -docs/node_modules/mri/package.json -docs/node_modules/mri/readme.md -docs/node_modules/mri/lib/index.js -docs/node_modules/mri/lib/index.mjs -docs/node_modules/node-addon-api/common.gypi -docs/node_modules/node-addon-api/except.gypi -docs/node_modules/node-addon-api/index.js -docs/node_modules/node-addon-api/LICENSE.md -docs/node_modules/node-addon-api/napi-inl.deprecated.h -docs/node_modules/node-addon-api/napi-inl.h -docs/node_modules/node-addon-api/napi.h -docs/node_modules/node-addon-api/node_addon_api.gyp -docs/node_modules/node-addon-api/node_api.gyp -docs/node_modules/node-addon-api/noexcept.gypi -docs/node_modules/node-addon-api/nothing.c -docs/node_modules/node-addon-api/package-support.json -docs/node_modules/node-addon-api/package.json -docs/node_modules/node-addon-api/README.md -docs/node_modules/node-addon-api/tools/check-napi.js -docs/node_modules/node-addon-api/tools/clang-format.js -docs/node_modules/node-addon-api/tools/conversion.js -docs/node_modules/node-addon-api/tools/eslint-format.js -docs/node_modules/node-addon-api/tools/README.md -docs/node_modules/picocolors/LICENSE -docs/node_modules/picocolors/package.json -docs/node_modules/picocolors/picocolors.browser.js -docs/node_modules/picocolors/picocolors.d.ts -docs/node_modules/picocolors/picocolors.js -docs/node_modules/picocolors/README.md -docs/node_modules/picocolors/types.d.ts -docs/node_modules/picomatch/CHANGELOG.md -docs/node_modules/picomatch/index.js -docs/node_modules/picomatch/LICENSE -docs/node_modules/picomatch/package.json -docs/node_modules/picomatch/README.md -docs/node_modules/picomatch/lib/constants.js -docs/node_modules/picomatch/lib/parse.js -docs/node_modules/picomatch/lib/picomatch.js -docs/node_modules/picomatch/lib/scan.js -docs/node_modules/picomatch/lib/utils.js -docs/node_modules/tailwindcss/index.css -docs/node_modules/tailwindcss/LICENSE -docs/node_modules/tailwindcss/package.json -docs/node_modules/tailwindcss/preflight.css -docs/node_modules/tailwindcss/README.md -docs/node_modules/tailwindcss/theme.css -docs/node_modules/tailwindcss/utilities.css -docs/node_modules/tailwindcss/dist/chunk-AZANAYY2.mjs -docs/node_modules/tailwindcss/dist/chunk-CH45MXZF.mjs -docs/node_modules/tailwindcss/dist/chunk-V2K3XTS4.mjs -docs/node_modules/tailwindcss/dist/colors-b_6i0Oi7.d.ts -docs/node_modules/tailwindcss/dist/colors.d.mts -docs/node_modules/tailwindcss/dist/colors.d.ts -docs/node_modules/tailwindcss/dist/colors.js -docs/node_modules/tailwindcss/dist/colors.mjs -docs/node_modules/tailwindcss/dist/default-theme.d.mts -docs/node_modules/tailwindcss/dist/default-theme.d.ts -docs/node_modules/tailwindcss/dist/default-theme.js -docs/node_modules/tailwindcss/dist/default-theme.mjs -docs/node_modules/tailwindcss/dist/flatten-color-palette.d.mts -docs/node_modules/tailwindcss/dist/flatten-color-palette.d.ts -docs/node_modules/tailwindcss/dist/flatten-color-palette.js -docs/node_modules/tailwindcss/dist/flatten-color-palette.mjs -docs/node_modules/tailwindcss/dist/lib.d.mts -docs/node_modules/tailwindcss/dist/lib.d.ts -docs/node_modules/tailwindcss/dist/lib.js -docs/node_modules/tailwindcss/dist/lib.mjs -docs/node_modules/tailwindcss/dist/plugin.d.mts -docs/node_modules/tailwindcss/dist/plugin.d.ts -docs/node_modules/tailwindcss/dist/plugin.js -docs/node_modules/tailwindcss/dist/plugin.mjs -docs/node_modules/tailwindcss/dist/resolve-config-BIFUA2FY.d.ts -docs/node_modules/tailwindcss/dist/resolve-config-QUZ9b-Gn.d.mts -docs/node_modules/tailwindcss/dist/types-B254mqw1.d.mts -docs/node_modules/tapable/LICENSE -docs/node_modules/tapable/package.json -docs/node_modules/tapable/README.md -docs/node_modules/tapable/tapable.d.ts -docs/node_modules/tapable/lib/AsyncParallelBailHook.js -docs/node_modules/tapable/lib/AsyncParallelHook.js -docs/node_modules/tapable/lib/AsyncSeriesBailHook.js -docs/node_modules/tapable/lib/AsyncSeriesHook.js -docs/node_modules/tapable/lib/AsyncSeriesLoopHook.js -docs/node_modules/tapable/lib/AsyncSeriesWaterfallHook.js -docs/node_modules/tapable/lib/Hook.js -docs/node_modules/tapable/lib/HookCodeFactory.js -docs/node_modules/tapable/lib/HookMap.js -docs/node_modules/tapable/lib/index.js -docs/node_modules/tapable/lib/MultiHook.js -docs/node_modules/tapable/lib/SyncBailHook.js -docs/node_modules/tapable/lib/SyncHook.js -docs/node_modules/tapable/lib/SyncLoopHook.js -docs/node_modules/tapable/lib/SyncWaterfallHook.js -docs/node_modules/tapable/lib/util-browser.js -docs/node_modules/to-regex-range/index.js -docs/node_modules/to-regex-range/LICENSE -docs/node_modules/to-regex-range/package.json -docs/node_modules/to-regex-range/README.md -docs/package.json -docs/package-lock.json -rust/target/.rustc_info.json -rust/target/CACHEDIR.TAG -rust/target/debug/.cargo-lock -rust/target/debug/libvodozemac_bindings_dart.a -rust/target/debug/libvodozemac_bindings_dart.d -rust/target/debug/libvodozemac_bindings_dart.dylib -rust/target/debug/.fingerprint/addr2line-164afbf588b3e8e0/dep-lib-addr2line -rust/target/debug/.fingerprint/addr2line-164afbf588b3e8e0/invoked.timestamp -rust/target/debug/.fingerprint/addr2line-164afbf588b3e8e0/lib-addr2line -rust/target/debug/.fingerprint/addr2line-164afbf588b3e8e0/lib-addr2line.json -rust/target/debug/.fingerprint/adler2-b8e1686650101ebc/dep-lib-adler2 -rust/target/debug/.fingerprint/adler2-b8e1686650101ebc/invoked.timestamp -rust/target/debug/.fingerprint/adler2-b8e1686650101ebc/lib-adler2 -rust/target/debug/.fingerprint/adler2-b8e1686650101ebc/lib-adler2.json -rust/target/debug/.fingerprint/aead-247e9024b30f32c8/dep-lib-aead -rust/target/debug/.fingerprint/aead-247e9024b30f32c8/invoked.timestamp -rust/target/debug/.fingerprint/aead-247e9024b30f32c8/lib-aead -rust/target/debug/.fingerprint/aead-247e9024b30f32c8/lib-aead.json -rust/target/debug/.fingerprint/aes-06933c0f73f54f0e/dep-lib-aes -rust/target/debug/.fingerprint/aes-06933c0f73f54f0e/invoked.timestamp -rust/target/debug/.fingerprint/aes-06933c0f73f54f0e/lib-aes -rust/target/debug/.fingerprint/aes-06933c0f73f54f0e/lib-aes.json -rust/target/debug/.fingerprint/allo-isolate-340d9aeefcfdded8/dep-lib-allo_isolate -rust/target/debug/.fingerprint/allo-isolate-340d9aeefcfdded8/invoked.timestamp -rust/target/debug/.fingerprint/allo-isolate-340d9aeefcfdded8/lib-allo_isolate -rust/target/debug/.fingerprint/allo-isolate-340d9aeefcfdded8/lib-allo_isolate.json -rust/target/debug/.fingerprint/anyhow-18484b5b80b5ad09/run-build-script-build-script-build -rust/target/debug/.fingerprint/anyhow-18484b5b80b5ad09/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/anyhow-30177ef9193b13a0/dep-lib-anyhow -rust/target/debug/.fingerprint/anyhow-30177ef9193b13a0/invoked.timestamp -rust/target/debug/.fingerprint/anyhow-30177ef9193b13a0/lib-anyhow -rust/target/debug/.fingerprint/anyhow-30177ef9193b13a0/lib-anyhow.json -rust/target/debug/.fingerprint/anyhow-77f46aa22ee05fc9/run-build-script-build-script-build -rust/target/debug/.fingerprint/anyhow-77f46aa22ee05fc9/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/anyhow-be8aa79faecb673b/build-script-build-script-build -rust/target/debug/.fingerprint/anyhow-be8aa79faecb673b/build-script-build-script-build.json -rust/target/debug/.fingerprint/anyhow-be8aa79faecb673b/dep-build-script-build-script-build -rust/target/debug/.fingerprint/anyhow-be8aa79faecb673b/invoked.timestamp -rust/target/debug/.fingerprint/anyhow-c40754bdc1d08d97/dep-lib-anyhow -rust/target/debug/.fingerprint/anyhow-c40754bdc1d08d97/invoked.timestamp -rust/target/debug/.fingerprint/anyhow-c40754bdc1d08d97/lib-anyhow -rust/target/debug/.fingerprint/anyhow-c40754bdc1d08d97/lib-anyhow.json -rust/target/debug/.fingerprint/anyhow-da043d455b1bfdee/build-script-build-script-build -rust/target/debug/.fingerprint/anyhow-da043d455b1bfdee/build-script-build-script-build.json -rust/target/debug/.fingerprint/anyhow-da043d455b1bfdee/dep-build-script-build-script-build -rust/target/debug/.fingerprint/anyhow-da043d455b1bfdee/invoked.timestamp -rust/target/debug/.fingerprint/arrayvec-f9646fd6f0b662a7/dep-lib-arrayvec -rust/target/debug/.fingerprint/arrayvec-f9646fd6f0b662a7/invoked.timestamp -rust/target/debug/.fingerprint/arrayvec-f9646fd6f0b662a7/lib-arrayvec -rust/target/debug/.fingerprint/arrayvec-f9646fd6f0b662a7/lib-arrayvec.json -rust/target/debug/.fingerprint/atomic-4fef8628930404bc/dep-lib-atomic -rust/target/debug/.fingerprint/atomic-4fef8628930404bc/invoked.timestamp -rust/target/debug/.fingerprint/atomic-4fef8628930404bc/lib-atomic -rust/target/debug/.fingerprint/atomic-4fef8628930404bc/lib-atomic.json -rust/target/debug/.fingerprint/autocfg-0322980e80f4dda5/dep-lib-autocfg -rust/target/debug/.fingerprint/autocfg-0322980e80f4dda5/invoked.timestamp -rust/target/debug/.fingerprint/autocfg-0322980e80f4dda5/lib-autocfg -rust/target/debug/.fingerprint/autocfg-0322980e80f4dda5/lib-autocfg.json -rust/target/debug/.fingerprint/backtrace-d6d745a43534420c/dep-lib-backtrace -rust/target/debug/.fingerprint/backtrace-d6d745a43534420c/invoked.timestamp -rust/target/debug/.fingerprint/backtrace-d6d745a43534420c/lib-backtrace -rust/target/debug/.fingerprint/backtrace-d6d745a43534420c/lib-backtrace.json -rust/target/debug/.fingerprint/base64-9782b8db13fc1943/dep-lib-base64 -rust/target/debug/.fingerprint/base64-9782b8db13fc1943/invoked.timestamp -rust/target/debug/.fingerprint/base64-9782b8db13fc1943/lib-base64 -rust/target/debug/.fingerprint/base64-9782b8db13fc1943/lib-base64.json -rust/target/debug/.fingerprint/base64ct-09659ecb40e5863e/dep-lib-base64ct -rust/target/debug/.fingerprint/base64ct-09659ecb40e5863e/invoked.timestamp -rust/target/debug/.fingerprint/base64ct-09659ecb40e5863e/lib-base64ct -rust/target/debug/.fingerprint/base64ct-09659ecb40e5863e/lib-base64ct.json -rust/target/debug/.fingerprint/block-buffer-57f418079f810658/dep-lib-block_buffer -rust/target/debug/.fingerprint/block-buffer-57f418079f810658/invoked.timestamp -rust/target/debug/.fingerprint/block-buffer-57f418079f810658/lib-block_buffer -rust/target/debug/.fingerprint/block-buffer-57f418079f810658/lib-block_buffer.json -rust/target/debug/.fingerprint/block-padding-3425e63b59c3e346/dep-lib-block_padding -rust/target/debug/.fingerprint/block-padding-3425e63b59c3e346/invoked.timestamp -rust/target/debug/.fingerprint/block-padding-3425e63b59c3e346/lib-block_padding -rust/target/debug/.fingerprint/block-padding-3425e63b59c3e346/lib-block_padding.json -rust/target/debug/.fingerprint/build-target-d161175fbbd29f4e/dep-lib-build_target -rust/target/debug/.fingerprint/build-target-d161175fbbd29f4e/invoked.timestamp -rust/target/debug/.fingerprint/build-target-d161175fbbd29f4e/lib-build_target -rust/target/debug/.fingerprint/build-target-d161175fbbd29f4e/lib-build_target.json -rust/target/debug/.fingerprint/byteorder-6c69f0cfa357c802/dep-lib-byteorder -rust/target/debug/.fingerprint/byteorder-6c69f0cfa357c802/invoked.timestamp -rust/target/debug/.fingerprint/byteorder-6c69f0cfa357c802/lib-byteorder -rust/target/debug/.fingerprint/byteorder-6c69f0cfa357c802/lib-byteorder.json -rust/target/debug/.fingerprint/bytes-9f047593c94b8196/dep-lib-bytes -rust/target/debug/.fingerprint/bytes-9f047593c94b8196/invoked.timestamp -rust/target/debug/.fingerprint/bytes-9f047593c94b8196/lib-bytes -rust/target/debug/.fingerprint/bytes-9f047593c94b8196/lib-bytes.json -rust/target/debug/.fingerprint/cbc-cdd262f0eb0b6f26/dep-lib-cbc -rust/target/debug/.fingerprint/cbc-cdd262f0eb0b6f26/invoked.timestamp -rust/target/debug/.fingerprint/cbc-cdd262f0eb0b6f26/lib-cbc -rust/target/debug/.fingerprint/cbc-cdd262f0eb0b6f26/lib-cbc.json -rust/target/debug/.fingerprint/cc-1ec0f1f8458cb965/dep-lib-cc -rust/target/debug/.fingerprint/cc-1ec0f1f8458cb965/invoked.timestamp -rust/target/debug/.fingerprint/cc-1ec0f1f8458cb965/lib-cc -rust/target/debug/.fingerprint/cc-1ec0f1f8458cb965/lib-cc.json -rust/target/debug/.fingerprint/cfg-if-3ebdd2ac77e576b4/dep-lib-cfg_if -rust/target/debug/.fingerprint/cfg-if-3ebdd2ac77e576b4/invoked.timestamp -rust/target/debug/.fingerprint/cfg-if-3ebdd2ac77e576b4/lib-cfg_if -rust/target/debug/.fingerprint/cfg-if-3ebdd2ac77e576b4/lib-cfg_if.json -rust/target/debug/.fingerprint/chacha20-956c523d15dcae2e/dep-lib-chacha20 -rust/target/debug/.fingerprint/chacha20-956c523d15dcae2e/invoked.timestamp -rust/target/debug/.fingerprint/chacha20-956c523d15dcae2e/lib-chacha20 -rust/target/debug/.fingerprint/chacha20-956c523d15dcae2e/lib-chacha20.json -rust/target/debug/.fingerprint/chacha20poly1305-b264df9570fa4bb8/dep-lib-chacha20poly1305 -rust/target/debug/.fingerprint/chacha20poly1305-b264df9570fa4bb8/invoked.timestamp -rust/target/debug/.fingerprint/chacha20poly1305-b264df9570fa4bb8/lib-chacha20poly1305 -rust/target/debug/.fingerprint/chacha20poly1305-b264df9570fa4bb8/lib-chacha20poly1305.json -rust/target/debug/.fingerprint/cipher-54d420ebddb605e6/dep-lib-cipher -rust/target/debug/.fingerprint/cipher-54d420ebddb605e6/invoked.timestamp -rust/target/debug/.fingerprint/cipher-54d420ebddb605e6/lib-cipher -rust/target/debug/.fingerprint/cipher-54d420ebddb605e6/lib-cipher.json -rust/target/debug/.fingerprint/cpufeatures-a83958b8d3becd82/dep-lib-cpufeatures -rust/target/debug/.fingerprint/cpufeatures-a83958b8d3becd82/invoked.timestamp -rust/target/debug/.fingerprint/cpufeatures-a83958b8d3becd82/lib-cpufeatures -rust/target/debug/.fingerprint/cpufeatures-a83958b8d3becd82/lib-cpufeatures.json -rust/target/debug/.fingerprint/crypto-common-af19c26aff2153a0/dep-lib-crypto_common -rust/target/debug/.fingerprint/crypto-common-af19c26aff2153a0/invoked.timestamp -rust/target/debug/.fingerprint/crypto-common-af19c26aff2153a0/lib-crypto_common -rust/target/debug/.fingerprint/crypto-common-af19c26aff2153a0/lib-crypto_common.json -rust/target/debug/.fingerprint/crypto-common-ed3b431853e7b51b/dep-lib-crypto_common -rust/target/debug/.fingerprint/crypto-common-ed3b431853e7b51b/invoked.timestamp -rust/target/debug/.fingerprint/crypto-common-ed3b431853e7b51b/lib-crypto_common -rust/target/debug/.fingerprint/crypto-common-ed3b431853e7b51b/lib-crypto_common.json -rust/target/debug/.fingerprint/curve25519-dalek-5d1124799e65242c/run-build-script-build-script-build -rust/target/debug/.fingerprint/curve25519-dalek-5d1124799e65242c/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/curve25519-dalek-99dce1f29c3bc595/dep-lib-curve25519_dalek -rust/target/debug/.fingerprint/curve25519-dalek-99dce1f29c3bc595/invoked.timestamp -rust/target/debug/.fingerprint/curve25519-dalek-99dce1f29c3bc595/lib-curve25519_dalek -rust/target/debug/.fingerprint/curve25519-dalek-99dce1f29c3bc595/lib-curve25519_dalek.json -rust/target/debug/.fingerprint/curve25519-dalek-e455512f25905d9b/build-script-build-script-build -rust/target/debug/.fingerprint/curve25519-dalek-e455512f25905d9b/build-script-build-script-build.json -rust/target/debug/.fingerprint/curve25519-dalek-e455512f25905d9b/dep-build-script-build-script-build -rust/target/debug/.fingerprint/curve25519-dalek-e455512f25905d9b/invoked.timestamp -rust/target/debug/.fingerprint/dart-sys-6d7ea2832798c569/build-script-build-script-build -rust/target/debug/.fingerprint/dart-sys-6d7ea2832798c569/build-script-build-script-build.json -rust/target/debug/.fingerprint/dart-sys-6d7ea2832798c569/dep-build-script-build-script-build -rust/target/debug/.fingerprint/dart-sys-6d7ea2832798c569/invoked.timestamp -rust/target/debug/.fingerprint/dart-sys-815ea59815e75bcf/dep-lib-dart_sys -rust/target/debug/.fingerprint/dart-sys-815ea59815e75bcf/invoked.timestamp -rust/target/debug/.fingerprint/dart-sys-815ea59815e75bcf/lib-dart_sys -rust/target/debug/.fingerprint/dart-sys-815ea59815e75bcf/lib-dart_sys.json -rust/target/debug/.fingerprint/dart-sys-fc5525cd8b578726/run-build-script-build-script-build -rust/target/debug/.fingerprint/dart-sys-fc5525cd8b578726/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/dashmap-f7e07885a57442dc/dep-lib-dashmap -rust/target/debug/.fingerprint/dashmap-f7e07885a57442dc/invoked.timestamp -rust/target/debug/.fingerprint/dashmap-f7e07885a57442dc/lib-dashmap -rust/target/debug/.fingerprint/dashmap-f7e07885a57442dc/lib-dashmap.json -rust/target/debug/.fingerprint/delegate-attr-d56d0e9b16a435b3/dep-lib-delegate_attr -rust/target/debug/.fingerprint/delegate-attr-d56d0e9b16a435b3/invoked.timestamp -rust/target/debug/.fingerprint/delegate-attr-d56d0e9b16a435b3/lib-delegate_attr -rust/target/debug/.fingerprint/delegate-attr-d56d0e9b16a435b3/lib-delegate_attr.json -rust/target/debug/.fingerprint/digest-109a3bc51d4b8490/dep-lib-digest -rust/target/debug/.fingerprint/digest-109a3bc51d4b8490/invoked.timestamp -rust/target/debug/.fingerprint/digest-109a3bc51d4b8490/lib-digest -rust/target/debug/.fingerprint/digest-109a3bc51d4b8490/lib-digest.json -rust/target/debug/.fingerprint/digest-d43f4616a513ec25/dep-lib-digest -rust/target/debug/.fingerprint/digest-d43f4616a513ec25/invoked.timestamp -rust/target/debug/.fingerprint/digest-d43f4616a513ec25/lib-digest -rust/target/debug/.fingerprint/digest-d43f4616a513ec25/lib-digest.json -rust/target/debug/.fingerprint/ed25519-4a15fe63621b2416/dep-lib-ed25519 -rust/target/debug/.fingerprint/ed25519-4a15fe63621b2416/invoked.timestamp -rust/target/debug/.fingerprint/ed25519-4a15fe63621b2416/lib-ed25519 -rust/target/debug/.fingerprint/ed25519-4a15fe63621b2416/lib-ed25519.json -rust/target/debug/.fingerprint/ed25519-dalek-df57d83c9aa0b0f3/dep-lib-ed25519_dalek -rust/target/debug/.fingerprint/ed25519-dalek-df57d83c9aa0b0f3/invoked.timestamp -rust/target/debug/.fingerprint/ed25519-dalek-df57d83c9aa0b0f3/lib-ed25519_dalek -rust/target/debug/.fingerprint/ed25519-dalek-df57d83c9aa0b0f3/lib-ed25519_dalek.json -rust/target/debug/.fingerprint/either-960afc30a188083e/dep-lib-either -rust/target/debug/.fingerprint/either-960afc30a188083e/invoked.timestamp -rust/target/debug/.fingerprint/either-960afc30a188083e/lib-either -rust/target/debug/.fingerprint/either-960afc30a188083e/lib-either.json -rust/target/debug/.fingerprint/equivalent-cb826c24d950b291/dep-lib-equivalent -rust/target/debug/.fingerprint/equivalent-cb826c24d950b291/invoked.timestamp -rust/target/debug/.fingerprint/equivalent-cb826c24d950b291/lib-equivalent -rust/target/debug/.fingerprint/equivalent-cb826c24d950b291/lib-equivalent.json -rust/target/debug/.fingerprint/flutter_rust_bridge-4a25ddda9ee0605f/build-script-build-script-build -rust/target/debug/.fingerprint/flutter_rust_bridge-4a25ddda9ee0605f/build-script-build-script-build.json -rust/target/debug/.fingerprint/flutter_rust_bridge-4a25ddda9ee0605f/dep-build-script-build-script-build -rust/target/debug/.fingerprint/flutter_rust_bridge-4a25ddda9ee0605f/invoked.timestamp -rust/target/debug/.fingerprint/flutter_rust_bridge-e3a9f2c084a60e41/run-build-script-build-script-build -rust/target/debug/.fingerprint/flutter_rust_bridge-e3a9f2c084a60e41/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/flutter_rust_bridge-f27219e8c9bea4cf/dep-lib-flutter_rust_bridge -rust/target/debug/.fingerprint/flutter_rust_bridge-f27219e8c9bea4cf/invoked.timestamp -rust/target/debug/.fingerprint/flutter_rust_bridge-f27219e8c9bea4cf/lib-flutter_rust_bridge -rust/target/debug/.fingerprint/flutter_rust_bridge-f27219e8c9bea4cf/lib-flutter_rust_bridge.json -rust/target/debug/.fingerprint/flutter_rust_bridge_macros-ebdd655fcadb605d/dep-lib-flutter_rust_bridge_macros -rust/target/debug/.fingerprint/flutter_rust_bridge_macros-ebdd655fcadb605d/invoked.timestamp -rust/target/debug/.fingerprint/flutter_rust_bridge_macros-ebdd655fcadb605d/lib-flutter_rust_bridge_macros -rust/target/debug/.fingerprint/flutter_rust_bridge_macros-ebdd655fcadb605d/lib-flutter_rust_bridge_macros.json -rust/target/debug/.fingerprint/futures-6a3338d6ee3d47df/dep-lib-futures -rust/target/debug/.fingerprint/futures-6a3338d6ee3d47df/invoked.timestamp -rust/target/debug/.fingerprint/futures-6a3338d6ee3d47df/lib-futures -rust/target/debug/.fingerprint/futures-6a3338d6ee3d47df/lib-futures.json -rust/target/debug/.fingerprint/futures-channel-80e8e3f3aa40a3d5/dep-lib-futures_channel -rust/target/debug/.fingerprint/futures-channel-80e8e3f3aa40a3d5/invoked.timestamp -rust/target/debug/.fingerprint/futures-channel-80e8e3f3aa40a3d5/lib-futures_channel -rust/target/debug/.fingerprint/futures-channel-80e8e3f3aa40a3d5/lib-futures_channel.json -rust/target/debug/.fingerprint/futures-core-2090651f2656334d/dep-lib-futures_core -rust/target/debug/.fingerprint/futures-core-2090651f2656334d/invoked.timestamp -rust/target/debug/.fingerprint/futures-core-2090651f2656334d/lib-futures_core -rust/target/debug/.fingerprint/futures-core-2090651f2656334d/lib-futures_core.json -rust/target/debug/.fingerprint/futures-executor-16793de88874db73/dep-lib-futures_executor -rust/target/debug/.fingerprint/futures-executor-16793de88874db73/invoked.timestamp -rust/target/debug/.fingerprint/futures-executor-16793de88874db73/lib-futures_executor -rust/target/debug/.fingerprint/futures-executor-16793de88874db73/lib-futures_executor.json -rust/target/debug/.fingerprint/futures-io-63ecdd207fa87dba/dep-lib-futures_io -rust/target/debug/.fingerprint/futures-io-63ecdd207fa87dba/invoked.timestamp -rust/target/debug/.fingerprint/futures-io-63ecdd207fa87dba/lib-futures_io -rust/target/debug/.fingerprint/futures-io-63ecdd207fa87dba/lib-futures_io.json -rust/target/debug/.fingerprint/futures-macro-9e7a0924881b0558/dep-lib-futures_macro -rust/target/debug/.fingerprint/futures-macro-9e7a0924881b0558/invoked.timestamp -rust/target/debug/.fingerprint/futures-macro-9e7a0924881b0558/lib-futures_macro -rust/target/debug/.fingerprint/futures-macro-9e7a0924881b0558/lib-futures_macro.json -rust/target/debug/.fingerprint/futures-sink-59aef06b1ce7ea0b/dep-lib-futures_sink -rust/target/debug/.fingerprint/futures-sink-59aef06b1ce7ea0b/invoked.timestamp -rust/target/debug/.fingerprint/futures-sink-59aef06b1ce7ea0b/lib-futures_sink -rust/target/debug/.fingerprint/futures-sink-59aef06b1ce7ea0b/lib-futures_sink.json -rust/target/debug/.fingerprint/futures-task-0833ac2045db9683/dep-lib-futures_task -rust/target/debug/.fingerprint/futures-task-0833ac2045db9683/invoked.timestamp -rust/target/debug/.fingerprint/futures-task-0833ac2045db9683/lib-futures_task -rust/target/debug/.fingerprint/futures-task-0833ac2045db9683/lib-futures_task.json -rust/target/debug/.fingerprint/futures-util-cf1b8ba26ed8a4b3/dep-lib-futures_util -rust/target/debug/.fingerprint/futures-util-cf1b8ba26ed8a4b3/invoked.timestamp -rust/target/debug/.fingerprint/futures-util-cf1b8ba26ed8a4b3/lib-futures_util -rust/target/debug/.fingerprint/futures-util-cf1b8ba26ed8a4b3/lib-futures_util.json -rust/target/debug/.fingerprint/generic-array-810ff7b5bbf2bd2e/build-script-build-script-build -rust/target/debug/.fingerprint/generic-array-810ff7b5bbf2bd2e/build-script-build-script-build.json -rust/target/debug/.fingerprint/generic-array-810ff7b5bbf2bd2e/dep-build-script-build-script-build -rust/target/debug/.fingerprint/generic-array-810ff7b5bbf2bd2e/invoked.timestamp -rust/target/debug/.fingerprint/generic-array-9c72fd8f563d25bd/dep-lib-generic_array -rust/target/debug/.fingerprint/generic-array-9c72fd8f563d25bd/invoked.timestamp -rust/target/debug/.fingerprint/generic-array-9c72fd8f563d25bd/lib-generic_array -rust/target/debug/.fingerprint/generic-array-9c72fd8f563d25bd/lib-generic_array.json -rust/target/debug/.fingerprint/generic-array-e7cb25425c0c5f54/run-build-script-build-script-build -rust/target/debug/.fingerprint/generic-array-e7cb25425c0c5f54/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/getrandom-019ee3c40b7cf82c/dep-lib-getrandom -rust/target/debug/.fingerprint/getrandom-019ee3c40b7cf82c/invoked.timestamp -rust/target/debug/.fingerprint/getrandom-019ee3c40b7cf82c/lib-getrandom -rust/target/debug/.fingerprint/getrandom-019ee3c40b7cf82c/lib-getrandom.json -rust/target/debug/.fingerprint/gimli-644a05e86b54988a/dep-lib-gimli -rust/target/debug/.fingerprint/gimli-644a05e86b54988a/invoked.timestamp -rust/target/debug/.fingerprint/gimli-644a05e86b54988a/lib-gimli -rust/target/debug/.fingerprint/gimli-644a05e86b54988a/lib-gimli.json -rust/target/debug/.fingerprint/hashbrown-d6c864ee9c675c93/dep-lib-hashbrown -rust/target/debug/.fingerprint/hashbrown-d6c864ee9c675c93/invoked.timestamp -rust/target/debug/.fingerprint/hashbrown-d6c864ee9c675c93/lib-hashbrown -rust/target/debug/.fingerprint/hashbrown-d6c864ee9c675c93/lib-hashbrown.json -rust/target/debug/.fingerprint/hex-ef63cab4a2086dea/dep-lib-hex -rust/target/debug/.fingerprint/hex-ef63cab4a2086dea/invoked.timestamp -rust/target/debug/.fingerprint/hex-ef63cab4a2086dea/lib-hex -rust/target/debug/.fingerprint/hex-ef63cab4a2086dea/lib-hex.json -rust/target/debug/.fingerprint/hkdf-fe190f0214b64e87/dep-lib-hkdf -rust/target/debug/.fingerprint/hkdf-fe190f0214b64e87/invoked.timestamp -rust/target/debug/.fingerprint/hkdf-fe190f0214b64e87/lib-hkdf -rust/target/debug/.fingerprint/hkdf-fe190f0214b64e87/lib-hkdf.json -rust/target/debug/.fingerprint/hmac-11c1b29e0e53e9f8/dep-lib-hmac -rust/target/debug/.fingerprint/hmac-11c1b29e0e53e9f8/invoked.timestamp -rust/target/debug/.fingerprint/hmac-11c1b29e0e53e9f8/lib-hmac -rust/target/debug/.fingerprint/hmac-11c1b29e0e53e9f8/lib-hmac.json -rust/target/debug/.fingerprint/indexmap-d8257cccb7676d45/dep-lib-indexmap -rust/target/debug/.fingerprint/indexmap-d8257cccb7676d45/invoked.timestamp -rust/target/debug/.fingerprint/indexmap-d8257cccb7676d45/lib-indexmap -rust/target/debug/.fingerprint/indexmap-d8257cccb7676d45/lib-indexmap.json -rust/target/debug/.fingerprint/inout-b9164dc3ab18b5ad/dep-lib-inout -rust/target/debug/.fingerprint/inout-b9164dc3ab18b5ad/invoked.timestamp -rust/target/debug/.fingerprint/inout-b9164dc3ab18b5ad/lib-inout -rust/target/debug/.fingerprint/inout-b9164dc3ab18b5ad/lib-inout.json -rust/target/debug/.fingerprint/itertools-0a3696ed8aac006f/dep-lib-itertools -rust/target/debug/.fingerprint/itertools-0a3696ed8aac006f/invoked.timestamp -rust/target/debug/.fingerprint/itertools-0a3696ed8aac006f/lib-itertools -rust/target/debug/.fingerprint/itertools-0a3696ed8aac006f/lib-itertools.json -rust/target/debug/.fingerprint/itoa-f09f1d4f251ad3f2/dep-lib-itoa -rust/target/debug/.fingerprint/itoa-f09f1d4f251ad3f2/invoked.timestamp -rust/target/debug/.fingerprint/itoa-f09f1d4f251ad3f2/lib-itoa -rust/target/debug/.fingerprint/itoa-f09f1d4f251ad3f2/lib-itoa.json -rust/target/debug/.fingerprint/lazy_static-ac30fe37778770b1/dep-lib-lazy_static -rust/target/debug/.fingerprint/lazy_static-ac30fe37778770b1/invoked.timestamp -rust/target/debug/.fingerprint/lazy_static-ac30fe37778770b1/lib-lazy_static -rust/target/debug/.fingerprint/lazy_static-ac30fe37778770b1/lib-lazy_static.json -rust/target/debug/.fingerprint/libc-e331c44b26e5d7a8/run-build-script-build-script-build -rust/target/debug/.fingerprint/libc-e331c44b26e5d7a8/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/libc-f3ddc1aa080aac3d/dep-lib-libc -rust/target/debug/.fingerprint/libc-f3ddc1aa080aac3d/invoked.timestamp -rust/target/debug/.fingerprint/libc-f3ddc1aa080aac3d/lib-libc -rust/target/debug/.fingerprint/libc-f3ddc1aa080aac3d/lib-libc.json -rust/target/debug/.fingerprint/libc-f7a8c95f93d5a1e7/build-script-build-script-build -rust/target/debug/.fingerprint/libc-f7a8c95f93d5a1e7/build-script-build-script-build.json -rust/target/debug/.fingerprint/libc-f7a8c95f93d5a1e7/dep-build-script-build-script-build -rust/target/debug/.fingerprint/libc-f7a8c95f93d5a1e7/invoked.timestamp -rust/target/debug/.fingerprint/log-7345ef26f7bf9f6d/dep-lib-log -rust/target/debug/.fingerprint/log-7345ef26f7bf9f6d/invoked.timestamp -rust/target/debug/.fingerprint/log-7345ef26f7bf9f6d/lib-log -rust/target/debug/.fingerprint/log-7345ef26f7bf9f6d/lib-log.json -rust/target/debug/.fingerprint/matrix-pickle-d3d613ced9b4e14e/dep-lib-matrix_pickle -rust/target/debug/.fingerprint/matrix-pickle-d3d613ced9b4e14e/invoked.timestamp -rust/target/debug/.fingerprint/matrix-pickle-d3d613ced9b4e14e/lib-matrix_pickle -rust/target/debug/.fingerprint/matrix-pickle-d3d613ced9b4e14e/lib-matrix_pickle.json -rust/target/debug/.fingerprint/matrix-pickle-derive-d08f83551ce17830/dep-lib-matrix_pickle_derive -rust/target/debug/.fingerprint/matrix-pickle-derive-d08f83551ce17830/invoked.timestamp -rust/target/debug/.fingerprint/matrix-pickle-derive-d08f83551ce17830/lib-matrix_pickle_derive -rust/target/debug/.fingerprint/matrix-pickle-derive-d08f83551ce17830/lib-matrix_pickle_derive.json -rust/target/debug/.fingerprint/md-5-f9c2224a9cf1cebb/dep-lib-md5 -rust/target/debug/.fingerprint/md-5-f9c2224a9cf1cebb/invoked.timestamp -rust/target/debug/.fingerprint/md-5-f9c2224a9cf1cebb/lib-md5 -rust/target/debug/.fingerprint/md-5-f9c2224a9cf1cebb/lib-md5.json -rust/target/debug/.fingerprint/memchr-ac59469faa88ab94/dep-lib-memchr -rust/target/debug/.fingerprint/memchr-ac59469faa88ab94/invoked.timestamp -rust/target/debug/.fingerprint/memchr-ac59469faa88ab94/lib-memchr -rust/target/debug/.fingerprint/memchr-ac59469faa88ab94/lib-memchr.json -rust/target/debug/.fingerprint/miniz_oxide-4aec6e156a999e9d/dep-lib-miniz_oxide -rust/target/debug/.fingerprint/miniz_oxide-4aec6e156a999e9d/invoked.timestamp -rust/target/debug/.fingerprint/miniz_oxide-4aec6e156a999e9d/lib-miniz_oxide -rust/target/debug/.fingerprint/miniz_oxide-4aec6e156a999e9d/lib-miniz_oxide.json -rust/target/debug/.fingerprint/num_cpus-764d2bb1d5c22ded/dep-lib-num_cpus -rust/target/debug/.fingerprint/num_cpus-764d2bb1d5c22ded/invoked.timestamp -rust/target/debug/.fingerprint/num_cpus-764d2bb1d5c22ded/lib-num_cpus -rust/target/debug/.fingerprint/num_cpus-764d2bb1d5c22ded/lib-num_cpus.json -rust/target/debug/.fingerprint/object-425e98224bda49c0/run-build-script-build-script-build -rust/target/debug/.fingerprint/object-425e98224bda49c0/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/object-9567b3b1d8fab212/dep-lib-object -rust/target/debug/.fingerprint/object-9567b3b1d8fab212/invoked.timestamp -rust/target/debug/.fingerprint/object-9567b3b1d8fab212/lib-object -rust/target/debug/.fingerprint/object-9567b3b1d8fab212/lib-object.json -rust/target/debug/.fingerprint/object-c3b9b7e9a4d32d23/build-script-build-script-build -rust/target/debug/.fingerprint/object-c3b9b7e9a4d32d23/build-script-build-script-build.json -rust/target/debug/.fingerprint/object-c3b9b7e9a4d32d23/dep-build-script-build-script-build -rust/target/debug/.fingerprint/object-c3b9b7e9a4d32d23/invoked.timestamp -rust/target/debug/.fingerprint/opaque-debug-d8fc60719854d562/dep-lib-opaque_debug -rust/target/debug/.fingerprint/opaque-debug-d8fc60719854d562/invoked.timestamp -rust/target/debug/.fingerprint/opaque-debug-d8fc60719854d562/lib-opaque_debug -rust/target/debug/.fingerprint/opaque-debug-d8fc60719854d562/lib-opaque_debug.json -rust/target/debug/.fingerprint/oslog-45a05e2223259518/build-script-build-script-build -rust/target/debug/.fingerprint/oslog-45a05e2223259518/build-script-build-script-build.json -rust/target/debug/.fingerprint/oslog-45a05e2223259518/dep-build-script-build-script-build -rust/target/debug/.fingerprint/oslog-45a05e2223259518/invoked.timestamp -rust/target/debug/.fingerprint/oslog-47697686a92290c4/run-build-script-build-script-build -rust/target/debug/.fingerprint/oslog-47697686a92290c4/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/oslog-86c0dae3b5ebd7d1/dep-lib-oslog -rust/target/debug/.fingerprint/oslog-86c0dae3b5ebd7d1/invoked.timestamp -rust/target/debug/.fingerprint/oslog-86c0dae3b5ebd7d1/lib-oslog -rust/target/debug/.fingerprint/oslog-86c0dae3b5ebd7d1/lib-oslog.json -rust/target/debug/.fingerprint/pin-project-lite-c0e0b96b944b72ce/dep-lib-pin_project_lite -rust/target/debug/.fingerprint/pin-project-lite-c0e0b96b944b72ce/invoked.timestamp -rust/target/debug/.fingerprint/pin-project-lite-c0e0b96b944b72ce/lib-pin_project_lite -rust/target/debug/.fingerprint/pin-project-lite-c0e0b96b944b72ce/lib-pin_project_lite.json -rust/target/debug/.fingerprint/pin-utils-347d9b0ada1581aa/dep-lib-pin_utils -rust/target/debug/.fingerprint/pin-utils-347d9b0ada1581aa/invoked.timestamp -rust/target/debug/.fingerprint/pin-utils-347d9b0ada1581aa/lib-pin_utils -rust/target/debug/.fingerprint/pin-utils-347d9b0ada1581aa/lib-pin_utils.json -rust/target/debug/.fingerprint/poly1305-4ca42376aa6be9d0/dep-lib-poly1305 -rust/target/debug/.fingerprint/poly1305-4ca42376aa6be9d0/invoked.timestamp -rust/target/debug/.fingerprint/poly1305-4ca42376aa6be9d0/lib-poly1305 -rust/target/debug/.fingerprint/poly1305-4ca42376aa6be9d0/lib-poly1305.json -rust/target/debug/.fingerprint/portable-atomic-45d6297cd3e0d750/dep-lib-portable_atomic -rust/target/debug/.fingerprint/portable-atomic-45d6297cd3e0d750/invoked.timestamp -rust/target/debug/.fingerprint/portable-atomic-45d6297cd3e0d750/lib-portable_atomic -rust/target/debug/.fingerprint/portable-atomic-45d6297cd3e0d750/lib-portable_atomic.json -rust/target/debug/.fingerprint/portable-atomic-529f8f45893147a2/build-script-build-script-build -rust/target/debug/.fingerprint/portable-atomic-529f8f45893147a2/build-script-build-script-build.json -rust/target/debug/.fingerprint/portable-atomic-529f8f45893147a2/dep-build-script-build-script-build -rust/target/debug/.fingerprint/portable-atomic-529f8f45893147a2/invoked.timestamp -rust/target/debug/.fingerprint/portable-atomic-7ddd09424df430c4/run-build-script-build-script-build -rust/target/debug/.fingerprint/portable-atomic-7ddd09424df430c4/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/ppv-lite86-f398655b4e837915/dep-lib-ppv_lite86 -rust/target/debug/.fingerprint/ppv-lite86-f398655b4e837915/invoked.timestamp -rust/target/debug/.fingerprint/ppv-lite86-f398655b4e837915/lib-ppv_lite86 -rust/target/debug/.fingerprint/ppv-lite86-f398655b4e837915/lib-ppv_lite86.json -rust/target/debug/.fingerprint/proc-macro-crate-f0082b20926418a8/dep-lib-proc_macro_crate -rust/target/debug/.fingerprint/proc-macro-crate-f0082b20926418a8/invoked.timestamp -rust/target/debug/.fingerprint/proc-macro-crate-f0082b20926418a8/lib-proc_macro_crate -rust/target/debug/.fingerprint/proc-macro-crate-f0082b20926418a8/lib-proc_macro_crate.json -rust/target/debug/.fingerprint/proc-macro-error-attr2-8b70c376ce3257e0/dep-lib-proc_macro_error_attr2 -rust/target/debug/.fingerprint/proc-macro-error-attr2-8b70c376ce3257e0/invoked.timestamp -rust/target/debug/.fingerprint/proc-macro-error-attr2-8b70c376ce3257e0/lib-proc_macro_error_attr2 -rust/target/debug/.fingerprint/proc-macro-error-attr2-8b70c376ce3257e0/lib-proc_macro_error_attr2.json -rust/target/debug/.fingerprint/proc-macro-error2-c695b5f469d78da0/dep-lib-proc_macro_error2 -rust/target/debug/.fingerprint/proc-macro-error2-c695b5f469d78da0/invoked.timestamp -rust/target/debug/.fingerprint/proc-macro-error2-c695b5f469d78da0/lib-proc_macro_error2 -rust/target/debug/.fingerprint/proc-macro-error2-c695b5f469d78da0/lib-proc_macro_error2.json -rust/target/debug/.fingerprint/proc-macro2-2cfdcb084b2c6623/build-script-build-script-build -rust/target/debug/.fingerprint/proc-macro2-2cfdcb084b2c6623/build-script-build-script-build.json -rust/target/debug/.fingerprint/proc-macro2-2cfdcb084b2c6623/dep-build-script-build-script-build -rust/target/debug/.fingerprint/proc-macro2-2cfdcb084b2c6623/invoked.timestamp -rust/target/debug/.fingerprint/proc-macro2-77566036764aba29/dep-lib-proc_macro2 -rust/target/debug/.fingerprint/proc-macro2-77566036764aba29/invoked.timestamp -rust/target/debug/.fingerprint/proc-macro2-77566036764aba29/lib-proc_macro2 -rust/target/debug/.fingerprint/proc-macro2-77566036764aba29/lib-proc_macro2.json -rust/target/debug/.fingerprint/proc-macro2-ebcc45b77f9f7e18/run-build-script-build-script-build -rust/target/debug/.fingerprint/proc-macro2-ebcc45b77f9f7e18/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/prost-6ac03ffdf0b4a2a8/dep-lib-prost -rust/target/debug/.fingerprint/prost-6ac03ffdf0b4a2a8/invoked.timestamp -rust/target/debug/.fingerprint/prost-6ac03ffdf0b4a2a8/lib-prost -rust/target/debug/.fingerprint/prost-6ac03ffdf0b4a2a8/lib-prost.json -rust/target/debug/.fingerprint/prost-derive-580231d6cc756203/dep-lib-prost_derive -rust/target/debug/.fingerprint/prost-derive-580231d6cc756203/invoked.timestamp -rust/target/debug/.fingerprint/prost-derive-580231d6cc756203/lib-prost_derive -rust/target/debug/.fingerprint/prost-derive-580231d6cc756203/lib-prost_derive.json -rust/target/debug/.fingerprint/quote-27765a9a6986ddb6/dep-lib-quote -rust/target/debug/.fingerprint/quote-27765a9a6986ddb6/invoked.timestamp -rust/target/debug/.fingerprint/quote-27765a9a6986ddb6/lib-quote -rust/target/debug/.fingerprint/quote-27765a9a6986ddb6/lib-quote.json -rust/target/debug/.fingerprint/rand-887c5902909e25ac/dep-lib-rand -rust/target/debug/.fingerprint/rand-887c5902909e25ac/invoked.timestamp -rust/target/debug/.fingerprint/rand-887c5902909e25ac/lib-rand -rust/target/debug/.fingerprint/rand-887c5902909e25ac/lib-rand.json -rust/target/debug/.fingerprint/rand_chacha-a41c11d53130c668/dep-lib-rand_chacha -rust/target/debug/.fingerprint/rand_chacha-a41c11d53130c668/invoked.timestamp -rust/target/debug/.fingerprint/rand_chacha-a41c11d53130c668/lib-rand_chacha -rust/target/debug/.fingerprint/rand_chacha-a41c11d53130c668/lib-rand_chacha.json -rust/target/debug/.fingerprint/rand_core-15945517f78be27d/dep-lib-rand_core -rust/target/debug/.fingerprint/rand_core-15945517f78be27d/invoked.timestamp -rust/target/debug/.fingerprint/rand_core-15945517f78be27d/lib-rand_core -rust/target/debug/.fingerprint/rand_core-15945517f78be27d/lib-rand_core.json -rust/target/debug/.fingerprint/rustc-demangle-1c06506ce10d9c74/dep-lib-rustc_demangle -rust/target/debug/.fingerprint/rustc-demangle-1c06506ce10d9c74/invoked.timestamp -rust/target/debug/.fingerprint/rustc-demangle-1c06506ce10d9c74/lib-rustc_demangle -rust/target/debug/.fingerprint/rustc-demangle-1c06506ce10d9c74/lib-rustc_demangle.json -rust/target/debug/.fingerprint/rustc_version-4bffee4549b1b915/dep-lib-rustc_version -rust/target/debug/.fingerprint/rustc_version-4bffee4549b1b915/invoked.timestamp -rust/target/debug/.fingerprint/rustc_version-4bffee4549b1b915/lib-rustc_version -rust/target/debug/.fingerprint/rustc_version-4bffee4549b1b915/lib-rustc_version.json -rust/target/debug/.fingerprint/ryu-bdc0719e1d5e75ad/dep-lib-ryu -rust/target/debug/.fingerprint/ryu-bdc0719e1d5e75ad/invoked.timestamp -rust/target/debug/.fingerprint/ryu-bdc0719e1d5e75ad/lib-ryu -rust/target/debug/.fingerprint/ryu-bdc0719e1d5e75ad/lib-ryu.json -rust/target/debug/.fingerprint/semver-240dade0dcd94914/run-build-script-build-script-build -rust/target/debug/.fingerprint/semver-240dade0dcd94914/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/semver-2917eb24389ccba9/build-script-build-script-build -rust/target/debug/.fingerprint/semver-2917eb24389ccba9/build-script-build-script-build.json -rust/target/debug/.fingerprint/semver-2917eb24389ccba9/dep-build-script-build-script-build -rust/target/debug/.fingerprint/semver-2917eb24389ccba9/invoked.timestamp -rust/target/debug/.fingerprint/semver-99393d098b3cea2a/dep-lib-semver -rust/target/debug/.fingerprint/semver-99393d098b3cea2a/invoked.timestamp -rust/target/debug/.fingerprint/semver-99393d098b3cea2a/lib-semver -rust/target/debug/.fingerprint/semver-99393d098b3cea2a/lib-semver.json -rust/target/debug/.fingerprint/serde-1e4a6467c246e708/run-build-script-build-script-build -rust/target/debug/.fingerprint/serde-1e4a6467c246e708/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/serde-3018e8407ad9eebe/dep-lib-serde -rust/target/debug/.fingerprint/serde-3018e8407ad9eebe/invoked.timestamp -rust/target/debug/.fingerprint/serde-3018e8407ad9eebe/lib-serde -rust/target/debug/.fingerprint/serde-3018e8407ad9eebe/lib-serde.json -rust/target/debug/.fingerprint/serde-85dd2d6a0500bde8/run-build-script-build-script-build -rust/target/debug/.fingerprint/serde-85dd2d6a0500bde8/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/serde-915479c99b24c77f/dep-lib-serde -rust/target/debug/.fingerprint/serde-915479c99b24c77f/invoked.timestamp -rust/target/debug/.fingerprint/serde-915479c99b24c77f/lib-serde -rust/target/debug/.fingerprint/serde-915479c99b24c77f/lib-serde.json -rust/target/debug/.fingerprint/serde-a934714ab46b4525/build-script-build-script-build -rust/target/debug/.fingerprint/serde-a934714ab46b4525/build-script-build-script-build.json -rust/target/debug/.fingerprint/serde-a934714ab46b4525/dep-build-script-build-script-build -rust/target/debug/.fingerprint/serde-a934714ab46b4525/invoked.timestamp -rust/target/debug/.fingerprint/serde-c4e6a553dc242a4a/build-script-build-script-build -rust/target/debug/.fingerprint/serde-c4e6a553dc242a4a/build-script-build-script-build.json -rust/target/debug/.fingerprint/serde-c4e6a553dc242a4a/dep-build-script-build-script-build -rust/target/debug/.fingerprint/serde-c4e6a553dc242a4a/invoked.timestamp -rust/target/debug/.fingerprint/serde_bytes-69174b286a59c27b/dep-lib-serde_bytes -rust/target/debug/.fingerprint/serde_bytes-69174b286a59c27b/invoked.timestamp -rust/target/debug/.fingerprint/serde_bytes-69174b286a59c27b/lib-serde_bytes -rust/target/debug/.fingerprint/serde_bytes-69174b286a59c27b/lib-serde_bytes.json -rust/target/debug/.fingerprint/serde_derive-4a581f1223b1317c/dep-lib-serde_derive -rust/target/debug/.fingerprint/serde_derive-4a581f1223b1317c/invoked.timestamp -rust/target/debug/.fingerprint/serde_derive-4a581f1223b1317c/lib-serde_derive -rust/target/debug/.fingerprint/serde_derive-4a581f1223b1317c/lib-serde_derive.json -rust/target/debug/.fingerprint/serde_json-3caf7847cd8276f6/run-build-script-build-script-build -rust/target/debug/.fingerprint/serde_json-3caf7847cd8276f6/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/serde_json-9ae25996604b0bfa/build-script-build-script-build -rust/target/debug/.fingerprint/serde_json-9ae25996604b0bfa/build-script-build-script-build.json -rust/target/debug/.fingerprint/serde_json-9ae25996604b0bfa/dep-build-script-build-script-build -rust/target/debug/.fingerprint/serde_json-9ae25996604b0bfa/invoked.timestamp -rust/target/debug/.fingerprint/serde_json-a106cf2483bbb775/dep-lib-serde_json -rust/target/debug/.fingerprint/serde_json-a106cf2483bbb775/invoked.timestamp -rust/target/debug/.fingerprint/serde_json-a106cf2483bbb775/lib-serde_json -rust/target/debug/.fingerprint/serde_json-a106cf2483bbb775/lib-serde_json.json -rust/target/debug/.fingerprint/serde_spanned-53fdf257259eec10/dep-lib-serde_spanned -rust/target/debug/.fingerprint/serde_spanned-53fdf257259eec10/invoked.timestamp -rust/target/debug/.fingerprint/serde_spanned-53fdf257259eec10/lib-serde_spanned -rust/target/debug/.fingerprint/serde_spanned-53fdf257259eec10/lib-serde_spanned.json -rust/target/debug/.fingerprint/sha2-8cebf057a5340c20/dep-lib-sha2 -rust/target/debug/.fingerprint/sha2-8cebf057a5340c20/invoked.timestamp -rust/target/debug/.fingerprint/sha2-8cebf057a5340c20/lib-sha2 -rust/target/debug/.fingerprint/sha2-8cebf057a5340c20/lib-sha2.json -rust/target/debug/.fingerprint/shlex-0dc44f95a1c7286e/dep-lib-shlex -rust/target/debug/.fingerprint/shlex-0dc44f95a1c7286e/invoked.timestamp -rust/target/debug/.fingerprint/shlex-0dc44f95a1c7286e/lib-shlex -rust/target/debug/.fingerprint/shlex-0dc44f95a1c7286e/lib-shlex.json -rust/target/debug/.fingerprint/signature-a736132ee8c4699d/dep-lib-signature -rust/target/debug/.fingerprint/signature-a736132ee8c4699d/invoked.timestamp -rust/target/debug/.fingerprint/signature-a736132ee8c4699d/lib-signature -rust/target/debug/.fingerprint/signature-a736132ee8c4699d/lib-signature.json -rust/target/debug/.fingerprint/slab-95a2f9771d99ca6a/dep-lib-slab -rust/target/debug/.fingerprint/slab-95a2f9771d99ca6a/invoked.timestamp -rust/target/debug/.fingerprint/slab-95a2f9771d99ca6a/lib-slab -rust/target/debug/.fingerprint/slab-95a2f9771d99ca6a/lib-slab.json -rust/target/debug/.fingerprint/slab-ea836004af215ca7/run-build-script-build-script-build -rust/target/debug/.fingerprint/slab-ea836004af215ca7/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/slab-fbb07541f5e3404a/build-script-build-script-build -rust/target/debug/.fingerprint/slab-fbb07541f5e3404a/build-script-build-script-build.json -rust/target/debug/.fingerprint/slab-fbb07541f5e3404a/dep-build-script-build-script-build -rust/target/debug/.fingerprint/slab-fbb07541f5e3404a/invoked.timestamp -rust/target/debug/.fingerprint/subtle-51f54fe4c58216fe/dep-lib-subtle -rust/target/debug/.fingerprint/subtle-51f54fe4c58216fe/invoked.timestamp -rust/target/debug/.fingerprint/subtle-51f54fe4c58216fe/lib-subtle -rust/target/debug/.fingerprint/subtle-51f54fe4c58216fe/lib-subtle.json -rust/target/debug/.fingerprint/syn-3c3bdea880cdd63f/dep-lib-syn -rust/target/debug/.fingerprint/syn-3c3bdea880cdd63f/invoked.timestamp -rust/target/debug/.fingerprint/syn-3c3bdea880cdd63f/lib-syn -rust/target/debug/.fingerprint/syn-3c3bdea880cdd63f/lib-syn.json -rust/target/debug/.fingerprint/thiserror-1841112df2392c68/run-build-script-build-script-build -rust/target/debug/.fingerprint/thiserror-1841112df2392c68/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/thiserror-1b1520b5f9e2ff21/build-script-build-script-build -rust/target/debug/.fingerprint/thiserror-1b1520b5f9e2ff21/build-script-build-script-build.json -rust/target/debug/.fingerprint/thiserror-1b1520b5f9e2ff21/dep-build-script-build-script-build -rust/target/debug/.fingerprint/thiserror-1b1520b5f9e2ff21/invoked.timestamp -rust/target/debug/.fingerprint/thiserror-542e73ef08f263e8/run-build-script-build-script-build -rust/target/debug/.fingerprint/thiserror-542e73ef08f263e8/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/thiserror-7ac054077a416f3a/dep-lib-thiserror -rust/target/debug/.fingerprint/thiserror-7ac054077a416f3a/invoked.timestamp -rust/target/debug/.fingerprint/thiserror-7ac054077a416f3a/lib-thiserror -rust/target/debug/.fingerprint/thiserror-7ac054077a416f3a/lib-thiserror.json -rust/target/debug/.fingerprint/thiserror-c5cb8d50071a87cb/dep-lib-thiserror -rust/target/debug/.fingerprint/thiserror-c5cb8d50071a87cb/invoked.timestamp -rust/target/debug/.fingerprint/thiserror-c5cb8d50071a87cb/lib-thiserror -rust/target/debug/.fingerprint/thiserror-c5cb8d50071a87cb/lib-thiserror.json -rust/target/debug/.fingerprint/thiserror-e7785d604e683f7f/build-script-build-script-build -rust/target/debug/.fingerprint/thiserror-e7785d604e683f7f/build-script-build-script-build.json -rust/target/debug/.fingerprint/thiserror-e7785d604e683f7f/dep-build-script-build-script-build -rust/target/debug/.fingerprint/thiserror-e7785d604e683f7f/invoked.timestamp -rust/target/debug/.fingerprint/thiserror-impl-0e763aa36af8214b/dep-lib-thiserror_impl -rust/target/debug/.fingerprint/thiserror-impl-0e763aa36af8214b/invoked.timestamp -rust/target/debug/.fingerprint/thiserror-impl-0e763aa36af8214b/lib-thiserror_impl -rust/target/debug/.fingerprint/thiserror-impl-0e763aa36af8214b/lib-thiserror_impl.json -rust/target/debug/.fingerprint/thiserror-impl-4a1a4380dab4b75e/dep-lib-thiserror_impl -rust/target/debug/.fingerprint/thiserror-impl-4a1a4380dab4b75e/invoked.timestamp -rust/target/debug/.fingerprint/thiserror-impl-4a1a4380dab4b75e/lib-thiserror_impl -rust/target/debug/.fingerprint/thiserror-impl-4a1a4380dab4b75e/lib-thiserror_impl.json -rust/target/debug/.fingerprint/threadpool-44e4babe3d09de5e/dep-lib-threadpool -rust/target/debug/.fingerprint/threadpool-44e4babe3d09de5e/invoked.timestamp -rust/target/debug/.fingerprint/threadpool-44e4babe3d09de5e/lib-threadpool -rust/target/debug/.fingerprint/threadpool-44e4babe3d09de5e/lib-threadpool.json -rust/target/debug/.fingerprint/tokio-e4ecd9ce6b3ad6f7/dep-lib-tokio -rust/target/debug/.fingerprint/tokio-e4ecd9ce6b3ad6f7/invoked.timestamp -rust/target/debug/.fingerprint/tokio-e4ecd9ce6b3ad6f7/lib-tokio -rust/target/debug/.fingerprint/tokio-e4ecd9ce6b3ad6f7/lib-tokio.json -rust/target/debug/.fingerprint/toml_datetime-a84c1bc8e0d858d0/dep-lib-toml_datetime -rust/target/debug/.fingerprint/toml_datetime-a84c1bc8e0d858d0/invoked.timestamp -rust/target/debug/.fingerprint/toml_datetime-a84c1bc8e0d858d0/lib-toml_datetime -rust/target/debug/.fingerprint/toml_datetime-a84c1bc8e0d858d0/lib-toml_datetime.json -rust/target/debug/.fingerprint/toml_edit-986f0899a4fb4937/dep-lib-toml_edit -rust/target/debug/.fingerprint/toml_edit-986f0899a4fb4937/invoked.timestamp -rust/target/debug/.fingerprint/toml_edit-986f0899a4fb4937/lib-toml_edit -rust/target/debug/.fingerprint/toml_edit-986f0899a4fb4937/lib-toml_edit.json -rust/target/debug/.fingerprint/typenum-8aa4ed9de6d3c41f/build-script-build-script-build -rust/target/debug/.fingerprint/typenum-8aa4ed9de6d3c41f/build-script-build-script-build.json -rust/target/debug/.fingerprint/typenum-8aa4ed9de6d3c41f/dep-build-script-build-script-build -rust/target/debug/.fingerprint/typenum-8aa4ed9de6d3c41f/invoked.timestamp -rust/target/debug/.fingerprint/typenum-b6ace1f022cb5bd9/run-build-script-build-script-build -rust/target/debug/.fingerprint/typenum-b6ace1f022cb5bd9/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/typenum-c585b7608e83e6b8/dep-lib-typenum -rust/target/debug/.fingerprint/typenum-c585b7608e83e6b8/invoked.timestamp -rust/target/debug/.fingerprint/typenum-c585b7608e83e6b8/lib-typenum -rust/target/debug/.fingerprint/typenum-c585b7608e83e6b8/lib-typenum.json -rust/target/debug/.fingerprint/unicode-ident-c4cd5a2669b29311/dep-lib-unicode_ident -rust/target/debug/.fingerprint/unicode-ident-c4cd5a2669b29311/invoked.timestamp -rust/target/debug/.fingerprint/unicode-ident-c4cd5a2669b29311/lib-unicode_ident -rust/target/debug/.fingerprint/unicode-ident-c4cd5a2669b29311/lib-unicode_ident.json -rust/target/debug/.fingerprint/universal-hash-0d2b04219ee9e6dc/dep-lib-universal_hash -rust/target/debug/.fingerprint/universal-hash-0d2b04219ee9e6dc/invoked.timestamp -rust/target/debug/.fingerprint/universal-hash-0d2b04219ee9e6dc/lib-universal_hash -rust/target/debug/.fingerprint/universal-hash-0d2b04219ee9e6dc/lib-universal_hash.json -rust/target/debug/.fingerprint/version_check-8c067b92ae3aff85/dep-lib-version_check -rust/target/debug/.fingerprint/version_check-8c067b92ae3aff85/invoked.timestamp -rust/target/debug/.fingerprint/version_check-8c067b92ae3aff85/lib-version_check -rust/target/debug/.fingerprint/version_check-8c067b92ae3aff85/lib-version_check.json -rust/target/debug/.fingerprint/vodozemac-5deaa4867e5f7789/dep-lib-vodozemac -rust/target/debug/.fingerprint/vodozemac-5deaa4867e5f7789/invoked.timestamp -rust/target/debug/.fingerprint/vodozemac-5deaa4867e5f7789/lib-vodozemac -rust/target/debug/.fingerprint/vodozemac-5deaa4867e5f7789/lib-vodozemac.json -rust/target/debug/.fingerprint/vodozemac-bindings-dart-2b7397f4e7e2bb1e/dep-lib-vodozemac_bindings_dart -rust/target/debug/.fingerprint/vodozemac-bindings-dart-2b7397f4e7e2bb1e/invoked.timestamp -rust/target/debug/.fingerprint/vodozemac-bindings-dart-2b7397f4e7e2bb1e/lib-vodozemac_bindings_dart -rust/target/debug/.fingerprint/vodozemac-bindings-dart-2b7397f4e7e2bb1e/lib-vodozemac_bindings_dart.json -rust/target/debug/.fingerprint/vodozemac-bindings-dart-2b7397f4e7e2bb1e/output-lib-vodozemac_bindings_dart -rust/target/debug/.fingerprint/winnow-ba9446fd5a967863/dep-lib-winnow -rust/target/debug/.fingerprint/winnow-ba9446fd5a967863/invoked.timestamp -rust/target/debug/.fingerprint/winnow-ba9446fd5a967863/lib-winnow -rust/target/debug/.fingerprint/winnow-ba9446fd5a967863/lib-winnow.json -rust/target/debug/.fingerprint/x25519-dalek-6d81351bf5812b82/dep-lib-x25519_dalek -rust/target/debug/.fingerprint/x25519-dalek-6d81351bf5812b82/invoked.timestamp -rust/target/debug/.fingerprint/x25519-dalek-6d81351bf5812b82/lib-x25519_dalek -rust/target/debug/.fingerprint/x25519-dalek-6d81351bf5812b82/lib-x25519_dalek.json -rust/target/debug/.fingerprint/zerocopy-0b442b8bf50f5845/dep-lib-zerocopy -rust/target/debug/.fingerprint/zerocopy-0b442b8bf50f5845/invoked.timestamp -rust/target/debug/.fingerprint/zerocopy-0b442b8bf50f5845/lib-zerocopy -rust/target/debug/.fingerprint/zerocopy-0b442b8bf50f5845/lib-zerocopy.json -rust/target/debug/.fingerprint/zerocopy-eb4af7765fc4cc7b/build-script-build-script-build -rust/target/debug/.fingerprint/zerocopy-eb4af7765fc4cc7b/build-script-build-script-build.json -rust/target/debug/.fingerprint/zerocopy-eb4af7765fc4cc7b/dep-build-script-build-script-build -rust/target/debug/.fingerprint/zerocopy-eb4af7765fc4cc7b/invoked.timestamp -rust/target/debug/.fingerprint/zerocopy-f19128e3aeb73ee4/run-build-script-build-script-build -rust/target/debug/.fingerprint/zerocopy-f19128e3aeb73ee4/run-build-script-build-script-build.json -rust/target/debug/.fingerprint/zeroize-aa8ff4b0509d5a40/dep-lib-zeroize -rust/target/debug/.fingerprint/zeroize-aa8ff4b0509d5a40/invoked.timestamp -rust/target/debug/.fingerprint/zeroize-aa8ff4b0509d5a40/lib-zeroize -rust/target/debug/.fingerprint/zeroize-aa8ff4b0509d5a40/lib-zeroize.json -rust/target/debug/.fingerprint/zeroize_derive-dc24d67d8749c978/dep-lib-zeroize_derive -rust/target/debug/.fingerprint/zeroize_derive-dc24d67d8749c978/invoked.timestamp -rust/target/debug/.fingerprint/zeroize_derive-dc24d67d8749c978/lib-zeroize_derive -rust/target/debug/.fingerprint/zeroize_derive-dc24d67d8749c978/lib-zeroize_derive.json -rust/target/debug/build/anyhow-18484b5b80b5ad09/invoked.timestamp -rust/target/debug/build/anyhow-18484b5b80b5ad09/output -rust/target/debug/build/anyhow-18484b5b80b5ad09/root-output -rust/target/debug/build/anyhow-18484b5b80b5ad09/stderr -rust/target/debug/build/anyhow-77f46aa22ee05fc9/invoked.timestamp -rust/target/debug/build/anyhow-77f46aa22ee05fc9/output -rust/target/debug/build/anyhow-77f46aa22ee05fc9/root-output -rust/target/debug/build/anyhow-77f46aa22ee05fc9/stderr -rust/target/debug/build/anyhow-be8aa79faecb673b/build_script_build-be8aa79faecb673b -rust/target/debug/build/anyhow-be8aa79faecb673b/build_script_build-be8aa79faecb673b.d -rust/target/debug/build/anyhow-be8aa79faecb673b/build-script-build -rust/target/debug/build/anyhow-da043d455b1bfdee/build_script_build-da043d455b1bfdee -rust/target/debug/build/anyhow-da043d455b1bfdee/build_script_build-da043d455b1bfdee.d -rust/target/debug/build/anyhow-da043d455b1bfdee/build-script-build -rust/target/debug/build/curve25519-dalek-5d1124799e65242c/invoked.timestamp -rust/target/debug/build/curve25519-dalek-5d1124799e65242c/output -rust/target/debug/build/curve25519-dalek-5d1124799e65242c/root-output -rust/target/debug/build/curve25519-dalek-5d1124799e65242c/stderr -rust/target/debug/build/curve25519-dalek-e455512f25905d9b/build_script_build-e455512f25905d9b -rust/target/debug/build/curve25519-dalek-e455512f25905d9b/build_script_build-e455512f25905d9b.d -rust/target/debug/build/curve25519-dalek-e455512f25905d9b/build-script-build -rust/target/debug/build/dart-sys-6d7ea2832798c569/build_script_build-6d7ea2832798c569 -rust/target/debug/build/dart-sys-6d7ea2832798c569/build_script_build-6d7ea2832798c569.d -rust/target/debug/build/dart-sys-6d7ea2832798c569/build-script-build -rust/target/debug/build/dart-sys-fc5525cd8b578726/invoked.timestamp -rust/target/debug/build/dart-sys-fc5525cd8b578726/output -rust/target/debug/build/dart-sys-fc5525cd8b578726/root-output -rust/target/debug/build/dart-sys-fc5525cd8b578726/stderr -rust/target/debug/build/dart-sys-fc5525cd8b578726/out/248f927bf32daba4-dart_api_dl.o -rust/target/debug/build/dart-sys-fc5525cd8b578726/out/libdart_api_dl.a -rust/target/debug/build/flutter_rust_bridge-4a25ddda9ee0605f/build_script_build-4a25ddda9ee0605f -rust/target/debug/build/flutter_rust_bridge-4a25ddda9ee0605f/build_script_build-4a25ddda9ee0605f.d -rust/target/debug/build/flutter_rust_bridge-4a25ddda9ee0605f/build-script-build -rust/target/debug/build/flutter_rust_bridge-e3a9f2c084a60e41/invoked.timestamp -rust/target/debug/build/flutter_rust_bridge-e3a9f2c084a60e41/output -rust/target/debug/build/flutter_rust_bridge-e3a9f2c084a60e41/root-output -rust/target/debug/build/flutter_rust_bridge-e3a9f2c084a60e41/stderr -rust/target/debug/build/generic-array-810ff7b5bbf2bd2e/build_script_build-810ff7b5bbf2bd2e -rust/target/debug/build/generic-array-810ff7b5bbf2bd2e/build_script_build-810ff7b5bbf2bd2e.d -rust/target/debug/build/generic-array-810ff7b5bbf2bd2e/build-script-build -rust/target/debug/build/generic-array-e7cb25425c0c5f54/invoked.timestamp -rust/target/debug/build/generic-array-e7cb25425c0c5f54/output -rust/target/debug/build/generic-array-e7cb25425c0c5f54/root-output -rust/target/debug/build/generic-array-e7cb25425c0c5f54/stderr -rust/target/debug/build/libc-e331c44b26e5d7a8/invoked.timestamp -rust/target/debug/build/libc-e331c44b26e5d7a8/output -rust/target/debug/build/libc-e331c44b26e5d7a8/root-output -rust/target/debug/build/libc-e331c44b26e5d7a8/stderr -rust/target/debug/build/libc-f7a8c95f93d5a1e7/build_script_build-f7a8c95f93d5a1e7 -rust/target/debug/build/libc-f7a8c95f93d5a1e7/build_script_build-f7a8c95f93d5a1e7.d -rust/target/debug/build/libc-f7a8c95f93d5a1e7/build-script-build -rust/target/debug/build/object-425e98224bda49c0/invoked.timestamp -rust/target/debug/build/object-425e98224bda49c0/output -rust/target/debug/build/object-425e98224bda49c0/root-output -rust/target/debug/build/object-425e98224bda49c0/stderr -rust/target/debug/build/object-c3b9b7e9a4d32d23/build_script_build-c3b9b7e9a4d32d23 -rust/target/debug/build/object-c3b9b7e9a4d32d23/build_script_build-c3b9b7e9a4d32d23.d -rust/target/debug/build/object-c3b9b7e9a4d32d23/build-script-build -rust/target/debug/build/oslog-45a05e2223259518/build_script_build-45a05e2223259518 -rust/target/debug/build/oslog-45a05e2223259518/build_script_build-45a05e2223259518.d -rust/target/debug/build/oslog-45a05e2223259518/build-script-build -rust/target/debug/build/oslog-47697686a92290c4/invoked.timestamp -rust/target/debug/build/oslog-47697686a92290c4/output -rust/target/debug/build/oslog-47697686a92290c4/root-output -rust/target/debug/build/oslog-47697686a92290c4/stderr -rust/target/debug/build/oslog-47697686a92290c4/out/db3b6bfb95261072-wrapper.o -rust/target/debug/build/oslog-47697686a92290c4/out/libwrapper.a -rust/target/debug/build/portable-atomic-529f8f45893147a2/build_script_build-529f8f45893147a2 -rust/target/debug/build/portable-atomic-529f8f45893147a2/build_script_build-529f8f45893147a2.d -rust/target/debug/build/portable-atomic-529f8f45893147a2/build-script-build -rust/target/debug/build/portable-atomic-7ddd09424df430c4/invoked.timestamp -rust/target/debug/build/portable-atomic-7ddd09424df430c4/output -rust/target/debug/build/portable-atomic-7ddd09424df430c4/root-output -rust/target/debug/build/portable-atomic-7ddd09424df430c4/stderr -rust/target/debug/build/proc-macro2-2cfdcb084b2c6623/build_script_build-2cfdcb084b2c6623 -rust/target/debug/build/proc-macro2-2cfdcb084b2c6623/build_script_build-2cfdcb084b2c6623.d -rust/target/debug/build/proc-macro2-2cfdcb084b2c6623/build-script-build -rust/target/debug/build/proc-macro2-ebcc45b77f9f7e18/invoked.timestamp -rust/target/debug/build/proc-macro2-ebcc45b77f9f7e18/output -rust/target/debug/build/proc-macro2-ebcc45b77f9f7e18/root-output -rust/target/debug/build/proc-macro2-ebcc45b77f9f7e18/stderr -rust/target/debug/build/semver-240dade0dcd94914/invoked.timestamp -rust/target/debug/build/semver-240dade0dcd94914/output -rust/target/debug/build/semver-240dade0dcd94914/root-output -rust/target/debug/build/semver-240dade0dcd94914/stderr -rust/target/debug/build/semver-2917eb24389ccba9/build_script_build-2917eb24389ccba9 -rust/target/debug/build/semver-2917eb24389ccba9/build_script_build-2917eb24389ccba9.d -rust/target/debug/build/semver-2917eb24389ccba9/build-script-build -rust/target/debug/build/serde-1e4a6467c246e708/invoked.timestamp -rust/target/debug/build/serde-1e4a6467c246e708/output -rust/target/debug/build/serde-1e4a6467c246e708/root-output -rust/target/debug/build/serde-1e4a6467c246e708/stderr -rust/target/debug/build/serde-85dd2d6a0500bde8/invoked.timestamp -rust/target/debug/build/serde-85dd2d6a0500bde8/output -rust/target/debug/build/serde-85dd2d6a0500bde8/root-output -rust/target/debug/build/serde-85dd2d6a0500bde8/stderr -rust/target/debug/build/serde-a934714ab46b4525/build_script_build-a934714ab46b4525 -rust/target/debug/build/serde-a934714ab46b4525/build_script_build-a934714ab46b4525.d -rust/target/debug/build/serde-a934714ab46b4525/build-script-build -rust/target/debug/build/serde-c4e6a553dc242a4a/build_script_build-c4e6a553dc242a4a -rust/target/debug/build/serde-c4e6a553dc242a4a/build_script_build-c4e6a553dc242a4a.d -rust/target/debug/build/serde-c4e6a553dc242a4a/build-script-build -rust/target/debug/build/serde_json-3caf7847cd8276f6/invoked.timestamp -rust/target/debug/build/serde_json-3caf7847cd8276f6/output -rust/target/debug/build/serde_json-3caf7847cd8276f6/root-output -rust/target/debug/build/serde_json-3caf7847cd8276f6/stderr -rust/target/debug/build/serde_json-9ae25996604b0bfa/build_script_build-9ae25996604b0bfa -rust/target/debug/build/serde_json-9ae25996604b0bfa/build_script_build-9ae25996604b0bfa.d -rust/target/debug/build/serde_json-9ae25996604b0bfa/build-script-build -rust/target/debug/build/slab-ea836004af215ca7/invoked.timestamp -rust/target/debug/build/slab-ea836004af215ca7/output -rust/target/debug/build/slab-ea836004af215ca7/root-output -rust/target/debug/build/slab-ea836004af215ca7/stderr -rust/target/debug/build/slab-ea836004af215ca7/out/autocfg_9310de16d1206673_0.ll -rust/target/debug/build/slab-fbb07541f5e3404a/build_script_build-fbb07541f5e3404a -rust/target/debug/build/slab-fbb07541f5e3404a/build_script_build-fbb07541f5e3404a.d -rust/target/debug/build/slab-fbb07541f5e3404a/build-script-build -rust/target/debug/build/thiserror-1841112df2392c68/invoked.timestamp -rust/target/debug/build/thiserror-1841112df2392c68/output -rust/target/debug/build/thiserror-1841112df2392c68/root-output -rust/target/debug/build/thiserror-1841112df2392c68/stderr -rust/target/debug/build/thiserror-1b1520b5f9e2ff21/build_script_build-1b1520b5f9e2ff21 -rust/target/debug/build/thiserror-1b1520b5f9e2ff21/build_script_build-1b1520b5f9e2ff21.d -rust/target/debug/build/thiserror-1b1520b5f9e2ff21/build-script-build -rust/target/debug/build/thiserror-542e73ef08f263e8/invoked.timestamp -rust/target/debug/build/thiserror-542e73ef08f263e8/output -rust/target/debug/build/thiserror-542e73ef08f263e8/root-output -rust/target/debug/build/thiserror-542e73ef08f263e8/stderr -rust/target/debug/build/thiserror-e7785d604e683f7f/build_script_build-e7785d604e683f7f -rust/target/debug/build/thiserror-e7785d604e683f7f/build_script_build-e7785d604e683f7f.d -rust/target/debug/build/thiserror-e7785d604e683f7f/build-script-build -rust/target/debug/build/typenum-8aa4ed9de6d3c41f/build_script_build-8aa4ed9de6d3c41f -rust/target/debug/build/typenum-8aa4ed9de6d3c41f/build_script_build-8aa4ed9de6d3c41f.d -rust/target/debug/build/typenum-8aa4ed9de6d3c41f/build-script-build -rust/target/debug/build/typenum-b6ace1f022cb5bd9/invoked.timestamp -rust/target/debug/build/typenum-b6ace1f022cb5bd9/output -rust/target/debug/build/typenum-b6ace1f022cb5bd9/root-output -rust/target/debug/build/typenum-b6ace1f022cb5bd9/stderr -rust/target/debug/build/typenum-b6ace1f022cb5bd9/out/tests.rs -rust/target/debug/build/zerocopy-eb4af7765fc4cc7b/build_script_build-eb4af7765fc4cc7b -rust/target/debug/build/zerocopy-eb4af7765fc4cc7b/build_script_build-eb4af7765fc4cc7b.d -rust/target/debug/build/zerocopy-eb4af7765fc4cc7b/build-script-build -rust/target/debug/build/zerocopy-f19128e3aeb73ee4/invoked.timestamp -rust/target/debug/build/zerocopy-f19128e3aeb73ee4/output -rust/target/debug/build/zerocopy-f19128e3aeb73ee4/root-output -rust/target/debug/build/zerocopy-f19128e3aeb73ee4/stderr -rust/target/debug/deps/addr2line-164afbf588b3e8e0.addr2line.f693c615d140639e-cgu.0.rcgu.o -rust/target/debug/deps/addr2line-164afbf588b3e8e0.d -rust/target/debug/deps/adler2-b8e1686650101ebc.adler2.6c9e449293f02ba-cgu.0.rcgu.o -rust/target/debug/deps/adler2-b8e1686650101ebc.d -rust/target/debug/deps/aead-247e9024b30f32c8.aead.8cb9c352d496241c-cgu.0.rcgu.o -rust/target/debug/deps/aead-247e9024b30f32c8.d -rust/target/debug/deps/aes-06933c0f73f54f0e.aes.29893202ab873644-cgu.0.rcgu.o -rust/target/debug/deps/aes-06933c0f73f54f0e.aes.29893202ab873644-cgu.1.rcgu.o -rust/target/debug/deps/aes-06933c0f73f54f0e.d -rust/target/debug/deps/allo_isolate-340d9aeefcfdded8.allo_isolate.cb0e4fe446c9d71d-cgu.0.rcgu.o -rust/target/debug/deps/allo_isolate-340d9aeefcfdded8.allo_isolate.cb0e4fe446c9d71d-cgu.1.rcgu.o -rust/target/debug/deps/allo_isolate-340d9aeefcfdded8.allo_isolate.cb0e4fe446c9d71d-cgu.2.rcgu.o -rust/target/debug/deps/allo_isolate-340d9aeefcfdded8.allo_isolate.cb0e4fe446c9d71d-cgu.3.rcgu.o -rust/target/debug/deps/allo_isolate-340d9aeefcfdded8.allo_isolate.cb0e4fe446c9d71d-cgu.4.rcgu.o -rust/target/debug/deps/allo_isolate-340d9aeefcfdded8.d -rust/target/debug/deps/anyhow-30177ef9193b13a0.d -rust/target/debug/deps/anyhow-c40754bdc1d08d97.anyhow.1537259e27be584d-cgu.0.rcgu.o -rust/target/debug/deps/anyhow-c40754bdc1d08d97.anyhow.1537259e27be584d-cgu.1.rcgu.o -rust/target/debug/deps/anyhow-c40754bdc1d08d97.anyhow.1537259e27be584d-cgu.2.rcgu.o -rust/target/debug/deps/anyhow-c40754bdc1d08d97.anyhow.1537259e27be584d-cgu.3.rcgu.o -rust/target/debug/deps/anyhow-c40754bdc1d08d97.d -rust/target/debug/deps/arrayvec-f9646fd6f0b662a7.arrayvec.afb1f5c9694395d2-cgu.0.rcgu.o -rust/target/debug/deps/arrayvec-f9646fd6f0b662a7.d -rust/target/debug/deps/atomic-4fef8628930404bc.atomic.d5772374b744b993-cgu.0.rcgu.o -rust/target/debug/deps/atomic-4fef8628930404bc.d -rust/target/debug/deps/autocfg-0322980e80f4dda5.d -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.00.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.01.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.02.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.03.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.04.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.05.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.06.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.07.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.08.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.09.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.10.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.11.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.12.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.13.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.14.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.backtrace.f59ee1b20d457c48-cgu.15.rcgu.o -rust/target/debug/deps/backtrace-d6d745a43534420c.d -rust/target/debug/deps/base64-9782b8db13fc1943.base64.cea7b0406dccc11a-cgu.0.rcgu.o -rust/target/debug/deps/base64-9782b8db13fc1943.d -rust/target/debug/deps/base64ct-09659ecb40e5863e.base64ct.2e48924b47fbd9e0-cgu.0.rcgu.o -rust/target/debug/deps/base64ct-09659ecb40e5863e.d -rust/target/debug/deps/block_buffer-57f418079f810658.block_buffer.bf221140ba64052e-cgu.0.rcgu.o -rust/target/debug/deps/block_buffer-57f418079f810658.d -rust/target/debug/deps/block_padding-3425e63b59c3e346.block_padding.190cf448a9e0de3d-cgu.0.rcgu.o -rust/target/debug/deps/block_padding-3425e63b59c3e346.d -rust/target/debug/deps/build_target-d161175fbbd29f4e.d -rust/target/debug/deps/byteorder-6c69f0cfa357c802.byteorder.95ebb8e67200fe65-cgu.0.rcgu.o -rust/target/debug/deps/byteorder-6c69f0cfa357c802.d -rust/target/debug/deps/bytes-9f047593c94b8196.bytes.d66aff4e9e632a5-cgu.0.rcgu.o -rust/target/debug/deps/bytes-9f047593c94b8196.bytes.d66aff4e9e632a5-cgu.1.rcgu.o -rust/target/debug/deps/bytes-9f047593c94b8196.bytes.d66aff4e9e632a5-cgu.2.rcgu.o -rust/target/debug/deps/bytes-9f047593c94b8196.bytes.d66aff4e9e632a5-cgu.3.rcgu.o -rust/target/debug/deps/bytes-9f047593c94b8196.d -rust/target/debug/deps/cbc-cdd262f0eb0b6f26.cbc.ccea5f2be791c9ad-cgu.0.rcgu.o -rust/target/debug/deps/cbc-cdd262f0eb0b6f26.d -rust/target/debug/deps/cc-1ec0f1f8458cb965.d -rust/target/debug/deps/cfg_if-3ebdd2ac77e576b4.cfg_if.cebf49501f1fab7-cgu.0.rcgu.o -rust/target/debug/deps/cfg_if-3ebdd2ac77e576b4.d -rust/target/debug/deps/chacha20-956c523d15dcae2e.chacha20.7ba30152085babe0-cgu.0.rcgu.o -rust/target/debug/deps/chacha20-956c523d15dcae2e.d -rust/target/debug/deps/chacha20poly1305-b264df9570fa4bb8.chacha20poly1305.57be8b1c513578d4-cgu.0.rcgu.o -rust/target/debug/deps/chacha20poly1305-b264df9570fa4bb8.d -rust/target/debug/deps/cipher-54d420ebddb605e6.cipher.a04820504cc2f526-cgu.0.rcgu.o -rust/target/debug/deps/cipher-54d420ebddb605e6.d -rust/target/debug/deps/cpufeatures-a83958b8d3becd82.cpufeatures.300b52d81885ca1f-cgu.0.rcgu.o -rust/target/debug/deps/cpufeatures-a83958b8d3becd82.d -rust/target/debug/deps/crypto_common-af19c26aff2153a0.d -rust/target/debug/deps/crypto_common-ed3b431853e7b51b.crypto_common.a1c7df194ddf805d-cgu.0.rcgu.o -rust/target/debug/deps/crypto_common-ed3b431853e7b51b.d -rust/target/debug/deps/curve25519_dalek-99dce1f29c3bc595.curve25519_dalek.62ce3be4b4993fe8-cgu.0.rcgu.o -rust/target/debug/deps/curve25519_dalek-99dce1f29c3bc595.curve25519_dalek.62ce3be4b4993fe8-cgu.1.rcgu.o -rust/target/debug/deps/curve25519_dalek-99dce1f29c3bc595.curve25519_dalek.62ce3be4b4993fe8-cgu.2.rcgu.o -rust/target/debug/deps/curve25519_dalek-99dce1f29c3bc595.curve25519_dalek.62ce3be4b4993fe8-cgu.3.rcgu.o -rust/target/debug/deps/curve25519_dalek-99dce1f29c3bc595.curve25519_dalek.62ce3be4b4993fe8-cgu.4.rcgu.o -rust/target/debug/deps/curve25519_dalek-99dce1f29c3bc595.curve25519_dalek.62ce3be4b4993fe8-cgu.5.rcgu.o -rust/target/debug/deps/curve25519_dalek-99dce1f29c3bc595.d -rust/target/debug/deps/dart_sys-815ea59815e75bcf.d -rust/target/debug/deps/dart_sys-815ea59815e75bcf.dart_sys.cf5a384e01deca29-cgu.0.rcgu.o -rust/target/debug/deps/dashmap-f7e07885a57442dc.d -rust/target/debug/deps/dashmap-f7e07885a57442dc.dashmap.961e40810f3a3506-cgu.0.rcgu.o -rust/target/debug/deps/delegate_attr-d56d0e9b16a435b3.d -rust/target/debug/deps/digest-109a3bc51d4b8490.d -rust/target/debug/deps/digest-d43f4616a513ec25.d -rust/target/debug/deps/digest-d43f4616a513ec25.digest.33dc7591ead2bfa6-cgu.0.rcgu.o -rust/target/debug/deps/ed25519_dalek-df57d83c9aa0b0f3.d -rust/target/debug/deps/ed25519_dalek-df57d83c9aa0b0f3.ed25519_dalek.2938153959685d67-cgu.0.rcgu.o -rust/target/debug/deps/ed25519-4a15fe63621b2416.d -rust/target/debug/deps/ed25519-4a15fe63621b2416.ed25519.4269fa9adb69da66-cgu.0.rcgu.o -rust/target/debug/deps/either-960afc30a188083e.d -rust/target/debug/deps/equivalent-cb826c24d950b291.d -rust/target/debug/deps/flutter_rust_bridge_macros-ebdd655fcadb605d.d -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.d -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.0.rcgu.o -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.1.rcgu.o -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.2.rcgu.o -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.3.rcgu.o -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.4.rcgu.o -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.5.rcgu.o -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.6.rcgu.o -rust/target/debug/deps/flutter_rust_bridge-f27219e8c9bea4cf.flutter_rust_bridge.1fae711729aec00a-cgu.7.rcgu.o -rust/target/debug/deps/futures_channel-80e8e3f3aa40a3d5.d -rust/target/debug/deps/futures_channel-80e8e3f3aa40a3d5.futures_channel.156de71ba716fc2f-cgu.0.rcgu.o -rust/target/debug/deps/futures_core-2090651f2656334d.d -rust/target/debug/deps/futures_core-2090651f2656334d.futures_core.2996c0f69da7b752-cgu.0.rcgu.o -rust/target/debug/deps/futures_executor-16793de88874db73.d -rust/target/debug/deps/futures_executor-16793de88874db73.futures_executor.8c8e67153a299bbc-cgu.0.rcgu.o -rust/target/debug/deps/futures_executor-16793de88874db73.futures_executor.8c8e67153a299bbc-cgu.1.rcgu.o -rust/target/debug/deps/futures_io-63ecdd207fa87dba.d -rust/target/debug/deps/futures_io-63ecdd207fa87dba.futures_io.ba445eba42d6e3a5-cgu.0.rcgu.o -rust/target/debug/deps/futures_macro-9e7a0924881b0558.d -rust/target/debug/deps/futures_sink-59aef06b1ce7ea0b.d -rust/target/debug/deps/futures_sink-59aef06b1ce7ea0b.futures_sink.2663ab15e6e3d87d-cgu.0.rcgu.o -rust/target/debug/deps/futures_task-0833ac2045db9683.d -rust/target/debug/deps/futures_task-0833ac2045db9683.futures_task.36e247978097a129-cgu.0.rcgu.o -rust/target/debug/deps/futures_util-cf1b8ba26ed8a4b3.d -rust/target/debug/deps/futures_util-cf1b8ba26ed8a4b3.futures_util.60bf894a742b15f7-cgu.0.rcgu.o -rust/target/debug/deps/futures_util-cf1b8ba26ed8a4b3.futures_util.60bf894a742b15f7-cgu.1.rcgu.o -rust/target/debug/deps/futures_util-cf1b8ba26ed8a4b3.futures_util.60bf894a742b15f7-cgu.2.rcgu.o -rust/target/debug/deps/futures-6a3338d6ee3d47df.d -rust/target/debug/deps/futures-6a3338d6ee3d47df.futures.22fe3a6d12b6b29d-cgu.0.rcgu.o -rust/target/debug/deps/generic_array-9c72fd8f563d25bd.d -rust/target/debug/deps/generic_array-9c72fd8f563d25bd.generic_array.d68b77a4f691c277-cgu.0.rcgu.o -rust/target/debug/deps/getrandom-019ee3c40b7cf82c.d -rust/target/debug/deps/getrandom-019ee3c40b7cf82c.getrandom.23713674a1d1580f-cgu.0.rcgu.o -rust/target/debug/deps/gimli-644a05e86b54988a.d -rust/target/debug/deps/gimli-644a05e86b54988a.gimli.61a7a513c5709da1-cgu.0.rcgu.o -rust/target/debug/deps/gimli-644a05e86b54988a.gimli.61a7a513c5709da1-cgu.1.rcgu.o -rust/target/debug/deps/gimli-644a05e86b54988a.gimli.61a7a513c5709da1-cgu.2.rcgu.o -rust/target/debug/deps/gimli-644a05e86b54988a.gimli.61a7a513c5709da1-cgu.3.rcgu.o -rust/target/debug/deps/gimli-644a05e86b54988a.gimli.61a7a513c5709da1-cgu.4.rcgu.o -rust/target/debug/deps/gimli-644a05e86b54988a.gimli.61a7a513c5709da1-cgu.5.rcgu.o -rust/target/debug/deps/hashbrown-d6c864ee9c675c93.d -rust/target/debug/deps/hex-ef63cab4a2086dea.d -rust/target/debug/deps/hkdf-fe190f0214b64e87.d -rust/target/debug/deps/hkdf-fe190f0214b64e87.hkdf.79b8c8f007a0d449-cgu.0.rcgu.o -rust/target/debug/deps/hmac-11c1b29e0e53e9f8.d -rust/target/debug/deps/hmac-11c1b29e0e53e9f8.hmac.c71f19cc906fed58-cgu.0.rcgu.o -rust/target/debug/deps/indexmap-d8257cccb7676d45.d -rust/target/debug/deps/inout-b9164dc3ab18b5ad.d -rust/target/debug/deps/inout-b9164dc3ab18b5ad.inout.fc0da34e153d2355-cgu.0.rcgu.o -rust/target/debug/deps/itertools-0a3696ed8aac006f.d -rust/target/debug/deps/itoa-f09f1d4f251ad3f2.d -rust/target/debug/deps/itoa-f09f1d4f251ad3f2.itoa.c82a01edfeecbe70-cgu.0.rcgu.o -rust/target/debug/deps/lazy_static-ac30fe37778770b1.d -rust/target/debug/deps/lazy_static-ac30fe37778770b1.lazy_static.df6bc87449aa930-cgu.0.rcgu.o -rust/target/debug/deps/libaddr2line-164afbf588b3e8e0.rlib -rust/target/debug/deps/libaddr2line-164afbf588b3e8e0.rmeta -rust/target/debug/deps/libadler2-b8e1686650101ebc.rlib -rust/target/debug/deps/libadler2-b8e1686650101ebc.rmeta -rust/target/debug/deps/libaead-247e9024b30f32c8.rlib -rust/target/debug/deps/libaead-247e9024b30f32c8.rmeta -rust/target/debug/deps/libaes-06933c0f73f54f0e.rlib -rust/target/debug/deps/libaes-06933c0f73f54f0e.rmeta -rust/target/debug/deps/liballo_isolate-340d9aeefcfdded8.rlib -rust/target/debug/deps/liballo_isolate-340d9aeefcfdded8.rmeta -rust/target/debug/deps/libanyhow-30177ef9193b13a0.rlib -rust/target/debug/deps/libanyhow-30177ef9193b13a0.rmeta -rust/target/debug/deps/libanyhow-c40754bdc1d08d97.rlib -rust/target/debug/deps/libanyhow-c40754bdc1d08d97.rmeta -rust/target/debug/deps/libarrayvec-f9646fd6f0b662a7.rlib -rust/target/debug/deps/libarrayvec-f9646fd6f0b662a7.rmeta -rust/target/debug/deps/libatomic-4fef8628930404bc.rlib -rust/target/debug/deps/libatomic-4fef8628930404bc.rmeta -rust/target/debug/deps/libautocfg-0322980e80f4dda5.rlib -rust/target/debug/deps/libautocfg-0322980e80f4dda5.rmeta -rust/target/debug/deps/libbacktrace-d6d745a43534420c.rlib -rust/target/debug/deps/libbacktrace-d6d745a43534420c.rmeta -rust/target/debug/deps/libbase64-9782b8db13fc1943.rlib -rust/target/debug/deps/libbase64-9782b8db13fc1943.rmeta -rust/target/debug/deps/libbase64ct-09659ecb40e5863e.rlib -rust/target/debug/deps/libbase64ct-09659ecb40e5863e.rmeta -rust/target/debug/deps/libblock_buffer-57f418079f810658.rlib -rust/target/debug/deps/libblock_buffer-57f418079f810658.rmeta -rust/target/debug/deps/libblock_padding-3425e63b59c3e346.rlib -rust/target/debug/deps/libblock_padding-3425e63b59c3e346.rmeta -rust/target/debug/deps/libbuild_target-d161175fbbd29f4e.rlib -rust/target/debug/deps/libbuild_target-d161175fbbd29f4e.rmeta -rust/target/debug/deps/libbyteorder-6c69f0cfa357c802.rlib -rust/target/debug/deps/libbyteorder-6c69f0cfa357c802.rmeta -rust/target/debug/deps/libbytes-9f047593c94b8196.rlib -rust/target/debug/deps/libbytes-9f047593c94b8196.rmeta -rust/target/debug/deps/libc-f3ddc1aa080aac3d.d -rust/target/debug/deps/libc-f3ddc1aa080aac3d.libc.69df56dc75325a73-cgu.0.rcgu.o -rust/target/debug/deps/libcbc-cdd262f0eb0b6f26.rlib -rust/target/debug/deps/libcbc-cdd262f0eb0b6f26.rmeta -rust/target/debug/deps/libcc-1ec0f1f8458cb965.rlib -rust/target/debug/deps/libcc-1ec0f1f8458cb965.rmeta -rust/target/debug/deps/libcfg_if-3ebdd2ac77e576b4.rlib -rust/target/debug/deps/libcfg_if-3ebdd2ac77e576b4.rmeta -rust/target/debug/deps/libchacha20-956c523d15dcae2e.rlib -rust/target/debug/deps/libchacha20-956c523d15dcae2e.rmeta -rust/target/debug/deps/libchacha20poly1305-b264df9570fa4bb8.rlib -rust/target/debug/deps/libchacha20poly1305-b264df9570fa4bb8.rmeta -rust/target/debug/deps/libcipher-54d420ebddb605e6.rlib -rust/target/debug/deps/libcipher-54d420ebddb605e6.rmeta -rust/target/debug/deps/libcpufeatures-a83958b8d3becd82.rlib -rust/target/debug/deps/libcpufeatures-a83958b8d3becd82.rmeta -rust/target/debug/deps/libcrypto_common-af19c26aff2153a0.rlib -rust/target/debug/deps/libcrypto_common-af19c26aff2153a0.rmeta -rust/target/debug/deps/libcrypto_common-ed3b431853e7b51b.rlib -rust/target/debug/deps/libcrypto_common-ed3b431853e7b51b.rmeta -rust/target/debug/deps/libcurve25519_dalek-99dce1f29c3bc595.rlib -rust/target/debug/deps/libcurve25519_dalek-99dce1f29c3bc595.rmeta -rust/target/debug/deps/libdart_sys-815ea59815e75bcf.rlib -rust/target/debug/deps/libdart_sys-815ea59815e75bcf.rmeta -rust/target/debug/deps/libdashmap-f7e07885a57442dc.rlib -rust/target/debug/deps/libdashmap-f7e07885a57442dc.rmeta -rust/target/debug/deps/libdelegate_attr-d56d0e9b16a435b3.dylib -rust/target/debug/deps/libdigest-109a3bc51d4b8490.rlib -rust/target/debug/deps/libdigest-109a3bc51d4b8490.rmeta -rust/target/debug/deps/libdigest-d43f4616a513ec25.rlib -rust/target/debug/deps/libdigest-d43f4616a513ec25.rmeta -rust/target/debug/deps/libed25519_dalek-df57d83c9aa0b0f3.rlib -rust/target/debug/deps/libed25519_dalek-df57d83c9aa0b0f3.rmeta -rust/target/debug/deps/libed25519-4a15fe63621b2416.rlib -rust/target/debug/deps/libed25519-4a15fe63621b2416.rmeta -rust/target/debug/deps/libeither-960afc30a188083e.rlib -rust/target/debug/deps/libeither-960afc30a188083e.rmeta -rust/target/debug/deps/libequivalent-cb826c24d950b291.rlib -rust/target/debug/deps/libequivalent-cb826c24d950b291.rmeta -rust/target/debug/deps/libflutter_rust_bridge_macros-ebdd655fcadb605d.dylib -rust/target/debug/deps/libflutter_rust_bridge-f27219e8c9bea4cf.rlib -rust/target/debug/deps/libflutter_rust_bridge-f27219e8c9bea4cf.rmeta -rust/target/debug/deps/libfutures_channel-80e8e3f3aa40a3d5.rlib -rust/target/debug/deps/libfutures_channel-80e8e3f3aa40a3d5.rmeta -rust/target/debug/deps/libfutures_core-2090651f2656334d.rlib -rust/target/debug/deps/libfutures_core-2090651f2656334d.rmeta -rust/target/debug/deps/libfutures_executor-16793de88874db73.rlib -rust/target/debug/deps/libfutures_executor-16793de88874db73.rmeta -rust/target/debug/deps/libfutures_io-63ecdd207fa87dba.rlib -rust/target/debug/deps/libfutures_io-63ecdd207fa87dba.rmeta -rust/target/debug/deps/libfutures_macro-9e7a0924881b0558.dylib -rust/target/debug/deps/libfutures_sink-59aef06b1ce7ea0b.rlib -rust/target/debug/deps/libfutures_sink-59aef06b1ce7ea0b.rmeta -rust/target/debug/deps/libfutures_task-0833ac2045db9683.rlib -rust/target/debug/deps/libfutures_task-0833ac2045db9683.rmeta -rust/target/debug/deps/libfutures_util-cf1b8ba26ed8a4b3.rlib -rust/target/debug/deps/libfutures_util-cf1b8ba26ed8a4b3.rmeta -rust/target/debug/deps/libfutures-6a3338d6ee3d47df.rlib -rust/target/debug/deps/libfutures-6a3338d6ee3d47df.rmeta -rust/target/debug/deps/libgeneric_array-9c72fd8f563d25bd.rlib -rust/target/debug/deps/libgeneric_array-9c72fd8f563d25bd.rmeta -rust/target/debug/deps/libgetrandom-019ee3c40b7cf82c.rlib -rust/target/debug/deps/libgetrandom-019ee3c40b7cf82c.rmeta -rust/target/debug/deps/libgimli-644a05e86b54988a.rlib -rust/target/debug/deps/libgimli-644a05e86b54988a.rmeta -rust/target/debug/deps/libhashbrown-d6c864ee9c675c93.rlib -rust/target/debug/deps/libhashbrown-d6c864ee9c675c93.rmeta -rust/target/debug/deps/libhex-ef63cab4a2086dea.rlib -rust/target/debug/deps/libhex-ef63cab4a2086dea.rmeta -rust/target/debug/deps/libhkdf-fe190f0214b64e87.rlib -rust/target/debug/deps/libhkdf-fe190f0214b64e87.rmeta -rust/target/debug/deps/libhmac-11c1b29e0e53e9f8.rlib -rust/target/debug/deps/libhmac-11c1b29e0e53e9f8.rmeta -rust/target/debug/deps/libindexmap-d8257cccb7676d45.rlib -rust/target/debug/deps/libindexmap-d8257cccb7676d45.rmeta -rust/target/debug/deps/libinout-b9164dc3ab18b5ad.rlib -rust/target/debug/deps/libinout-b9164dc3ab18b5ad.rmeta -rust/target/debug/deps/libitertools-0a3696ed8aac006f.rlib -rust/target/debug/deps/libitertools-0a3696ed8aac006f.rmeta -rust/target/debug/deps/libitoa-f09f1d4f251ad3f2.rlib -rust/target/debug/deps/libitoa-f09f1d4f251ad3f2.rmeta -rust/target/debug/deps/liblazy_static-ac30fe37778770b1.rlib -rust/target/debug/deps/liblazy_static-ac30fe37778770b1.rmeta -rust/target/debug/deps/liblibc-f3ddc1aa080aac3d.rlib -rust/target/debug/deps/liblibc-f3ddc1aa080aac3d.rmeta -rust/target/debug/deps/liblog-7345ef26f7bf9f6d.rlib -rust/target/debug/deps/liblog-7345ef26f7bf9f6d.rmeta -rust/target/debug/deps/libmatrix_pickle_derive-d08f83551ce17830.dylib -rust/target/debug/deps/libmatrix_pickle-d3d613ced9b4e14e.rlib -rust/target/debug/deps/libmatrix_pickle-d3d613ced9b4e14e.rmeta -rust/target/debug/deps/libmd5-f9c2224a9cf1cebb.rlib -rust/target/debug/deps/libmd5-f9c2224a9cf1cebb.rmeta -rust/target/debug/deps/libmemchr-ac59469faa88ab94.rlib -rust/target/debug/deps/libmemchr-ac59469faa88ab94.rmeta -rust/target/debug/deps/libminiz_oxide-4aec6e156a999e9d.rlib -rust/target/debug/deps/libminiz_oxide-4aec6e156a999e9d.rmeta -rust/target/debug/deps/libnum_cpus-764d2bb1d5c22ded.rlib -rust/target/debug/deps/libnum_cpus-764d2bb1d5c22ded.rmeta -rust/target/debug/deps/libobject-9567b3b1d8fab212.rlib -rust/target/debug/deps/libobject-9567b3b1d8fab212.rmeta -rust/target/debug/deps/libopaque_debug-d8fc60719854d562.rlib -rust/target/debug/deps/libopaque_debug-d8fc60719854d562.rmeta -rust/target/debug/deps/liboslog-86c0dae3b5ebd7d1.rlib -rust/target/debug/deps/liboslog-86c0dae3b5ebd7d1.rmeta -rust/target/debug/deps/libpin_project_lite-c0e0b96b944b72ce.rlib -rust/target/debug/deps/libpin_project_lite-c0e0b96b944b72ce.rmeta -rust/target/debug/deps/libpin_utils-347d9b0ada1581aa.rlib -rust/target/debug/deps/libpin_utils-347d9b0ada1581aa.rmeta -rust/target/debug/deps/libpoly1305-4ca42376aa6be9d0.rlib -rust/target/debug/deps/libpoly1305-4ca42376aa6be9d0.rmeta -rust/target/debug/deps/libportable_atomic-45d6297cd3e0d750.rlib -rust/target/debug/deps/libportable_atomic-45d6297cd3e0d750.rmeta -rust/target/debug/deps/libppv_lite86-f398655b4e837915.rlib -rust/target/debug/deps/libppv_lite86-f398655b4e837915.rmeta -rust/target/debug/deps/libproc_macro_crate-f0082b20926418a8.rlib -rust/target/debug/deps/libproc_macro_crate-f0082b20926418a8.rmeta -rust/target/debug/deps/libproc_macro_error_attr2-8b70c376ce3257e0.dylib -rust/target/debug/deps/libproc_macro_error2-c695b5f469d78da0.rlib -rust/target/debug/deps/libproc_macro_error2-c695b5f469d78da0.rmeta -rust/target/debug/deps/libproc_macro2-77566036764aba29.rlib -rust/target/debug/deps/libproc_macro2-77566036764aba29.rmeta -rust/target/debug/deps/libprost_derive-580231d6cc756203.dylib -rust/target/debug/deps/libprost-6ac03ffdf0b4a2a8.rlib -rust/target/debug/deps/libprost-6ac03ffdf0b4a2a8.rmeta -rust/target/debug/deps/libquote-27765a9a6986ddb6.rlib -rust/target/debug/deps/libquote-27765a9a6986ddb6.rmeta -rust/target/debug/deps/librand_chacha-a41c11d53130c668.rlib -rust/target/debug/deps/librand_chacha-a41c11d53130c668.rmeta -rust/target/debug/deps/librand_core-15945517f78be27d.rlib -rust/target/debug/deps/librand_core-15945517f78be27d.rmeta -rust/target/debug/deps/librand-887c5902909e25ac.rlib -rust/target/debug/deps/librand-887c5902909e25ac.rmeta -rust/target/debug/deps/librustc_demangle-1c06506ce10d9c74.rlib -rust/target/debug/deps/librustc_demangle-1c06506ce10d9c74.rmeta -rust/target/debug/deps/librustc_version-4bffee4549b1b915.rlib -rust/target/debug/deps/librustc_version-4bffee4549b1b915.rmeta -rust/target/debug/deps/libryu-bdc0719e1d5e75ad.rlib -rust/target/debug/deps/libryu-bdc0719e1d5e75ad.rmeta -rust/target/debug/deps/libsemver-99393d098b3cea2a.rlib -rust/target/debug/deps/libsemver-99393d098b3cea2a.rmeta -rust/target/debug/deps/libserde_bytes-69174b286a59c27b.rlib -rust/target/debug/deps/libserde_bytes-69174b286a59c27b.rmeta -rust/target/debug/deps/libserde_derive-4a581f1223b1317c.dylib -rust/target/debug/deps/libserde_json-a106cf2483bbb775.rlib -rust/target/debug/deps/libserde_json-a106cf2483bbb775.rmeta -rust/target/debug/deps/libserde_spanned-53fdf257259eec10.rlib -rust/target/debug/deps/libserde_spanned-53fdf257259eec10.rmeta -rust/target/debug/deps/libserde-3018e8407ad9eebe.rlib -rust/target/debug/deps/libserde-3018e8407ad9eebe.rmeta -rust/target/debug/deps/libserde-915479c99b24c77f.rlib -rust/target/debug/deps/libserde-915479c99b24c77f.rmeta -rust/target/debug/deps/libsha2-8cebf057a5340c20.rlib -rust/target/debug/deps/libsha2-8cebf057a5340c20.rmeta -rust/target/debug/deps/libshlex-0dc44f95a1c7286e.rlib -rust/target/debug/deps/libshlex-0dc44f95a1c7286e.rmeta -rust/target/debug/deps/libsignature-a736132ee8c4699d.rlib -rust/target/debug/deps/libsignature-a736132ee8c4699d.rmeta -rust/target/debug/deps/libslab-95a2f9771d99ca6a.rlib -rust/target/debug/deps/libslab-95a2f9771d99ca6a.rmeta -rust/target/debug/deps/libsubtle-51f54fe4c58216fe.rlib -rust/target/debug/deps/libsubtle-51f54fe4c58216fe.rmeta -rust/target/debug/deps/libsyn-3c3bdea880cdd63f.rlib -rust/target/debug/deps/libsyn-3c3bdea880cdd63f.rmeta -rust/target/debug/deps/libthiserror_impl-0e763aa36af8214b.dylib -rust/target/debug/deps/libthiserror_impl-4a1a4380dab4b75e.dylib -rust/target/debug/deps/libthiserror-7ac054077a416f3a.rlib -rust/target/debug/deps/libthiserror-7ac054077a416f3a.rmeta -rust/target/debug/deps/libthiserror-c5cb8d50071a87cb.rlib -rust/target/debug/deps/libthiserror-c5cb8d50071a87cb.rmeta -rust/target/debug/deps/libthreadpool-44e4babe3d09de5e.rlib -rust/target/debug/deps/libthreadpool-44e4babe3d09de5e.rmeta -rust/target/debug/deps/libtokio-e4ecd9ce6b3ad6f7.rlib -rust/target/debug/deps/libtokio-e4ecd9ce6b3ad6f7.rmeta -rust/target/debug/deps/libtoml_datetime-a84c1bc8e0d858d0.rlib -rust/target/debug/deps/libtoml_datetime-a84c1bc8e0d858d0.rmeta -rust/target/debug/deps/libtoml_edit-986f0899a4fb4937.rlib -rust/target/debug/deps/libtoml_edit-986f0899a4fb4937.rmeta -rust/target/debug/deps/libtypenum-c585b7608e83e6b8.rlib -rust/target/debug/deps/libtypenum-c585b7608e83e6b8.rmeta -rust/target/debug/deps/libunicode_ident-c4cd5a2669b29311.rlib -rust/target/debug/deps/libunicode_ident-c4cd5a2669b29311.rmeta -rust/target/debug/deps/libuniversal_hash-0d2b04219ee9e6dc.rlib -rust/target/debug/deps/libuniversal_hash-0d2b04219ee9e6dc.rmeta -rust/target/debug/deps/libversion_check-8c067b92ae3aff85.rlib -rust/target/debug/deps/libversion_check-8c067b92ae3aff85.rmeta -rust/target/debug/deps/libvodozemac_bindings_dart.a -rust/target/debug/deps/libvodozemac_bindings_dart.dylib -rust/target/debug/deps/libvodozemac-5deaa4867e5f7789.rlib -rust/target/debug/deps/libvodozemac-5deaa4867e5f7789.rmeta -rust/target/debug/deps/libwinnow-ba9446fd5a967863.rlib -rust/target/debug/deps/libwinnow-ba9446fd5a967863.rmeta -rust/target/debug/deps/libx25519_dalek-6d81351bf5812b82.rlib -rust/target/debug/deps/libx25519_dalek-6d81351bf5812b82.rmeta -rust/target/debug/deps/libzerocopy-0b442b8bf50f5845.rlib -rust/target/debug/deps/libzerocopy-0b442b8bf50f5845.rmeta -rust/target/debug/deps/libzeroize_derive-dc24d67d8749c978.dylib -rust/target/debug/deps/libzeroize-aa8ff4b0509d5a40.rlib -rust/target/debug/deps/libzeroize-aa8ff4b0509d5a40.rmeta -rust/target/debug/deps/log-7345ef26f7bf9f6d.d -rust/target/debug/deps/log-7345ef26f7bf9f6d.log.7a4cab816c417c85-cgu.0.rcgu.o -rust/target/debug/deps/matrix_pickle_derive-d08f83551ce17830.d -rust/target/debug/deps/matrix_pickle-d3d613ced9b4e14e.d -rust/target/debug/deps/matrix_pickle-d3d613ced9b4e14e.matrix_pickle.d30b8fe49c656480-cgu.0.rcgu.o -rust/target/debug/deps/md5-f9c2224a9cf1cebb.d -rust/target/debug/deps/memchr-ac59469faa88ab94.d -rust/target/debug/deps/memchr-ac59469faa88ab94.memchr.fb14dde628436174-cgu.0.rcgu.o -rust/target/debug/deps/miniz_oxide-4aec6e156a999e9d.d -rust/target/debug/deps/miniz_oxide-4aec6e156a999e9d.miniz_oxide.70f49cc6168fa3d7-cgu.0.rcgu.o -rust/target/debug/deps/miniz_oxide-4aec6e156a999e9d.miniz_oxide.70f49cc6168fa3d7-cgu.1.rcgu.o -rust/target/debug/deps/num_cpus-764d2bb1d5c22ded.d -rust/target/debug/deps/num_cpus-764d2bb1d5c22ded.num_cpus.9ccb24fbee3afa08-cgu.0.rcgu.o -rust/target/debug/deps/object-9567b3b1d8fab212.d -rust/target/debug/deps/object-9567b3b1d8fab212.object.804f94b809b84143-cgu.0.rcgu.o -rust/target/debug/deps/object-9567b3b1d8fab212.object.804f94b809b84143-cgu.1.rcgu.o -rust/target/debug/deps/object-9567b3b1d8fab212.object.804f94b809b84143-cgu.2.rcgu.o -rust/target/debug/deps/object-9567b3b1d8fab212.object.804f94b809b84143-cgu.3.rcgu.o -rust/target/debug/deps/opaque_debug-d8fc60719854d562.d -rust/target/debug/deps/opaque_debug-d8fc60719854d562.opaque_debug.37c460bb41219005-cgu.0.rcgu.o -rust/target/debug/deps/oslog-86c0dae3b5ebd7d1.d -rust/target/debug/deps/oslog-86c0dae3b5ebd7d1.oslog.85ce6f5e29ee16c6-cgu.0.rcgu.o -rust/target/debug/deps/oslog-86c0dae3b5ebd7d1.oslog.85ce6f5e29ee16c6-cgu.1.rcgu.o -rust/target/debug/deps/oslog-86c0dae3b5ebd7d1.oslog.85ce6f5e29ee16c6-cgu.2.rcgu.o -rust/target/debug/deps/oslog-86c0dae3b5ebd7d1.oslog.85ce6f5e29ee16c6-cgu.3.rcgu.o -rust/target/debug/deps/pin_project_lite-c0e0b96b944b72ce.d -rust/target/debug/deps/pin_project_lite-c0e0b96b944b72ce.pin_project_lite.d8eb04edfaee2809-cgu.0.rcgu.o -rust/target/debug/deps/pin_utils-347d9b0ada1581aa.d -rust/target/debug/deps/pin_utils-347d9b0ada1581aa.pin_utils.2b02ba6a334648c9-cgu.0.rcgu.o -rust/target/debug/deps/poly1305-4ca42376aa6be9d0.d -rust/target/debug/deps/poly1305-4ca42376aa6be9d0.poly1305.aab856f7ce66ac02-cgu.0.rcgu.o -rust/target/debug/deps/portable_atomic-45d6297cd3e0d750.d -rust/target/debug/deps/portable_atomic-45d6297cd3e0d750.portable_atomic.a1052b0848525637-cgu.0.rcgu.o -rust/target/debug/deps/ppv_lite86-f398655b4e837915.d -rust/target/debug/deps/ppv_lite86-f398655b4e837915.ppv_lite86.fc60ad8bc46c27e0-cgu.0.rcgu.o -rust/target/debug/deps/proc_macro_crate-f0082b20926418a8.d -rust/target/debug/deps/proc_macro_error_attr2-8b70c376ce3257e0.d -rust/target/debug/deps/proc_macro_error2-c695b5f469d78da0.d -rust/target/debug/deps/proc_macro2-77566036764aba29.d -rust/target/debug/deps/prost_derive-580231d6cc756203.d -rust/target/debug/deps/prost-6ac03ffdf0b4a2a8.d -rust/target/debug/deps/prost-6ac03ffdf0b4a2a8.prost.bebe6b1802afbf2-cgu.0.rcgu.o -rust/target/debug/deps/quote-27765a9a6986ddb6.d -rust/target/debug/deps/rand_chacha-a41c11d53130c668.d -rust/target/debug/deps/rand_chacha-a41c11d53130c668.rand_chacha.b276a411b0ab46ef-cgu.0.rcgu.o -rust/target/debug/deps/rand_core-15945517f78be27d.d -rust/target/debug/deps/rand_core-15945517f78be27d.rand_core.5849b0c3c819e810-cgu.0.rcgu.o -rust/target/debug/deps/rand-887c5902909e25ac.d -rust/target/debug/deps/rand-887c5902909e25ac.rand.c22d4a683fca1f1f-cgu.0.rcgu.o -rust/target/debug/deps/rand-887c5902909e25ac.rand.c22d4a683fca1f1f-cgu.1.rcgu.o -rust/target/debug/deps/rustc_demangle-1c06506ce10d9c74.d -rust/target/debug/deps/rustc_demangle-1c06506ce10d9c74.rustc_demangle.b10bf964b2395674-cgu.0.rcgu.o -rust/target/debug/deps/rustc_demangle-1c06506ce10d9c74.rustc_demangle.b10bf964b2395674-cgu.1.rcgu.o -rust/target/debug/deps/rustc_demangle-1c06506ce10d9c74.rustc_demangle.b10bf964b2395674-cgu.2.rcgu.o -rust/target/debug/deps/rustc_demangle-1c06506ce10d9c74.rustc_demangle.b10bf964b2395674-cgu.3.rcgu.o -rust/target/debug/deps/rustc_version-4bffee4549b1b915.d -rust/target/debug/deps/ryu-bdc0719e1d5e75ad.d -rust/target/debug/deps/ryu-bdc0719e1d5e75ad.ryu.7f038a4afc95d2a2-cgu.0.rcgu.o -rust/target/debug/deps/semver-99393d098b3cea2a.d -rust/target/debug/deps/serde_bytes-69174b286a59c27b.d -rust/target/debug/deps/serde_bytes-69174b286a59c27b.serde_bytes.c33a42d7458f850f-cgu.0.rcgu.o -rust/target/debug/deps/serde_derive-4a581f1223b1317c.d -rust/target/debug/deps/serde_json-a106cf2483bbb775.d -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.0.rcgu.o -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.1.rcgu.o -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.2.rcgu.o -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.3.rcgu.o -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.4.rcgu.o -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.5.rcgu.o -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.6.rcgu.o -rust/target/debug/deps/serde_json-a106cf2483bbb775.serde_json.b71802e9d70a88c2-cgu.7.rcgu.o -rust/target/debug/deps/serde_spanned-53fdf257259eec10.d -rust/target/debug/deps/serde-3018e8407ad9eebe.d -rust/target/debug/deps/serde-3018e8407ad9eebe.serde.6b8eb0096fb2818f-cgu.0.rcgu.o -rust/target/debug/deps/serde-915479c99b24c77f.d -rust/target/debug/deps/sha2-8cebf057a5340c20.d -rust/target/debug/deps/sha2-8cebf057a5340c20.sha2.18ce0fdda93ef68c-cgu.0.rcgu.o -rust/target/debug/deps/shlex-0dc44f95a1c7286e.d -rust/target/debug/deps/signature-a736132ee8c4699d.d -rust/target/debug/deps/signature-a736132ee8c4699d.signature.108761011abcfc95-cgu.0.rcgu.o -rust/target/debug/deps/slab-95a2f9771d99ca6a.d -rust/target/debug/deps/slab-95a2f9771d99ca6a.slab.9252060e621483ac-cgu.0.rcgu.o -rust/target/debug/deps/subtle-51f54fe4c58216fe.d -rust/target/debug/deps/subtle-51f54fe4c58216fe.subtle.33aaa453fdd68ec-cgu.0.rcgu.o -rust/target/debug/deps/syn-3c3bdea880cdd63f.d -rust/target/debug/deps/thiserror_impl-0e763aa36af8214b.d -rust/target/debug/deps/thiserror_impl-4a1a4380dab4b75e.d -rust/target/debug/deps/thiserror-7ac054077a416f3a.d -rust/target/debug/deps/thiserror-7ac054077a416f3a.thiserror.cb3b0eae3293a2b3-cgu.0.rcgu.o -rust/target/debug/deps/thiserror-c5cb8d50071a87cb.d -rust/target/debug/deps/thiserror-c5cb8d50071a87cb.thiserror.f7d1e228092cc92a-cgu.0.rcgu.o -rust/target/debug/deps/threadpool-44e4babe3d09de5e.d -rust/target/debug/deps/threadpool-44e4babe3d09de5e.threadpool.97889fe982380356-cgu.0.rcgu.o -rust/target/debug/deps/threadpool-44e4babe3d09de5e.threadpool.97889fe982380356-cgu.1.rcgu.o -rust/target/debug/deps/threadpool-44e4babe3d09de5e.threadpool.97889fe982380356-cgu.2.rcgu.o -rust/target/debug/deps/threadpool-44e4babe3d09de5e.threadpool.97889fe982380356-cgu.3.rcgu.o -rust/target/debug/deps/threadpool-44e4babe3d09de5e.threadpool.97889fe982380356-cgu.4.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.d -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.00.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.01.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.02.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.03.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.04.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.05.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.06.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.07.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.08.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.09.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.10.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.11.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.12.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.13.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.14.rcgu.o -rust/target/debug/deps/tokio-e4ecd9ce6b3ad6f7.tokio.ff60c7772c0aacd5-cgu.15.rcgu.o -rust/target/debug/deps/toml_datetime-a84c1bc8e0d858d0.d -rust/target/debug/deps/toml_edit-986f0899a4fb4937.d -rust/target/debug/deps/typenum-c585b7608e83e6b8.d -rust/target/debug/deps/typenum-c585b7608e83e6b8.typenum.946ec7ccbe8e16ff-cgu.0.rcgu.o -rust/target/debug/deps/unicode_ident-c4cd5a2669b29311.d -rust/target/debug/deps/universal_hash-0d2b04219ee9e6dc.d -rust/target/debug/deps/universal_hash-0d2b04219ee9e6dc.universal_hash.fbc2038d34f16fe6-cgu.0.rcgu.o -rust/target/debug/deps/version_check-8c067b92ae3aff85.d -rust/target/debug/deps/vodozemac_bindings_dart.0b7wep0wndvbikuz63wlusa3w.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.0c0d76uxor7cpv4h553gomqxi.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.0newlywfny5b5rynyieut5paw.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.0ow1c1eifiwf44z7kbxf83hbb.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.0v0h4cbhmbaqofpzhohuq5lci.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.1a3rdyljruzcf3acl28v0jfrq.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.1pag7mzs0173nhkpksxcnfvoo.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.1v7zpdfhi48xftmvolx8isb8h.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.1w6fzjd6ytku2pwfrc6kswesb.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.2l0cvcpdye8o93lrwcvn91e87.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.2sz7a42fxgty50kwabfh9dvlg.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.2z65sp395xb05hm8ozaxc39qh.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.2zbnw35cs39oxqako5nbcbl7t.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.2zpwbsvwzpjxig6jbgtkdi2p1.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.3ar6hnw706qf9082awrqkl67b.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.3e1hm2r1c5b8k085fxt68omo1.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.3ec2i5hw48z2zffaiqslc5131.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.3g5mjx2qfrlwcw6o180nwomqb.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.3nj3t2svkjz2biz2vdw2g19gl.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.3ovv8gjd8bynj19qrif1p6fpr.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4a5te843z27kmfba33ta6no4x.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4c1srqswu94vkb5vkeiwdcokt.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4fsitbiprw6xdfastldsvv62n.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4ho8cv1x3pwmbu2jhgdosihg1.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4khpbgufjkspjgrbclij687lj.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4utk2jybmqb1zuldind3i9oj7.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4wuybxcaq9sc55wxm6nrmgsyf.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.4zhmfjshug6iu39a1w4zvptoz.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.5abx54rdpxvivw8jdbfn9v4lj.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.5devbxwg53c0racmuizk9ag3i.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.05fiu8g2edug6on2ykzbrqbj5.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.5hyodhupotgmram6w35flbcno.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.5j2pj5x68rlcnxomv7rkekmd3.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.5vqecyq3ztokqdb2v3awk23io.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6a5owrhojhjrnr370odxijfxk.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6fxpalcvoe04yrfyejzp97wxu.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6gqjb0f8de3d8p9apnumoc3wi.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6ing9nzsv2oh2qmsvuwt4w16g.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6p0mbjovjny4zude17cle8sgx.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6u0dmzg0r5a8hhtb6jwmg9e0o.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6w61mlslyht3ew2e6f9yc0l23.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.6xp7v25qfpp5o7ayvfa1azmqz.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.7c4f4cc2foublor3jb4vj7omj.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.07jvi0ekeyl66ftemy5dcyod3.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.7krne0cuxiqfme8hcmazm152h.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.7n7x2z67evkklzp7tz6c38pv7.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.7p8ty437nb5g43wze2qhc6h0e.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.7txtu6qepdnaikl76pnieso9u.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.7vqax8zl48t80wsqb12fbo5yi.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.7yzddw83kbyrmf7k3z8j48gjv.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.8bk2yxpjpdzqwk3tv6w8phogp.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.8c72w9n20amwne1ptkcdm9n13.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.8rczu6si46xr4ee0j2hubw3in.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.8s3af67qe7bv59h54vq74y6he.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.8swr81l84d174ecge750jv4wy.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.8u8l9el3cpaf5hgj597grzayp.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9h9gn4sxxve9aepns4ibeyu3i.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9i8fo33danf5amec1okxypkfs.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9l7mmvhreewp3i9uisbdraf6m.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9nxhp0xsij9upbf052bvhdb5y.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9qsu2moi6wmcabavbvcfwrh1f.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9qucrao0f9flkylwam86mvpp9.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9tkxehxuwqeh4eer68e038bao.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.9y38vjw1o1k6faayegg7acz12.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.16vnzlev4vjg8i110f1aj28os.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.26p0kcwjm3esfd1jg843in8ej.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.31pmyqr43h7g040kawg7c9uup.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.32y62mjb5pqjpyp0pzcsmsaos.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.35ectk0hb60uwbmdufnsvey02.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.39dz4ryb3y9by0mp7j0ytj0q8.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.39x1h12g7gxfqqehcp930jxvb.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.41phux2qnn7k5nbvyjnu8zn0e.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.48ipfq3g54i2k0lpo68xwuujl.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.51mbzgu9dqh3cb9iv3dwt3unm.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.62c1be37wgvrtba5jloen4128.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.65zylfs02v79g0a2u6lqj10mc.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.74jxz19cd6nqxiyq3382e7ern.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.81rzioze5cma136lvpgpvhfbf.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.92d5jy14d58mq11c7dq3hrspd.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.92omzezsdmgl1lemje3z5v0w0.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.95iqze0ievqs3mai4mavmgs26.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.545fo7v2lbiwq8f4uh8lpcnxv.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.86715h5p6jb1n30g8hprajzu6.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.a9v3gpw1dsaoymski7nwpijmv.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.a456fz08wz35l4okm6l5timnw.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.aayrnpf583pjpfkzxn4td9wug.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.agvvgey4xk1ewd3wuh6pa537a.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.apjc492sfsltyktp4zp8tij84.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.aq9k5fe549hqgvo07iav5hkz2.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.aqpqv03zi9cq6t6jxf8oene87.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.b0l7scyzsxp4imqkeo1qkwvmk.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.b4qnkfvasttocmcjk94ewqt8b.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.b09dobcflgq7m0ojjh9q6efmw.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.ba3s5t4iy5z20m89uc3weerng.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.bfplvc5f4hq6lgm1ggaceqra3.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.bg0l5z5988mf94i24wjq2i7hw.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.bnwclk8qvi8sjhzrr8fwf12z2.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.boaurd4rgtipsk9zamylmdm98.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.brt5vccvizpqknwv0teg7twxi.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.c2e7llhl2sapcvjah3k7ozx5a.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.c7vzu4y2uszfmhrvh7hp7uodp.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.c8909zdab81rrhw4p8nairmep.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.ceopco3js6jiba46oxjhxjdcb.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.cfy56mswwmpxlc11wdug2z186.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.crlqkzn0vueljaieybd2i41o3.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.crzwdhw5d21j3txhgpdikskks.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.cz4yjkoo23yr0e1hgxcm6lyxq.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.d -rust/target/debug/deps/vodozemac_bindings_dart.d01jlu7xht4v9rp3vd6djdv1d.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.d1yiym2ekfqzhubntuhiohfa8.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.d5n3p5iark9elpctdfhlp6p48.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.d9jum4natezhhklom59a8i3v7.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.df2gqlz8vlant2hrg967j3o5b.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.dk3ewxzg7bf64g0h7z9rf4bq5.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.dntxrhnap16ui3t1ldkxp6yru.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.dqdr5w5815sm2uni86io04xk8.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.e6kuf893ue7ur87tfu6zj6vlf.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.e9rqwl730yhplj4aogbj7kbgb.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.ejjm0vshi16qa767iimt2r0y6.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.esh1h02ipr6eqnkr00iksbi18.rcgu.o -rust/target/debug/deps/vodozemac_bindings_dart.ev7yzm959j77w3r9oq6jlj9xp.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.d -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.00.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.01.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.02.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.03.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.04.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.05.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.06.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.07.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.08.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.09.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.10.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.11.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.12.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.13.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.14.rcgu.o -rust/target/debug/deps/vodozemac-5deaa4867e5f7789.vodozemac.1044d244d093fb61-cgu.15.rcgu.o -rust/target/debug/deps/winnow-ba9446fd5a967863.d -rust/target/debug/deps/x25519_dalek-6d81351bf5812b82.d -rust/target/debug/deps/x25519_dalek-6d81351bf5812b82.x25519_dalek.28b83a226f4acf2a-cgu.0.rcgu.o -rust/target/debug/deps/zerocopy-0b442b8bf50f5845.d -rust/target/debug/deps/zerocopy-0b442b8bf50f5845.zerocopy.d79ce33b6f731921-cgu.0.rcgu.o -rust/target/debug/deps/zeroize_derive-dc24d67d8749c978.d -rust/target/debug/deps/zeroize-aa8ff4b0509d5a40.d -rust/target/debug/deps/zeroize-aa8ff4b0509d5a40.zeroize.2d6ef323a76e2505-cgu.0.rcgu.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp.lock -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/0b7wep0wndvbikuz63wlusa3w.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/0c0d76uxor7cpv4h553gomqxi.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/0newlywfny5b5rynyieut5paw.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/0ow1c1eifiwf44z7kbxf83hbb.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/0v0h4cbhmbaqofpzhohuq5lci.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/1a3rdyljruzcf3acl28v0jfrq.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/1pag7mzs0173nhkpksxcnfvoo.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/1v7zpdfhi48xftmvolx8isb8h.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/1w6fzjd6ytku2pwfrc6kswesb.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/2l0cvcpdye8o93lrwcvn91e87.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/2sz7a42fxgty50kwabfh9dvlg.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/2z65sp395xb05hm8ozaxc39qh.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/2zbnw35cs39oxqako5nbcbl7t.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/2zpwbsvwzpjxig6jbgtkdi2p1.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/3ar6hnw706qf9082awrqkl67b.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/3e1hm2r1c5b8k085fxt68omo1.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/3ec2i5hw48z2zffaiqslc5131.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/3g5mjx2qfrlwcw6o180nwomqb.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/3nj3t2svkjz2biz2vdw2g19gl.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/3ovv8gjd8bynj19qrif1p6fpr.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4a5te843z27kmfba33ta6no4x.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4c1srqswu94vkb5vkeiwdcokt.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4fsitbiprw6xdfastldsvv62n.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4ho8cv1x3pwmbu2jhgdosihg1.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4khpbgufjkspjgrbclij687lj.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4utk2jybmqb1zuldind3i9oj7.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4wuybxcaq9sc55wxm6nrmgsyf.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/4zhmfjshug6iu39a1w4zvptoz.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/5abx54rdpxvivw8jdbfn9v4lj.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/5devbxwg53c0racmuizk9ag3i.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/05fiu8g2edug6on2ykzbrqbj5.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/5hyodhupotgmram6w35flbcno.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/5j2pj5x68rlcnxomv7rkekmd3.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/5vqecyq3ztokqdb2v3awk23io.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6a5owrhojhjrnr370odxijfxk.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6fxpalcvoe04yrfyejzp97wxu.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6gqjb0f8de3d8p9apnumoc3wi.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6ing9nzsv2oh2qmsvuwt4w16g.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6p0mbjovjny4zude17cle8sgx.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6u0dmzg0r5a8hhtb6jwmg9e0o.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6w61mlslyht3ew2e6f9yc0l23.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/6xp7v25qfpp5o7ayvfa1azmqz.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/7c4f4cc2foublor3jb4vj7omj.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/07jvi0ekeyl66ftemy5dcyod3.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/7krne0cuxiqfme8hcmazm152h.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/7n7x2z67evkklzp7tz6c38pv7.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/7p8ty437nb5g43wze2qhc6h0e.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/7txtu6qepdnaikl76pnieso9u.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/7vqax8zl48t80wsqb12fbo5yi.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/7yzddw83kbyrmf7k3z8j48gjv.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/8bk2yxpjpdzqwk3tv6w8phogp.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/8c72w9n20amwne1ptkcdm9n13.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/8rczu6si46xr4ee0j2hubw3in.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/8s3af67qe7bv59h54vq74y6he.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/8swr81l84d174ecge750jv4wy.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/8u8l9el3cpaf5hgj597grzayp.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9h9gn4sxxve9aepns4ibeyu3i.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9i8fo33danf5amec1okxypkfs.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9l7mmvhreewp3i9uisbdraf6m.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9nxhp0xsij9upbf052bvhdb5y.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9qsu2moi6wmcabavbvcfwrh1f.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9qucrao0f9flkylwam86mvpp9.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9tkxehxuwqeh4eer68e038bao.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/9y38vjw1o1k6faayegg7acz12.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/16vnzlev4vjg8i110f1aj28os.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/26p0kcwjm3esfd1jg843in8ej.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/31pmyqr43h7g040kawg7c9uup.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/32y62mjb5pqjpyp0pzcsmsaos.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/35ectk0hb60uwbmdufnsvey02.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/39dz4ryb3y9by0mp7j0ytj0q8.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/39x1h12g7gxfqqehcp930jxvb.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/41phux2qnn7k5nbvyjnu8zn0e.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/48ipfq3g54i2k0lpo68xwuujl.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/51mbzgu9dqh3cb9iv3dwt3unm.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/62c1be37wgvrtba5jloen4128.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/65zylfs02v79g0a2u6lqj10mc.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/74jxz19cd6nqxiyq3382e7ern.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/81rzioze5cma136lvpgpvhfbf.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/92d5jy14d58mq11c7dq3hrspd.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/92omzezsdmgl1lemje3z5v0w0.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/95iqze0ievqs3mai4mavmgs26.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/545fo7v2lbiwq8f4uh8lpcnxv.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/86715h5p6jb1n30g8hprajzu6.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/a9v3gpw1dsaoymski7nwpijmv.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/a456fz08wz35l4okm6l5timnw.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/aayrnpf583pjpfkzxn4td9wug.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/agvvgey4xk1ewd3wuh6pa537a.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/apjc492sfsltyktp4zp8tij84.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/aq9k5fe549hqgvo07iav5hkz2.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/aqpqv03zi9cq6t6jxf8oene87.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/b0l7scyzsxp4imqkeo1qkwvmk.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/b4qnkfvasttocmcjk94ewqt8b.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/b09dobcflgq7m0ojjh9q6efmw.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/ba3s5t4iy5z20m89uc3weerng.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/bfplvc5f4hq6lgm1ggaceqra3.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/bg0l5z5988mf94i24wjq2i7hw.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/bnwclk8qvi8sjhzrr8fwf12z2.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/boaurd4rgtipsk9zamylmdm98.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/brt5vccvizpqknwv0teg7twxi.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/c2e7llhl2sapcvjah3k7ozx5a.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/c7vzu4y2uszfmhrvh7hp7uodp.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/c8909zdab81rrhw4p8nairmep.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/ceopco3js6jiba46oxjhxjdcb.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/cfy56mswwmpxlc11wdug2z186.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/crlqkzn0vueljaieybd2i41o3.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/crzwdhw5d21j3txhgpdikskks.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/cz4yjkoo23yr0e1hgxcm6lyxq.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/d01jlu7xht4v9rp3vd6djdv1d.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/d1yiym2ekfqzhubntuhiohfa8.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/d5n3p5iark9elpctdfhlp6p48.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/d9jum4natezhhklom59a8i3v7.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/dep-graph.bin -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/df2gqlz8vlant2hrg967j3o5b.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/dk3ewxzg7bf64g0h7z9rf4bq5.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/dntxrhnap16ui3t1ldkxp6yru.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/dqdr5w5815sm2uni86io04xk8.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/e6kuf893ue7ur87tfu6zj6vlf.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/e9rqwl730yhplj4aogbj7kbgb.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/ejjm0vshi16qa767iimt2r0y6.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/esh1h02ipr6eqnkr00iksbi18.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/ev7yzm959j77w3r9oq6jlj9xp.o -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/query-cache.bin -rust/target/debug/incremental/vodozemac_bindings_dart-0h76klyu5099p/s-h7x3o3csj7-0th25mp-6nxuk0pxy291laxqooepeqc0y/work-products.bin -rust/.gitignore -rust/Cargo.lock -rust/Cargo.toml -rust/LICENSE -rust/src/bindings.rs -rust/src/frb_generated.rs -rust/src/lib.rs libcrypto.3.dylib android/app/src/main/jniLibs/** +android/app/google-services.json web/pkg/package.json web/pkg/vodozemac_bindings_dart_bg.wasm web/pkg/vodozemac_bindings_dart.js +web/native_executor.js* diff --git a/.metadata b/.metadata index 572440b8e..6995a4055 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: "abb292a07e20d696c4568099f918f6c5f330e6b0" + revision: "fcf2c11572af6f390246c056bc905eca609533a0" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 - base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 - - platform: linux - create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 - base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 + create_revision: fcf2c11572af6f390246c056bc905eca609533a0 + base_revision: fcf2c11572af6f390246c056bc905eca609533a0 + - platform: android + create_revision: fcf2c11572af6f390246c056bc905eca609533a0 + base_revision: fcf2c11572af6f390246c056bc905eca609533a0 # User provided section diff --git a/CHANGELOG.md b/CHANGELOG.md index aa2aa50a2..989fe36f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,423 @@ +## v2.4.0 +FluffyChat 2.4.0 adds a new improved GUI for managing stickers with tutorials how to +easily add your own sticker packs. +It also improves the search and image gallery in chats, especially encrypted chats. +Besides that this update comes with a lot of fixes and improvements under the hood. + +- feat: Create new sticker packs (krille-chan) +- feat: Edit displayname and attribution for sticker packs (krille-chan) +- feat: Improved search (Christian Kußowski) +- feat: Set usage of custom emojis and stickers (krille-chan) +- feat: Upload multiple stickers at once (krille-chan) +- build: (deps): bump actions/checkout from 5 to 6 (dependabot[bot]) +- build: (deps): bump animations from 2.1.0 to 2.1.1 (dependabot[bot]) +- build: (deps): bump device_info_plus from 12.2.0 to 12.3.0 (dependabot[bot]) +- build: (deps): bump emoji_picker_flutter from 4.3.0 to 4.4.0 (dependabot[bot]) +- build: (deps): bump file_picker from 10.3.6 to 10.3.7 (dependabot[bot]) +- build: (deps): bump file_picker from 10.3.7 to 10.3.8 (dependabot[bot]) +- build: (deps): bump file_selector from 1.0.4 to 1.1.0 (dependabot[bot]) +- build: (deps): bump flutter_foreground_task from 9.1.0 to 9.2.0 (dependabot[bot]) +- build: (deps): bump flutter_webrtc from 1.2.0 to 1.2.1 (dependabot[bot]) +- build: (deps): bump go_router from 17.0.0 to 17.0.1 (dependabot[bot]) +- build: (deps): bump image from 4.5.4 to 4.6.0 (dependabot[bot]) +- build: (deps): bump image from 4.6.0 to 4.7.1 (dependabot[bot]) +- build: (deps): bump image from 4.7.1 to 4.7.2 (dependabot[bot]) +- build: (deps): bump matrix from 4.0.0 to 4.0.1 (dependabot[bot]) +- build: (deps): bump shared_preferences from 2.5.3 to 2.5.4 (dependabot[bot]) +- build: (deps): bump translations_cleaner from 0.0.5 to 0.1.0 (dependabot[bot]) +- build: (deps): bump universal_html from 2.2.4 to 2.3.0 (dependabot[bot]) +- build: Reenable shrink resources and minify in gradle (Christian Kußowski) +- build: Upgrade to flutter 3.38.4 (Christian Kußowski) +- build: Use matrix sdk vom pub.dev again (Christian Kußowski) +- chore(translations): Translated using Weblate (Basque) (xabirequejo) +- chore(translations): Translated using Weblate (Belarusian) (Alex Katon) +- chore(translations): Translated using Weblate (Bengali) (Kom nake) +- chore(translations): Translated using Weblate (Chinese (Simplified Han script)) (Creeper) +- chore(translations): Translated using Weblate (Chinese (Simplified Han script)) (大王叫我来巡山) +- chore(translations): Translated using Weblate (Croatian) (Milo Ivir) +- chore(translations): Translated using Weblate (Czech) (arxari) +- chore(translations): Translated using Weblate (Dutch) (Jelv) +- chore(translations): Translated using Weblate (Estonian) (Priit Jõerüüt) +- chore(translations): Translated using Weblate (Finnish) (Ricky Tigg) +- chore(translations): Translated using Weblate (French) (hugues de keyzer) +- chore(translations): Translated using Weblate (Galician) (josé m.) +- chore(translations): Translated using Weblate (Georgian) (Nicholas Winterhalter) +- chore(translations): Translated using Weblate (Georgian) (Temuri Doghonadze) +- chore(translations): Translated using Weblate (German) (Bella) +- chore(translations): Translated using Weblate (German) (nautilusx) +- chore(translations): Translated using Weblate (Greek) (Rain4Cats) +- chore(translations): Translated using Weblate (Irish) (Aindriú Mac Giolla Eoin) +- chore(translations): Translated using Weblate (Italian) (Alessio Olivieri) +- chore(translations): Translated using Weblate (Italian) (Claudio Maradonna) +- chore(translations): Translated using Weblate (Latvian) (Edgars Andersons) +- chore(translations): Translated using Weblate (Norwegian Bokmål) (Frank Paul Silye) +- chore(translations): Translated using Weblate (Portuguese (Brazil)) (LucasMZ) +- chore(translations): Translated using Weblate (Russian) (BeMeritus) +- chore(translations): Translated using Weblate (Russian) (Christian) +- chore(translations): Translated using Weblate (Russian) (Дмитрий Михирев) +- chore(translations): Translated using Weblate (Serbian) (Luka) +- chore(translations): Translated using Weblate (Spanish) (Kimby) +- chore(translations): Translated using Weblate (Ukrainian) (Andriy Kushnir) +- chore(translations): Translated using Weblate (Ukrainian) (Ihor Hordiichuk) +- chore(translations): Translated using Weblate (Uzbek) (BeMeritus) +- chore(translations): Translated using Weblate (Uzbek) (Maftuna Vohidjonovna) +- chore: Allow all chars for emoji search (Christian Kußowski) +- chore: Allow export of readonly sticker packs (Christian Kußowski) +- chore: delete unrelated line (ShootingStarDragons) +- chore: Display attribution for sticker packs (krille-chan) +- chore: Escape title in check duplicates job (Christian Kußowski) +- chore: ignore for now deprecated regex (Christian Kußowski) +- chore: Improve room custom emote UX (krille-chan) +- chore: Improve sticker editor UX (krille-chan) +- chore: Improve text selection color for messages (Christian Kußowski) +- chore: Improve via calculation (Christian Kußowski) +- chore: Increase padding for code blocks (Christian Kußowski) +- chore: Link how do I get stickers FAQ entry (Christian Kußowski) +- chore: Localize emoji picker (Christian Kußowski) +- chore: Make attribution url field clickable (Christian Kußowski) +- chore: Make cross signing self sign mandatory for bootstrap (Christian Kußowski) +- chore: make locale for emoji suggestions configureable (krille-chan) +- chore: Make sticker previews in editor clickable (krille-chan) +- chore: Make stickers smaller than normal image messages (Christian Kußowski) +- chore: Override sqlcipher license (Christian Kußowski) +- chore: Remove horizontal scrolling for code blocks (Christian Kußowski) +- chore: remove the userId param (ShootingStarDragons) +- chore: Replace copy action with pin event action in UI (Christian Kußowski) +- chore: Request keys for last room messages after bootstrap (krille-chan) +- chore: Simplify adaptive bottom sheet code (Christian Kußowski) +- chore: Update privacy policy links (Christian Kußowski) +- chore: Update start poll design (Christian Kußowski) +- chore: Update user device keys before creating bootstrap (Christian Kußowski) +- chore: Use license_checker from pub again (Christian Kußowski) +- chore: Wait for secrets after bootstrap verification (Christian Kußowski) +- Fix regression after new linter rules (Andriy Kushnir) +- fix: Better wait for secrets after verification bootstrap (Christian Kußowski) +- fix: Broken ruzzian plurals (Christian Kußowski) +- fix: Do not auto load history in rooms with collapsed state only (Christian Kußowski) +- fix: Do not display empty file description body (Christian Kußowski) +- fix: Do not render html in unformatted messages (Christian Kußowski) +- fix: Do not request hero users for rooms with name (Christian Kußowski) +- fix: enable users without passwords to delete their accounts by using uia request in account deletion (ggurdin) +- fix: Hide no fcm warning after dismissed (Christian Kußowski) +- fix: jump cannot work properly when there are multi users (ShootingStarDragons) +- fix: Make keyboard moving bottom sheet up (Christian Kußowski) +- fix: No description for video and audio messages displayed (Christian Kußowski) +- fix: Render not permitted html tags as text instead of hiding (Christian Kußowski) +- fix: Scrolling in fragmented timeline (Christian Kußowski) +- fix: spoiler formatting in reply (denalena) +- fix: State problem when not changing emote name (krille-chan) +- fix: Unlock app with leading 0 in pin is not possible (Christian Kußowski) +- fix: when user has multi counts,notification not works well (ShootingStarDragons) +- refactor: Adjust state event design (Christian Kußowski) +- refactor: Always open Chat Backup as page right after login (Christian Kußowski) +- refactor: Display all sticker packs in same editor with filterchips (krille-chan) +- refactor: File selector just use default FileType and remove not working zip selector (Christian Kußowski) +- refactor: Improved design and UX for sticker editor (krille-chan) +- refactor: Remove native imaging and enable web worker (Christian Kußowski) +- refactor: Remove unused feature tor browser detector (Christian Kußowski) +- refactor: Remove unused html onfocus streams (Christian Kußowski) +- refactor: Remove unused msix (Christian Kußowski) +- refactor: Remove workaround for download files (Christian Kußowski) +- refactor: Update flutter lints (Christian Kußowski) +- refactor: Update to Dart 3.10 with . shorthands (Christian Kußowski) +- refactor: Use localized emojis for suggestion input (krille-chan) +- refactor: Use own highlight rendering with working scrollbar and text selection (Christian Kußowski) +- chore: Update links to matrix spaces to avoid confusion (Andriy Kushnir) + +## v2.3.0 + +FluffyChat v2.3.0 fixes a possible database corruption bug on Android, also fixes a +major performance leak and introduces polls and threads. + +- feat: Implement polls (Christian Kußowski) +- feat: Implement threads (krille-chan) +- fix: Database corruption because notification tab isolate starts too late (Christian Kußowski) +- fix: Import sticker packs (Christian Kußowski) +- refactor: Improved UIA support for OIDC and SSO (Christian Kußowski) +- refactor: Invite users when upgrading private room (Christian Kußowski) +- build: (deps): bump cross_file from 0.3.4+2 to 0.3.5 (dependabot[bot]) +- build: (deps): bump file_picker from 10.3.3 to 10.3.6 (dependabot[bot]) +- build: (deps): bump go_router from 16.3.0 to 17.0.0 (dependabot[bot]) +- build: (deps): bump http from 1.5.0 to 1.6.0 (dependabot[bot]) +- build: (deps): bump image_picker from 1.2.0 to 1.2.1 (dependabot[bot]) +- build: (deps): bump qr_code_scanner_plus from 2.0.13 to 2.0.14 (dependabot[bot]) +- build: (deps): bump video_player from 2.10.0 to 2.10.1 (dependabot[bot]) +- build: Switch back to stable web auth package (krille-chan) +- build: Use matrix sdk 4.0.0 (Christian Kußowski) +- build: Use secure storage from pub.dev again (krille-chan) +- chore: Remove Notification actions when using UnifiedPush (Christian Kußowski) +- chore(translations): Added translation using Weblate (Uzbek) (bahrom04) +- chore(translations): Translated using Weblate (Basque) (xabirequejo) +- chore(translations): Translated using Weblate (Belarusian) (Alex Katon) +- chore(translations): Translated using Weblate (Chinese (Simplified Han script)) (大王叫我来巡山) +- chore(translations): Translated using Weblate (Dutch) (Jelv) +- chore(translations): Translated using Weblate (Estonian) (Priit Jõerüüt) +- chore(translations): Translated using Weblate (Finnish) (Priit Jõerüüt) +- chore(translations): Translated using Weblate (Finnish) (Ricky Tigg) +- chore(translations): Translated using Weblate (French) (luneth) +- chore(translations): Translated using Weblate (Galician) (josé m.) +- chore(translations): Translated using Weblate (Irish) (Aindriú Mac Giolla Eoin) +- chore(translations): Translated using Weblate (Latvian) (Edgars Andersons) +- chore(translations): Translated using Weblate (Norwegian Bokmål) (Frank Paul Silye) +- chore(translations): Translated using Weblate (Uzbek) (bahrom04) +- chore(translations): Translated using Weblate (Uzbek) (BeMeritus) + +## v2.2.0 + +FluffyChat 2.2.0 introduces a new UX for spaces and adds support for restricted +join rules. + +The app also now has a new design for recording voice messages including a new +pause button. + +You also now see the progress when downloading files. + +On web the performance for image compressing has been drastically improved. Also +the config.json file loading has finally been fixed. Please be aware that the format +has changed as the available configs are now generated automatically. + +Did you know that you can set a lot of additional configs in the new config viewer +inside the app? This now supports much more options but please use with care! + +On Android FluffyChat now supports notification actions (reply and mark as read) and +also now supports Android Auto integration. Please do not cause an accident when using +the app while driving! + +On iOS the notifications are now localized. Stay tuned for more improvements on iOS +notifications in the next releases! + +Besides that this release brings a ton of bug fixes and performance improvements and +of course updated translations. Big thanks to all the volunteers who have helped on weblate! + +- feat: Add donation buttons except for PlayStore&AppStore version (Christian Kußowski) +- feat: Add iOS Notification Service Extension for localizable push notifications (Christian Kußowski) +- feat: Add notification actions (krille-chan) +- feat: Add support for restricted join rule (Christian Kußowski) +- feat: Display progress for downloading content (Christian Kußowski) +- feat: Display progress on redact events and clear archive dialogs (Christian Kußowski) +- feat: Enable native imaging for web (Christian Kußowski) +- feat: Forward notifications to Android Auto (Christian Kußowski) +- feat: Nicer record voice message UI with pause function (Christian Kußowski) +- feat: Notification actions on android (krille-chan) +- design: Improved spaces UX (Christian Kußowski) +- fix: Correctly pass through obscure text (Christian Kußowski) +- fix: Create a subdirectory in the tmp directory (Inex Code) +- fix: Cupertino text dialogs (Christian Kußowski) +- fix: Null pointer crash in chat list item (Christian Kußowski) +- chore(translations): Translated using Weblate (Arabic) (jamazi) +- chore(translations): Translated using Weblate (Basque) (xabirequejo) +- chore(translations): Translated using Weblate (Belarusian) (Alex Katon) +- chore(translations): Translated using Weblate (Chinese (Simplified Han script)) (大王叫我来巡山) +- chore(translations): Translated using Weblate (Croatian) (Milo Ivir) +- chore(translations): Translated using Weblate (Czech) (Flibble) +- chore(translations): Translated using Weblate (Dutch) (Jelv) +- chore(translations): Translated using Weblate (Esperanto) (Anonymous) +- chore(translations): Translated using Weblate (Estonian) (Priit Jõerüüt) +- chore(translations): Translated using Weblate (Finnish) (Aminda Suomalainen) +- chore(translations): Translated using Weblate (Galician) (josé m) +- chore(translations): Translated using Weblate (German) (Alex Katon) +- chore(translations): Translated using Weblate (German) (cheese1) +- chore(translations): Translated using Weblate (German) (Christian) +- chore(translations): Translated using Weblate (German) (Jana) +- chore(translations): Translated using Weblate (Greek) (Λευτέρης Τ) +- chore(translations): Translated using Weblate (Hebrew) (Anonymous) +- chore(translations): Translated using Weblate (Hungarian) (Balázs Meskó) +- chore(translations): Translated using Weblate (Indonesian) (Linerly) +- chore(translations): Translated using Weblate (Irish) (Aindriú Mac Giolla Eoin) +- chore(translations): Translated using Weblate (Japanese) (Allan Nordhøy) +- chore(translations): Translated using Weblate (Korean) (loyedison92) +- chore(translations): Translated using Weblate (Latvian) (Edgars Andersons) +- chore(translations): Translated using Weblate (Latvian) (ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝)) +- chore(translations): Translated using Weblate (Lithuanian) (Anonymous) +- chore(translations): Translated using Weblate (Norwegian Bokmål) (Frank Paul Silye) +- chore(translations): Translated using Weblate (Norwegian Bokmål) (sunniva) +- chore(translations): Translated using Weblate (Polish) (Paweł Gronowski) +- chore(translations): Translated using Weblate (Portuguese (Brazil)) (LucasMZ) +- chore(translations): Translated using Weblate (Portuguese (Portugal)) (Anonymous) +- chore(translations): Translated using Weblate (Romanian) (Anonymous) +- chore(translations): Translated using Weblate (Russian) (Christian) +- chore(translations): Translated using Weblate (Russian) (Дмитрий Михирев) +- chore(translations): Translated using Weblate (Russian) (Жора Змейкин) +- chore(translations): Translated using Weblate (Serbian) (Anonymous) +- chore(translations): Translated using Weblate (Slovak) (Anonymous) +- chore(translations): Translated using Weblate (Spanish) (Kimby) +- chore(translations): Translated using Weblate (Spanish) (LucasMZ) +- chore(translations): Translated using Weblate (Turkish) (Christian) +- chore(translations): Translated using Weblate (Turkish) (Ufuk Karal) +- chore(translations): Translated using Weblate (Vietnamese) (desperadohp) +- chore: add a link to the platforms' different behaviors for the 24h format (kaanelloed) +- chore: Add ActionsPadding in column mode (Christian Kußowski) +- chore: Add close icon to snackbar in column mode (Christian Kußowski) +- chore: Adjust chat details button design (Christian Kußowski) +- chore: Adjust encryption page design (Christian Kußowski) +- chore: Adjust input bar button design (Christian Kußowski) +- chore: Adjust material dialog button design (krille-chan) +- chore: Adjust padding for status msg list (Christian Kußowski) +- chore: Adjust status header design (krille-chan) +- chore: Better display rooms without known messages (Christian Kußowski) +- chore: Better routing to space id (Christian Kußowski) +- chore: Correctly remove knockRestricted from possible join rules (Christian Kußowski) +- chore: Fix load config.json (Christian Kußowski) +- chore: Go back to pub.dev matrix dep (Christian Kußowski) +- chore: Hide restricted and knock restricted for unsupported room versions (Christian Kußowski) +- chore: Hide topic if empty and cannot be changed (Christian Kußowski) +- chore: Improve chat details icon color design (Christian Kußowski) +- chore: Improve ignore list UX (Christian Kußowski) +- chore: Leave invites and DM rooms when blocking a user (Christian Kußowski) +- chore: Make privacy url not configurable (Christian Kußowski) +- chore: Make progress indicators rounder (Christian Kußowski) +- chore: Mention rebases (Christian Kußowski) +- chore: Move emote settings button to chat popup menu (Christian Kußowski) +- chore: Move encryption button to input row (Christian Kußowski) +- chore: Revert "refactor: Make ChatListItem cache lasteventbody for better performance" (Christian Kußowski) +- chore: Translate iOS notifications into german (Christian Kußowski) +- chore: Try out refactored user device keys update method (Christian Kußowski) +- chore: Update matrix dart sdk (Christian Kußowski) +- chore: use time format based on system settings (kaanelloed) +- ci: Build only for arm64 (Christian Kußowski) +- refactor: Make ChatListItem cache lasteventbody for better performance (Christian Kußowski) +- refactor: Remove unused directories (Christian Kußowski) +- refactor: Rename models for matrix spec 1.16 (Christian Kußowski) +- refactor: Replace flutter typeahead with autocomplete to fix (Christian Kußowski) +- refactor: Use AppSettings enum based configuration everywhere and fix load from json on web (Christian Kußowski) +- build: Flutter upgrade 3.35.5 (Christian Kußowski) +- build: Matrix version 3.0.0 (Christian Kußowski) +- build: Remove dependency for telephony (Christian Kußowski) +- build: Remove openssl dependency (Christian Kußowski) +- build: Use correct fcm_shared_isolate version (Christian Kußowski) +- build: Use correct version of vodozemac for web build (Christian Kußowski) + +## v2.1.1 +Bugfix release to trigger flatpak arm64 release again. + +- build: (deps): bump actions/checkout from 4 to 5 (dependabot[bot]) +- build: (deps): bump app_links from 6.4.0 to 6.4.1 (dependabot[bot]) +- build: (deps): bump file_picker from 10.2.3 to 10.3.1 (dependabot[bot]) +- build: (deps): bump msix from 3.16.10 to 3.16.12 (dependabot[bot]) +- build: (deps): bump package_info_plus from 8.3.0 to 8.3.1 (dependabot[bot]) +- build: (deps): bump share_plus from 11.0.0 to 11.1.0 (dependabot[bot]) +- build: Also build linux on github runners (Christian Kußowski) +- build: Update macos podfile (Christian Kußowski) +- chore: Follow up subtitle font style (Christian Kußowski) +- chore: Slightly adjust font sizes and design (Christian Kußowski) +- chore(translations): Translated using Weblate (Chinese (Traditional Han script)) (miullu) +- chore(translations): Translated using Weblate (Norwegian Bokmål) (Frank Paul Silye) +- chore(translations): Translated using Weblate (Polish) (Piotr Orzechowski) +- feat: support xdp selector for linux (ShootingStarDragons) +- fix: Follow up fix rectangle avatars (Christian Kußowski) +- refactor: Remove broken push error reporter (Christian Kußowski) + +## v2.1.0 +FluffyChat 2.1.0 brings support for room version 12 and a lot of bugfixes, updated translations and performance improvements. Also chat state events are now collapsed by default. + +- feat: Collapse all state events by default (Christian Kußowski) +- feat(linux/notify): support avatar icon (ShootingStarDragons) +- build: (deps): bump archive from 3.6.1 to 4.0.7 (dependabot[bot]) +- build: (deps): bump chewie from 1.11.3 to 1.12.1 (dependabot[bot]) +- build: (deps): bump desktop_drop from 0.4.4 to 0.6.1 (dependabot[bot]) +- build: (deps): bump device_info_plus from 10.1.2 to 11.5.0 (dependabot[bot]) +- build: (deps): bump dynamic_color from 1.7.0 to 1.8.1 (dependabot[bot]) +- build: (deps): bump file_picker from 10.2.0 to 10.2.1 (dependabot[bot]) +- build: (deps): bump file_picker from 8.3.7 to 10.2.0 (dependabot[bot]) +- build: (deps): bump flutter_foreground_task from 6.5.0 to 9.1.0 (dependabot[bot]) +- build: (deps): bump flutter_local_notifications from 19.3.0 to 19.4.0 (dependabot[bot]) +- build: (deps): bump flutter_map from 6.2.1 to 8.1.1 (dependabot[bot]) +- build: (deps): bump flutter_map from 8.1.1 to 8.2.1 (dependabot[bot]) +- build: (deps): bump flutter_native_splash from 2.4.4 to 2.4.6 (dependabot[bot]) +- build: (deps): bump flutter_webrtc from 0.12.12+hotfix.1 to 1.0.0 (dependabot[bot]) +- build: (deps): bump geolocator from 13.0.4 to 14.0.2 (dependabot[bot]) +- build: (deps): bump go_router from 15.1.2 to 16.0.0 (dependabot[bot]) +- build: (deps): bump go_router from 16.0.0 to 16.1.0 (dependabot[bot]) +- build: (deps): bump just_audio from 0.9.46 to 0.10.4 (dependabot[bot]) +- build: (deps): bump matrix from 1.0.1 to 1.1.0 (dependabot[bot]) +- build: (deps): bump mime from 1.0.6 to 2.0.0 (dependabot[bot]) +- build: (deps): bump msix from 3.16.9 to 3.16.10 (dependabot[bot]) +- build: (deps): bump permission_handler from 11.4.0 to 12.0.1 (dependabot[bot]) +- build: (deps): bump pretty_qr_code from 3.4.0 to 3.5.0 (dependabot[bot]) +- build: (deps): bump share_plus from 10.1.4 to 11.0.0 (dependabot[bot]) +- build: (deps): bump sqflite_common_ffi from 2.3.5 to 2.3.6 (dependabot[bot]) +- build: (deps): bump sqlcipher_flutter_libs from 0.6.6 to 0.6.7 (dependabot[bot]) +- build: (deps): bump unifiedpush from 5.0.2 to 6.0.2 (dependabot[bot]) +- build: (deps): bump url_launcher from 6.3.1 to 6.3.2 (dependabot[bot]) +- build: Update Flutter 3.32.8 (Christian Kußowski) +- build: Upgrade emoji picker flutter (Christian Kußowski) +- build: Use gradle in kotlin (Christian Kußowski) +- build: Use macos-latest for debug ios job (Christian Kußowski) +- chore: Add CI workflow for thank you message after issue got completed (Christian Kußowski) +- chore: Add dependabot to code owners for pubspec (Christian Kußowski) +- chore: Add ISC as compatible license to aGPLv3 (Christian Kußowski) +- chore: Better error handling for push notifications (Christian Kußowski) +- chore: Bump Flutter version in pubspec.lock (Rafał Hirsch) +- chore: do format (ShootingStarDragons) +- chore: Fix snapcraft build (krille-chan) +- chore: Nicer own reaction color design (Christian Kußowski) +- chore: Remove double quotes from screenshot filenames (Christian Kußowski) +- chore: Remove lifecyclestate logs (Christian Kußowski) +- chore: Remove unused workflow (krille-chan) +- chore: Set height for screenshots in readme (krille-chan) +- chore: Set text message max length to 16384 but make it configurable (Christian Kußowski) +- chore: Update height for screenshots in readme (krille-chan) +- chore: Update screenshots in readme (krille-chan) +- fix: Add flexible to file messages to prevent long name overflow (Kelrap) +- fix: Avoid using MediaQuery.of() (imnotlxy) +- fix: Config viewer not updating state (Christian Kußowski) +- fix: Delete database correctly after corruption (krille-chan) +- fix: Design glitches in sticker dialog (Christian Kußowski) +- fix: limit characters and lines so redact message can't be too long (avashilling) +- fix: LoadingSnackBar consistently remains visible until the upload is complete (JaWeee) +- fix: notification clicked still cannot jump (ShootingStarDragons) +- fix: open chat from notification (ShootingStarDragons) +- fix: Open database crashes on start (krille-chan) +- fix: Open external account management when trying to delete devices (Christian Kußowski) +- fix: Use retry http client (Christian Kußowski) +- fix: Workaround for reversed width and height of compressed videos sent from Android (Christian Kußowski) +- refactor: Add support for Room version 12 via matrix dart sdk update +- refactor: Better UX for accepting declining invite (krille-chan) +- refactor: ignore bubble gradient when using system high contrast mode (Christian Kußowski) +- refactor: Make notification avatars rounded (krille-chan) +- refactor: Remove unused dependencies (krille-chan) +- chore(translations): Translated using Weblate (Basque) (xabirequejo) +- chore(translations): Translated using Weblate (Belarusian) (Alex Katon) +- chore(translations): Translated using Weblate (German) (Christian) +- chore(translations): Translated using Weblate (Norwegian Bokmål) (Frank Paul Silye) +- chore(translations): Translated using Weblate (Portuguese (Brazil)) (LucasMZ) +- chore(translations): Translated using Weblate (Spanish) (Fuan200) +- chore(translations): Translated using Weblate (Tamil) (தமிழ்நேரம்) +- chore(translations): Translated using Weblate (Ukrainian) (Ihor Hordiichuk) +- Translated using Weblate (Arabic) (LucasMZ) +- Translated using Weblate (Basque) (xabirequejo) +- Translated using Weblate (Chinese (Simplified Han script)) (大王叫我来巡山) +- Translated using Weblate (Chinese (Traditional Han script)) (miullu) +- Translated using Weblate (Czech) (LucasMZ) +- Translated using Weblate (Dutch) (Jelv) +- Translated using Weblate (English) (LucasMZ) +- Translated using Weblate (Estonian) (Priit Jõerüüt) +- Translated using Weblate (Galician) (josé m) +- Translated using Weblate (German) (nautilusx) +- Translated using Weblate (Hebrew) (LucasMZ) +- Translated using Weblate (Hungarian) (LucasMZ) +- Translated using Weblate (Indonesian) (Linerly) +- Translated using Weblate (Irish) (Aindriú Mac Giolla Eoin) +- Translated using Weblate (Irish) (LucasMZ) +- Translated using Weblate (Italian) (Angelo Schirinzi) +- Translated using Weblate (Italian) (LucasMZ) +- Translated using Weblate (Latvian) (Edgars Andersons) +- Translated using Weblate (Latvian) (LucasMZ) +- Translated using Weblate (Norwegian Bokmål) (Christian) +- Translated using Weblate (Norwegian Bokmål) (Frank Paul Silye) +- Translated using Weblate (Persian) (Alireza Rashidi) +- Translated using Weblate (Polish) (Piotr Orzechowski) +- Translated using Weblate (Portuguese (Brazil)) (LucasMZ) +- Translated using Weblate (Portuguese) (Ana Elisa Ramos) +- Translated using Weblate (Portuguese) (LucasMZ) +- Translated using Weblate (Russian) (LucasMZ) +- Translated using Weblate (Tamil) (தமிழ்நேரம்) +- Translated using Weblate (Tamil) (LucasMZ) +- Translated using Weblate (Telugu) (LucasMZ) +- Translated using Weblate (Ukrainian) (Ihor Hordiichuk) +- Translated using Weblate (Ukrainian) (LucasMZ) +- Translated using Weblate (Vietnamese) (LucasMZ) + ## v2.0.0 This version migrates to Vodozemac and Matrix Dart SDK 1.0.0. This is a breaking diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..6c192cc44 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,172 @@ +# Contributing to FluffyChat +Contributions are always welcome. Yet we might lack manpower to review all of them in time. + +To improve the process please make sure that you read the following guidelines carefully: + +## Contributing Guidelines + +1. Always create a Pull Request for any changes. +2. Whenever possible please make sure that your Pull Request only contains **one** commit. Cases where multiple commits make sense are very rare. +3. Do not add merge commits. Use rebases. +4. Every Pull Request should change only one thing. For bigger changes it is often better to split them up in multiple Pull Requests. +5. [Sign your commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits). +6. Format the commit message as [Conventional Commits](https://www.conventionalcommits.org). +7. Format (`flutter format lib`) and sort impots (`dart run import_sorter:main --no-comments`) in all code files. +8. For bigger or complex changes (more than a couple of code lines) write an issue or refer to an existing issue and ask for approval from the maintainers (@krille-chan) **before** starting to implement it. This way you reduce the risk that your Pull Request get's declined. +9. Prefer simple and easy to maintain solutions over complexity and fancy ones. + +# Code Style + +FluffyChat tries to be as minimal as possible even in the code style. We try to keep the code clean, simple and easy to read. The source code of the app is under `/lib` with the main entry point `/lib/main.dart`. + + + + + + +**Table of Contents** + +- [Directory Structure:](#directory-structure) +- [Separation of Controllers and Views](#separation-of-controllers-and-views) +- [Formatting](#formatting) +- [Code Analyzis](#code-analyzis) + + + + + + +### Directory Structure: + + +- /lib + - /config + - app_config.dart + - ...Constants, styles and other configurations + - /utils + - handy_function.dart + - ...Helper functions and extensions + - /pages + - /chat + - chat.dart + - chat_view.dart + - /chat_list + - chat_list.dart + - chat_list_view.dart + - ...The pages of the app separated in Controllers and Views + - /widgets + - /layouts + - ...Custom widgets created for this project + - main.dart + + +Most of the business model is in the Famedly Matrix Dart SDK. We try to not keep a model inside of the source code but extend it under `/utils`. + +### Separation of Controllers and Views + +We split views and controller logic with stateful widgets as controller where the build method just builds a stateless widget which receives the state as the only parameter. A common controller would look like this: + +```dart +// /lib/controller/enter_name_controller.dart +import 'package:flutter/material.dart'; + +class EnterName extends StatefulWidget { + @override + EnterNameController createState() => EnterNameController(); +} + +class EnterNameController extends State { + final TextEditingController textEditingController = TextEditingController(); + String name = 'Unknown'; + + /// Changes the name with the content in the textfield. If the textfield is + /// empty, this breaks up and displays a SnackBar. + void setNameAction() { + if (textEditingController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('You have not entered your name'), + ), + ); + return; + } + setState(() => name = textEditingController.text); + } + + @override + Widget build(BuildContext context) => EnterNameView(this); +} +``` + +So we have a controller for a `EnterName` view which as a `TextEditingController`, a state `name` and an action `void setNameAction()`. Actions must always be methods of a type, that we dont need to pass parameters in the corresponding view class and must have dartdoc comments. + +The view class could look like this: + +```dart +// /lib/views/enter_name_view.dart +import 'package:flutter/material.dart'; + +class EnterNameView extends StatelessWidget { + final EnterNameController controller; + + const EnterNameView(this.controller, {Key key}) : super(key: key); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Your name: ${controller.name}'), + ), + body: Center( + child: TextField( + controller: controller.textEditingController, + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: controller.setNameAction, + child: Icon(Icons.save), + ), + ); + } +} +``` + +Views should just contain code which describes the view. All other parameters or logic should be in the controller. The job of the view class is just to take the current state and build the widget tree and pipe the callbacks back. If there is any calulation necessary which is not solveable as a simple if-else or switch statement, it should be done in an external helper function unter `/lib/utils/`. + +All file names must be lower_snake_case. All views must have a `View` suffix and all controller must have a `Controller` suffix. Widgets may have a controller too but they should pass the callbacks back to the view where possible. Calling one line methods directly in the view is only recommended if there is no need to pass a parameter. + +To perform an action on state initialization we use the initState method: +```dart +@override + void initState() { + // TODO: implement initState + super.initState(); + } +``` + +And the dispose method to perform an action on disposing: +```dart +@override + void dispose() { + // TODO: implement dispose + super.dispose(); + } +``` + +To run code after the widget was created first we use the WidgetBindings in the initState: +```dart +@override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + // Do something when build is finished + }); + super.initState(); + } +``` + +### Formatting + +We do not allow code with wrong formatting. Please run `flutter format lib` if your IDE doesn't do this automatically. + +### Code Analyzis + +We do not allow codes with dart errors or warnings. We use the [flutter_lints](https://pub.dev/packages/flutter_lints) package for static code analysis with additional rules under `analysis_options.yaml`. diff --git a/PRIVACY.md b/PRIVACY.md index 439899d0e..53c7cdd82 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -7,8 +7,9 @@ FluffyChat is available on Android, iOS, Linux and as a web version. Desktop ver * [Encryption](#encryption) * [App Permissions](#app-permissions) * [Push Notifications](#push-notifications) +* [PlayStore Safety Standards](#playstore-safety) -## Matrix +## # Matrix FluffyChat uses the Matrix protocol. This means that FluffyChat is just a client that can be connected to any compatible matrix server. The respective data protection agreement of the server selected by the user then applies. For convenience, one or more servers are set as default that the FluffyChat developers consider trustworthy. The developers of FluffyChat do not guarantee their trustworthiness. Before the first communication, users are informed which server they are connecting to. @@ -17,17 +18,17 @@ FluffyChat only communicates with the selected server and with [OpenStreetMap](h More information is available at: [https://matrix.org](https://matrix.org) -## Database +## # Database FluffyChat caches some data received from the server in a local sqflite database on the device of the user. On web indexedDB is used. FluffyChat always tries to encrypt the database by using SQLCipher and stores the encryption key in the [Secure Storage](https://pub.dev/packages/flutter_secure_storage) of the device. More information is available at: [https://pub.dev/packages/sqflite](https://pub.dev/packages/sqflite) and [https://pub.dev/packages/sqlcipher_flutter_libs](https://pub.dev/packages/sqlcipher_flutter_libs) -## Encryption +## # Encryption All communication of substantive content between Fluffychat and any server is done in secure way, using transport encryption to protect it. -FluffyChat also uses End-To-End-Encryption by using [libolm](https://gitlab.matrix.org/matrix-org/olm) and enables it by default for private chats. +FluffyChat also uses End-To-End-Encryption by using [Vodozemac](https://github.com/matrix-org/vodozemac) and enables it by default for private chats. -## App Permissions +## # App Permissions The permissions are the same on Android and iOS but may differ in the name. This are the Android Permissions: @@ -50,7 +51,7 @@ The user is able to send files from the device's file system. #### Location FluffyChat makes it possible to share the current location via the chat. When the user shares their location, FluffyChat uses the device location service and sends the geo-data via Matrix. -## Push Notifications +## # Push Notifications FluffyChat uses the Firebase Cloud Messaging service for push notifications on Android and iOS. This takes place in the following steps: 1. The matrix server sends the push notification to the FluffyChat Push Gateway 2. The FluffyChat Push Gateway forwards the message in a different format to Firebase Cloud Messaging @@ -58,7 +59,7 @@ FluffyChat uses the Firebase Cloud Messaging service for push notifications on A 4. The device receives the push notification from Firebase Cloud Messaging and displays it as a notification The source code of the push gateway can be viewed here: -[https://gitlab.com/famedly/services/famedly-push-gateway](https://gitlab.com/famedly/services/famedly-push-gateway) +[https://github.com/krille-chan/fluffygate](https://github.com/krille-chan/fluffygate) `event_id_only` is used as the format for the push notification. A typical push notification therefore only contains: - Event ID @@ -94,7 +95,7 @@ A typical push notification could look like this: FluffyChat sets the `event_id_only` flag at the Matrix Server. This server is then responsible to send the correct data. -# Explanation of FluffyChat's Compliance with Google Play Store's Safety Standards +# # Explanation of FluffyChat's Compliance with Google Play Store's Safety Standards FluffyChat is committed to promoting a safe and respectful environment for all users. As a Matrix client, FluffyChat connects users to various Matrix servers. Please note that FluffyChat does not host or manage any servers directly, and as such, we do not have the capability to enforce content moderation or deletion within the app itself. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..ff51b140f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +If you believe you have discovered a security vulnerability in this project, **do not open a public issue**. + +To report a security issue responsibly, please go to the Security tab or visit: + +https://github.com/krille-chan/fluffychat/security/advisories/new + +The following information can help us address the issue: + +- Clear steps to reproduce the vulnerability +- The software version you are using +- The affected platforms + +We appreciate your report and will respond as quickly as possible. Please note that this project is maintained by volunteers and is provided without guarantees. diff --git a/analysis_options.yaml b/analysis_options.yaml index 26cfe8535..7458c0646 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -14,40 +14,4 @@ analyzer: todo: ignore use_build_context_synchronously: ignore exclude: - - lib/generated_plugin_registrant.dart - lib/l10n/*.dart - - assets/l10n/*.arb - -dart_code_metrics: - metrics: - cyclomatic-complexity: 20 - number-of-arguments: 4 - maximum-nesting-level: 5 - source-lines-of-code: 50 - maintainability-index: 40 - rules: - - no-boolean-literal-compare - - no-empty-block - - prefer-conditional-expressions - - no-equal-then-else - - no-magic-number: - severity: style - - avoid-late-keyword: - severity: style - - avoid-non-null-assertion: - severity: style - - avoid-unused-parameters - - binary-expression-operand-order - - avoid-unnecessary-setstate - - avoid-wrapping-in-padding - - prefer-const-border-radius - - prefer-single-widget-per-file: - ignore-private-widgets: true - - prefer-extracting-callbacks - metrics-exclude: - - test/** - rules-exclude: - - test/** - anti-patterns: - - long-method - - long-parameter-list diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index a693ef2e6..a853244cf 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -8,21 +8,19 @@ plugins { id("dev.flutter.flutter-gradle-plugin") } -// conditionally apply google-services (keeps your original intent) if (file("google-services.json").exists()) { apply(plugin = "com.google.gms.google-services") } dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") // For flutter_local_notifications // Workaround for: https://github.com/MaikuB/flutter_local_notifications/issues/2286 - + implementation("androidx.core:core-ktx:1.17.0") // For Android Auto implementation(platform("com.google.firebase:firebase-bom:32.8.0")) implementation("com.google.firebase:firebase-analytics") implementation("com.google.firebase:firebase-database") - - implementation("androidx.multidex:multidex:2.0.1") } + // Workaround for https://pub.dev/packages/unifiedpush#the-build-fails-because-of-duplicate-classes configurations.all { // Use the latest version published: https://central.sonatype.com/artifact/com.google.crypto.tink/tink-android @@ -37,20 +35,20 @@ configurations.all { } } + android { namespace = "com.talktolearn.chat" - compileSdk = 35 - // compileSdk = flutter.compileSdkVersion - // ndkVersion = "27.0.12077973" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 isCoreLibraryDesugaringEnabled = true } kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() + jvmTarget = JavaVersion.VERSION_11.toString() } signingConfigs { @@ -80,20 +78,16 @@ android { targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName + ndk { // Workaround for https://github.com/flutter/flutter/issues/162153#issuecomment-2612443642 + abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64", "x86") + } } buildTypes { - debug { - signingConfig = signingConfigs.getByName("debug") - versionNameSuffix = "-debug" - isMinifyEnabled = false - isShrinkResources = false - } release { - isMinifyEnabled = false - isShrinkResources = false - // use the release signing config we created above (will be used only if key properties exist) signingConfig = signingConfigs.getByName("release") + isMinifyEnabled = true + isShrinkResources = true proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 12d15f6a7..dab923214 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,14 +17,10 @@ - - - - - - + + + @@ -161,29 +157,13 @@ android:foregroundServiceType="camera|microphone|mediaProjection"> - - - - - + + - - - - - - - - - - - + + diff --git a/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt b/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt index 1afc46062..747183cd3 100644 --- a/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt +++ b/android/app/src/main/kotlin/chat/fluffy/fluffychat/MainActivity.kt @@ -4,13 +4,11 @@ import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import android.content.Context -import androidx.multidex.MultiDex class MainActivity : FlutterActivity() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) - MultiDex.install(this) } diff --git a/android/app/src/main/kotlin/chat/fluffy/fluffychat/UnifiedPushService.kt b/android/app/src/main/kotlin/chat/fluffy/fluffychat/UnifiedPushService.kt deleted file mode 100644 index c5d4ba9d7..000000000 --- a/android/app/src/main/kotlin/chat/fluffy/fluffychat/UnifiedPushService.kt +++ /dev/null @@ -1,23 +0,0 @@ -package chat.fluffy.fluffychat - -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.embedding.engine.dart.DartExecutor -import org.unifiedpush.flutter.connector.UnifiedPushReceiver - -import android.content.Context - -class UnifiedPushReceiver : UnifiedPushReceiver() { - override fun getEngine(context: Context): FlutterEngine { - var engine = MainActivity.engine - if (engine == null) { - engine = MainActivity.provideEngine(context) - engine.localizationPlugin.sendLocalesToFlutter( - context.resources.configuration - ) - engine.dartExecutor.executeDartEntrypoint( - DartExecutor.DartEntrypoint.createDefault() - ) - } - return engine - } -} \ No newline at end of file diff --git a/android/app/src/main/res/xml/automotive_app_desc.xml b/android/app/src/main/res/xml/automotive_app_desc.xml new file mode 100644 index 000000000..dd5581b2a --- /dev/null +++ b/android/app/src/main/res/xml/automotive_app_desc.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/android/app/src/main/res/xml/locale_config.xml b/android/app/src/main/res/xml/locale_config.xml index a37191e79..240911b54 100644 --- a/android/app/src/main/res/xml/locale_config.xml +++ b/android/app/src/main/res/xml/locale_config.xml @@ -50,6 +50,7 @@ + diff --git a/android/build.gradle.kts b/android/build.gradle.kts index f0f9ebc77..89176ef44 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -19,13 +19,3 @@ subprojects { tasks.register("clean") { delete(rootProject.layout.buildDirectory) } - -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.google.gms:google-services:4.4.1") - } -} \ No newline at end of file diff --git a/android/fastlane/metadata/android/en-US/changelogs/default.txt b/android/fastlane/metadata/android/en-US/changelogs/default.txt new file mode 100644 index 000000000..5eed864e7 --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/default.txt @@ -0,0 +1,4 @@ +FluffyChat 2.4.0 adds a new improved GUI for managing stickers with tutorials how to +easily add your own sticker packs. +It also improves the search and image gallery in chats, especially encrypted chats. +Besides that this update comes with a lot of fixes and improvements under the hood. \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e33907ed2..9c957989d 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Mar 17 08:36:03 CET 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index 699da6575..a5d070147 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -16,25 +16,13 @@ pluginManagement { } } -buildscript { - repositories { - mavenCentral() - maven { - url = uri("https://storage.googleapis.com/r8-releases/raw") - } - } - dependencies { - classpath("com.android.tools:r8:8.2.24") - } -} - plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.7.3" apply false - id("org.jetbrains.kotlin.android") version "2.1.10" apply false + id("com.android.application") version "8.9.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false if (file("app/google-services.json").exists()) { id("com.google.gms.google-services") version "4.3.8" apply false - } + } } -include(":app") \ No newline at end of file +include(":app") diff --git a/appimage/.gitignore b/appimage/.gitignore deleted file mode 100644 index eeda4edb4..000000000 --- a/appimage/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -FluffyChat.AppDir -*.AppImage -*.AppImage.zsync diff --git a/appimage/AppRun b/appimage/AppRun deleted file mode 100644 index 95b7c0568..000000000 --- a/appimage/AppRun +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -cd "$(dirname "$0")" -exec ./fluffychat \ No newline at end of file diff --git a/appimage/FluffyChat.desktop b/appimage/FluffyChat.desktop deleted file mode 100644 index ceee4c91d..000000000 --- a/appimage/FluffyChat.desktop +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Type=Application -Version=1.0 -Name=FluffyChat -Comment=Matrix Client. Chat with your friends -Exec=AppRun -Icon=fluffychat -Terminal=false -Categories=Network;Chat;InstantMessaging;X-Matrix; \ No newline at end of file diff --git a/appimage/README.md b/appimage/README.md deleted file mode 100644 index 60f82dea0..000000000 --- a/appimage/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# FluffyChat AppImage - -FluffyChat is provided as AppImage too. To Download, visit fluffychat.im. - -## Building - -- Ensure you install `appimagetool` - -```shell -flutter build linux - -# copy binaries to appimage dir -cp -r build/linux/{x64,arm64}/release/bundle appimage/FluffyChat.AppDir -cd appimage - -# prepare AppImage files -cp FluffyChat.desktop FluffyChat.AppDir/ -mkdir -p FluffyChat.AppDir/usr/share/icons -cp ../assets/logo.svg FluffyChat.AppDir/fluffychat.svg -cp AppRun FluffyChat.AppDir - -# build the AppImage -appimagetool FluffyChat.AppDir -``` diff --git a/config.sample.json b/config.sample.json index 03ac42a6a..ad34192a1 100644 --- a/config.sample.json +++ b/config.sample.json @@ -1,10 +1,29 @@ { - "application_name": "PangeaChat", - "application_welcome_message": null, - "default_homeserver": "matrix.pangea.chat", - "web_base_url": "https://web.pangea.chat", - "privacy_url": "https://pangea.chat/privacy", - "render_html": false, - "hide_redacted_events": false, - "hide_unknown_events": false + "applicationName": "PangeaChat", + "defaultHomeserver": "matrix.pangea.chat", + "privacyUrl": "https://pangea.chat/privacy", + "audioRecordingNumChannels": 1, + "audioRecordingAutoGain": true, + "audioRecordingEchoCancel": false, + "audioRecordingNoiseSuppress": true, + "audioRecordingBitRate": 64000, + "audioRecordingSamplingRate": 44100, + "renderHtml": true, + "fontSizeFactor": 1, + "hideRedactedEvents": false, + "hideUnknownEvents": true, + "separateChatTypes": false, + "autoplayImages": true, + "sendTypingNotifications": true, + "sendPublicReadReceipts": true, + "swipeRightToLeftToReply": true, + "sendOnEnter": false, + "showPresences": true, + "displayNavigationRail": false, + "experimentalVoip": false, + "shareKeysWith": "all", + "noEncryptionWarningShown": false, + "displayChatDetailsColumn": false, + "colorSchemeSeedInt": 4283835834, + "enableSoftLogout": false } \ No newline at end of file diff --git a/docs/LICENSE b/docs/LICENSE deleted file mode 100644 index 2e59d7230..000000000 --- a/docs/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Tailwind Toolbox - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/docs/OFL.txt b/docs/OFL.txt deleted file mode 100644 index 075d1aefb..000000000 --- a/docs/OFL.txt +++ /dev/null @@ -1,91 +0,0 @@ -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/appstore-badge.png b/docs/appstore-badge.png deleted file mode 100644 index e96d5c069..000000000 Binary files a/docs/appstore-badge.png and /dev/null differ diff --git a/docs/browser-badge.png b/docs/browser-badge.png deleted file mode 100644 index fa6f278ad..000000000 Binary files a/docs/browser-badge.png and /dev/null differ diff --git a/docs/favicon.png b/docs/favicon.png deleted file mode 100644 index d55415b19..000000000 Binary files a/docs/favicon.png and /dev/null differ diff --git a/docs/fdroid_button.png b/docs/fdroid_button.png deleted file mode 100644 index 31e43f283..000000000 Binary files a/docs/fdroid_button.png and /dev/null differ diff --git a/docs/feature1.gif b/docs/feature1.gif deleted file mode 100644 index db6c8beb8..000000000 Binary files a/docs/feature1.gif and /dev/null differ diff --git a/docs/feature2.gif b/docs/feature2.gif deleted file mode 100644 index d6cf974b2..000000000 Binary files a/docs/feature2.gif and /dev/null differ diff --git a/docs/feature3.gif b/docs/feature3.gif deleted file mode 100644 index cd56dc532..000000000 Binary files a/docs/feature3.gif and /dev/null differ diff --git a/docs/feature4.gif b/docs/feature4.gif deleted file mode 100644 index 4f1c35dfd..000000000 Binary files a/docs/feature4.gif and /dev/null differ diff --git a/docs/feature5.gif b/docs/feature5.gif deleted file mode 100644 index b301d8fa0..000000000 Binary files a/docs/feature5.gif and /dev/null differ diff --git a/docs/feature6.gif b/docs/feature6.gif deleted file mode 100644 index d9fdba8d3..000000000 Binary files a/docs/feature6.gif and /dev/null differ diff --git a/docs/feature7.gif b/docs/feature7.gif deleted file mode 100644 index ced2184e1..000000000 Binary files a/docs/feature7.gif and /dev/null differ diff --git a/docs/feature8.gif b/docs/feature8.gif deleted file mode 100644 index 95bf4e817..000000000 Binary files a/docs/feature8.gif and /dev/null differ diff --git a/docs/feature9.gif b/docs/feature9.gif deleted file mode 100644 index a05001d08..000000000 Binary files a/docs/feature9.gif and /dev/null differ diff --git a/docs/firefox_icon.png b/docs/firefox_icon.png deleted file mode 100644 index 6accca90e..000000000 Binary files a/docs/firefox_icon.png and /dev/null differ diff --git a/docs/flathub-badge-en.png b/docs/flathub-badge-en.png deleted file mode 100644 index facf256f8..000000000 Binary files a/docs/flathub-badge-en.png and /dev/null differ diff --git a/docs/google-play-badge.png b/docs/google-play-badge.png deleted file mode 100644 index 6a93e1843..000000000 Binary files a/docs/google-play-badge.png and /dev/null differ diff --git a/docs/index.html b/docs/index.html index fcde6c04c..e5974b531 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,183 +1,19 @@ - - - FluffyChat Official Website - - - - - - - - - - - - + Redirecting to fluffy.chat + + - -
- -
- FluffyChat Logo -

The cutest messenger in [matrix] + +

+ Redirecting... +

+

Note: If your browser does not redirect you, please use this link:

- -
- Mobile screenshot - Desktop screenshot -
- -
- - - - - - - - -
- -
-
- Animated dancing woman -

Easy to use

-

FluffyChat is designed to be as easy to use as possible. No one - should be left behind.

-
-
- Animated pencil -

Material You

-

The well polished design is based on Material You and works great on - all platforms.

-
-
- Animated mechanical arm -

Secure

-

With end-to-end encryption, cross-signing and encrypted backups, - FluffyChat is one of the most secure messenger out there.

-
- - -
- Animated planet earth -

Decentral

-

You can choose the server you want to use or - even self-host your own!

-
-
- Animated bell -

Push Notifications

-

You can choose between Firebase Cloud Messaging or the more privacy - focused Unified Push.

-
-
- Animated rocket -

Spaces

-

With spaces you can join or create a community which organizes chats - and users. Using sub-spaces you can even nest your communities.

-
- - -
- Animated glass sphere -

Video calls

-

Still an experimental feature but you can already try out video and - audio calls, compatible with other [matrix] clients.

-
-
- Animated chick -

Stickers

-

Create your own sticker sets and share them with your friends. You - can even use them as inline emojis.

-
-
- Animated whoa emoji -

Compatible

-

FluffyChat is compatible with any other [matrix] client like Element, - Nheko, Cinny - or NeoChat. -

-
-
- - -
- -
- + https://fluffy.chat \ No newline at end of file diff --git a/docs/info-logo.png b/docs/info-logo.png deleted file mode 100644 index ceb42f09b..000000000 Binary files a/docs/info-logo.png and /dev/null differ diff --git a/docs/kofi_button_dark.png b/docs/kofi_button_dark.png deleted file mode 100644 index b005e7a39..000000000 Binary files a/docs/kofi_button_dark.png and /dev/null differ diff --git a/docs/mastodon.svg b/docs/mastodon.svg deleted file mode 100644 index 0f8baebfc..000000000 --- a/docs/mastodon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.37.png b/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.37.png deleted file mode 100644 index 4dfe010aa..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.37.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.41.png b/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.41.png deleted file mode 100644 index e55f4cc37..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.41.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.46.png b/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.46.png deleted file mode 100644 index cada45825..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.46.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.48.png b/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.48.png deleted file mode 100644 index 368bcef35..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.48.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.53.png b/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.53.png deleted file mode 100644 index b687a9eb9..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.53.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.55.png b/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.55.png deleted file mode 100644 index c6e74f4b2..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPad Pro 13-inch (M4) - 2025-06-19 at 11.20.55.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.48.32.png b/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.48.32.png deleted file mode 100644 index 8030c6865..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.48.32.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.58.02.png b/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.58.02.png deleted file mode 100644 index 06be1ba89..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.58.02.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.43.png b/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.43.png deleted file mode 100644 index 50b9e9ef0..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.43.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.46.png b/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.46.png deleted file mode 100644 index f824292ad..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.46.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.50.png b/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.50.png deleted file mode 100644 index cd2df4cb8..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.50.png and /dev/null differ diff --git a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.53.png b/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.53.png deleted file mode 100644 index e54dabcee..000000000 Binary files a/docs/screenshots/Simulator Screenshot - iPhone 16 Plus - 2025-06-19 at 10.59.53.png and /dev/null differ diff --git "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.06.59.png" "b/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.06.59.png" deleted file mode 100644 index 86df3241b..000000000 Binary files "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.06.59.png" and /dev/null differ diff --git "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.05.png" "b/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.05.png" deleted file mode 100644 index 642cae8dd..000000000 Binary files "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.05.png" and /dev/null differ diff --git "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.13.png" "b/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.13.png" deleted file mode 100644 index 8a247035b..000000000 Binary files "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.13.png" and /dev/null differ diff --git "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.15.png" "b/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.15.png" deleted file mode 100644 index d3b98b457..000000000 Binary files "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.15.png" and /dev/null differ diff --git "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.18.png" "b/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.18.png" deleted file mode 100644 index 1357fac80..000000000 Binary files "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.18.png" and /dev/null differ diff --git "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.19.png" "b/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.19.png" deleted file mode 100644 index d15994c6e..000000000 Binary files "a/docs/screenshots/Simulator Screenshot - iPhone 6.5\" - 2025-06-19 at 11.07.19.png" and /dev/null differ diff --git a/docs/screenshots/android_1.png b/docs/screenshots/android_1.png deleted file mode 100644 index 953e2411f..000000000 Binary files a/docs/screenshots/android_1.png and /dev/null differ diff --git a/docs/screenshots/android_2.png b/docs/screenshots/android_2.png deleted file mode 100644 index 44d8ba036..000000000 Binary files a/docs/screenshots/android_2.png and /dev/null differ diff --git a/docs/screenshots/android_3.png b/docs/screenshots/android_3.png deleted file mode 100644 index 5b30b0ca0..000000000 Binary files a/docs/screenshots/android_3.png and /dev/null differ diff --git a/docs/screenshots/android_4.png b/docs/screenshots/android_4.png deleted file mode 100644 index 7d03630bc..000000000 Binary files a/docs/screenshots/android_4.png and /dev/null differ diff --git a/docs/screenshots/android_5.png b/docs/screenshots/android_5.png deleted file mode 100644 index 943240e8f..000000000 Binary files a/docs/screenshots/android_5.png and /dev/null differ diff --git a/docs/screenshots/android_6.png b/docs/screenshots/android_6.png deleted file mode 100644 index 8385b548a..000000000 Binary files a/docs/screenshots/android_6.png and /dev/null differ diff --git a/docs/screenshots/desktop.png b/docs/screenshots/desktop.png deleted file mode 100644 index 1ffcbad5e..000000000 Binary files a/docs/screenshots/desktop.png and /dev/null differ diff --git a/docs/screenshots/linux_1.png b/docs/screenshots/linux_1.png deleted file mode 100644 index 3470d1f79..000000000 Binary files a/docs/screenshots/linux_1.png and /dev/null differ diff --git a/docs/screenshots/linux_2.png b/docs/screenshots/linux_2.png deleted file mode 100644 index 7e3a592f7..000000000 Binary files a/docs/screenshots/linux_2.png and /dev/null differ diff --git a/docs/screenshots/linux_3.png b/docs/screenshots/linux_3.png deleted file mode 100644 index 1ff12c6e5..000000000 Binary files a/docs/screenshots/linux_3.png and /dev/null differ diff --git a/docs/screenshots/linux_4.png b/docs/screenshots/linux_4.png deleted file mode 100644 index 67e84a863..000000000 Binary files a/docs/screenshots/linux_4.png and /dev/null differ diff --git a/docs/screenshots/linux_5.png b/docs/screenshots/linux_5.png deleted file mode 100644 index 72e5e2f09..000000000 Binary files a/docs/screenshots/linux_5.png and /dev/null differ diff --git a/docs/screenshots/mobile.png b/docs/screenshots/mobile.png deleted file mode 100644 index ccd019f6e..000000000 Binary files a/docs/screenshots/mobile.png and /dev/null differ diff --git a/docs/snap-store-badge.svg b/docs/snap-store-badge.svg deleted file mode 100644 index 294d8737f..000000000 --- a/docs/snap-store-badge.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/tailwind.config.js b/docs/tailwind.config.js deleted file mode 100644 index 2991816a3..000000000 --- a/docs/tailwind.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - purge: [ - './index.html' - ], - darkMode: false, - theme: { - extend: {}, - }, - variants: { - extend: {}, - }, - plugins: [], - } - \ No newline at end of file diff --git a/fdroid/.gitignore b/fdroid/.gitignore deleted file mode 100644 index 5b807a9c7..000000000 --- a/fdroid/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -repo -srclibs -tmp -chat.fluffy.fluffychat \ No newline at end of file diff --git a/fdroid/config.nightly.py b/fdroid/config.nightly.py deleted file mode 100644 index 86fe4932c..000000000 --- a/fdroid/config.nightly.py +++ /dev/null @@ -1,13 +0,0 @@ -repo_url = "https://fluffychat.im/repo/nightly/repo" -repo_name = "FluffyChat nightly repo" -repo_icon = "fdroid-icon.png" -repo_description = """ -FluffyChat nightly repo -""" - -archive_older = 0 - -local_copy_dir = "/fdroid" - -keystore = "key.nightly.jks" -repo_keyalias = "vmd66783.contaboserver.net" diff --git a/fdroid/config.stable.py b/fdroid/config.stable.py deleted file mode 100644 index d0a3e2e82..000000000 --- a/fdroid/config.stable.py +++ /dev/null @@ -1,13 +0,0 @@ -repo_url = "https://fluffychat.im/repo/stable/repo" -repo_name = "FluffyChat repo" -repo_icon = "fdroid-icon.png" -repo_description = """ -FluffyChat repo -""" - -archive_older = 0 - -local_copy_dir = "/fdroid" - -keystore = "key.jks" -repo_keyalias = "key" diff --git a/fdroid/fdroid-icon.png b/fdroid/fdroid-icon.png deleted file mode 100644 index 0c0d41738..000000000 Binary files a/fdroid/fdroid-icon.png and /dev/null differ diff --git a/fdroid/metadata/chat.fluffy.fluffychat.yml b/fdroid/metadata/chat.fluffy.fluffychat.yml deleted file mode 100644 index 32ffd4a90..000000000 --- a/fdroid/metadata/chat.fluffy.fluffychat.yml +++ /dev/null @@ -1,53 +0,0 @@ -Categories: - - Internet - - Phone & SMS -License: AGPL-3.0-only -AuthorName: Famedly -SourceCode: https://github.com/krille-chan/fluffychat -IssueTracker: https://github.com/krille-chan/fluffychat/-/issues -Translation: https://hosted.weblate.org/projects/fluffychat/ -Changelog: https://gitlab.com/ChristianPauly/fluffychat-flutter/-/blob/main/CHANGELOG.md - -AutoName: FluffyChat -Summary: "Chat with your friends with FluffyChat.\n" -Description: | - FluffyChat is an open, nonprofit and cute matrix messenger app for Ubuntu Touch, Android and iOS. - - Open - Opensource and open development where everyone can join. - - Nonprofit - FluffyChat is donation funded. - - Cute ♥ - Cute design and many theme settings including a dark mode. - - One-to-one and groupchats - Unlimited groups and direct chats. - - Easy - FluffyChat is made as simple to use as possible. - - Free - Free to use for everyone without ads. - - Decentralized - There is no "FluffyChat server" you are forced to use. Use the server you find trustworthy or host - your own. - - Compatible - Compatible with Riot, Fractal, Nekho and all matrix messengers. - - - FluffyChat comes with a dream - - Imagine a world where everyone can choose the messenger they like and is still able to chat with - all of their friends. - A world where there are no companies spying on you when you send selfies to friends and lovers. - And a world where apps are made for fluffyness and not for profit. ♥ - -RepoType: git -Repo: https://github.com/krille-chan/fluffychat.git - -AutoUpdateMode: None -UpdateCheckMode: None diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 90f423077..3a0139365 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -1,4 +1,3 @@ -import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pages/chat/chat_view.dart'; import 'package:fluffychat/pages/chat_list/chat_list_body.dart'; import 'package:fluffychat/pages/chat_list/search_title.dart'; @@ -6,7 +5,6 @@ import 'package:fluffychat/pages/invitation_selection/invitation_selection_view. import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:hive_flutter/hive_flutter.dart'; import 'package:integration_test/integration_test.dart'; import 'package:fluffychat/main.dart' as app; @@ -26,12 +24,8 @@ void main() { () async { // this random dialog popping up is super hard to cover in tests SharedPreferences.setMockInitialValues({ - SettingKeys.showNoGoogle: false, + 'chat.fluffy.show_no_google': false, }); - try { - Hive.deleteFromDisk(); - Hive.initFlutter(); - } catch (_) {} }, ); diff --git a/ios/Gemfile.lock b/ios/Gemfile.lock index 6b1c9350e..8d3288e5c 100644 --- a/ios/Gemfile.lock +++ b/ios/Gemfile.lock @@ -2,6 +2,7 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (3.0.3) + abbrev (0.1.2) addressable (2.8.4) public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) @@ -145,9 +146,11 @@ GEM mini_mime (1.1.0) multi_json (1.15.0) multipart-post (2.0.0) + mutex_m (0.3.0) nanaimo (0.3.0) naturally (2.2.1) os (1.1.1) + ostruct (0.6.3) plist (3.6.0) public_suffix (5.0.3) rake (13.0.3) @@ -156,7 +159,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.9) + rexml (3.4.2) rouge (2.0.7) ruby2_keywords (0.0.4) rubyzip (2.3.0) @@ -199,7 +202,10 @@ PLATFORMS ruby DEPENDENCIES + abbrev fastlane + mutex_m + ostruct BUNDLED WITH 2.1.4 \ No newline at end of file diff --git a/ios/Localizable.xcstrings b/ios/Localizable.xcstrings new file mode 100644 index 000000000..7895903ad --- /dev/null +++ b/ios/Localizable.xcstrings @@ -0,0 +1,58 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "%lld unread messages" : { + "comment" : "Default notification title", + "localizations" : { + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungelesene Nachricht" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ungelesene nachrichten" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unread message" + } + }, + "other" : { + "stringUnit" : { + "state" : "new", + "value" : "%lld unread messages" + } + } + } + } + } + } + }, + "New message - open app to read" : { + "comment" : "Default notification body", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neue Nachricht - App öffnen zum lesen" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/ios/Notification Service Extension/Info.plist b/ios/Notification Service Extension/Info.plist new file mode 100644 index 000000000..57421ebf9 --- /dev/null +++ b/ios/Notification Service Extension/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/ios/Notification Service Extension/Notification Service Extension.entitlements b/ios/Notification Service Extension/Notification Service Extension.entitlements new file mode 100644 index 000000000..c85a07dc3 --- /dev/null +++ b/ios/Notification Service Extension/Notification Service Extension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.talktolearn.chat + + + diff --git a/ios/Notification Service Extension/NotificationService.swift b/ios/Notification Service Extension/NotificationService.swift new file mode 100644 index 000000000..20d3b8800 --- /dev/null +++ b/ios/Notification Service Extension/NotificationService.swift @@ -0,0 +1,65 @@ +// +// NotificationService.swift +// Notification Extension +// +// Created by Christian Pauly on 26.08.25. +// + +import UserNotifications +import os + +class NotificationService: UNNotificationServiceExtension { + + var contentHandler: ((UNNotificationContent) -> Void)? + var bestAttemptContent: UNMutableNotificationContent? + + // #Pangea + // override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + // self.contentHandler = contentHandler + // bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) + + // if let bestAttemptContent = bestAttemptContent { + // // Uncomment to read the push message payload: + // // os_log("[FluffyChatPushHelper] New message received: %{public}@", log: .default, type: .error, bestAttemptContent.userInfo) + // os_log("[FluffyChatPushHelper] New message received") + + // guard let roomId = bestAttemptContent.userInfo["room_id"] as? String, + // let eventId = bestAttemptContent.userInfo["event_id"] as? String else { + // os_log("[FluffyChatPushHelper] Room ID or Event ID is missing!") + // let emptyContent = UNMutableNotificationContent() + // contentHandler(emptyContent) + // return + // } + // bestAttemptContent.threadIdentifier = roomId + + // if + // let jsonString = bestAttemptContent.userInfo["counts"] as? String, + // let jsonData = jsonString.data(using: .utf8), + // let jsonMap = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any], + // let unread = jsonMap["unread"] as? Int { + // bestAttemptContent.title = String( + // localized: "\(unread) unread messages", + // comment: "Default notification title" + // ) + // bestAttemptContent.badge = NSNumber(integerLiteral: unread) + // } + + // // TODO: Download and decrypt event to display a better body: + // bestAttemptContent.body = String( + // localized: "New message - open app to read", + // comment: "Default notification body" + // ) + + // contentHandler(bestAttemptContent) + // } + // } + + // override func serviceExtensionTimeWillExpire() { + // // Called just before the extension will be terminated by the system. + // // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. + // if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { + // contentHandler(bestAttemptContent) + // } + // } + // Pangea# +} diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 7d65b14fd..49ea0e829 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 18EB8101724ECEB31DC90D37 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + 18EB8101724ECEB31DC90D37 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3F86C7E35D199E7DD2B134F9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69EC0FDC95BB0C70B544A84C /* Pods_Runner.framework */; }; 59BB4671C68B58E6B34292B2 /* Pods_FluffyChat_Share.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52740D3A973E95DFFBA1E6CD /* Pods_FluffyChat_Share.framework */; }; @@ -21,6 +21,10 @@ C1005C48261071B5002F4F32 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C1005C46261071B5002F4F32 /* MainInterface.storyboard */; }; C1005C4C261071B5002F4F32 /* FluffyChat Share.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = C1005C42261071B5002F4F32 /* FluffyChat Share.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; C137635E2AD1446100A8F905 /* notification.caf in Resources */ = {isa = PBXBuildFile; fileRef = C137635D2AD1446100A8F905 /* notification.caf */; }; + C14695592E642D400075F2F7 /* Notification Service Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = C14695522E642D400075F2F7 /* Notification Service Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + C14695662E642E450075F2F7 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = C14695652E642E450075F2F7 /* Localizable.xcstrings */; }; + C14695672E642E450075F2F7 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = C14695652E642E450075F2F7 /* Localizable.xcstrings */; }; + C14695682E642E450075F2F7 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = C14695652E642E450075F2F7 /* Localizable.xcstrings */; }; C149567C25C7274F00A16396 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C149567B25C7274F00A16396 /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ @@ -32,6 +36,13 @@ remoteGlobalIDString = C1005C41261071B5002F4F32; remoteInfo = "FluffyChat Share"; }; + C14695572E642D400075F2F7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = C14695512E642D400075F2F7; + remoteInfo = "Notification Service Extension"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -52,6 +63,7 @@ dstSubfolderSpec = 13; files = ( C1005C4C261071B5002F4F32 /* FluffyChat Share.appex in Embed App Extensions */, + C14695592E642D400075F2F7 /* Notification Service Extension.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -84,6 +96,8 @@ C1005C49261071B5002F4F32 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C1005C53261072D4002F4F32 /* FluffyChat Share.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "FluffyChat Share.entitlements"; sourceTree = ""; }; C137635D2AD1446100A8F905 /* notification.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = notification.caf; sourceTree = ""; }; + C14695522E642D400075F2F7 /* Notification Service Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Notification Service Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + C14695652E642E450075F2F7 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; C149567B25C7274F00A16396 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; C149567D25C7276200A16396 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; C9EB6E6475A19949A37A2634 /* Pods_FluffyChat_Share.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FluffyChat_Share.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -91,12 +105,33 @@ F3778959E67CDA0CDB0D97BC /* Pods-FluffyChat Share.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FluffyChat Share.release.xcconfig"; path = "Target Support Files/Pods-FluffyChat Share/Pods-FluffyChat Share.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + C146955D2E642D400075F2F7 /* Exceptions for "Notification Service Extension" folder in "Notification Service Extension" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = C14695512E642D400075F2F7 /* Notification Service Extension */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + C14695532E642D400075F2F7 /* Notification Service Extension */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + C146955D2E642D400075F2F7 /* Exceptions for "Notification Service Extension" folder in "Notification Service Extension" target */, + ); + path = "Notification Service Extension"; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 18EB8101724ECEB31DC90D37 /* BuildFile in Frameworks */, + 18EB8101724ECEB31DC90D37 /* (null) in Frameworks */, 3F86C7E35D199E7DD2B134F9 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -110,6 +145,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C146954F2E642D400075F2F7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -135,9 +177,11 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + C14695652E642E450075F2F7 /* Localizable.xcstrings */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, C1005C43261071B5002F4F32 /* FluffyChat Share */, + C14695532E642D400075F2F7 /* Notification Service Extension */, 97C146EF1CF9000F007C117D /* Products */, E89DCAC000D371640E94E65B /* Pods */, E4B51FC6310E8231ADAAC605 /* Frameworks */, @@ -150,6 +194,7 @@ children = ( 97C146EE1CF9000F007C117D /* Runner.app */, C1005C42261071B5002F4F32 /* FluffyChat Share.appex */, + C14695522E642D400075F2F7 /* Notification Service Extension.appex */, ); name = Products; sourceTree = ""; @@ -227,6 +272,7 @@ ); dependencies = ( C1005C4B261071B5002F4F32 /* PBXTargetDependency */, + C14695582E642D400075F2F7 /* PBXTargetDependency */, ); name = Runner; productName = Runner; @@ -251,13 +297,33 @@ productReference = C1005C42261071B5002F4F32 /* FluffyChat Share.appex */; productType = "com.apple.product-type.app-extension"; }; + C14695512E642D400075F2F7 /* Notification Service Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = C146955E2E642D400075F2F7 /* Build configuration list for PBXNativeTarget "Notification Service Extension" */; + buildPhases = ( + C146954E2E642D400075F2F7 /* Sources */, + C146954F2E642D400075F2F7 /* Frameworks */, + C14695502E642D400075F2F7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + C14695532E642D400075F2F7 /* Notification Service Extension */, + ); + name = "Notification Service Extension"; + productName = "Notification Service Extension"; + productReference = C14695522E642D400075F2F7 /* Notification Service Extension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1240; + LastSwiftUpdateCheck = 1640; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -268,23 +334,28 @@ C1005C41261071B5002F4F32 = { CreatedOnToolsVersion = 12.4; }; + C14695512E642D400075F2F7 = { + CreatedOnToolsVersion = 16.4; + }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, + de, ); mainGroup = 97C146E51CF9000F007C117D; + preferredProjectObjectVersion = 77; productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, C1005C41261071B5002F4F32 /* FluffyChat Share */, + C14695512E642D400075F2F7 /* Notification Service Extension */, ); }; /* End PBXProject section */ @@ -299,6 +370,7 @@ C149567C25C7274F00A16396 /* GoogleService-Info.plist in Resources */, C137635E2AD1446100A8F905 /* notification.caf in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + C14695672E642E450075F2F7 /* Localizable.xcstrings in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -307,10 +379,19 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + C14695682E642E450075F2F7 /* Localizable.xcstrings in Resources */, C1005C48261071B5002F4F32 /* MainInterface.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; + C14695502E642D400075F2F7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C14695662E642E450075F2F7 /* Localizable.xcstrings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -443,6 +524,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C146954E2E642D400075F2F7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -451,6 +539,11 @@ target = C1005C41261071B5002F4F32 /* FluffyChat Share */; targetProxy = C1005C4A261071B5002F4F32 /* PBXContainerItemProxy */; }; + C14695582E642D400075F2F7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C14695512E642D400075F2F7 /* Notification Service Extension */; + targetProxy = C14695572E642D400075F2F7 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -527,6 +620,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; + SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -620,6 +714,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -670,6 +765,7 @@ SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -780,6 +876,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_ENABLE_EXPLICIT_MODULES = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -852,6 +949,126 @@ }; name = Profile; }; + C146955A2E642D400075F2F7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Notification Service Extension/Notification Service Extension.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = PJ8L5H7L7H; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Notification Service Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Notification Service Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.talktolearn.chat.Notification-Service-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C146955B2E642D400075F2F7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Notification Service Extension/Notification Service Extension.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = PJ8L5H7L7H; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Notification Service Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Notification Service Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.talktolearn.chat.Notification-Service-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + C146955C2E642D400075F2F7 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Notification Service Extension/Notification Service Extension.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = PJ8L5H7L7H; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Notification Service Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Notification Service Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.talktolearn.chat.Notification-Service-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -885,6 +1102,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + C146955E2E642D400075F2F7 /* Build configuration list for PBXNativeTarget "Notification Service Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C146955A2E642D400075F2F7 /* Debug */, + C146955B2E642D400075F2F7 /* Release */, + C146955C2E642D400075F2F7 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 975f985a6..55e038192 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -2,158 +2,29 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; abstract class AppConfig { // #Pangea - // static String _applicationName = 'FluffyChat'; - static String _applicationName = 'Pangea Chat'; + static String get defaultHomeserver => Environment.synapseURL; + // Pangea# + // Const and final configuration values (immutable) // #Pangea - - static String get applicationName => _applicationName; - static String? _applicationWelcomeMessage; - - static String? get applicationWelcomeMessage => _applicationWelcomeMessage; - // #Pangea - // static String _defaultHomeserver = 'matrix.org'; - static String get _defaultHomeserver => Environment.synapseURL; + // static const Color primaryColor = Color(0xFF5625BA); + // static const Color primaryColorLight = Color(0xFFCCBDEA); + // static const Color secondaryColor = Color(0xFF41a2bc); + static const Color primaryColor = Color(0xFF8560E0); + static const Color primaryColorLight = Color(0xFFDBC9FF); + static const Color secondaryColor = Color.fromARGB(255, 253, 191, 1); // Pangea# - static String get defaultHomeserver => _defaultHomeserver; - static double fontSizeFactor = 1; static const Color chatColor = primaryColor; - static Color? colorSchemeSeed = primaryColor; static const double messageFontSize = 16.0; static const bool allowOtherHomeservers = true; static const bool enableRegistration = true; - // #Pangea - static const double toolbarMaxHeight = 250.0; - static const double toolbarMinHeight = 150.0; - static const double toolbarMinWidth = 350.0; - static const double toolbarMenuHeight = 50.0; - static const double defaultHeaderHeight = 56.0; - static const double toolbarButtonsHeight = 50.0; - static const double toolbarSpacing = 8.0; - static const double toolbarIconSize = 24.0; - static const double readingAssistanceInputBarHeight = 175.0; - static const double reactionsPickerHeight = 48.0; - static const double chatInputRowOverlayPadding = 8.0; - static const double selectModeInputBarHeight = 0; - // reactionsPickerHeight + (chatInputRowOverlayPadding * 2) + toolbarSpacing; - static const double practiceModeInputBarHeight = - readingAssistanceInputBarHeight + - toolbarButtonsHeight + - (chatInputRowOverlayPadding * 2) + - toolbarSpacing; - - static TextStyle messageTextStyle( - Event? event, - Color textColor, - ) { - final fontSize = messageFontSize * fontSizeFactor; - final bigEmotes = event != null && - event.onlyEmotes && - event.numberEmotes > 0 && - event.numberEmotes <= 3; - - return TextStyle( - color: textColor, - fontSize: bigEmotes ? fontSize * 5 : fontSize, - decoration: - (event?.redacted ?? false) ? TextDecoration.lineThrough : null, - height: 1.3, - ); - } - - // static const Color primaryColor = Color(0xFF5625BA); - // static const Color primaryColorLight = Color(0xFFCCBDEA); - static const Color primaryColor = Color(0xFF8560E0); - static const Color primaryColorLight = Color(0xFFDBC9FF); - // static const Color secondaryColor = Color(0xFF41a2bc); - static const Color secondaryColor = Color.fromARGB(255, 253, 191, 1); - static const Color activeToggleColor = Color(0xFF33D057); - static const Color success = Color(0xFF33D057); - static const Color warning = Color.fromARGB(255, 210, 124, 12); - static const Color gold = Color.fromARGB(255, 253, 191, 1); - static const Color silver = Color.fromARGB(255, 192, 192, 192); - static const Color bronze = Color.fromARGB(255, 205, 127, 50); - static const Color goldLight = Color.fromARGB(255, 254, 223, 73); - - static const Color yellowLight = Color.fromARGB(255, 247, 218, 120); - static const Color yellowDark = Color.fromARGB(255, 253, 191, 1); - - static const Color error = Colors.red; - static const int overlayAnimationDuration = 250; - static const int roomCreationTimeoutSeconds = 15; - // static String _privacyUrl = - // 'https://gitlab.com/famedly/fluffychat/-/blob/main/PRIVACY.md'; - static String _privacyUrl = "https://www.pangeachat.com/privacy"; - //Pangea# - - static const Set defaultReactions = {'👍', '❤️', '😂', '😮', '😢'}; - - static String get privacyUrl => _privacyUrl; - // #Pangea - // static const String website = 'https://fluffychat.im'; - static const String website = "https://pangea.chat/"; - // Pangea# - static const String enablePushTutorial = - 'https://github.com/krille-chan/fluffychat/wiki/Push-Notifications-without-Google-Services'; - static const String encryptionTutorial = - 'https://github.com/krille-chan/fluffychat/wiki/How-to-use-end-to-end-encryption-in-FluffyChat'; - static const String startChatTutorial = - 'https://github.com/krille-chan/fluffychat/wiki/How-to-Find-Users-in-FluffyChat'; - static const String appId = 'im.fluffychat.FluffyChat'; - // #Pangea - // static const String appOpenUrlScheme = 'im.fluffychat'; - static const String appOpenUrlScheme = 'matrix.pangea.chat'; - // Pangea# - static String _webBaseUrl = 'https://fluffychat.im/web'; - - static String get webBaseUrl => _webBaseUrl; - static const String sourceCodeUrl = - 'https://github.com/krille-chan/fluffychat'; - // #Pangea - // static const String supportUrl = - // 'https://github.com/krille-chan/fluffychat/issues'; - // static const String changelogUrl = - // 'https://github.com/krille-chan/fluffychat/blob/main/CHANGELOG.md'; - static const String supportUrl = 'https://www.pangeachat.com/faqs'; - static const String termsOfServiceUrl = - 'https://www.pangeachat.com/terms-of-service'; - // Pangea# - static final Uri newIssueUrl = Uri( - scheme: 'https', - host: 'github.com', - path: '/krille-chan/fluffychat/issues/new', - ); - // #Pangea - static const bool enableSentry = true; - static const String sentryDns = - 'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143'; - // Pangea# - static bool renderHtml = true; - // #Pangea - // static bool hideRedactedEvents = false; - static bool hideRedactedEvents = true; - // Pangea# - static bool hideUnknownEvents = true; - static bool hideUnimportantStateEvents = true; - static bool separateChatTypes = false; - static bool autoplayImages = true; - static bool sendTypingNotifications = true; - static bool sendPublicReadReceipts = true; - static bool swipeRightToLeftToReply = true; - static bool? sendOnEnter; - static bool useActivityImageAsChatBackground = true; - static bool showPresences = true; - // #Pangea - // static bool displayNavigationRail = false; - static bool displayNavigationRail = true; - // Pangea# - static bool experimentalVoip = false; static const bool hideTypingUsernames = false; - static const bool hideAllStateEvents = false; + static const String inviteLinkPrefix = 'https://matrix.to/#/'; static const String deepLinkPrefix = 'im.fluffychat://chat/'; static const String schemePrefix = 'matrix:'; @@ -165,81 +36,107 @@ abstract class AppConfig { // Pangea# static const double borderRadius = 18.0; static const double columnWidth = 360.0; + + // #Pangea + // static const String website = 'https://fluffy.chat'; + static const String website = "https://pangea.chat/"; + // Pangea# + static const String enablePushTutorial = + 'https://fluffy.chat/faq/#push_without_google_services'; + static const String encryptionTutorial = + 'https://fluffy.chat/faq/#how_to_use_end_to_end_encryption'; + static const String startChatTutorial = + 'https://fluffy.chat/faq/#how_do_i_find_other_users'; + static const String howDoIGetStickersTutorial = + 'https://fluffy.chat/faq/#how_do_i_get_stickers'; + static const String appId = 'im.fluffychat.FluffyChat'; + // #Pangea + // static const String appOpenUrlScheme = 'im.fluffychat'; + static const String appOpenUrlScheme = 'matrix.pangea.chat'; + // Pangea# + + static const String sourceCodeUrl = + 'https://github.com/krille-chan/fluffychat'; + // static const String supportUrl = + // 'https://github.com/krille-chan/fluffychat/issues'; + // static const String changelogUrl = 'https://fluffy.chat/en/changelog/'; + // static const String donationUrl = 'https://ko-fi.com/krille'; + static const String supportUrl = 'https://www.pangeachat.com/faqs'; + static const String termsOfServiceUrl = + 'https://www.pangeachat.com/terms-of-service'; + // Pangea# + + static const Set defaultReactions = {'👍', '❤️', '😂', '😮', '😢'}; + + static final Uri newIssueUrl = Uri( + scheme: 'https', + host: 'github.com', + path: '/krille-chan/fluffychat/issues/new', + ); + static final Uri homeserverList = Uri( scheme: 'https', host: 'servers.joinmatrix.org', path: 'servers.json', ); + + static final Uri privacyUrl = Uri( + scheme: 'https', + host: 'fluffy.chat', + path: '/en/privacy', + ); + + static const String mainIsolatePortName = 'main_isolate'; + static const String pushIsolatePortName = 'push_isolate'; + // #Pangea - static String googlePlayMangementUrl = - "https://play.google.com/store/account/subscriptions"; - static String googlePlayHistoryUrl = - "https://play.google.com/store/account/orderhistory"; + static String assetsBaseURL = + "https://pangea-chat-client-assets.s3.us-east-1.amazonaws.com"; + static String androidUpdateURL = + "https://play.google.com/store/apps/details?id=com.talktolearn.chat"; + static String iosUpdateURL = "itms-apps://itunes.apple.com/app/id1445118630"; static String googlePlayPaymentMethodUrl = "https://play.google.com/store/paymentmethods"; static String appleMangementUrl = "https://apps.apple.com/account/subscriptions"; - static String stripePerMonth = - "https://buy.stripe.com/test_bIY6ssd8z5Uz8ec8ww"; - static String iosPromoCode = - "https://apps.apple.com/redeem?ctx=offercodes&id=1445118630&code="; - static String androidUpdateURL = - "https://play.google.com/store/apps/details?id=com.talktolearn.chat"; - static String iosUpdateURL = "itms-apps://itunes.apple.com/app/id1445118630"; - - static String windowsTTSDownloadInstructions = - "https://support.microsoft.com/en-us/topic/download-languages-and-voices-for-immersive-reader-read-mode-and-read-aloud-4c83a8d8-7486-42f7-8e46-2b0fdf753130"; - static String androidTTSDownloadInstructions = - "https://support.google.com/accessibility/android/answer/6006983?hl=en"; - static String assetsBaseURL = - "https://pangea-chat-client-assets.s3.us-east-1.amazonaws.com"; - + static String googlePlayMangementUrl = + "https://play.google.com/store/account/subscriptions"; + static String googlePlayHistoryUrl = + "https://play.google.com/store/account/orderhistory"; + static bool useActivityImageAsChatBackground = true; + static const int overlayAnimationDuration = 250; + static const Color gold = Color.fromARGB(255, 253, 191, 1); + static const Color goldLight = Color.fromARGB(255, 254, 223, 73); + static const Color success = Color(0xFF33D057); + static const Color error = Colors.red; + static const Color warning = Color.fromARGB(255, 210, 124, 12); + static const Color activeToggleColor = Color(0xFF33D057); + static const Color yellowLight = Color.fromARGB(255, 247, 218, 120); + static const Color yellowDark = Color.fromARGB(255, 253, 191, 1); + static const double toolbarMaxHeight = 250.0; + static const double toolbarMinWidth = 350.0; + static const double toolbarMinHeight = 150.0; + static const double toolbarMenuHeight = 50.0; + static const double readingAssistanceInputBarHeight = 175.0; static String errorSubscriptionId = "pangea_subscription_error"; - static double volume = 1.0; - static bool showedActivityMenu = false; - // Pangea# + static TextStyle messageTextStyle(Event? event, Color textColor) { + final fontSize = messageFontSize * AppSettings.fontSizeFactor.value; + final bigEmotes = + event != null && + event.onlyEmotes && + event.numberEmotes > 0 && + event.numberEmotes <= 3; - static void loadFromJson(Map json) { - if (json['chat_color'] != null) { - try { - colorSchemeSeed = Color(json['chat_color']); - } catch (e) { - Logs().w( - 'Invalid color in config.json! Please make sure to define the color in this format: "0xffdd0000"', - e, - ); - } - } - if (json['application_name'] is String) { - _applicationName = json['application_name']; - } - if (json['application_welcome_message'] is String) { - _applicationWelcomeMessage = json['application_welcome_message']; - } - // #Pangea - // if (json['default_homeserver'] is String) { - // _defaultHomeserver = json['default_homeserver']; - // } - // Pangea# - if (json['privacy_url'] is String) { - _privacyUrl = json['privacy_url']; - } - if (json['web_base_url'] is String) { - _webBaseUrl = json['web_base_url']; - } - if (json['render_html'] is bool) { - // #Pangea - // this is interfering with our PangeaRichText functionality, removing it for now - renderHtml = false; - // renderHtml = json['render_html']; - // Pangea# - } - if (json['hide_redacted_events'] is bool) { - hideRedactedEvents = json['hide_redacted_events']; - } - if (json['hide_unknown_events'] is bool) { - hideUnknownEvents = json['hide_unknown_events']; - } + return TextStyle( + color: textColor, + fontSize: bigEmotes ? fontSize * 5 : fontSize, + decoration: (event?.redacted ?? false) + ? TextDecoration.lineThrough + : null, + height: 1.3, + ); } + + // Pangea# } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 032bf08e5..ddc628d92 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/archive/archive.dart'; +import 'package:fluffychat/pages/bootstrap/bootstrap_dialog.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; @@ -29,7 +30,6 @@ import 'package:fluffychat/pages/settings_chat/settings_chat.dart'; import 'package:fluffychat/pages/settings_emotes/settings_emotes.dart'; import 'package:fluffychat/pages/settings_homeserver/settings_homeserver.dart'; import 'package:fluffychat/pages/settings_ignore_list/settings_ignore_list.dart'; -import 'package:fluffychat/pages/settings_multiple_emotes/settings_multiple_emotes.dart'; import 'package:fluffychat/pages/settings_notifications/settings_notifications.dart'; import 'package:fluffychat/pages/settings_password/settings_password.dart'; import 'package:fluffychat/pages/settings_security/settings_security.dart'; @@ -75,26 +75,22 @@ abstract class AppRoutes { static FutureOr loggedInRedirect( BuildContext context, GoRouterState state, - ) { // #Pangea - // Matrix.of(context).widget.clients.any((client) => client.isLogged()) - // ? '/rooms' - // : null; - return PAuthGaurd.homeRedirect(context, state); - // Pangea# - } + // ) => Matrix.of(context).widget.clients.any((client) => client.isLogged()) + // ? '/rooms' + // : null; + ) => PAuthGaurd.homeRedirect(context, state); + // Pangea# static FutureOr loggedOutRedirect( BuildContext context, GoRouterState state, - ) { // #Pangea - // Matrix.of(context).widget.clients.any((client) => client.isLogged()) + // ) => Matrix.of(context).widget.clients.any((client) => client.isLogged()) // ? null // : '/home'; - return PAuthGaurd.roomsRedirect(context, state); - // Pangea# - } + ) => PAuthGaurd.roomsRedirect(context, state); + // Pangea# AppRoutes(); @@ -103,8 +99,8 @@ abstract class AppRoutes { path: '/', redirect: (context, state) => Matrix.of(context).widget.clients.any((client) => client.isLogged()) - ? '/rooms' - : '/home', + ? '/rooms' + : '/home', ), GoRoute( path: '/home', @@ -145,28 +141,20 @@ abstract class AppRoutes { // #Pangea GoRoute( path: 'language', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const LanguageSelectionPage(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const LanguageSelectionPage()), routes: [ GoRoute( path: 'signup', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const SignupPage(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const SignupPage()), routes: [ GoRoute( path: 'email', pageBuilder: (context, state) => defaultPageBuilder( context, state, - const SignupPage( - withEmail: true, - ), + const SignupPage(withEmail: true), ), ), ], @@ -178,28 +166,19 @@ abstract class AppRoutes { ), GoRoute( path: '/logs', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const LogViewer(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const LogViewer()), ), GoRoute( path: '/configs', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const ConfigViewer(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const ConfigViewer()), ), // #Pangea GoRoute( path: '/registration', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const LanguageSelectionPage(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const LanguageSelectionPage()), redirect: PAuthGaurd.onboardingRedirect, routes: [ GoRoute( @@ -212,14 +191,13 @@ abstract class AppRoutes { ), GoRoute( path: 'notifications', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const EnableNotifications(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const EnableNotifications()), redirect: (context, state) async { - final redirect = - await PAuthGaurd.onboardingRedirect(context, state); + final redirect = await PAuthGaurd.onboardingRedirect( + context, + state, + ); if (redirect != null) return redirect; final enabled = await Matrix.of(context).notificationsEnabled; if (enabled) return "/registration/course"; @@ -228,11 +206,8 @@ abstract class AppRoutes { ), GoRoute( path: 'course', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const SpaceCodeOnboarding(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const SpaceCodeOnboarding()), ), ], ), @@ -247,6 +222,15 @@ abstract class AppRoutes { ), ), // Pangea# + GoRoute( + path: '/backup', + redirect: loggedOutRedirect, + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + BootstrapDialog(wipe: state.uri.queryParameters['wipe'] == 'true'), + ), + ), ShellRoute( // Never use a transition on the shell route. Changing the PageBuilder // here based on a MediaQuery causes the child to briefly be rendered @@ -260,16 +244,14 @@ abstract class AppRoutes { // ? TwoColumnLayout( // mainView: ChatList( // activeChat: state.pathParameters['roomid'], + // activeSpace: state.uri.queryParameters['spaceId'], // displayNavigationRail: // state.path?.startsWith('/rooms/settings') != true, // ), // sideView: child, // ) // : child, - TwoColumnLayout( - state: state, - sideView: child, - ), + TwoColumnLayout(state: state, sideView: child), // Pangea# ), routes: [ @@ -292,17 +274,14 @@ abstract class AppRoutes { // Pangea# : ChatList( activeChat: state.pathParameters['roomid'], - activeSpaceId: state.pathParameters['spaceid'], + activeSpace: state.uri.queryParameters['spaceId'], ), ), routes: [ GoRoute( path: 'archive', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const Archive(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const Archive()), routes: [ GoRoute( path: ':roomid', @@ -321,23 +300,14 @@ abstract class AppRoutes { ), GoRoute( path: 'newprivatechat', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const NewPrivateChat(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const NewPrivateChat()), redirect: loggedOutRedirect, ), GoRoute( path: 'newgroup', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - // #Pangea - // const NewGroup(), - NewGroup(spaceId: state.uri.queryParameters['space']), - // Pangea# - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const NewGroup()), redirect: loggedOutRedirect, ), GoRoute( @@ -350,25 +320,10 @@ abstract class AppRoutes { redirect: loggedOutRedirect, ), // #Pangea - // ShellRoute( - // pageBuilder: (context, state, child) => defaultPageBuilder( - // context, - // state, - // FluffyThemes.isColumnMode(context) - // ? TwoColumnLayout( - // mainView: PangeaSideView(path: state.fullPath), - // sideView: child, - // ) - // : child, - // ), - // routes: [ GoRoute( path: 'course', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const FindCoursePage(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const FindCoursePage()), routes: [ GoRoute( path: 'private', @@ -638,11 +593,8 @@ abstract class AppRoutes { ), GoRoute( path: 'style', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const SettingsStyle(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const SettingsStyle()), redirect: loggedOutRedirect, ), GoRoute( @@ -656,18 +608,15 @@ abstract class AppRoutes { ), GoRoute( path: 'chat', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const SettingsChat(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const SettingsChat()), routes: [ GoRoute( path: 'emotes', pageBuilder: (context, state) => defaultPageBuilder( context, state, - const EmotesSettings(), + EmotesSettings(roomId: state.pathParameters['roomid']), ), ), ], @@ -756,9 +705,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - const SettingsLearning( - isDialog: false, - ), + const SettingsLearning(isDialog: false), ), redirect: loggedOutRedirect, ), @@ -778,11 +725,8 @@ abstract class AppRoutes { // #Pangea GoRoute( path: 'spaces', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const EmptyPage(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const EmptyPage()), redirect: (context, state) { if (state.pathParameters['spaceid'] == null) { return "/rooms"; @@ -804,15 +748,12 @@ abstract class AppRoutes { routes: [ GoRoute( path: 'details', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const EmptyPage(), - ), + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const EmptyPage()), redirect: (context, state) { String subroute = state.fullPath?.split(":spaceid/details").last ?? - ""; + ""; if (state.uri.queryParameters.isNotEmpty) { final queryString = state.uri.queryParameters.entries @@ -909,10 +850,10 @@ abstract class AppRoutes { roomId: state.pathParameters['roomid']!, initialFilter: state.uri.queryParameters['filter'] != null - ? InvitationFilter.fromString( - state.uri.queryParameters['filter']!, - ) - : null, + ? InvitationFilter.fromString( + state.uri.queryParameters['filter']!, + ) + : null, ), ), redirect: loggedOutRedirect, @@ -964,9 +905,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - ChatSearchPage( - roomId: state.pathParameters['roomid']!, - ), + ChatSearchPage(roomId: state.pathParameters['roomid']!), ), redirect: loggedOutRedirect, ), @@ -986,6 +925,10 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, + // #Pangea + // InvitationSelection( + // roomId: state.pathParameters['roomid']!, + // ), PangeaInvitationSelection( roomId: state.pathParameters['roomid']!, initialFilter: state.uri.queryParameters['filter'] != null @@ -994,6 +937,7 @@ abstract class AppRoutes { ) : null, ), + // Pangea# ), redirect: loggedOutRedirect, ), @@ -1002,9 +946,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - ChatDetails( - roomId: state.pathParameters['roomid']!, - ), + ChatDetails(roomId: state.pathParameters['roomid']!), ), // #Pangea routes: roomDetailsRoutes('roomid'), @@ -1045,42 +987,18 @@ abstract class AppRoutes { // pageBuilder: (context, state) => defaultPageBuilder( // context, // state, - // PangeaInvitationSelection( + // InvitationSelection( // roomId: state.pathParameters['roomid']!, - // initialFilter: - // state.uri.queryParameters['filter'] != null - // ? InvitationFilter.fromString( - // state.uri.queryParameters['filter']!, - // ) - // : null, // ), // ), // redirect: loggedOutRedirect, // ), // GoRoute( - // path: 'multiple_emotes', - // pageBuilder: (context, state) => defaultPageBuilder( - // context, - // state, - // const MultipleEmotesSettings(), - // ), - // redirect: loggedOutRedirect, - // ), - // GoRoute( // path: 'emotes', // pageBuilder: (context, state) => defaultPageBuilder( // context, // state, - // const EmotesSettings(), - // ), - // redirect: loggedOutRedirect, - // ), - // GoRoute( - // path: 'emotes/:state_key', - // pageBuilder: (context, state) => defaultPageBuilder( - // context, - // state, - // const EmotesSettings(), + // EmotesSettings(roomId: state.pathParameters['roomid']), // ), // redirect: loggedOutRedirect, // ), @@ -1100,147 +1018,126 @@ abstract class AppRoutes { BuildContext context, GoRouterState state, Widget child, - ) => - NoTransitionPage( - key: state.pageKey, - restorationId: state.pageKey.value, - child: child, - ); + ) => NoTransitionPage( + key: state.pageKey, + restorationId: state.pageKey.value, + child: child, + ); static Page defaultPageBuilder( BuildContext context, GoRouterState state, Widget child, - ) => - // #Pangea - noTransitionPageBuilder(context, state, child); - // FluffyThemes.isColumnMode(context) - // ? noTransitionPageBuilder(context, state, child) - // : MaterialPage( - // key: state.pageKey, - // restorationId: state.pageKey.value, - // child: child, - // ); + // #Pangea + // ) => FluffyThemes.isColumnMode(context) + // ? noTransitionPageBuilder(context, state, child) + // : MaterialPage( + // key: state.pageKey, + // restorationId: state.pageKey.value, + // child: child, + // ); + ) => noTransitionPageBuilder(context, state, child); // Pangea# // #Pangea static List get newRoomRoutes => [ - GoRoute( - path: 'newgroup', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const NewGroup(), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'newspace', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const NewGroup(createGroupType: CreateGroupType.space), - ), - redirect: loggedOutRedirect, - ), - ]; + GoRoute( + path: 'newgroup', + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const NewGroup()), + redirect: loggedOutRedirect, + ), + GoRoute( + path: 'newspace', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + const NewGroup(createGroupType: CreateGroupType.space), + ), + redirect: loggedOutRedirect, + ), + ]; static List roomDetailsRoutes(String roomKey) => [ - GoRoute( - path: '/edit', - redirect: loggedOutRedirect, - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - EditCourse(roomId: state.pathParameters[roomKey]!), - ), + GoRoute( + path: '/edit', + redirect: loggedOutRedirect, + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + EditCourse(roomId: state.pathParameters[roomKey]!), + ), + ), + GoRoute( + path: '/analytics', + redirect: loggedOutRedirect, + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + SpaceAnalytics(roomId: state.pathParameters[roomKey]!), + ), + ), + GoRoute( + path: 'access', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ChatAccessSettings(roomId: state.pathParameters[roomKey]!), + ), + redirect: loggedOutRedirect, + ), + GoRoute( + path: 'members', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ChatMembersPage( + roomId: state.pathParameters[roomKey]!, + filter: state.uri.queryParameters['filter'], ), - GoRoute( - path: '/analytics', - redirect: loggedOutRedirect, - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - SpaceAnalytics( - roomId: state.pathParameters[roomKey]!, - ), - ), + ), + redirect: loggedOutRedirect, + ), + GoRoute( + path: 'permissions', + pageBuilder: (context, state) => + defaultPageBuilder(context, state, const ChatPermissionsSettings()), + redirect: loggedOutRedirect, + ), + GoRoute( + path: 'invite', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + PangeaInvitationSelection( + roomId: state.pathParameters[roomKey]!, + initialFilter: state.uri.queryParameters['filter'] != null + ? InvitationFilter.fromString( + state.uri.queryParameters['filter']!, + ) + : null, ), - GoRoute( - path: 'access', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - ChatAccessSettings( - roomId: state.pathParameters[roomKey]!, - ), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'members', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - ChatMembersPage( - roomId: state.pathParameters[roomKey]!, - filter: state.uri.queryParameters['filter'], - ), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'permissions', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const ChatPermissionsSettings(), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'invite', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - PangeaInvitationSelection( - roomId: state.pathParameters[roomKey]!, - initialFilter: state.uri.queryParameters['filter'] != null - ? InvitationFilter.fromString( - state.uri.queryParameters['filter']!, - ) - : null, - ), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'multiple_emotes', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const MultipleEmotesSettings(), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'emotes', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const EmotesSettings(), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'emotes/:state_key', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const EmotesSettings(), - ), - redirect: loggedOutRedirect, - ), - ]; + ), + redirect: loggedOutRedirect, + ), + GoRoute( + path: 'emotes', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + EmotesSettings(roomId: state.pathParameters[roomKey]!), + ), + redirect: loggedOutRedirect, + ), + GoRoute( + path: 'emotes/:state_key', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + EmotesSettings(roomId: state.pathParameters[roomKey]!), + ), + redirect: loggedOutRedirect, + ), + ]; // Pangea# } diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index c37e79f13..04a5f854a 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -1,47 +1,16 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; + +import 'package:async/async.dart'; +import 'package:http/http.dart' as http; +import 'package:matrix/matrix_api_lite/utils/logs.dart'; import 'package:shared_preferences/shared_preferences.dart'; -abstract class SettingKeys { - static const String renderHtml = 'chat.fluffy.renderHtml'; - static const String hideRedactedEvents = 'chat.fluffy.hideRedactedEvents'; - static const String hideUnknownEvents = 'chat.fluffy.hideUnknownEvents'; - static const String hideUnimportantStateEvents = - 'chat.fluffy.hideUnimportantStateEvents'; - static const String separateChatTypes = 'chat.fluffy.separateChatTypes'; - static const String sentry = 'sentry'; - static const String theme = 'theme'; - static const String amoledEnabled = 'amoled_enabled'; - static const String codeLanguage = 'code_language'; - static const String showNoGoogle = 'chat.fluffy.show_no_google'; - static const String fontSizeFactor = 'chat.fluffy.font_size_factor'; - static const String showNoPid = 'chat.fluffy.show_no_pid'; - static const String databasePassword = 'database-password'; - static const String appLockKey = 'chat.fluffy.app_lock'; - static const String unifiedPushRegistered = - 'chat.fluffy.unifiedpush.registered'; - static const String unifiedPushEndpoint = 'chat.fluffy.unifiedpush.endpoint'; - static const String ownStatusMessage = 'chat.fluffy.status_msg'; - static const String dontAskForBootstrapKey = - 'chat.fluffychat.dont_ask_bootstrap'; - static const String autoplayImages = 'chat.fluffy.autoplay_images'; - static const String sendTypingNotifications = - 'chat.fluffy.send_typing_notifications'; - static const String sendPublicReadReceipts = - 'chat.fluffy.send_public_read_receipts'; - static const String sendOnEnter = 'chat.fluffy.send_on_enter'; - static const String swipeRightToLeftToReply = - 'chat.fluffy.swipeRightToLeftToReply'; - static const String experimentalVoip = 'chat.fluffy.experimental_voip'; - static const String showPresences = 'chat.fluffy.show_presences'; - static const String displayNavigationRail = - 'chat.fluffy.display_navigation_rail'; - // #Pangea - static const String volume = 'pangea.volume'; - static const String showedActivityMenu = - 'pangea.showed_activity_menu_tutorial'; - // Pangea# -} +import 'package:fluffychat/utils/platform_infos.dart'; enum AppSettings { + textMessageMaxLength('textMessageMaxLength', 16384), audioRecordingNumChannels('audioRecordingNumChannels', 1), audioRecordingAutoGain('audioRecordingAutoGain', true), audioRecordingEchoCancel('audioRecordingEchoCancel', false), @@ -51,6 +20,9 @@ enum AppSettings { // audioRecordingSamplingRate('audioRecordingSamplingRate', 44100), audioRecordingSamplingRate('audioRecordingSamplingRate', 22050), // Pangea# + showNoGoogle('chat.fluffy.show_no_google', false), + unifiedPushRegistered('chat.fluffy.unifiedpush.registered', false), + unifiedPushEndpoint('chat.fluffy.unifiedpush.endpoint', ''), pushNotificationsGatewayUrl( 'pushNotificationsGatewayUrl', // #Pangea @@ -64,49 +36,168 @@ enum AppSettings { // 'event_id_only', // ), // Pangea# + renderHtml('chat.fluffy.renderHtml', true), + fontSizeFactor('chat.fluffy.font_size_factor', 1.0), + hideRedactedEvents('chat.fluffy.hideRedactedEvents', false), + hideUnknownEvents('chat.fluffy.hideUnknownEvents', true), + separateChatTypes('chat.fluffy.separateChatTypes', false), + autoplayImages('chat.fluffy.autoplay_images', true), + sendTypingNotifications('chat.fluffy.send_typing_notifications', true), + sendPublicReadReceipts('chat.fluffy.send_public_read_receipts', true), + swipeRightToLeftToReply('chat.fluffy.swipeRightToLeftToReply', true), + sendOnEnter('chat.fluffy.send_on_enter', false), + showPresences('chat.fluffy.show_presences', true), + displayNavigationRail('chat.fluffy.display_navigation_rail', false), + experimentalVoip('chat.fluffy.experimental_voip', false), shareKeysWith('chat.fluffy.share_keys_with_2', 'all'), noEncryptionWarningShown( 'chat.fluffy.no_encryption_warning_shown', false, ), - displayChatDetailsColumn( - 'chat.fluffy.display_chat_details_column', - false, - ), + displayChatDetailsColumn('chat.fluffy.display_chat_details_column', false), + // AppConfig-mirrored settings + // #Pangea + // applicationName('chat.fluffy.application_name', 'FluffyChat'), + // defaultHomeserver('chat.fluffy.default_homeserver', 'matrix.org'), + applicationName('chat.fluffy.application_name', 'Pangea Chat'), + // Pangea# + // colorSchemeSeed stored as ARGB int + // #Pangea + // colorSchemeSeedInt('chat.fluffy.color_scheme_seed', 0xFF5625BA), + colorSchemeSeedInt('chat.fluffy.color_scheme_seed', 0xFF8560E0), + volume('pangea.volume', 1.0), + // Pangea# + emojiSuggestionLocale('emoji_suggestion_locale', ''), enableSoftLogout('chat.fluffy.enable_soft_logout', false); final String key; final T defaultValue; const AppSettings(this.key, this.defaultValue); + + static SharedPreferences get store => _store!; + static SharedPreferences? _store; + + static Future init({bool loadWebConfigFile = true}) async { + if (AppSettings._store != null) return AppSettings.store; + + final store = AppSettings._store = await SharedPreferences.getInstance(); + + // Migrate wrong datatype for fontSizeFactor + final fontSizeFactorString = Result( + () => store.getString(AppSettings.fontSizeFactor.key), + ).asValue?.value; + if (fontSizeFactorString != null) { + Logs().i('Migrate wrong datatype for fontSizeFactor!'); + await store.remove(AppSettings.fontSizeFactor.key); + final fontSizeFactor = double.tryParse(fontSizeFactorString); + if (fontSizeFactor != null) { + await store.setDouble(AppSettings.fontSizeFactor.key, fontSizeFactor); + } + } + + if (store.getBool(AppSettings.sendOnEnter.key) == null) { + await store.setBool(AppSettings.sendOnEnter.key, !PlatformInfos.isMobile); + } + if (kIsWeb && loadWebConfigFile) { + try { + final configJsonString = utf8.decode( + (await http.get(Uri.parse('config.json'))).bodyBytes, + ); + final configJson = + json.decode(configJsonString) as Map; + for (final setting in AppSettings.values) { + if (store.get(setting.key) != null) continue; + final configValue = configJson[setting.name]; + if (configValue == null) continue; + if (configValue is bool) { + await store.setBool(setting.key, configValue); + } + if (configValue is String) { + await store.setString(setting.key, configValue); + } + if (configValue is int) { + await store.setInt(setting.key, configValue); + } + if (configValue is double) { + await store.setDouble(setting.key, configValue); + } + } + } on FormatException catch (_) { + Logs().v('[ConfigLoader] config.json not found'); + } catch (e) { + Logs().v('[ConfigLoader] config.json not found', e); + } + } + + return store; + } } extension AppSettingsBoolExtension on AppSettings { - bool getItem(SharedPreferences store) => store.getBool(key) ?? defaultValue; + bool get value { + final value = Result(() => AppSettings.store.getBool(key)); + final error = value.asError; + if (error != null) { + Logs().e( + 'Unable to fetch $key from storage. Removing entry...', + error.error, + error.stackTrace, + ); + } + return value.asValue?.value ?? defaultValue; + } - Future setItem(SharedPreferences store, bool value) => - store.setBool(key, value); + Future setItem(bool value) => AppSettings.store.setBool(key, value); } extension AppSettingsStringExtension on AppSettings { - String getItem(SharedPreferences store) => - store.getString(key) ?? defaultValue; + String get value { + final value = Result(() => AppSettings.store.getString(key)); + final error = value.asError; + if (error != null) { + Logs().e( + 'Unable to fetch $key from storage. Removing entry...', + error.error, + error.stackTrace, + ); + } + return value.asValue?.value ?? defaultValue; + } - Future setItem(SharedPreferences store, String value) => - store.setString(key, value); + Future setItem(String value) => AppSettings.store.setString(key, value); } extension AppSettingsIntExtension on AppSettings { - int getItem(SharedPreferences store) => store.getInt(key) ?? defaultValue; + int get value { + final value = Result(() => AppSettings.store.getInt(key)); + final error = value.asError; + if (error != null) { + Logs().e( + 'Unable to fetch $key from storage. Removing entry...', + error.error, + error.stackTrace, + ); + } + return value.asValue?.value ?? defaultValue; + } - Future setItem(SharedPreferences store, int value) => - store.setInt(key, value); + Future setItem(int value) => AppSettings.store.setInt(key, value); } extension AppSettingsDoubleExtension on AppSettings { - double getItem(SharedPreferences store) => - store.getDouble(key) ?? defaultValue; + double get value { + final value = Result(() => AppSettings.store.getDouble(key)); + final error = value.asError; + if (error != null) { + Logs().e( + 'Unable to fetch $key from storage. Removing entry...', + error.error, + error.stackTrace, + ); + } + return value.asValue?.value ?? defaultValue; + } - Future setItem(SharedPreferences store, double value) => - store.setDouble(key, value); + Future setItem(double value) => AppSettings.store.setDouble(key, value); } diff --git a/lib/config/themes.dart b/lib/config/themes.dart index 58b845f62..eadce08c4 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'app_config.dart'; abstract class FluffyThemes { @@ -18,15 +19,12 @@ abstract class FluffyThemes { width > columnWidth * 2 + navRailWidth; static bool isColumnMode(BuildContext context) => - isColumnModeByWidth(MediaQuery.of(context).size.width); + isColumnModeByWidth(MediaQuery.sizeOf(context).width); static bool isThreeColumnMode(BuildContext context) => - MediaQuery.of(context).size.width > FluffyThemes.columnWidth * 3.5; + MediaQuery.sizeOf(context).width > FluffyThemes.columnWidth * 3.5; - static LinearGradient backgroundGradient( - BuildContext context, - int alpha, - ) { + static LinearGradient backgroundGradient(BuildContext context, int alpha) { final colorScheme = Theme.of(context).colorScheme; return LinearGradient( begin: Alignment.topCenter, @@ -49,11 +47,7 @@ abstract class FluffyThemes { ]) { final colorScheme = ColorScheme.fromSeed( brightness: brightness, - seedColor: seed ?? - AppConfig.colorSchemeSeed ?? - Theme.of(context).colorScheme.primary, - // primary: AppConfig.primaryColor, - // secondary: AppConfig.gold, + seedColor: seed ?? Color(AppSettings.colorSchemeSeedInt.value), ); final isColumnMode = FluffyThemes.isColumnMode(context); return ThemeData( @@ -98,14 +92,18 @@ abstract class FluffyThemes { ), appBarTheme: AppBarTheme( toolbarHeight: isColumnMode ? 72 : 56, - shadowColor: - isColumnMode ? colorScheme.surfaceContainer.withAlpha(128) : null, + shadowColor: isColumnMode + ? colorScheme.surfaceContainer.withAlpha(128) + : null, // #Pangea // surfaceTintColor: isColumnMode ? colorScheme.surface : null, // backgroundColor: isColumnMode ? colorScheme.surface : null, surfaceTintColor: colorScheme.surface, backgroundColor: colorScheme.surface, // Pangea# + actionsPadding: isColumnMode + ? const EdgeInsets.symmetric(horizontal: 16.0) + : null, systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: brightness.reversed, @@ -116,18 +114,21 @@ abstract class FluffyThemes { ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( - side: BorderSide( - width: 1, - color: colorScheme.primary, - ), + side: BorderSide(width: 1, color: colorScheme.primary), shape: RoundedRectangleBorder( side: BorderSide(color: colorScheme.primary), borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), ), ), ), + progressIndicatorTheme: ProgressIndicatorThemeData( + strokeCap: StrokeCap.round, + color: colorScheme.primary, + refreshBackgroundColor: colorScheme.primaryContainer, + ), snackBarTheme: isColumnMode ? const SnackBarThemeData( + showCloseIcon: true, behavior: SnackBarBehavior.floating, width: FluffyThemes.columnWidth * 1.5, ) @@ -196,8 +197,8 @@ extension BubbleColorTheme on ThemeData { : colorScheme.onPrimaryContainer; Color get secondaryBubbleColor => HSLColor.fromColor( - brightness == Brightness.light - ? colorScheme.tertiary - : colorScheme.tertiaryContainer, - ).withSaturation(0.5).toColor(); + brightness == Brightness.light + ? colorScheme.tertiary + : colorScheme.tertiaryContainer, + ).withSaturation(0.5).toColor(); } diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 2b5f3ca32..21deae2ce 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -2510,15 +2510,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "رجاء حاول مجددا أو اختر خادما مختلفا.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "تسجيل الدخول باستخدام {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "importNow": "استيراد الآن", "@importNow": {}, "importEmojis": "استيراد الرموز التعبيرية", @@ -2972,7 +2963,7 @@ "@gallery": {}, "swipeRightToLeftToReply": "اسحب من اليمين إلى اليسار للرد", "@swipeRightToLeftToReply": {}, - "alwaysUse24HourFormat": "خطأ", + "alwaysUse24HourFormat": "علّم لعرض الوقت بنسق 24 ساعة دائما.‏", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -3182,7 +3173,8 @@ "@previous": {}, "otherPartyNotLoggedIn": "لم يقم الطرف الآخر بتسجيل الدخول حالياً وبالتالي لا يمكنه تلقي الرسائل!", "@otherPartyNotLoggedIn": {}, - "setCustomPermissionLevel": "تعيين مستوى إذن مخصص", + "setCustomPermissionLevel": "حدد مستوى صلاحية مخصص", + "@setCustomPermissionLevel": {}, "setPermissionsLevelDescription": "يرجى اختيار دور معرف مسبقًا أدناه أو إدخال مستوى إذن مخصص بين 0 و 100.", "ignoreUser": "تجاهل المستخدم", "normalUser": "مستخدم عادي", @@ -4391,10 +4383,6 @@ "inviteYourFriends": "ادعُ أصدقائك", "playWithAI": "اللعب مع الذكاء الاصطناعي الآن", "courseStartDesc": "روبوت بانجيا جاهز للعمل في أي وقت!\n\n...لكن التعلم أفضل مع الأصدقاء!", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, "@setPermissionsLevelDescription": { "type": "String", "placeholders": {} @@ -11282,4 +11270,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index 6e79bd96a..992dd6e34 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1,783 +1,3495 @@ { - "alwaysUse24HourFormat": "завсёды выкарыстоўваць фармат 24-гадзіннага часу", + "alwaysUse24HourFormat": "адключана", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, "repeatPassword": "Паўтарыце пароль", - "notAnImage": "Не з'яўляецца файлам выявы.", - "setCustomPermissionLevel": "Усталяваць карыстацкі ўзровень дазволу", - "setPermissionsLevelDescription": "Калі ласка, выберыце папярэдне вызначаную ролю ніжэй або ўвядзіце карыстацкі ўзровень дазволу паміж 0 і 100.", + "@repeatPassword": {}, + "notAnImage": "Не файл выявы.", + "@notAnImage": {}, + "setCustomPermissionLevel": "Задаць карыстальніцкі ўзровень дазволу", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Калі ласка, выберыце ролю ніжэй ці ўвядзіце карыстальніцкі ўзровень дазволу паміж 0 і 100.", + "@setPermissionsLevelDescription": {}, "ignoreUser": "Ігнараваць карыстальніка", + "@ignoreUser": {}, "normalUser": "Звычайны карыстальнік", - "remove": "Выдаліць", - "importNow": "Імпартаваць цяпер", + "@normalUser": {}, + "remove": "Прыбраць", + "@remove": { + "type": "String", + "placeholders": {} + }, + "importNow": "Імпартаваць зараз", + "@importNow": {}, "importEmojis": "Імпартаваць эмодзі", + "@importEmojis": {}, "importFromZipFile": "Імпартаваць з файла .zip", - "exportEmotePack": "Экспартаваць пакет эмодзі ў выглядзе .zip", - "replace": "Заменіць", - "about": "Пра", + "@importFromZipFile": {}, + "exportEmotePack": "Экспартаваць пак эмодзі як .zip", + "@exportEmotePack": {}, + "replace": "Замяніць", + "@replace": {}, + "about": "Пра праграму", + "@about": {}, "aboutHomeserver": "Пра {homeserver}", - "accept": "Прымаць", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "accept": "Прыняць", + "@accept": { + "type": "String", + "placeholders": {} + }, "acceptedTheInvitation": "👍 {username} прыняў запрашэнне", + "@acceptedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, "account": "Уліковы запіс", - "activatedEndToEndEncryption": "🔐 {username} актываваў канчатковае шыфраванне", - "addEmail": "Дадаць электронную пошту", - "confirmMatrixId": "Калі ласка, пацвердзіце свой Matrix ID, каб выдаліць свой уліковы запіс.", - "supposedMxid": "Гэта павінна быць {mxid}", - "addChatDescription": "Дадаць апісанне чата...", + "@account": { + "type": "String", + "placeholders": {} + }, + "activatedEndToEndEncryption": "🔐 {username} актывіраваў end to end шыфраванне", + "@activatedEndToEndEncryption": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "addEmail": "Дадаць email", + "@addEmail": { + "type": "String", + "placeholders": {} + }, + "confirmMatrixId": "Калі ласка, спраўдзіце свой Matrix ID перад выдаленнем свайго ўліковага запісу.", + "@confirmMatrixId": {}, + "supposedMxid": "Гэта павінна быць накшталт {mxid}", + "@supposedMxid": { + "type": "String", + "placeholders": { + "mxid": { + "type": "String" + } + } + }, + "addChatDescription": "Дадайце апісанне чату...", + "@addChatDescription": {}, "addToSpace": "Дадаць у прастору", - "admin": "Адміністратар", - "alias": "аліяс", - "all": "Усе", + "@addToSpace": {}, + "admin": "Адмін", + "@admin": { + "type": "String", + "placeholders": {} + }, + "alias": "псеўданім", + "@alias": { + "type": "String", + "placeholders": {} + }, + "all": "Усё", + "@all": { + "type": "String", + "placeholders": {} + }, "allChats": "Усе чаты", - "commandHint_roomupgrade": "Абнавіць гэтую пакой да дадзенай версіі пакоя", - "commandHint_googly": "Адправіць вочы-гуглі", - "commandHint_cuddle": "Адправіць абдымкі", - "commandHint_hug": "Адправіць абдымкі", - "googlyEyesContent": "{senderName} пасылае вам вочы-гуглі", - "cuddleContent": "{senderName} абдымае вас", + "@allChats": { + "type": "String", + "placeholders": {} + }, + "commandHint_roomupgrade": "Абнавіце гэты пакой да згаданай версіі", + "@commandHint_roomupgrade": {}, + "commandHint_googly": "Даслаць смешныя вочы", + "@commandHint_googly": {}, + "commandHint_cuddle": "Даслаць усмешку", + "@commandHint_cuddle": {}, + "commandHint_hug": "Даслаць абдымашкі", + "@commandHint_hug": {}, + "googlyEyesContent": "{senderName} даслаў(-ла) вам смешныя вочы", + "@googlyEyesContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "cuddleContent": "{senderName} усміхаецца вам", + "@cuddleContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, "hugContent": "{senderName} абдымае вас", - "answeredTheCall": "{senderName} адказаў на званок", - "anyoneCanJoin": "Любой можа далучыцца! Аднак адміністратар можа выгнаць і забараніць усіх, хто паводзіць сябе неадэкватна. Тых, хто забаронены, могуць не вярнуцца!", - "appLock": "Блакіроўка прыкладання", - "appLockDescription": "Заблакаваць прыкладанне, калі не карыстаецеся, з дапамогай PIN-кода", + "@hugContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "answeredTheCall": "{senderName} адказаў(-ла) на выклік", + "@answeredTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "anyoneCanJoin": "Усе могуць далучыцца", + "@anyoneCanJoin": { + "type": "String", + "placeholders": {} + }, + "appLock": "Код-пароль", + "@appLock": { + "type": "String", + "placeholders": {} + }, + "appLockDescription": "Блакіруе праграму, пакуль вы не ўвядзіце пін-код", + "@appLockDescription": {}, "archive": "Архіў", - "areGuestsAllowedToJoin": "Ці дазволена гасцявым карыстальнікам далучацца", + "@archive": { + "type": "String", + "placeholders": {} + }, + "areGuestsAllowedToJoin": "Ці дазволена карыстальнікам-гасцям далучыцца", + "@areGuestsAllowedToJoin": { + "type": "String", + "placeholders": {} + }, "areYouSure": "Вы ўпэўнены?", - "areYouSureYouWantToLogout": "Вы ўпэўнены, што хочаце выйсці?", - "askSSSSSign": "Каб мець магчымасць падпісаць іншага чалавека, уводзьце ваш бяспечны пароль або ключ аднаўлення.", - "askVerificationRequest": "Пацвердзіць гэты запыт на праверку ад {username}?", - "autoplayImages": "Аўтаматычна прайграваць анімаваныя стыкеры і эмоцыі", - "badServerLoginTypesException": "Хатні сервер падтрымлівае тыпы ўваходу:\n{serverVersions}\nАле гэта дадатак падтрымлівае толькі:\n{supportedVersions}", - "sendTypingNotifications": "Адправіць апавяшчэнні пра набор тэксту", - "swipeRightToLeftToReply": "Праскрасьце справа налева, каб адказаць", - "sendOnEnter": "Адправіць пры націску Enter", - "badServerVersionsException": "Хатні сервер падтрымлівае версіі спецыфікацыі:\n{serverVersions}\nАле гэта дадатак падтрымлівае толькі {supportedVersions}", - "countChatsAndCountParticipants": "{chats} размоў і {participants} удзельнікаў", - "noMoreChatsFound": "Больш размоў не знойдзена...", - "noChatsFoundHere": "Тут яшчэ не знойдзена размоў. Стварыце новую размову з кімсьці, выкарыстоўваючы кнопку ніжэй. ⤵️", - "joinedChats": "Далучаныя размовы", - "unread": "Непрачытаныя", + "@areYouSure": { + "type": "String", + "placeholders": {} + }, + "areYouSureYouWantToLogout": "Вы ўпэўнены, што хаціце выйсці?", + "@areYouSureYouWantToLogout": { + "type": "String", + "placeholders": {} + }, + "askSSSSSign": "Каб спраўдзіць іншага чалавека, калі ласка, увядзіце фразу-пароль ці ключ аднаўлення.", + "@askSSSSSign": { + "type": "String", + "placeholders": {} + }, + "askVerificationRequest": "Прыняць запрос на верыфікацыю ад {username}?", + "@askVerificationRequest": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "autoplayImages": "Аўтаматычна прайграваць аніміраваныя стыкеры і эмодзі", + "@autoplayImages": { + "type": "String", + "placeholder": {} + }, + "badServerLoginTypesException": "Хатні сервер падтрымлівае наступныя метады ўваходу:\n{serverVersions}\nАле гэта праграма падтрымлівае толькі:\n{supportedVersions}", + "@badServerLoginTypesException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "sendTypingNotifications": "Дасылаць паведамленне пра друк", + "@sendTypingNotifications": {}, + "swipeRightToLeftToReply": "Змахніце ўлева, каб адказаць", + "@swipeRightToLeftToReply": {}, + "sendOnEnter": "Дасылаць на enter", + "@sendOnEnter": {}, + "badServerVersionsException": "Хатні сервер падтрымлівае наступныя версіі спецыфікацыі:\n{serverVersions}\nАле гэта праграма падтрымлівае толькі:{supportedVersions}", + "@badServerVersionsException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "countChatsAndCountParticipants": "{chats} чатаў і {participants} удзельнікаў", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noMoreChatsFound": "Болей чатаў не знойдзена...", + "@noMoreChatsFound": {}, + "noChatsFoundHere": "Здаецца, тут пуста. Пачніце новы чат з кімсьці праз кнопку ніжэй. ⤵️", + "@noChatsFoundHere": {}, + "joinedChats": "Чаты, да якіх вы далучыліся", + "@joinedChats": {}, + "unread": "Непрачытаные", + "@unread": {}, "space": "Прастора", + "@space": {}, "spaces": "Прасторы", - "banFromChat": "Забароніць у размове", - "banned": "Забаронены", - "bannedUser": "{username} заблакаваў {targetName}", - "blockDevice": "Заблакаваць прыладу", - "blocked": "Заблакавана", - "botMessages": "Паведамленні бота", - "cancel": "Адмяніць", + "@spaces": {}, + "banFromChat": "Заблакіраваць ў чаце", + "@banFromChat": { + "type": "String", + "placeholders": {} + }, + "banned": "Заблакіраваны", + "@banned": { + "type": "String", + "placeholders": {} + }, + "bannedUser": "{username} заблакіраваў {targetName}", + "@bannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "blockDevice": "Заблакіраваць прыладу", + "@blockDevice": { + "type": "String", + "placeholders": {} + }, + "blocked": "Заблакіравана", + "@blocked": { + "type": "String", + "placeholders": {} + }, + "botMessages": "Паведамленні ботаў", + "@botMessages": { + "type": "String", + "placeholders": {} + }, + "cancel": "Скасаваць", + "@cancel": { + "type": "String", + "placeholders": {} + }, "cantOpenUri": "Немагчыма адкрыць URI {uri}", - "changeDeviceName": "Змяніць імя прылады", - "changedTheChatAvatar": "{username} змяніў аватар чата", - "changedTheChatDescriptionTo": "{username} змяніў апісанне чата на: '{description}'", - "changedTheChatNameTo": "{username} змяніў імя чата на: '{chatname}'", + "@cantOpenUri": { + "type": "String", + "placeholders": { + "uri": { + "type": "String" + } + } + }, + "changeDeviceName": "Змяніць назву прылады", + "@changeDeviceName": { + "type": "String", + "placeholders": {} + }, + "changedTheChatAvatar": "{username} змяніў выяву чата", + "@changedTheChatAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheChatDescriptionTo": "{username} змяніў апісанне чата на :'{description}'", + "@changedTheChatDescriptionTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "description": { + "type": "String" + } + } + }, + "changedTheChatNameTo": "{username} змяніў назву чата на '{chatname}'", + "@changedTheChatNameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "chatname": { + "type": "String" + } + } + }, "changedTheChatPermissions": "{username} змяніў дазволы чата", - "changedTheDisplaynameTo": "{username} змяніў сваё імя для паказу на: '{displayname}'", - "changedTheGuestAccessRules": "{username} змяніў правілы доступу для гасцей", - "changedTheGuestAccessRulesTo": "{username} змяніў правілы доступу для гасцей на: {rules}", + "@changedTheChatPermissions": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheDisplaynameTo": "{username} змяніў адлюстроўваемае імя на: '{displayname}'", + "@changedTheDisplaynameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "displayname": { + "type": "String" + } + } + }, + "changedTheGuestAccessRules": "{username} змяніў правілы гасцявога доступу", + "@changedTheGuestAccessRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheGuestAccessRulesTo": "{username} змяніў правілы гасцявога доступу на: {rules}", + "@changedTheGuestAccessRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, "changedTheHistoryVisibility": "{username} змяніў бачнасць гісторыі", + "@changedTheHistoryVisibility": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, "changedTheHistoryVisibilityTo": "{username} змяніў бачнасць гісторыі на: {rules}", - "changedTheJoinRules": "{username} змяніў правілы далучэння", - "changedTheJoinRulesTo": "{username} змяніў правілы далучэння на: {joinRules}", + "@changedTheHistoryVisibilityTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, + "changedTheJoinRules": "{username} змяніў правілы падлучэння", + "@changedTheJoinRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheJoinRulesTo": "{username} змяніў правілы падлучэння на: {joinRules}", + "@changedTheJoinRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "joinRules": { + "type": "String" + } + } + }, "changedTheProfileAvatar": "{username} змяніў свой аватар", - "changedTheRoomAliases": "{username} змяніў аліасы пакоя", - "changedTheRoomInvitationLink": "{username} змяніў спасылку на запрашэнне", + "@changedTheProfileAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "noMessagesYet": "Паведамленняў пакуль што няма", + "@noMessagesYet": {}, + "changedTheRoomAliases": "{username} змяніў псеўданімы пакою", + "@changedTheRoomAliases": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheRoomInvitationLink": "{username} змяніў(-ла) запрашальную спасылку", + "@changedTheRoomInvitationLink": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, "changePassword": "Змяніць пароль", - "changeTheHomeserver": "Змяніць хатні сервер", - "changeTheme": "Змяніць стыль", + "@changePassword": { + "type": "String", + "placeholders": {} + }, + "changeTheHomeserver": "Змяніць дамашні сервер", + "@changeTheHomeserver": { + "type": "String", + "placeholders": {} + }, + "changeTheme": "Змяніць ваш стыль", + "@changeTheme": { + "type": "String", + "placeholders": {} + }, "changeTheNameOfTheGroup": "Змяніць назву групы", - "changeYourAvatar": "Змяніць аватар", - "channelCorruptedDecryptError": "Шыфраванне было пашкоджана", + "@changeTheNameOfTheGroup": { + "type": "String", + "placeholders": {} + }, + "changeYourAvatar": "Змяніць вашу выяву", + "@changeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "channelCorruptedDecryptError": "Шыфраванне было сапсавана", + "@channelCorruptedDecryptError": { + "type": "String", + "placeholders": {} + }, "chat": "Чат", - "yourChatBackupHasBeenSetUp": "Рэзервовая копія вашага чата наладжана.", - "chatBackup": "Рэзервовая копія чата", - "chatBackupDescription": "Вашы старыя паведамленні захаваны з дапамогай ключа аднаўлення. Калі ласка, пераканайцеся, што вы яго не страціце.", + "@chat": { + "type": "String", + "placeholders": {} + }, + "yourChatBackupHasBeenSetUp": "Рэзервовае капіраванне чатаў было наладжана.", + "@yourChatBackupHasBeenSetUp": {}, + "chatBackup": "Рэзервовае капіраванне чатаў", + "@chatBackup": { + "type": "String", + "placeholders": {} + }, + "chatBackupDescription": "Вашы паведамленні абаронены ключом аднаўлення. Калі ласка, пераканайцеся ў тым, што вы яго не згубіце.", + "@chatBackupDescription": { + "type": "String", + "placeholders": {} + }, "chatDetails": "Дэталі чата", - "chatHasBeenAddedToThisSpace": "Чат дададзены ў гэты прастор", + "@chatDetails": { + "type": "String", + "placeholders": {} + }, + "chatHasBeenAddedToThisSpace": "Чат быў дададзены ў гэту прастору", + "@chatHasBeenAddedToThisSpace": {}, "chats": "Чаты", - "chooseAStrongPassword": "Выберыце моцны пароль", + "@chats": { + "type": "String", + "placeholders": {} + }, + "chooseAStrongPassword": "Стварыце надзейны пароль", + "@chooseAStrongPassword": { + "type": "String", + "placeholders": {} + }, "clearArchive": "Ачысціць архіў", + "@clearArchive": {}, "close": "Закрыць", - "commandHint_markasdm": "Адзначыць як прамое паведамленне для дадзенага Matrix ID", - "commandHint_markasgroup": "Адзначыць як групу", - "commandHint_ban": "Забароніць дадзенага карыстальніка ў гэтым пакоі", + "@close": { + "type": "String", + "placeholders": {} + }, + "commandHint_markasdm": "Пазначыць як пакой асабоных паведамленняў для дадання Matrix ID", + "@commandHint_markasdm": {}, + "commandHint_markasgroup": "Пазначыць як групу", + "@commandHint_markasgroup": {}, + "commandHint_ban": "Заблакіраваць карыстальніка у гэтым пакое", + "@commandHint_ban": { + "type": "String", + "description": "Usage hint for the command /ban" + }, "commandHint_clearcache": "Ачысціць кэш", - "commandHint_create": "Стварыць пусты групавы чат\nВыкарыстоўвайце --no-encryption, каб адключыць шыфраванне", - "commandHint_discardsession": "Адхіліць сесію", - "commandHint_dm": "Пачаць прамы чат\nВыкарыстоўвайце --no-encryption, каб адключыць шыфраванне", - "commandHint_html": "Адправіць тэкст у фармаце HTML", - "commandHint_invite": "Запрасіць дадзенага карыстальніка ў гэтую пакой", - "commandHint_join": "Далучыцца да дадзенай пакоі", - "commandHint_kick": "Выдаліць дадзенага карыстальніка з гэтай пакоі", - "commandHint_leave": "Пакінуць гэтую пакой", + "@commandHint_clearcache": { + "type": "String", + "description": "Usage hint for the command /clearcache" + }, + "commandHint_create": "Стварыць пусты групавы чат.\nВыкарыстоўвайце --no-encryption каб адключыць шыфраванне", + "@commandHint_create": { + "type": "String", + "description": "Usage hint for the command /create" + }, + "commandHint_discardsession": "Спыніць сеанс", + "@commandHint_discardsession": { + "type": "String", + "description": "Usage hint for the command /discardsession" + }, + "commandHint_dm": "Пачаць асобны чат.\nВыкарыстоўвайце --no-encryption, каб адключыць шыфраванне", + "@commandHint_dm": { + "type": "String", + "description": "Usage hint for the command /dm" + }, + "commandHint_html": "Даслаць тэкст з разметкай HTML", + "@commandHint_html": { + "type": "String", + "description": "Usage hint for the command /html" + }, + "commandHint_invite": "Запрасіць карыстальніка ў гэты пакой", + "@commandHint_invite": { + "type": "String", + "description": "Usage hint for the command /invite" + }, + "commandHint_join": "Далучыцца да гэтага пакою", + "@commandHint_join": { + "type": "String", + "description": "Usage hint for the command /join" + }, + "commandHint_kick": "Выдаліць карыстальніка з гэтага пакою", + "@commandHint_kick": { + "type": "String", + "description": "Usage hint for the command /kick" + }, + "commandHint_leave": "Пакінуць гэты пакой", + "@commandHint_leave": { + "type": "String", + "description": "Usage hint for the command /leave" + }, "commandHint_me": "Апішыце сябе", - "commandHint_myroomavatar": "Усталяваць вашу фатаграфію для гэтай пакоі (па mxc-uri)", - "commandHint_myroomnick": "Усталяваць вашу адлюстравальную імя для гэтай пакоі", - "commandHint_op": "Усталяваць узровень паўнамоцтваў дадзенага карыстальніка (па змаўчанні: 50)", - "commandHint_plain": "Адправіць тэкст без фарматавання", - "commandHint_react": "Адправіць адказ у выглядзе рэакцыі", - "commandHint_send": "Адправіць тэкст", - "commandHint_unban": "Разбаніць дадзенага карыстальніка ў гэтай пакоі", - "commandInvalid": "Несапраўдная каманда", - "commandMissing": "{command} не з'яўляецца камандай.", + "@commandHint_me": { + "type": "String", + "description": "Usage hint for the command /me" + }, + "commandHint_myroomavatar": "Задаць выяву гэтага пакою (ад mxc-uri)", + "@commandHint_myroomavatar": { + "type": "String", + "description": "Usage hint for the command /myroomavatar" + }, + "commandHint_myroomnick": "Задайце адлюстроўваемае імя для гэтага пакою", + "@commandHint_myroomnick": { + "type": "String", + "description": "Usage hint for the command /myroomnick" + }, + "commandHint_op": "Задайце ўзровень правоў гэтага карыстальніка (па змаўчанні: 50)", + "@commandHint_op": { + "type": "String", + "description": "Usage hint for the command /op" + }, + "commandHint_plain": "Даслаць неадфарматыраваны тэкст", + "@commandHint_plain": { + "type": "String", + "description": "Usage hint for the command /plain" + }, + "commandHint_react": "Даслаць адказ як рэакцыю", + "@commandHint_react": { + "type": "String", + "description": "Usage hint for the command /react" + }, + "commandHint_send": "Даслаць тэкст", + "@commandHint_send": { + "type": "String", + "description": "Usage hint for the command /send" + }, + "commandHint_unban": "Разблакіраваць карыстальніка ў гэтым пакое", + "@commandHint_unban": { + "type": "String", + "description": "Usage hint for the command /unban" + }, + "commandInvalid": "Няслушная каманда", + "@commandInvalid": { + "type": "String" + }, + "commandMissing": "{command} не каманда.", + "@commandMissing": { + "type": "String", + "placeholders": { + "command": { + "type": "String" + } + }, + "description": "State that {command} is not a valid /command." + }, "compareEmojiMatch": "Калі ласка, параўнайце эмодзі", - "compareNumbersMatch": "Калі ласка, параўнайце нумары", + "@compareEmojiMatch": { + "type": "String", + "placeholders": {} + }, + "compareNumbersMatch": "Калі ласка, параўнайце лічбы", + "@compareNumbersMatch": { + "type": "String", + "placeholders": {} + }, "configureChat": "Наладзіць чат", + "@configureChat": { + "type": "String", + "placeholders": {} + }, "confirm": "Пацвердзіць", - "connect": "Падключыць", + "@confirm": { + "type": "String", + "placeholders": {} + }, + "connect": "Далучыцца", + "@connect": { + "type": "String", + "placeholders": {} + }, "contactHasBeenInvitedToTheGroup": "Кантакт быў запрошаны ў групу", - "containsDisplayName": "Змяшчае імя паказу", - "containsUserName": "Змяшчае імя карыстальніка", - "contentHasBeenReported": "Змест быў паведамлены адміністратарам сервера", - "copiedToClipboard": "Скапіравана ў буфер абмену", - "copy": "Скапіяраваць", - "copyToClipboard": "Скапіяраваць у буфер", + "@contactHasBeenInvitedToTheGroup": { + "type": "String", + "placeholders": {} + }, + "containsDisplayName": "Мае адлюстроўваемае імя", + "@containsDisplayName": { + "type": "String", + "placeholders": {} + }, + "containsUserName": "Мае імя карыстальніка", + "@containsUserName": { + "type": "String", + "placeholders": {} + }, + "contentHasBeenReported": "Пра кантэнт было паведамлена адміністратарам сервера", + "@contentHasBeenReported": { + "type": "String", + "placeholders": {} + }, + "copiedToClipboard": "Скапіравана ў буфер", + "@copiedToClipboard": { + "type": "String", + "placeholders": {} + }, + "copy": "Капіраваць", + "@copy": { + "type": "String", + "placeholders": {} + }, + "copyToClipboard": "Скапіраваць у буфер", + "@copyToClipboard": { + "type": "String", + "placeholders": {} + }, "couldNotDecryptMessage": "Немагчыма расшыфраваць паведамленне: {error}", - "checkList": "Праверачны спіс", - "countParticipants": "{count} удзельнікаў", - "countInvited": "{count} запрошана", + "@couldNotDecryptMessage": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "checkList": "Кантрольны спіс", + "@checkList": {}, + "countParticipants": "{count} удзельніка(-ў)", + "@countParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "countInvited": "Запрошана {count}", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, "create": "Стварыць", - "createdTheChat": "💬 {username} стварыў чат", + "@create": { + "type": "String", + "placeholders": {} + }, + "createdTheChat": "💬 {username} стварыў(-ла) чат", + "@createdTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, "createGroup": "Стварыць групу", - "createNewSpace": "Новы прастор", + "@createGroup": {}, + "createNewSpace": "Новая прастора", + "@createNewSpace": { + "type": "String", + "placeholders": {} + }, "currentlyActive": "Зараз актыўны", + "@currentlyActive": { + "type": "String", + "placeholders": {} + }, "darkTheme": "Цёмная", + "@darkTheme": { + "type": "String", + "placeholders": {} + }, "dateAndTimeOfDay": "{date}, {timeOfDay}", - "dateWithoutYear": "{month}-{day}", + "@dateAndTimeOfDay": { + "type": "String", + "placeholders": { + "date": { + "type": "String" + }, + "timeOfDay": { + "type": "String" + } + } + }, + "dateWithoutYear": "{day}-{month}", + "@dateWithoutYear": { + "type": "String", + "placeholders": { + "month": { + "type": "String" + }, + "day": { + "type": "String" + } + } + }, "dateWithYear": "{year}-{month}-{day}", - "deactivateAccountWarning": "Гэта дэзактывуе ваш уліковы запіс. Гэта нельга будзе адмяніць! Вы ўпэўнены?", - "defaultPermissionLevel": "Звычайны ўзровень дазволу для новых карыстальнікаў", + "@dateWithYear": { + "type": "String", + "placeholders": { + "year": { + "type": "String" + }, + "month": { + "type": "String" + }, + "day": { + "type": "String" + } + } + }, + "deactivateAccountWarning": "Гэта дэактывіруе ваш уліковы запіс. Гэта дзеянне не можа быць адменена! Вы ўпэўнены?", + "@deactivateAccountWarning": { + "type": "String", + "placeholders": {} + }, + "defaultPermissionLevel": "Узровень дазволаў для новых карыстальнікаў па змаўчанні", + "@defaultPermissionLevel": { + "type": "String", + "placeholders": {} + }, "delete": "Выдаліць", + "@delete": { + "type": "String", + "placeholders": {} + }, "deleteAccount": "Выдаліць уліковы запіс", + "@deleteAccount": { + "type": "String", + "placeholders": {} + }, "deleteMessage": "Выдаліць паведамленне", + "@deleteMessage": { + "type": "String", + "placeholders": {} + }, "device": "Прылада", + "@device": { + "type": "String", + "placeholders": {} + }, "deviceId": "ID прылады", + "@deviceId": { + "type": "String", + "placeholders": {} + }, "devices": "Прылады", - "directChats": "Прамыя чаты", + "@devices": { + "type": "String", + "placeholders": {} + }, + "directChats": "Асобныя чаты", + "@directChats": { + "type": "String", + "placeholders": {} + }, "allRooms": "Усе групавыя чаты", - "displaynameHasBeenChanged": "Імя для паказу было зменена", - "downloadFile": "Загрузіць файл", - "edit": "Рэдагаваць", - "editBlockedServers": "Рэдагаваць заблакаваныя серверы", - "chatPermissions": "Дазволы для чата", - "editDisplayname": "Рэдагаваць імя для паказу", - "editRoomAliases": "Рэдагаваць аліасы пакоя", - "editRoomAvatar": "Рэдагаваць аватар пакоя", - "emoteExists": "Эмот ужо існуе!", - "emoteInvalid": "Недапушчальны кароткі код эмоцыі!", - "emoteKeyboardNoRecents": "Нядаўна выкарыстаныя эмоцыі з'явяцца тут...", - "emotePacks": "Пакеты эмоцый для пакоя", - "emoteSettings": "Налады эмоцый", - "globalChatId": "Глабальны ідэнтыфікатар чату", - "accessAndVisibility": "Доступ і бачнасць", - "accessAndVisibilityDescription": "Хто можа далучыцца да гэтага чату і як яго можна знайсці.", - "calls": "Званкі", - "customEmojisAndStickers": "Карыстальніцкія эмодзі і наклейкі", - "customEmojisAndStickersBody": "Дадайце або падзяліцеся карыстальніцкімі эмодзі або наклейкамі, якія можна выкарыстоўваць у любым чаце.", - "emoteShortcode": "Кароткі код эмоцыі", - "emoteWarnNeedToPick": "Вам трэба выбраць кароткі код эмоцыі і малюнак!", + "@allRooms": { + "type": "String", + "placeholders": {} + }, + "displaynameHasBeenChanged": "Адлюстроўваемае імя было зменена", + "@displaynameHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "downloadFile": "Спампаваць файл", + "@downloadFile": { + "type": "String", + "placeholders": {} + }, + "edit": "Змяніць", + "@edit": { + "type": "String", + "placeholders": {} + }, + "editBlockedServers": "Змяніць заблакіраваныя сервера", + "@editBlockedServers": { + "type": "String", + "placeholders": {} + }, + "chatPermissions": "Дазволы чату", + "@chatPermissions": {}, + "editDisplayname": "Змяніць адлюстроўваемае імя", + "@editDisplayname": { + "type": "String", + "placeholders": {} + }, + "editRoomAliases": "Змяніць псеўданімы пакою", + "@editRoomAliases": { + "type": "String", + "placeholders": {} + }, + "editRoomAvatar": "Змяніць выяву пакою", + "@editRoomAvatar": { + "type": "String", + "placeholders": {} + }, + "emoteExists": "Эмодзі ўжо існуе!", + "@emoteExists": { + "type": "String", + "placeholders": {} + }, + "emoteInvalid": "Недапушчальнае скарачэнне эмодзі!", + "@emoteInvalid": { + "type": "String", + "placeholders": {} + }, + "emoteKeyboardNoRecents": "Нядаўна ўжытыя эмодзі паявяцца тут...", + "@emoteKeyboardNoRecents": { + "type": "String", + "placeholders": {} + }, + "emotePacks": "Наборы эмодзі для пакою", + "@emotePacks": { + "type": "String", + "placeholders": {} + }, + "emoteSettings": "Налады эмодзі", + "@emoteSettings": { + "type": "String", + "placeholders": {} + }, + "globalChatId": "ID габальнага чату", + "@globalChatId": {}, + "accessAndVisibility": "Даступнасць і бачнасць", + "@accessAndVisibility": {}, + "accessAndVisibilityDescription": "Каму дазволена далучацца да гэтага чату і як ён можа быць знойдзены.", + "@accessAndVisibilityDescription": {}, + "calls": "Выклікі", + "@calls": {}, + "customEmojisAndStickers": "Карыстальніцкія эмодзі і стыкеры", + "@customEmojisAndStickers": {}, + "customEmojisAndStickersBody": "Дадаць ці падзяліцца карыстальніцкімі эмодзі ці стыкерамі, што могуць быць ужыты ў любым чаце.", + "@customEmojisAndStickersBody": {}, + "emoteShortcode": "Скарачэнне эмодзі", + "@emoteShortcode": { + "type": "String", + "placeholders": {} + }, + "emoteWarnNeedToPick": "Вы павінны задаць скарачэнне эмодзі і выяву!", + "@emoteWarnNeedToPick": { + "type": "String", + "placeholders": {} + }, "emptyChat": "Пусты чат", - "enableEmotesGlobally": "Уключыць пакет эмоцый глабальна", + "@emptyChat": { + "type": "String", + "placeholders": {} + }, + "enableEmotesGlobally": "Уключыць набор эмодзі глабальна", + "@enableEmotesGlobally": { + "type": "String", + "placeholders": {} + }, "enableEncryption": "Уключыць шыфраванне", - "enableEncryptionWarning": "Вы больш не зможаце адключыць шыфраванне. Ці ўпэўнены вы?", - "encrypted": "Шыфравана", + "@enableEncryption": { + "type": "String", + "placeholders": {} + }, + "enableEncryptionWarning": "Вы больш не зможаце адключыць шыфраванне. Вы ўпэўнены?", + "@enableEncryptionWarning": { + "type": "String", + "placeholders": {} + }, + "encrypted": "Зашыфравана", + "@encrypted": { + "type": "String", + "placeholders": {} + }, "encryption": "Шыфраванне", + "@encryption": { + "type": "String", + "placeholders": {} + }, "encryptionNotEnabled": "Шыфраванне не ўключана", - "endedTheCall": "{senderName} скончыў званок", - "enterAnEmailAddress": "Увядзіце адрас электроннай пошты", - "homeserver": "Асноўны сервер", - "enterYourHomeserver": "Увядзіце ваш асноўны сервер", + "@encryptionNotEnabled": { + "type": "String", + "placeholders": {} + }, + "endedTheCall": "{senderName} скончыў выклік", + "@endedTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "enableMultiAccounts": "(БЭТА) Уключыць некалькі ўліковых запісаў на гэтай прыладзе", + "@enableMultiAccounts": {}, + "enterAnEmailAddress": "Увядзіце электроную пошту (email)", + "@enterAnEmailAddress": { + "type": "String", + "placeholders": {} + }, + "homeserver": "Дамашні сервер", + "@homeserver": {}, + "enterYourHomeserver": "Увядзіце адрас дамашняга сервера", + "@enterYourHomeserver": { + "type": "String", + "placeholders": {} + }, "errorObtainingLocation": "Памылка атрымання месцазнаходжання: {error}", + "@errorObtainingLocation": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } + } + }, "everythingReady": "Усё гатова!", - "extremeOffensive": "Вельмі абразлівы", - "fileName": "Імя файла", + "@everythingReady": { + "type": "String", + "placeholders": {} + }, + "extremeOffensive": "Занадта абражальны", + "@extremeOffensive": { + "type": "String", + "placeholders": {} + }, + "fileName": "Назва файла", + "@fileName": { + "type": "String", + "placeholders": {} + }, "fluffychat": "FluffyChat", - "fontSize": "Памер шрыфта", + "@fluffychat": { + "type": "String", + "placeholders": {} + }, + "fontSize": "Памер шрыфту", + "@fontSize": { + "type": "String", + "placeholders": {} + }, "forward": "Пераслаць", + "@forward": { + "type": "String", + "placeholders": {} + }, "fromJoining": "З далучэння", + "@fromJoining": { + "type": "String", + "placeholders": {} + }, "fromTheInvitation": "З запрашэння", - "goToTheNewRoom": "Перайдзіце ў новую пакой", + "@fromTheInvitation": { + "type": "String", + "placeholders": {} + }, + "goToTheNewRoom": "Перайсці ў новы пакой", + "@goToTheNewRoom": { + "type": "String", + "placeholders": {} + }, "group": "Група", - "chatDescription": "Апісанне чата", - "chatDescriptionHasBeenChanged": "Апісанне чата зменена", - "groupIsPublic": "Група адкрытая", + "@group": { + "type": "String", + "placeholders": {} + }, + "chatDescription": "Апісанне чату", + "@chatDescription": {}, + "chatDescriptionHasBeenChanged": "Апісанне чату зменена", + "@chatDescriptionHasBeenChanged": {}, + "groupIsPublic": "Група публічная", + "@groupIsPublic": { + "type": "String", + "placeholders": {} + }, "groups": "Групы", - "groupWith": "Група з {displayname}", - "guestsAreForbidden": "Гасцям забаронена ўдзел", - "guestsCanJoin": "Гасцям дазволена далучацца", - "hasWithdrawnTheInvitationFor": "{username} адклікаў запрашэнне для {targetName}", + "@groups": { + "type": "String", + "placeholders": {} + }, + "groupWith": "Групы з {displayname}", + "@groupWith": { + "type": "String", + "placeholders": { + "displayname": { + "type": "String" + } + } + }, + "guestsAreForbidden": "Госці забаронены", + "@guestsAreForbidden": { + "type": "String", + "placeholders": {} + }, + "guestsCanJoin": "Госці могуць далучацца", + "@guestsCanJoin": { + "type": "String", + "placeholders": {} + }, + "hasWithdrawnTheInvitationFor": "{username} адазваў запрашэнне для {targetName}", + "@hasWithdrawnTheInvitationFor": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, "help": "Дапамога", - "hideRedactedEvents": "Схаваць выдаленыя падзеі", - "hideRedactedMessages": "Схаваць выдаленыя паведамленні", - "hideRedactedMessagesBody": "Калі нехта выдаляе паведамленне, гэта паведамленне больш не будзе бачным у чаце.", - "hideInvalidOrUnknownMessageFormats": "Схаваць недапушчальныя або невядомыя фарматы паведамленняў", - "howOffensiveIsThisContent": "Насколькі гэта змесціва абразлівае?", + "@help": { + "type": "String", + "placeholders": {} + }, + "hideRedactedEvents": "Схаваць адрэдагаваныя падзеі", + "@hideRedactedEvents": { + "type": "String", + "placeholders": {} + }, + "hideRedactedMessages": "Схаваць адрэдагаваныя паведамленні", + "@hideRedactedMessages": {}, + "hideRedactedMessagesBody": "Калі хтосьці рэдагуе паведамленне, яно будзе схавана ў чаце.", + "@hideRedactedMessagesBody": {}, + "hideInvalidOrUnknownMessageFormats": "Хаваць памылковыя ці невядомыя фарматы паведамленняў", + "@hideInvalidOrUnknownMessageFormats": {}, + "howOffensiveIsThisContent": "Наколькі абражальны гэты кантэнт?", + "@howOffensiveIsThisContent": { + "type": "String", + "placeholders": {} + }, "id": "ID", - "identity": "Ідэнтычнасць", - "block": "Блакаваць", - "blockedUsers": "Заблакаваныя карыстальнікі", - "blockListDescription": "Вы можаце блакаваць карыстальнікаў, якія вас турбуюць. Вы не будзеце атрымліваць паведамленні або запрашэнні ў пакоі ад карыстальнікаў у вашым асабістым спісе блакавання.", + "@id": { + "type": "String", + "placeholders": {} + }, + "identity": "Ідэнтыфікацыя", + "@identity": { + "type": "String", + "placeholders": {} + }, + "block": "Заблакіраваць", + "@block": {}, + "blockedUsers": "Заблакіраваныя карыстальнікі", + "@blockedUsers": {}, + "blockListDescription": "Вы можаце заблакіраваць карыстальнікаў, якія вам перашкаджаюць. Вы не атрымаеце ад іх ні паведамленняў, ні запрашэнняў.", + "@blockListDescription": {}, "blockUsername": "Ігнараваць імя карыстальніка", - "iHaveClickedOnLink": "Я націснуў на спасылку", - "incorrectPassphraseOrKey": "Няправільная парольная фраза або ключ аднаўлення", - "inoffensive": "Бязвіннае", + "@blockUsername": {}, + "iHaveClickedOnLink": "Я перайшоў па спасылцы", + "@iHaveClickedOnLink": { + "type": "String", + "placeholders": {} + }, + "incorrectPassphraseOrKey": "Няслушныя фраза-пароль ці ключ аднаўлення", + "@incorrectPassphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "inoffensive": "Безабідны", + "@inoffensive": { + "type": "String", + "placeholders": {} + }, "inviteContact": "Запрасіць кантакт", - "inviteContactToGroupQuestion": "Ці хочаце вы запрасіць {contact} у чат \"{groupName}\"?", - "inviteContactToGroup": "Запрасіць кантакт у {groupName}", - "noChatDescriptionYet": "Пакуль няма апісання чата.", - "tryAgain": "Паспрабуйце яшчэ раз", + "@inviteContact": { + "type": "String", + "placeholders": {} + }, + "inviteContactToGroupQuestion": "Вы хаціце запрасіць {contact} да чату \"{groupName}\"?", + "@inviteContactToGroupQuestion": {}, + "inviteContactToGroup": "Запрасіць кантакт да {groupName}", + "@inviteContactToGroup": { + "type": "String", + "placeholders": { + "groupName": { + "type": "String" + } + } + }, + "noChatDescriptionYet": "Апісанне чату яшчэ няма.", + "@noChatDescriptionYet": {}, + "tryAgain": "Паспрабуйце зноў", + "@tryAgain": {}, "invalidServerName": "Недапушчальная назва сервера", - "invited": "Запрошана", - "redactMessageDescription": "Паведамленне будзе выдалена для ўсіх удзельнікаў гэтай размовы. Гэта нельга адмяніць.", - "optionalRedactReason": "(Па жаданні) Прычына выдалення гэтага паведамлення...", - "invitedUser": "📩 {username} запрасіў {targetName}", - "invitedUsersOnly": "Толькі для запрошаных карыстальнікаў", - "inviteForMe": "Запрасіць мяне", - "inviteText": "{username} запрасіў вас у FluffyChat.\n1. Наведайце fluffychat.im і ўсталюйце дадатак \n2. Зарэгіструйцеся або ўвайдзіце \n3. Адкрыйце спасылку запрашэння: \n {link}", - "isTyping": "пішэ...", - "joinedTheChat": "👋 {username} далучыўся да чата", - "joinRoom": "Далучыцца да пакоя", - "kicked": "👞 {username} выгнаў {targetName}", - "kickedAndBanned": "🙅 {username} выгнаў і забаніў {targetName}", - "kickFromChat": "Выгнаць з чата", + "@invalidServerName": {}, + "invited": "Запрошаны", + "@invited": { + "type": "String", + "placeholders": {} + }, + "redactMessageDescription": "Гэта паведамленне будзе адрэдагавана для усіх карыстальнікаў. Вы не зможаце яго адмяніць.", + "@redactMessageDescription": {}, + "optionalRedactReason": "(Неабавязкова) Прычына рэдагавання паведамлення...", + "@optionalRedactReason": {}, + "invitedUser": "📩 {username} запрасіў(-ла) {targetName}", + "@invitedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "invitedUsersOnly": "Толькі запрошаныя карыстальнікі", + "@invitedUsersOnly": { + "type": "String", + "placeholders": {} + }, + "inviteForMe": "Запрашэнне для мяне", + "@inviteForMe": { + "type": "String", + "placeholders": {} + }, + "inviteText": "{username} запрасіў вас у FluffyChat.\n1. Перайдзіце на fluffychat.im і ўстанавіце праграму\n2. Зарэгестрыруйцесь альбо ўвайдзіце\n3. Перайдзіце па запрашальнай спасылцы:\n{link}", + "@inviteText": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "link": { + "type": "String" + } + } + }, + "isTyping": "піша…", + "@isTyping": { + "type": "String", + "placeholders": {} + }, + "joinedTheChat": "👋 {username} далучыўся(лася) да чату", + "@joinedTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "joinRoom": "Далучыцца да пакою", + "@joinRoom": { + "type": "String", + "placeholders": {} + }, + "kicked": "👞 {username} выгнаў(-ла) {targetName}", + "@kicked": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "kickedAndBanned": "🙅 {username} выгнаў(-ла) і заблакіраваў(-ла) {targetName}", + "@kickedAndBanned": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "kickFromChat": "Выгнаць з чату", + "@kickFromChat": { + "type": "String", + "placeholders": {} + }, "lastActiveAgo": "Апошняя актыўнасць: {localizedTimeShort}", - "leave": "Заставаць", - "leftTheChat": "Пакінуў чат", + "@lastActiveAgo": { + "type": "String", + "placeholders": { + "localizedTimeShort": { + "type": "String" + } + } + }, + "leave": "Выйсці", + "@leave": { + "type": "String", + "placeholders": {} + }, + "leftTheChat": "Выйсці з чату", + "@leftTheChat": { + "type": "String", + "placeholders": {} + }, "license": "Ліцэнзія", - "lightTheme": "Святлая тэма", + "@license": { + "type": "String", + "placeholders": {} + }, + "lightTheme": "Светлая", + "@lightTheme": { + "type": "String", + "placeholders": {} + }, "loadCountMoreParticipants": "Загрузіць яшчэ {count} удзельнікаў", - "dehydrate": "Экспарт сесіі і выдаленне з прылады", - "dehydrateWarning": "Гэтая дзея не можа быць адменена. Пераканайцеся, што вы бяспечна захавалі файл рэзервовай копіі.", + "@loadCountMoreParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "dehydrate": "Экспарт сеансу і ачыстка прылады", + "@dehydrate": {}, + "dehydrateWarning": "Гэта дзеянне не можа быць адменена. Пераканайцеся, што вы бяспечна захавалі файл рэзервовай копіі.", + "@dehydrateWarning": {}, "dehydrateTor": "Карыстальнікі TOR: Экспарт сесіі", - "dehydrateTorLong": "Для карыстальнікаў TOR рэкамендуецца экспартаваць сесію перад закрыццём акна.", + "@dehydrateTor": {}, + "dehydrateTorLong": "Для карыстальнікаў TOR прапануем экспартаваць сесію перад тым, як закрыць акно.", + "@dehydrateTorLong": {}, "hydrateTor": "Карыстальнікі TOR: Імпарт экспарту сесіі", - "hydrateTorLong": "Ці экспартавалі вы сваю сесію ў апошні раз у TOR? Хутка імпартуйце яе і працягвайце перапіску.", - "hydrate": "Вярнуць з файла рэзервовай копіі", - "loadingPleaseWait": "Загрузка… Калі ласка, пачакайце.", - "loadMore": "Загрузіць яшчэ…", - "locationDisabledNotice": "Службы месцазнаходжання выключаны. Уключыце іх, каб мець магчымасць дзяліцца вашым месцазнаходжаннем.", - "locationPermissionDeniedNotice": "Дазвол на месцазнаходжанне адхілены. Падайце дазвол, каб дзяліцца вашым месцазнаходжаннем.", - "login": "Увайсці", + "@hydrateTor": {}, + "hydrateTorLong": "Вы экспартавалі сесію ў мінулы раз праз TOR? Хутка імпартуйце яе і працягніце размовы.", + "@hydrateTorLong": {}, + "hydrate": "Аднавіць з рэзервовай копіі", + "@hydrate": {}, + "loadingPleaseWait": "Загрузка... Калі ласка, пачакайце.", + "@loadingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "loadMore": "Загрузіць больш…", + "@loadMore": { + "type": "String", + "placeholders": {} + }, + "locationDisabledNotice": "Сервісы месцазнаходжанн, адключаны. Калі ласка, уключыце іх, каб зноў дзяліцца месцазнаходжаннем.", + "@locationDisabledNotice": { + "type": "String", + "placeholders": {} + }, + "locationPermissionDeniedNotice": "Дазвол атрымання месцазнаходжання скасаваны. Калі ласка, дайце яго каб дзяліцца месцазнаходжаннем.", + "@locationPermissionDeniedNotice": { + "type": "String", + "placeholders": {} + }, + "login": "Логін", + "@login": { + "type": "String", + "placeholders": {} + }, "logInTo": "Увайсці ў {homeserver}", + "@logInTo": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, "logout": "Выйсці", - "memberChanges": "Змены ўдзельнікаў", - "mention": "Згадак", + "@logout": { + "type": "String", + "placeholders": {} + }, + "memberChanges": "Змяненні ўдзельнікаў", + "@memberChanges": { + "type": "String", + "placeholders": {} + }, + "mention": "Згадаць", + "@mention": { + "type": "String", + "placeholders": {} + }, "messages": "Паведамленні", - "messagesStyle": "Стыль паведамленняў:", + "@messages": { + "type": "String", + "placeholders": {} + }, + "messagesStyle": "Паведамленні:", + "@messagesStyle": {}, "moderator": "Мадэратар", - "muteChat": "Адключыць гук у чаце", - "needPantalaimonWarning": "Калі ласка, памятайце, што для выкарыстання канчатковага шыфравання вам патрэбен Pantalaimon.", + "@moderator": { + "type": "String", + "placeholders": {} + }, + "muteChat": "Заглушыць чат", + "@muteChat": { + "type": "String", + "placeholders": {} + }, + "needPantalaimonWarning": "Пераканайцеся, калі ласка, што вы выкарыстоўваеце Pantalaimon для end-to-end шыфравання.", + "@needPantalaimonWarning": { + "type": "String", + "placeholders": {} + }, "newChat": "Новы чат", - "newMessageInFluffyChat": "📝 Новае паведамленне ў FluffyChat", + "@newChat": { + "type": "String", + "placeholders": {} + }, + "newMessageInFluffyChat": "💬 Новае паведамленне ў FluffyChat", + "@newMessageInFluffyChat": { + "type": "String", + "placeholders": {} + }, "newVerificationRequest": "Новы запыт на верыфікацыю!", + "@newVerificationRequest": { + "type": "String", + "placeholders": {} + }, "next": "Далей", + "@next": { + "type": "String", + "placeholders": {} + }, "no": "Не", + "@no": { + "type": "String", + "placeholders": {} + }, "noConnectionToTheServer": "Няма злучэння з серверам", - "noEmotesFound": "Эмодзі не знойдзены. 😝", - "noEncryptionForPublicRooms": "Шыфраванне можна ўключыць толькі тады, калі пакой больш не з'яўляецца публічным.", - "noGoogleServicesWarning": "Выглядае, што Firebase Cloud Messaging недаступны на вашым прыладзе. Каб атрымліваць апавяшчэнні, рэкамендуем усталяваць ntfy. З ntfy або іншым пастаўшчыком Unified Push вы можаце атрымліваць апавяшчэнні ў бяспечным рэжыме. Вы можаце спампаваць ntfy з PlayStore або F-Droid.", - "noMatrixServer": "{server1} не з'яўляецца матрычным серверам, выкарыстоўваць замест яго {server2}?", - "shareInviteLink": "Падзяліцца спасылкай-прывітаннем", + "@noConnectionToTheServer": { + "type": "String", + "placeholders": {} + }, + "noEmotesFound": "Эмодзі не знойдзены. 😕", + "@noEmotesFound": { + "type": "String", + "placeholders": {} + }, + "noEncryptionForPublicRooms": "Вы зможаце актывіраваць шыфраванне як толькі пакой перастане быць агульнадаступным.", + "@noEncryptionForPublicRooms": { + "type": "String", + "placeholders": {} + }, + "noGoogleServicesWarning": "Здаецца, на вашай прыладзе няма ці недаступны Firebase Cloud Messaging. Каб далей атрымліваць паведамленні, мы прапануем усталяваць ntfy ці іншы правайдар паведамленняў, каб атрымліваць іх бяспечна. Вы можаце спампаваць ntfy з PlayStore ці F-Droid.", + "@noGoogleServicesWarning": { + "type": "String", + "placeholders": {} + }, + "noMatrixServer": "{server1} не з'яўляецца серверам matrix. Выкарыстоўваць {server2} замест яго?", + "@noMatrixServer": { + "type": "String", + "placeholders": { + "server1": { + "type": "String" + }, + "server2": { + "type": "String" + } + } + }, + "shareInviteLink": "Падзяліцца запрашальнай спасылкай", + "@shareInviteLink": {}, "scanQrCode": "Сканіраваць QR-код", - "none": "Няма", - "noPasswordRecoveryDescription": "Вы яшчэ не дадалі спосаб аднаўлення пароля.", + "@scanQrCode": {}, + "none": "Нічога", + "@none": { + "type": "String", + "placeholders": {} + }, + "noPasswordRecoveryDescription": "Вы яшчэ не дадалі метад аднаўлення пароля.", + "@noPasswordRecoveryDescription": { + "type": "String", + "placeholders": {} + }, "noPermission": "Няма дазволу", - "noRoomsFound": "Ніякіх пакояў не знойдзена…", - "notifications": "Апавяшчэнні", - "notificationsEnabledForThisAccount": "Апавяшчэнні ўключаны для гэтага акаўнта", - "numUsersTyping": "{count} карыстальнік(і) набіраюць…", - "obtainingLocation": "Атрыманне месцазнаходжання…", - "offensive": "Абразлівы", - "offline": "Не ў сетцы", - "ok": "Добра", - "online": "У сетцы", - "onlineKeyBackupEnabled": "Рэзервовая копія ключоў у сетцы ўключана", - "oopsPushError": "Ой! На жаль, узнікла памылка пры наладжванні апавяшчэнняў пра пуш.", - "oopsSomethingWentWrong": "Ой, нешта пайшло не так…", - "openAppToReadMessages": "Адкрыйце прыкладанне, каб прачытаць паведамленні", - "openCamera": "Адкрыць камеру", - "openVideoCamera": "Адкрыць камеру для відэа", - "oneClientLoggedOut": "Адзін з вашых кліентаў выйшаў з сістэмы", - "addAccount": "Дадаць акаўнт", - "editBundlesForAccount": "Рэдагаваць пакеты для гэтага акаўнта", - "addToBundle": "Дадаць у пакет", - "removeFromBundle": "Выдаліць з гэтага пакета", - "bundleName": "Назва пакета", - "enableMultiAccounts": "(BETA) Уключыць некалькі акаўнтаў на гэтым прыладзе", - "openInMaps": "Адкрыць у картах", - "link": "Спасылка", - "serverRequiresEmail": "Гэты сервер патрабуе пацверджання вашай электроннай пошты для рэгістрацыі.", - "or": "Ці", - "participant": "Удзельнік", - "passphraseOrKey": "пароль або ключ аднаўлення", - "password": "Пароль", - "passwordForgotten": "Забылі пароль", - "passwordHasBeenChanged": "Пароль быў зменены", - "hideMemberChangesInPublicChats": "Схаваць змены ўдзельнікаў у публічных чатах", - "hideMemberChangesInPublicChatsBody": "Не паказваць у хроніцы чата, калі хтосьці далучаецца або выходзіць з публічнага чата для паляпшэння чытальнасці.", - "overview": "Агляд", - "notifyMeFor": "Паведамляць мне пра", - "passwordRecoverySettings": "Налады аднаўлення пароля", - "passwordRecovery": "Аднаўленне пароля", - "people": "Людзі", - "pickImage": "Выбраць малюнак", - "pin": "Закладка", - "play": "Гуляць {fileName}", - "pleaseChoose": "Калі ласка, выберыце", - "pleaseChooseAPasscode": "Калі ласка, выберыце пароль", - "pleaseClickOnLink": "Калі ласка, націсніце на спасылку ў электроннай пошце і працягвайце.", - "pleaseEnter4Digits": "Калі ласка, увядзіце 4 лічбы або пакіньце пустым, каб адключыць блакіроўку прыкладання.", - "pleaseEnterRecoveryKey": "Калі ласка, увядзіце свой ключ аднаўлення:", - "pleaseEnterYourPassword": "Калі ласка, увядзіце свой пароль", - "pleaseEnterYourPin": "Калі ласка, увядзіце свой PIN-код", - "pleaseEnterYourUsername": "Калі ласка, увядзіце сваё імя карыстальніка", - "pleaseFollowInstructionsOnWeb": "Калі ласка, выконвайце інструкцыі на сайце і націсніце далей.", - "privacy": "Прыватнасць", - "publicRooms": "Публічныя пакоі", - "pushRules": "Правілы апавяшчэнняў", - "reason": "Прычына", - "recording": "Запіс", - "redactedBy": "Зачараваны {username}", - "directChat": "Прамой чат", - "redactedByBecause": "Зачараваны {username} з прычыны: \"{reason}\"", - "redactedAnEvent": "{username} зачараваў падзею", - "redactMessage": "Зачысціць паведамленне", - "register": "Рэгістрацыя", - "reject": "Адхіліць", - "rejectedTheInvitation": "{username} адхіліў запрашэнне", - "rejoin": "Паўторна далучыцца", - "removeAllOtherDevices": "Выдаліць усе іншыя прылады", - "removedBy": "Выдалена {username}", - "removeDevice": "Выдаліць прыладу", - "unbanFromChat": "Разбаніць у чаце", - "removeYourAvatar": "Выдаліць ваш аватар", - "replaceRoomWithNewerVersion": "Замяніць пакой на новую версію", - "reply": "Адказаць", - "reportMessage": "Паведаміць пра паведамленне", - "requestPermission": "Запытаць дазвол", - "roomHasBeenUpgraded": "Памяшканне было абноўлена", - "roomVersion": "Версія памяшкання", - "saveFile": "Захаваць файл", - "search": "Пошук", - "security": "Бяспека", - "recoveryKey": "Ключ аднаўлення", - "recoveryKeyLost": "Стратылі ключ аднаўлення?", - "seenByUser": "Праглядзена {username}", - "send": "Адправіць", - "sendAMessage": "Адправіць паведамленне", - "sendAsText": "Адправіць тэкстам", - "sendAudio": "Адправіць аўдыё", - "sendFile": "Адправіць файл", - "sendImage": "Адправіць малюнак", - "sendImages": "Адправіць {count} малюнак", - "sendMessages": "Адправіць паведамленні", - "sendOriginal": "Адправіць арыгінал", - "sendSticker": "Адправіць стыкер", - "sendVideo": "Адправіць відэа", - "sentAFile": "📁 {username} адправіў файл", - "sentAnAudio": "🎤 {username} адправіў аўдыё", - "sentAPicture": "🖼️ {username} адправіў малюнак", - "sentASticker": "😊 {username} адправіў стыкер", - "sentAVideo": "🎥 {username} адправіў відэа", - "sentCallInformations": "{senderName} адправіў інфармацыю пра званок", - "separateChatTypes": "Адрозніваць асабістыя і групавыя чаты", - "setAsCanonicalAlias": "Усталяваць як асноўны аліас", - "setCustomEmotes": "Усталяваць карыстацкія эмоцыі", - "setChatDescription": "Усталяваць апісанне чата", - "setInvitationLink": "Усталяваць спасылку для запрашэння", - "setPermissionsLevel": "Усталяваць узровень дазволаў", - "setStatus": "Усталяваць статус", + "@noPermission": { + "type": "String", + "placeholders": {} + }, + "noRoomsFound": "Пакоі не знойдзены…", + "@noRoomsFound": { + "type": "String", + "placeholders": {} + }, + "notifications": "Паведамленні", + "@notifications": { + "type": "String", + "placeholders": {} + }, + "setChatDescription": "Задаць апісанне чату", + "@setChatDescription": {}, + "setInvitationLink": "Задаць запрашальную спасылку", + "@setInvitationLink": { + "type": "String", + "placeholders": {} + }, + "setPermissionsLevel": "Задаць ўзровееь дазволаў", + "@setPermissionsLevel": { + "type": "String", + "placeholders": {} + }, + "setStatus": "Задаць статус", + "@setStatus": { + "type": "String", + "placeholders": {} + }, "settings": "Налады", + "@settings": { + "type": "String", + "placeholders": {} + }, "share": "Падзяліцца", - "sharedTheLocation": "{username} падзяліўся сваім месцазнаходжаннем", + "@share": { + "type": "String", + "placeholders": {} + }, + "sharedTheLocation": "{username} падзяліўся(-лася) сваім месцазнаходжаннем", + "@sharedTheLocation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, "shareLocation": "Падзяліцца месцазнаходжаннем", + "@shareLocation": { + "type": "String", + "placeholders": {} + }, "showPassword": "Паказаць пароль", + "@showPassword": { + "type": "String", + "placeholders": {} + }, "presenceStyle": "Прысутнасць:", - "presencesToggle": "Паказваць паведамленні аб стане іншых карыстальнікаў", - "singlesignon": "Адзін увайсці", + "@presenceStyle": { + "type": "String", + "placeholders": {} + }, + "presencesToggle": "Паказваць паведасленні статусаў іншых карыстальнікаў", + "@presencesToggle": { + "type": "String", + "placeholders": {} + }, + "singlesignon": "Адзіны ўваход", + "@singlesignon": { + "type": "String", + "placeholders": {} + }, "skip": "Прапусціць", - "sourceCode": "Код крыніцы", - "spaceIsPublic": "Прастора адкрытая", + "@skip": { + "type": "String", + "placeholders": {} + }, + "sourceCode": "Зыходны код", + "@sourceCode": { + "type": "String", + "placeholders": {} + }, + "spaceIsPublic": "Прастора публічна", + "@spaceIsPublic": { + "type": "String", + "placeholders": {} + }, "spaceName": "Назва прасторы", - "startedACall": "{senderName} пачаў званок", - "startFirstChat": "Пачніце свой першы чат", + "@spaceName": { + "type": "String", + "placeholders": {} + }, + "startedACall": "{senderName} пачаў выклік", + "@startedACall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "startFirstChat": "Пачніце ваш першы чат", + "@startFirstChat": {}, "status": "Статус", - "statusExampleMessage": "Як ты сёння?", - "submit": "Адправіць", - "synchronizingPleaseWait": "Сінхранізацыя… Калі ласка, пачакайце.", - "synchronizingPleaseWaitCounter": " Сінхранізацыя… ({percentage}%)", + "@status": { + "type": "String", + "placeholders": {} + }, + "statusExampleMessage": "Як вашыя справы?", + "@statusExampleMessage": { + "type": "String", + "placeholders": {} + }, + "submit": "Дастасаваць", + "@submit": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWait": "Сінхранізацыя... Калі ласка, пачакайце.", + "@synchronizingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWaitCounter": " Сінхарнізацыя… ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, "systemTheme": "Сістэма", - "theyDontMatch": "Не супадаюць", - "theyMatch": "Супадаюць", + "@systemTheme": { + "type": "String", + "placeholders": {} + }, + "theyDontMatch": "Яны не супадаюць", + "@theyDontMatch": { + "type": "String", + "placeholders": {} + }, + "theyMatch": "Яны супадаюць", + "@theyMatch": { + "type": "String", + "placeholders": {} + }, "title": "FluffyChat", - "toggleFavorite": "Уключыць/выключыць любімы", - "toggleMuted": "Уключыць/выключыць маўчанне", - "toggleUnread": "Адзначыць як прачытана/непрачытана", - "tooManyRequestsWarning": "Занадта шмат запытаў. Калі ласка, паспрабуйце яшчэ раз пазней!", - "transferFromAnotherDevice": "Перадаць з іншага прылады", - "tryToSendAgain": "Паспрабуйце яшчэ раз адправіць", - "unavailable": "Надаступна", - "unbannedUser": "{username} разблакаваў {targetName}", - "unblockDevice": "Разблакаваць прыладу", - "unknownDevice": "Невядомая прылада", - "unknownEncryptionAlgorithm": "Невядомы алгарытм шыфравання", - "unknownEvent": "Невядомая падзея '{type}'", - "unmuteChat": "Адключыць маўчанне чата", - "unpin": "Адпінкаваць", - "unreadChats": "{unreadCount, plural, =1{1 нечытаны чат} other{{unreadCount} нечытаных чатаў}}", - "userAndOthersAreTyping": "{username} і {count} іншых набіраюць...", - "userAndUserAreTyping": "{username} і {username2} набіраюць...", - "userIsTyping": "{username} набірае...", - "userLeftTheChat": "🚪 {username} пакінуў чат", - "username": "Імя карыстальніка", - "userSentUnknownEvent": "{username} адправіў падзею {type}", - "unverified": "Непацверджана", - "verified": "Пацверджана", - "verify": "Пацвердзіць", - "verifyStart": "Пачаць пацверджанне", - "verifySuccess": "Вы паспяхова пацвердзілі!", - "verifyTitle": "Пацверджанне іншага акаўнта", - "videoCall": "Відэазванок", - "visibilityOfTheChatHistory": "Виднасьць гісторыі чата", - "visibleForAllParticipants": "Видна для ўсіх удзельнікаў", - "visibleForEveryone": "Видна для ўсіх", - "voiceMessage": "Галасавае паведамленне", - "waitingPartnerAcceptRequest": "Чакаем, пакуль партнёр прыме запыт…", - "waitingPartnerEmoji": "Чакаем, пакуль партнёр прыме эмодзі…", - "waitingPartnerNumbers": "Чакаем, пакуль партнёр прыме нумары…", - "wallpaper": "Фонавы малюнак:", - "warning": "Папярэджанне!", - "weSentYouAnEmail": "Мы адправілі вам электроннае паведамленне", - "whoCanPerformWhichAction": "Хто можа выконваць якія дзеянні", - "whoIsAllowedToJoinThisGroup": "Хто можа далучыцца да гэтай групы", - "whyDoYouWantToReportThis": "Чаму вы хочаце паведаміць пра гэта?", + "@title": { + "description": "Title for the application", + "type": "String", + "placeholders": {} + }, + "toggleFavorite": "Пераключыць Захаванае", + "@toggleFavorite": { + "type": "String", + "placeholders": {} + }, + "toggleMuted": "Пераключыць гук", + "@toggleMuted": { + "type": "String", + "placeholders": {} + }, + "visibleForEveryone": "Бачна ўсім", + "@visibleForEveryone": { + "type": "String", + "placeholders": {} + }, + "voiceMessage": "Галасавое паведамленне", + "@voiceMessage": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerAcceptRequest": "Чакаем, калі партнёр прыме запыт…", + "@waitingPartnerAcceptRequest": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerEmoji": "Чакаем, калі партнёр прыме эмодзі…", + "@waitingPartnerEmoji": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerNumbers": "Чакаем, калі партнёр зацвердзіць лічбы…", + "@waitingPartnerNumbers": { + "type": "String", + "placeholders": {} + }, + "wallpaper": "Шпалеры:", + "@wallpaper": { + "type": "String", + "placeholders": {} + }, + "warning": "Увага!", + "@warning": { + "type": "String", + "placeholders": {} + }, + "weSentYouAnEmail": "Мы даслалі вам ліст на email", + "@weSentYouAnEmail": { + "type": "String", + "placeholders": {} + }, + "whoCanPerformWhichAction": "Хто якія дзеянні можа выконваць", + "@whoCanPerformWhichAction": { + "type": "String", + "placeholders": {} + }, + "whoIsAllowedToJoinThisGroup": "Каму дазволена далучацца да гэтай группы", + "@whoIsAllowedToJoinThisGroup": { + "type": "String", + "placeholders": {} + }, + "whyDoYouWantToReportThis": "Чаму вы хаціце паскардзіцца?", + "@whyDoYouWantToReportThis": { + "type": "String", + "placeholders": {} + }, "wipeChatBackup": "Ачысціць рэзервовую копію чата, каб стварыць новы ключ аднаўлення?", - "withTheseAddressesRecoveryDescription": "З гэтымі адрасамі вы можаце аднавіць свой пароль.", - "writeAMessage": "Напішыце паведамленне…", + "@wipeChatBackup": { + "type": "String", + "placeholders": {} + }, + "withTheseAddressesRecoveryDescription": "З гэтымі адрасамі, вы сожаце аднавіць свой пароль.", + "@withTheseAddressesRecoveryDescription": { + "type": "String", + "placeholders": {} + }, + "writeAMessage": "Напісать паведамленне…", + "@writeAMessage": { + "type": "String", + "placeholders": {} + }, "yes": "Так", + "@yes": { + "type": "String", + "placeholders": {} + }, "you": "Вы", + "@you": { + "type": "String", + "placeholders": {} + }, "youAreNoLongerParticipatingInThisChat": "Вы больш не ўдзельнічаеце ў гэтым чаце", - "youHaveBeenBannedFromThisChat": "Вас забанілі ў гэтым чаце", + "@youAreNoLongerParticipatingInThisChat": { + "type": "String", + "placeholders": {} + }, + "youHaveBeenBannedFromThisChat": "Вы былі заблакіраваны ў гэтым чаце", + "@youHaveBeenBannedFromThisChat": { + "type": "String", + "placeholders": {} + }, "yourPublicKey": "Ваш публічны ключ", + "@yourPublicKey": { + "type": "String", + "placeholders": {} + }, "messageInfo": "Інфармацыя пра паведамленне", + "@messageInfo": {}, "time": "Час", + "@time": {}, "messageType": "Тып паведамлення", - "sender": "Адрасант", + "@messageType": {}, + "sender": "Адпраўшчык", + "@sender": {}, "openGallery": "Адкрыць галерэю", + "@openGallery": {}, "removeFromSpace": "Выдаліць з прасторы", - "addToSpaceDescription": "Выберыце прастору, у якую трэба дадаць гэты чат.", + "@removeFromSpace": {}, + "addToSpaceDescription": "Выберыце прастору, да якой дадаць гэты чат.", + "@addToSpaceDescription": {}, "start": "Пачаць", - "pleaseEnterRecoveryKeyDescription": "Каб разблакаваць вашы старыя паведамленні, увядзіце ключ аднаўлення, які быў створаны ў папярэдняй сесіі. Ваш ключ аднаўлення НЕ з'яўляецца вашым паролем.", + "@start": {}, + "pleaseEnterRecoveryKeyDescription": "Каб разблакіраваць вашы мінулыя паведамленні, калі ласка, увядзіце ключ аднаўлення, што быў згенерыраваны ў мінулай сесіі. Ключ аднаўлення гэта НЕ ваш пароль.", + "@pleaseEnterRecoveryKeyDescription": {}, "publish": "Апублікаваць", + "@publish": {}, "videoWithSize": "Відэа ({size})", + "@videoWithSize": { + "type": "String", + "placeholders": { + "size": { + "type": "String" + } + } + }, "openChat": "Адкрыць чат", + "@openChat": {}, "markAsRead": "Адзначыць як прачытанае", - "reportUser": "Паведаміць пра карыстальніка", + "@markAsRead": {}, + "reportUser": "Паскардзіцца на карыстальніка", + "@reportUser": {}, "dismiss": "Адхіліць", - "reactedWith": "{sender} рэагаваў з {reaction}", - "pinMessage": "Закрэслiць у пакоі", - "confirmEventUnpin": "Ці ўпэўнены вы, што жадаеце навечна адкрэслiць падзею?", + "@dismiss": {}, + "reactedWith": "{sender} рэагуе з {reaction}", + "@reactedWith": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "reaction": { + "type": "String" + } + } + }, + "pinMessage": "Прымацаваць да пакою", + "@pinMessage": {}, + "confirmEventUnpin": "Вы ўпэўнены ў тым, што хаціце назаўсёды адмацаваць гэту падзею?", + "@confirmEventUnpin": {}, "emojis": "Эмодзі", - "placeCall": "Звярнуцца", - "voiceCall": "Галасавы званок", + "@emojis": {}, + "placeCall": "Здзейсніць выклік", + "@placeCall": {}, + "voiceCall": "Галасавы выклік", + "@voiceCall": {}, "unsupportedAndroidVersion": "Непадтрымліваемая версія Android", - "unsupportedAndroidVersionLong": "Гэтая функцыя патрабуе больш новую версію Android. Калі ласка, праверце наяўнасць абнаўленняў або падтрымку Lineage OS.", - "videoCallsBetaWarning": "Звярніце ўвагу, што відэазванкі зараз знаходзяцца ў бэта-версіі. Яны могуць не працаваць як трэба або цалкам не працаваць на ўсіх платформах.", - "experimentalVideoCalls": "Эксперыментальныя відэазванкі", - "emailOrUsername": "Электронная пошта або імя карыстальніка", + "@unsupportedAndroidVersion": {}, + "unsupportedAndroidVersionLong": "Гэта функцыя патрабуе навейшай версіі Android. Калі ласка, праверце наяўнасць абнаўленняў ці падтрымку Linage OS.", + "@unsupportedAndroidVersionLong": {}, + "videoCallsBetaWarning": "Звярніце ўвагу, што відэа выклікі знаходзяцца ў бэце. Яны могуць працаваць некарэктна ці не на ўсіх платформах.", + "@videoCallsBetaWarning": {}, + "experimentalVideoCalls": "Эксперыментальныя відэа выклікі", + "@experimentalVideoCalls": {}, + "emailOrUsername": "Email ці імя карыстальніка", + "@emailOrUsername": {}, "indexedDbErrorTitle": "Праблемы з прыватным рэжымам", - "indexedDbErrorLong": "Захоўванне паведамленняў, на жаль, не ўключана ў прыватным рэжыме па змаўчанні.\nКалі ласка, наведайце\n - about:config\n - усталюйце dom.indexedDB.privateBrowsing.enabled у true\nІнакш, запусціць FluffyChat не ўдасца.", - "switchToAccount": "Пераключыцца на рахунак {number}", - "nextAccount": "Наступны рахунак", - "previousAccount": "Папярэдні рахунак", + "@indexedDbErrorTitle": {}, + "indexedDbErrorLong": "Сховішча паведамленняў, на жаль, не ўключана ў прыватным рэжыме па змаўчанні.\nКалі ласка, наведайце\n - about:config\n - што dom.indexedDB.privateBrowsing.enabled уключаны\nІнакш, FluffyChat будзе немагчыма запусціць.", + "@indexedDbErrorLong": {}, + "switchToAccount": "Пераключыцца на ўліковы запіс {number}", + "@switchToAccount": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "nextAccount": "Наступны ўліковы запіс", + "@nextAccount": {}, + "previousAccount": "Мінулы ўліковы запіс", + "@previousAccount": {}, "addWidget": "Дадаць віджэт", + "@addWidget": {}, "widgetVideo": "Відэа", - "widgetEtherpad": "Тэкстовая нататка", + "@widgetVideo": {}, + "widgetEtherpad": "Тэкставая нататка", + "@widgetEtherpad": {}, "widgetJitsi": "Jitsi Meet", - "widgetCustom": "Паводле ўласнага выбару", + "@widgetJitsi": {}, + "widgetCustom": "Карыстальніцкі", + "@widgetCustom": {}, "widgetName": "Імя", - "widgetUrlError": "Гэта недапушчальны URL.", - "widgetNameError": "Калі ласка, увядзіце адлюстравальнае імя.", - "errorAddingWidget": "Памылка пры даданні віджэта.", - "youRejectedTheInvitation": "Вы адхілілі запрашэнне", - "youJoinedTheChat": "Вы далучыліся да чата", + "@widgetName": {}, + "widgetUrlError": "Гэта хібны URL.", + "@widgetUrlError": {}, + "widgetNameError": "Калі ласка, укажыце адлюстроўваемаем імя.", + "@widgetNameError": {}, + "errorAddingWidget": "Памылка дадання віджэту.", + "@errorAddingWidget": {}, + "youRejectedTheInvitation": "Вы скасавалі запрашэнне", + "@youRejectedTheInvitation": {}, + "youJoinedTheChat": "Вы далучыліся да чату", + "@youJoinedTheChat": {}, "youAcceptedTheInvitation": "👍 Вы прынялі запрашэнне", - "youBannedUser": "Вы заблакавалі {user}", - "youHaveWithdrawnTheInvitationFor": "Вы адклікалі запрашэнне для {user}", - "youInvitedToBy": "📩 Вы былі запрошаны праз спасылку на:\n{alias}", - "youInvitedBy": "📩 Вы былі запрошаны {user}", - "invitedBy": "📩 Запрошаны {user}", + "@youAcceptedTheInvitation": {}, + "youBannedUser": "Вы заблакіравалі {user}", + "@youBannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youHaveWithdrawnTheInvitationFor": "Вы адазвалі запрашэнне для {user}", + "@youHaveWithdrawnTheInvitationFor": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youInvitedToBy": "📩 Вы былі запрошаны па спасылцы на:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": { + "type": "String" + } + } + }, + "youInvitedBy": "📩 Вы былі запрошаны карыстальнікам {user}", + "@youInvitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "invitedBy": "📩 Запрошаны(-а) {user}", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, "youInvitedUser": "📩 Вы запрасілі {user}", + "@youInvitedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, "youKicked": "👞 Вы выгналі {user}", - "youKickedAndBanned": "🙅‍♀️ Вы выгналі і заблакавалі {user}", - "youUnbannedUser": "Вы разблакавалі {user}", - "hasKnocked": "🚪 {user} пастукаў", - "usersMustKnock": "Карыстальнікі павінны пастукаць", + "@youKicked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youKickedAndBanned": "🙅 Вы выгналі і заблакіравалі {user}", + "@youKickedAndBanned": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youUnbannedUser": "Вы разблакіравалі {user}", + "@youUnbannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "hasKnocked": "🚪 {user} пагрукаўся", + "@hasKnocked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "usersMustKnock": "Карыстальнікі абавязаны пагрукацца", + "@usersMustKnock": {}, "noOneCanJoin": "Ніхто не можа далучыцца", - "userWouldLikeToChangeTheChat": "{user} хацеў бы далучыцца да чата.", - "noPublicLinkHasBeenCreatedYet": "Пакуль не створана публічная спасылка", - "knock": "Пастукаць", + "@noOneCanJoin": {}, + "userWouldLikeToChangeTheChat": "{user} хоча далучыцца да чату.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "noPublicLinkHasBeenCreatedYet": "Публічны ключ яшчэ не створаны", + "@noPublicLinkHasBeenCreatedYet": {}, + "knock": "Пагрукацца", + "@knock": {}, "users": "Карыстальнікі", - "unlockOldMessages": "Разблакаваць старыя паведамленні", - "storeInSecureStorageDescription": "Захоўвайце ключ аднаўлення ў бяспечным сховішчы гэтага прылады.", - "saveKeyManuallyDescription": "Захоўвайце гэты ключ уручную, выклікаючы дыялог сістэмнага абмену або буфера абмену.", - "storeInAndroidKeystore": "Захоўваць у Android KeyStore", - "storeInAppleKeyChain": "Захоўваць у Apple KeyChain", - "storeSecurlyOnThisDevice": "Захоўваць бяспечна на гэтым прыладзе", + "@users": {}, + "unlockOldMessages": "Адкрыць старыя паведамленні", + "@unlockOldMessages": {}, + "storeInSecureStorageDescription": "Захаваць код аднаўлення ў бяспечным месцы на прыладзе.", + "@storeInSecureStorageDescription": {}, + "saveKeyManuallyDescription": "Захаваць гэты ключ самастойна, выклікам сістэмнага акна Падзяліцца ці праз буфер.", + "@saveKeyManuallyDescription": {}, + "storeInAndroidKeystore": "Захаваць у Android KeyStore", + "@storeInAndroidKeystore": {}, + "storeInAppleKeyChain": "Захаваць у Apple KeyChain", + "@storeInAppleKeyChain": {}, + "storeSecurlyOnThisDevice": "Захаваць на гэтай прыладзе", + "@storeSecurlyOnThisDevice": {}, "countFiles": "{count} файлаў", + "@countFiles": { + "placeholders": { + "count": { + "type": "int" + } + } + }, "user": "Карыстальнік", - "custom": "Наступны", - "foregroundServiceRunning": "Гэта апавяшчэнне з'явіцца, калі запушчана сэрвіс на пераднім плане.", - "screenSharingTitle": "падзяленне экранам", - "screenSharingDetail": "Вы дзяліцеся сваім экранам у FuffyChat", - "callingPermissions": "Дазволы для званкоў", - "callingAccount": "Уліковы запіс для званкоў", - "callingAccountDetails": "Дазваляе FluffyChat выкарыстоўваць натыўнае прыкладанне для нумарацыі Android.", + "@user": {}, + "custom": "Карыстальніцкае", + "@custom": {}, + "foregroundServiceRunning": "Гэта паведамленне з'явіцца, калі асноўныя службы запрацуюць.", + "@foregroundServiceRunning": {}, + "screenSharingTitle": "падзяліцца экранам", + "@screenSharingTitle": {}, + "screenSharingDetail": "Вы дзеліцеся экранам у FluffyChat", + "@screenSharingDetail": {}, + "callingPermissions": "Дазволы на выклікі", + "@callingPermissions": {}, + "callingAccount": "Уліковы запіс для выклікаў", + "@callingAccount": {}, + "callingAccountDetails": "Дазваляе FluffyChat выкарыстоўваць сістэмную праграму для выклікаў.", + "@callingAccountDetails": {}, "appearOnTop": "З'яўляцца зверху", - "appearOnTopDetails": "Дазваляе прыкладанню з'яўляцца зверху (не патрэбна, калі ў вас ужо наладжаны ўліковы запіс для званкоў у FluffyChat)", + "@appearOnTop": {}, + "appearOnTopDetails": "Дазваляе праграму з'яўляцца наверсе (не патрэбна, калі FluffyChat наладжаны як уліковы запіс для выклікаў)", + "@appearOnTopDetails": {}, "otherCallingPermissions": "Мікрафон, камера і іншыя дазволы FluffyChat", - "whyIsThisMessageEncrypted": "Чаму гэтае паведамленне недаступнае для прачытання?", - "noKeyForThisMessage": "Гэта можа адбыцца, калі паведамленне было адпраўлена да таго, як вы ўвайшлі ў свой уліковы запіс на гэтым прыладзе.\n\nМагчыма, адпраўнік заблакаваў ваш прыладу або нешта пайшло не так з інтэрнэт-злучэннем.\n\nЦі можаце вы прачытаць паведамленне на іншай сесіі? Тады вы можаце перанесці паведамленне з яе! Перайдзіце ў Налады > Прылады і пераканайцеся, што вашы прылады пацвердзілі адзін аднаго. Калі вы адкрыеце пакой наступны раз і абедзве сесіі будуць у пераднім плане, ключы будуць аўтаматычна перададзены.\n\nЦі не хочаце вы страціць ключы пры выхадзе або пераключэнні прылад? Пераканайцеся, што вы ўключылі рэзервнае капіраванне чата ў наладах.", + "@otherCallingPermissions": {}, + "whyIsThisMessageEncrypted": "Чаму гэта паведамленне нельга прачытаць?", + "@whyIsThisMessageEncrypted": {}, + "noKeyForThisMessage": "Гэта можа здарыцца з-за таго, што паведамленне было даслана да таго, як вы увайшлі ў уліковы запіс на гэтай прыладзе.\n\nТаксама верагодна, што адпраўшчык заблакіраваў вашу прыладу ці ў вас хібы з інтэрнэтам.\n\nВы можаце чытаць гэта паведамленне з іншага сеансу? Тад дашліце паведамленне адтуль! Перайдзіце ў Налады > Прылады і пераканайцеся ў тым, што вашы прылады верыфікавалі адна адну. Калі вы адкрыеце пакой наступны раз і абодве сэсіі будуць запушчаны, ключы павінны сінхранізавацца аўтаматычна.\n\nВы не хаціце згубіць клбчы, калі будзеце выходзіць ці змяняць прылады? Пераканайцеся ў тым, што вы уключылі рэзервовае капіраванне чатаў у наладах.", + "@noKeyForThisMessage": {}, "newGroup": "Новая група", - "newSpace": "Новы прастор", - "enterSpace": "Увайсці ў прастор", + "@newGroup": {}, + "newSpace": "Новая прастора", + "@newSpace": {}, + "enterSpace": "Увайсці ў прастору", + "@enterSpace": {}, "enterRoom": "Увайсці ў пакой", + "@enterRoom": {}, "allSpaces": "Усе прасторы", - "numChats": "{number} размоў", - "hideUnimportantStateEvents": "Схаваць неважлівыя падзеі стану", - "hidePresences": "Схаваць спіс статусаў?", + "@allSpaces": {}, + "numChats": "{number} чатаў", + "@numChats": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "hideUnimportantStateEvents": "Хаваць неабавязковыя падзеі статусу", + "@hideUnimportantStateEvents": {}, + "hidePresences": "Хаваць спіс статусаў?", + "@hidePresences": {}, "doNotShowAgain": "Не паказваць зноў", - "wasDirectChatDisplayName": "Пустая размова (была {oldDisplayName})", - "newSpaceDescription": "Прасторы дазваляюць аб'яднаць вашы размовы і ствараць прыватныя або публічныя супольнасці.", - "encryptThisChat": "Зашыфраваць гэтую размову", - "disableEncryptionWarning": "З-за мер бяспекі вы не можаце адключыць шыфраванне ў размове, дзе яно было ўключана раней.", - "sorryThatsNotPossible": "Прабачце... гэта немагчыма", - "deviceKeys": "Ключы прылады:", - "reopenChat": "Паўторна адкрыць размову", - "noBackupWarning": "Увага! Без уключэння рэзервовага капіравання размовы вы страціце доступ да зашыфраваных паведамленняў. Вельмі рэкамендуецца ўключыць рэзервовае капіраванне перад выходам.", + "@doNotShowAgain": {}, + "wasDirectChatDisplayName": "Пусты чат (быў {oldDisplayName})", + "@wasDirectChatDisplayName": { + "type": "String", + "placeholders": { + "oldDisplayName": { + "type": "String" + } + } + }, + "newSpaceDescription": "Прасторы дазваляюць аб'ядноўваць вашы чаты і ствараць агульныя ці асобныя супольнасці.", + "@newSpaceDescription": {}, + "encryptThisChat": "Шыфраваць гэты чат", + "@encryptThisChat": {}, + "disableEncryptionWarning": "У мэтах бяспекі, вы не можаце адклбчауь шыфраванне ў гэтым чаце, дзе яно было ўключана.", + "@disableEncryptionWarning": {}, + "sorryThatsNotPossible": "Прабачце... Гэта немагчыма", + "@sorryThatsNotPossible": {}, + "deviceKeys": "Ключы прылад:", + "@deviceKeys": {}, + "reopenChat": "Адкрыць чат зноў", + "@reopenChat": {}, + "noBackupWarning": "Увага! Без уключэння рэзервовага капіравання чатаў, вы страціце доступ да вашых зашыфраваных паведамленняў. Настойліва рэкамендуецца уключыць фукнцыю да таго, як выйсці.", + "@noBackupWarning": {}, "noOtherDevicesFound": "Іншыя прылады не знойдзены", - "fileIsTooBigForServer": "Немагчыма адправіць! Сервер падтрымлівае прыкладання толькі да {max}.", - "fileHasBeenSavedAt": "Файл захаваны ў {path}", - "jumpToLastReadMessage": "Перайсці да апошняга прачытанага паведамлення", - "readUpToHere": "Прачытайце да гэтага месца", + "@noOtherDevicesFound": {}, + "fileIsTooBigForServer": "Немагчыма даслаць! Сервер падтрымлівае файлы да {max}.", + "@fileIsTooBigForServer": { + "type": "String", + "placeholders": { + "max": { + "type": "String" + } + } + }, + "fileHasBeenSavedAt": "Файл быў захаваны ў {path}", + "@fileHasBeenSavedAt": { + "type": "String", + "placeholders": { + "path": { + "type": "String" + } + } + }, + "jumpToLastReadMessage": "Перайсці да апошняга паведамлення", + "@jumpToLastReadMessage": {}, + "readUpToHere": "Чытаць тут", + "@readUpToHere": {}, "jump": "Перайсці", - "openLinkInBrowser": "Адкрыйце спасылку ў браўзеры", - "reportErrorDescription": "😞 Ой. Адбылася памылка. Калі хочаце, вы можаце паведаміць пра гэтую памылку распрацоўшчыкам.", - "report": "справаздача", + "@jump": {}, + "openLinkInBrowser": "Адкрыць спасылку ў браўзеры", + "@openLinkInBrowser": {}, + "reportErrorDescription": "😭 О не, штосьці пайшло не так. Калі жадаеце, можаце падаць справаздачу аб памылке распрауоўшчыкам.", + "@reportErrorDescription": {}, + "report": "паскардзіцца", + "@report": {}, "signInWithPassword": "Увайсці з паролем", - "pleaseTryAgainLaterOrChooseDifferentServer": "Калі ласка, паспрабуйце яшчэ раз пазней або выберыце іншы сервер.", - "signInWith": "Увайсці з {provider}", - "profileNotFound": "Карыстальнік не знойдзены на серверы. Магчыма, ёсць праблема з злучэннем або карыстальнік не існуе.", - "setTheme": "Усталяваць тэму:", - "setColorTheme": "Усталяваць каляровую тэму:", - "invite": "Запрасіць", - "inviteGroupChat": "📨 Запрасіць у групавы чат", - "invitePrivateChat": "📨 Запрасіць у прыватны чат", - "invalidInput": "Несапраўдны ўвод!", - "wrongPinEntered": "Уведзены няправільны PIN! Паспрабуйце яшчэ раз праз {seconds} секунд...", - "pleaseEnterANumber": "Калі ласка, увядзіце лік, большы за 0", - "archiveRoomDescription": "Гэтая размова будзе перамешчана ў архіў. Іншыя карыстальнікі змогуць бачыць, што вы пакінулі размову.", - "roomUpgradeDescription": "Пасля гэтага размова будзе перастворана з новай версіяй пакоя. Удзельнікі будуць праінфармаваны, што ім трэба перайсці на новую размову. Больш інфармацыі пра версіі пакояў можна знайсці на https://spec.matrix.org/latest/rooms/", - "removeDevicesDescription": "Вы будзеце выведзены з гэтага прылады і больш не зможаце атрымліваць паведамленні.", - "banUserDescription": "Карыстальнік будзе заблакаваны ў гэтым чаце і не зможа зноў увайсці, пакуль яго не разблакуюць.", - "unbanUserDescription": "Карыстальнік зможа зноў увайсці ў чат, калі паспрабуе.", - "kickUserDescription": "Карыстальнік выдалены з чата, але не заблакаваны. У публічных чатах карыстальнік можа далучыцца зноў у любы час.", - "makeAdminDescription": "Калі вы зробіце гэтага карыстальніка адміністратарам, вы можаце не мець магчымасці адмяніць гэта, бо ён атрымае тыя ж правы, што і вы.", - "pushNotificationsNotAvailable": "Паведамленні ў выглядзе пуш-апавяшчэнняў недаступныя", - "learnMore": "Даведайцеся больш", - "yourGlobalUserIdIs": "Ваш глабальны ідэнтыфікатар карыстальніка: ", - "noUsersFoundWithQuery": "На жаль, карыстальнік з запытам \"{query}\" не знойдзены. Праверце, ці не дапусцілі вы памылку ў напісанні.", - "knocking": "Дзвін", - "chatCanBeDiscoveredViaSearchOnServer": "Чат можна знайсці праз пошук на {server}", - "searchChatsRooms": "Пошук #чатоў, @карыстальнікаў...", - "nothingFound": "Нічога не знойдзена...", - "groupName": "Назва групы", - "createGroupAndInviteUsers": "Стварыць групу і запрасіць карыстальнікаў", - "groupCanBeFoundViaSearch": "Групу можна знайсці праз пошук", - "wrongRecoveryKey": "Прабачце... здаецца, гэта не правільны ключ аднаўлення.", - "startConversation": "Пачаць размову", - "commandHint_sendraw": "Адправіць сыры json", - "databaseMigrationTitle": "База дадзеных аптымізавана", - "databaseMigrationBody": "Калі ласка, пачакайце. Гэта можа заняць некалькі момантаў.", - "leaveEmptyToClearStatus": "Астатняйце пустым, каб ачысціць свой статус.", - "select": "Выбраць", - "searchForUsers": "Шукайце @карыстальнікаў...", - "pleaseEnterYourCurrentPassword": "Калі ласка, увядзіце ваш існуючы пароль", - "newPassword": "Новы пароль", - "pleaseChooseAStrongPassword": "Калі ласка, выберыце моцны пароль", - "passwordsDoNotMatch": "Паролі не супадаюць", - "passwordIsWrong": "Уведзены вамі пароль няправільны", - "publicLink": "Публічная спасылка", - "publicChatAddresses": "Публічныя адрасы чатаў", - "createNewAddress": "Стварыць новы адрас", - "joinSpace": "Далучыцца да прасторы", - "publicSpaces": "Публічныя прасторы", - "addChatOrSubSpace": "Дадаць чат або падпрастору", - "subspace": "Падпрастора", - "decline": "Адмовіцца", - "thisDevice": "Гэта прылада:", - "initAppError": "Памылка падчас ініцыялізацыі прыкладання", - "userRole": "Роля карыстальніка", - "minimumPowerLevel": "{level} — мінімальны ўзровень паўнамоцтваў.", - "searchIn": "Шукайце ў чате \"{chat}\"...", - "searchMore": "Шукайце яшчэ...", - "gallery": "Галерэя", - "files": "Файлы", - "databaseBuildErrorBody": "Не ўдаецца пабудаваць базу дадзеных SQlite. Праграма пакуль выкарыстоўвае старую базу дадзеных. Калі ласка, паведаміце пра гэтую памылку распрацоўшчыкам на {url}. Паведамленне пра памылку: {error}", - "sessionLostBody": "Ваш сеанс страчаны. Калі ласка, паведаміце пра гэтую памылку распрацоўшчыкам на {url}. Паведамленне пра памылку: {error}", - "restoreSessionBody": "Праграма цяпер спрабуе аднавіць ваш сеанс з рэзервовай копіі. Калі ласка, паведаміце пра гэтую памылку распрацоўшчыкам на {url}. Паведамленне пра памылку: {error}", - "forwardMessageTo": "Пераслаць паведамленне ў {roomName}?", - "sendReadReceipts": "Адправіць пацверджанні пра чытанне", - "sendTypingNotificationsDescription": "Іншыя ўдзельнікі чату могуць бачыць, калі вы набіраеце новае паведамленне.", - "sendReadReceiptsDescription": "Іншыя ўдзельнікі чату могуць бачыць, калі вы прачыталі паведамленне.", - "formattedMessages": "Фарматаваныя паведамленні", - "formattedMessagesDescription": "Паказваць багаты змест паведамленняў, напрыклад, тэкст у тлустым шрыфце з выкарыстаннем markdown.", - "verifyOtherUser": "🔐 Праверыць іншага карыстальніка", - "verifyOtherUserDescription": "Калі вы правяраеце іншага карыстальніка, вы можаце быць упэўнены, што ведаеце, з кім сапраўды перапісываецеся. 💪\n\nКалі вы пачынаеце праверку, вы і іншы карыстальнік ўбачыце ў аплікацыі ўсплывальнае акно. Там вы ўбачыце серыю эмодзі або нумароў, якія трэба параўнаць паміж сабой.\n\nЛепшы спосаб зрабіць гэта — сустрэцца ці пачаць відэазванок. 🧑‍🤝‍🧑", - "verifyOtherDevice": "🔐 Праверыць іншае прыладу", - "verifyOtherDeviceDescription": "Калі вы правяраеце іншую прыладу, гэтыя прылады могуць абменьвацца ключамі, што павышае вашу агульную бяспеку. 💪 Калі вы пачынаеце праверку, у аплікацыі з'явіцца ўсплывальнае акно на абодвух прыладах. Там вы ўбачыце серыю эмодзі або нумароў, якія трэба параўнаць. Лепш мець абедзве прылады пад рукой перад пачаткам праверкі. 🤳", - "acceptedKeyVerification": "{sender} прыняў праверку ключа", - "canceledKeyVerification": "{sender} адмяніў праверку ключа", - "completedKeyVerification": "{sender} завяршыў праверку ключа", - "isReadyForKeyVerification": "{sender} гатовы да праверкі ключа", - "requestedKeyVerification": "{sender} запытаў праверку ключа", - "startedKeyVerification": "{sender} пачаў праверку ключа", - "transparent": "Празрысты", - "incomingMessages": "Уходзячыя паведамленні", - "stickers": "Наліпкі", - "discover": "Адкрыць", - "commandHint_ignore": "Ігнараваць дадзены ідэнтыфікатар матрыцы", - "commandHint_unignore": "Адмяніць ігнараванне дадзенага ідэнтыфікатара матрыцы", - "unreadChatsInApp": "{appname}: {unread} нявідкладных размоваў", - "noDatabaseEncryption": "Шыфраванне базы дадзеных не падтрымліваецца на гэтай платформе", - "thereAreCountUsersBlocked": "Зараз заблакавана {count} карыстальнікаў.", - "restricted": "Абмежаваны", - "knockRestricted": "Абмежавана ўдар", - "goToSpace": "Перайсці ў прастору: {space}", - "markAsUnread": "Адзначыць як нявідкладнае", - "userLevel": "{level} - Карыстальнік", - "moderatorLevel": "{level} - Мадэратар", - "adminLevel": "{level} - Адміністратар", - "changeGeneralChatSettings": "Змяніць агульныя налады чата", - "inviteOtherUsers": "Запрасіць іншых карыстальнікаў у гэты чат", - "changeTheChatPermissions": "Змяніць дазволы чата", - "changeTheVisibilityOfChatHistory": "Змяніць бачнасць гісторыі чата", - "changeTheCanonicalRoomAlias": "Змяніць асноўны публічны адрас чата", - "sendRoomNotifications": "Адправіць паведамленні @room", - "changeTheDescriptionOfTheGroup": "Змяніць апісанне чата", - "chatPermissionsDescription": "Вызначце, які ўзровень паўнамоцтваў неабходны для пэўных дзеянняў у гэтым чаце. Узроўні паўнамоцтваў 0, 50 і 100 звычайна прадстаўляюць карыстальнікаў, мадэратараў і адміністратараў, але магчымы любыя градацыі.", - "updateInstalled": "🎉 Усталявана абнаўленне {version}!", - "changelog": "Змяненні", - "sendCanceled": "Адпраўка скасавана", - "loginWithMatrixId": "Увайсці з Matrix-ID", - "discoverHomeservers": "Адкрыйце хатнія серверы", - "whatIsAHomeserver": "Што такое хатні сервер?", - "homeserverDescription": "Усе вашы дадзеныя захоўваюцца на хатнім серверы, так жа, як і ў пастаўшчыка электроннай пошты. Вы можаце выбраць, які хатні сервер выкарыстоўваць, пры гэтым вы можаце працягваць камунікаваць з усімі. Даведайцеся больш на https://matrix.org.", - "doesNotSeemToBeAValidHomeserver": "Гэта, здаецца, несумяшчальны хатні сервер. Няправільны URL?", - "calculatingFileSize": "Разлік памеру файла...", - "prepareSendingAttachment": "Падрыхтоўка да адпраўкі прыкладання...", - "sendingAttachment": "Адпраўка прыкладання...", - "generatingVideoThumbnail": "Стварэнне эскізу відэа...", - "compressVideo": "Сцісканне відэа...", - "sendingAttachmentCountOfCount": "Адпраўка прыкладання {index} з {length}...", - "serverLimitReached": "Даступна мяжа сервера! Чаканне {seconds} секунд...", - "oneOfYourDevicesIsNotVerified": "Адзін з вашых прылад не праверены", - "noticeChatBackupDeviceVerification": "Заўвага: Калі вы падключаеце ўсе свае прылады да рэзервовай копіі чата, яны аўтаматычна праверяцца.", - "continueText": "Працягваць", - "welcomeText": "Прывітанне! 👋 Гэта FluffyChat. Вы можаце ўвайсці на любы хатні сервер, сумяшчальны з https://matrix.org. А потым перапісацца з кім заўгодна. Гэта вялікая дэцэнтралізаваная сетка паведамленняў!", - "blur": "Размыццё:", - "opacity": "Непразрыстасць:", - "setWallpaper": "Усталяваць фонавы малюнак", - "manageAccount": "Кіраванне акаўнтам", - "noContactInformationProvided": "Сервер не прадастаўляе сапраўдную кантактную інфармацыю", - "contactServerAdmin": "Звяжыцеся з адміністратарам сервера", - "contactServerSecurity": "Звяжыцеся з бяспекай сервера", - "supportPage": "Старонка падтрымкі", - "serverInformation": "Інфармацыя пра сервер:", + "@signInWithPassword": {}, + "manageAccount": "Кіраванне ўліковым запісам", + "@manageAccount": {}, + "noContactInformationProvided": "Сервер не мае ніякай вернай кантактнай інфармацыі", + "@noContactInformationProvided": {}, + "contactServerAdmin": "Звязацца з адміністратарам сервера", + "@contactServerAdmin": {}, + "contactServerSecurity": "Звязацца з сервернай бяспекай", + "@contactServerSecurity": {}, + "supportPage": "Падтрымка", + "@supportPage": {}, + "serverInformation": "Серверная інфармацыя:", + "@serverInformation": {}, "name": "Імя", + "@name": {}, "version": "Версія", - "website": "Вэб-сайт", - "compress": "Сціскаць", - "boldText": "Тлусты тэкст", + "@version": {}, + "website": "Сайт", + "@website": {}, + "compress": "Сцісканне", + "@compress": {}, + "boldText": "Цёмны", + "@boldText": {}, "italicText": "Курсіў", - "strikeThrough": "Падкрэслены", + "@italicText": {}, + "strikeThrough": "Перакрэслены", + "@strikeThrough": {}, "pleaseFillOut": "Калі ласка, запоўніце", - "invalidUrl": "Несапраўдны URL", + "@pleaseFillOut": {}, + "invalidUrl": "Няслушны url", + "@invalidUrl": {}, "addLink": "Дадаць спасылку", - "unableToJoinChat": "Немагчыма далучыцца да чата. Магчыма, іншая бок ужо закрыла размову.", - "previous": "Папярэдні", - "otherPartyNotLoggedIn": "Іншая бок у дадзены момант не ўвайшла ў сістэму і таму не можа атрымліваць паведамленні!", - "appWantsToUseForLogin": "Выкарыстоўваць '{server}' для ўваходу", - "appWantsToUseForLoginDescription": "Вы дазваляеце дадатку і вэб-сайту дзяліцца інфармацыяй пра вас.", + "@addLink": {}, + "unableToJoinChat": "Немагчыма далучыцца да чату. Магчыма, іншы бок ужо скончыў размову.", + "@unableToJoinChat": {}, + "previous": "Мінулы", + "@previous": {}, + "otherPartyNotLoggedIn": "Іншы бок зараз не увайшоў, таму не можа атрымліваць паведамленні!", + "@otherPartyNotLoggedIn": {}, + "appWantsToUseForLogin": "Выкарыстоўваць '{server}' для ўвахода", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appWantsToUseForLoginDescription": "Тым самым, вы дазваляеце праграме і сайту дзяліцца інфармацыяй пра вас.", + "@appWantsToUseForLoginDescription": {}, "open": "Адкрыць", + "@open": {}, "waitingForServer": "Чаканне сервера...", - "appIntroduction": "FluffyChat дазваляе вам перагаварываць з сябрамі ў розных мессенджарах. Даведайцеся больш на https://matrix.org або проста націсніце *Працягнуць*.", - "newChatRequest": "📩 Новы запыт на чат", - "contentNotificationSettings": "Налады апавяшчэнняў пра змест", - "generalNotificationSettings": "Агульныя налады апавяшчэнняў", - "roomNotificationSettings": "Налады апавяшчэнняў пакоя", - "userSpecificNotificationSettings": "Індывідуальныя налады апавяшчэнняў карыстальніка", - "otherNotificationSettings": "Іншыя налады апавяшчэнняў", - "notificationRuleContainsUserName": "Утрымлівае імя карыстальніка", - "notificationRuleContainsUserNameDescription": "Паведамляе карыстальніку, калі паведамленне ўтрымлівае яго імя карыстальніка.", - "notificationRuleMaster": "Заглушыць усе апавяшчэнні", - "notificationRuleMasterDescription": "Пераўзыходзіць усе іншыя правілы і адключае ўсе апавяшчэнні.", - "notificationRuleSuppressNotices": "Падаўленне аўтаматычных паведамленняў", - "notificationRuleSuppressNoticesDescription": "Падаўляе апавяшчэнні ад аўтаматычных кліентаў, такіх як боты.", - "notificationRuleInviteForMe": "Запрашэнне для мяне", - "notificationRuleInviteForMeDescription": "Паведамляе карыстальніку, калі яго запрашаюць у пакой.", - "notificationRuleMemberEvent": "Падзея ўдзельніка", - "notificationRuleMemberEventDescription": "Падаўляе апавяшчэнні пра падзеі ўдзельнікаў.", - "notificationRuleIsUserMention": "Згадка карыстальніка", - "notificationRuleIsUserMentionDescription": "Паведамляе карыстальніку, калі яго прамым чынам згадваюць у паведамленні.", - "notificationRuleContainsDisplayName": "Змяшчае імя паказу", - "notificationRuleContainsDisplayNameDescription": "Паведамляе карыстальніку, калі паведамленне ўтрымлівае яго імя паказу.", - "notificationRuleIsRoomMention": "Згадка ў пакоі", - "notificationRuleIsRoomMentionDescription": "Паведамляе карыстальніку, калі ёсць згадка ў пакоі.", - "notificationRuleRoomnotif": "Апавяшчэнне пакоя", - "notificationRuleRoomnotifDescription": "Паведамляе карыстальніку, калі паведамленне ўтрымлівае '@room'.", - "notificationRuleTombstone": "Мемарыял", - "notificationRuleTombstoneDescription": "Паведамляе карыстальніку пра паведамленні аб дэактывацыі пакоя.", - "notificationRuleReaction": "Рэакцыя", - "notificationRuleReactionDescription": "Падаўляе апавяшчэнні пра рэакцыі.", - "notificationRuleRoomServerAcl": "ACL сервера пакоя", - "notificationRuleRoomServerAclDescription": "Падаўляе апавяшчэнні пра спісы кантролю доступу да сервера пакояў (ACL).", - "notificationRuleSuppressEdits": "Падаўляе рэдагаванні", - "notificationRuleSuppressEditsDescription": "Падаўляе апавяшчэнні пра рэдагаваныя паведамленні.", - "notificationRuleCall": "Званок", - "notificationRuleCallDescription": "Паведамляе карыстальніку пра званкі.", - "notificationRuleEncryptedRoomOneToOne": "Зашыфраваны пакой адзін-на-адзін", - "notificationRuleEncryptedRoomOneToOneDescription": "Паведамляе карыстальніку пра паведамленні ў зашыфраваных пакоях адзін-на-адзін.", - "notificationRuleRoomOneToOne": "Пакой адзін-на-адзін", - "notificationRuleRoomOneToOneDescription": "Паведамляе карыстальніку пра паведамленні ў пакоях адзін-на-адзін.", - "notificationRuleMessage": "Паведамленне", - "notificationRuleMessageDescription": "Паведамляе карыстальніку пра агульныя паведамленні.", - "notificationRuleEncrypted": "Зашыфраваны", - "notificationRuleEncryptedDescription": "Паведамляе карыстальніку пра паведамленні ў зашыфраваных пакоях.", - "notificationRuleJitsi": "Jitsi", - "notificationRuleJitsiDescription": "Паведамляе карыстальніку пра падзеі віджэта Jitsi.", - "notificationRuleServerAcl": "Падаўляе падзеі ACL сервера", - "notificationRuleServerAclDescription": "Падаўляе апавяшчэнні пра падзеі ACL сервера.", - "unknownPushRule": "Невядомае правіла пуша '{rule}'", - "sentVoiceMessage": "🎙️ {duration} - Гаварэчае паведамленне ад {sender}", - "deletePushRuleCanNotBeUndone": "Калі вы выдаліце гэтае наладжванне апавяшчэнняў, яго нельга будзе адменіць.", - "more": "Больш", - "shareKeysWith": "Падзяліцца ключамі з...", - "shareKeysWithDescription": "Якія прылады павінны быць даверанымі, каб яны маглі чытаць вашыя паведамленні ў зашыфраваных чатах?", + "@waitingForServer": {}, + "appIntroduction": "FluffyChat дазваляе вам і вашым сябрам размаўляць скрозь розныя мэсэнджары. Даведайцеся болей на https://matrix.org ці націсніце *Працягнуць*.", + "@appIntroduction": {}, + "newChatRequest": "📩 Запыт новага чату", + "@newChatRequest": {}, + "contentNotificationSettings": "Налады паведамленняў кантэнту", + "@contentNotificationSettings": {}, + "generalNotificationSettings": "Агульныя налады паведамленняў", + "@generalNotificationSettings": {}, + "roomNotificationSettings": "Налады паведамленняў пакою", + "@roomNotificationSettings": {}, + "userSpecificNotificationSettings": "Налады паведамленняў карыстальніка", + "@userSpecificNotificationSettings": {}, + "otherNotificationSettings": "Іншыя налады паведамленняў", + "@otherNotificationSettings": {}, + "notificationRuleContainsUserName": "Змяшчае імя карыстальніка", + "@notificationRuleContainsUserName": {}, + "notificationRuleContainsUserNameDescription": "Паведамляе пра тое, што паведамленне мае імя карыстальніка.", + "@notificationRuleContainsUserNameDescription": {}, + "notificationRuleMaster": "Заглушыць усе паведамленні", + "@notificationRuleMaster": {}, + "notificationRuleMasterDescription": "Перазапісвае ўсе іншыя правілы і адключае паведамленні.", + "@notificationRuleMasterDescription": {}, + "notificationRuleSuppressNotices": "Адключыць аўтаматычныя паведамленні", + "@notificationRuleSuppressNotices": {}, + "notificationRuleSuppressNoticesDescription": "Адключыць паведамленні ад аўтаматызаваных кліентаў, накшталт ботаў.", + "@notificationRuleSuppressNoticesDescription": {}, + "notificationRuleInviteForMe": "Запрашэнне мяне", + "@notificationRuleInviteForMe": {}, + "notificationRuleInviteForMeDescription": "Паведамляе карыстальніка, калі яго запрашаюць у пакой.", + "@notificationRuleInviteForMeDescription": {}, "allDevices": "Усе прылады", - "crossVerifiedDevicesIfEnabled": "Пераправераныя прылады, калі ўключана", - "crossVerifiedDevices": "Пераправераныя прылады", - "verifiedDevicesOnly": "Толькі правераныя прылады", - "takeAPhoto": "Зрабіць фота", + "@allDevices": {}, + "crossVerifiedDevicesIfEnabled": "З перакрыжаваным спраўджваннем прылад, калі ўключана", + "@crossVerifiedDevicesIfEnabled": {}, + "crossVerifiedDevices": "Перакрыжавана спраўджаныя прылады", + "@crossVerifiedDevices": {}, + "verifiedDevicesOnly": "Толькі спраўджаныя прылады", + "@verifiedDevicesOnly": {}, + "takeAPhoto": "Зрабіць здымак", + "@takeAPhoto": {}, "recordAVideo": "Запісаць відэа", - "optionalMessage": "(Дадакова) паведамленне...", - "notSupportedOnThisDevice": "Не падтрымліваецца на гэтым прыладзе", - "enterNewChat": "Пачаць новы чат", + "@recordAVideo": {}, + "optionalMessage": "(Апцыянальна) паведамленне...", + "@optionalMessage": {}, + "notSupportedOnThisDevice": "Не падтрымліваецца на гэтай прыладзе", + "@notSupportedOnThisDevice": {}, + "enterNewChat": "Увядзіце новы чат", + "@enterNewChat": {}, "approve": "Пацвердзіць", - "youHaveKnocked": "Вы пастукалі", - "pleaseWaitUntilInvited": "Калі ласка, пачакайце, пакуль нехта з пакоя не запросяць вас.", - "commandHint_logout": "Выйсці з вашага бягучага прылады", - "commandHint_logoutall": "Выйсці з усіх актыўных прылад", - "displayNavigationRail": "Паказаць навігацыйную паласу на мабільным", + "@approve": {}, + "youHaveKnocked": "Вы былі выгнаны", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Калі ласка, пачакайце, пакуль хтосьці з пакою вас не запрасіць.", + "@pleaseWaitUntilInvited": {}, + "commandHint_logout": "Выйсці з бягуяай прылады", + "@commandHint_logout": {}, + "commandHint_logoutall": "Выйсці на ўсіх актыўных прыладах", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Паказваць навігацыйны след на тэлефоне", + "@displayNavigationRail": {}, "customReaction": "Карыстальніцкая рэакцыя", + "@customReaction": {}, + "moreEvents": "Больш падзей", + "@moreEvents": {}, + "declineInvitation": "Скасаваць запрашэнне", + "@declineInvitation": {}, + "notificationsEnabledForThisAccount": "Паведамленні ўклбчаны для гжтага ўліковага запісу", + "@notificationsEnabledForThisAccount": { + "type": "String", + "placeholders": {} + }, + "numUsersTyping": "{count} карыстальнікаў пішуць…", + "@numUsersTyping": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "obtainingLocation": "Атрыманне месцазнаходжання…", + "@obtainingLocation": { + "type": "String", + "placeholders": {} + }, + "offensive": "Абражальна", + "@offensive": { + "type": "String", + "placeholders": {} + }, + "offline": "Не ў сетцы", + "@offline": { + "type": "String", + "placeholders": {} + }, + "ok": "Ок", + "@ok": { + "type": "String", + "placeholders": {} + }, + "online": "У сетцы", + "@online": { + "type": "String", + "placeholders": {} + }, + "onlineKeyBackupEnabled": "Рэзервовае капіраванне ключэй уключана", + "@onlineKeyBackupEnabled": { + "type": "String", + "placeholders": {} + }, + "oopsPushError": "Упс! На жаль, пры наладжванні пуш-паведамленняў, адбылася памылка.", + "@oopsPushError": { + "type": "String", + "placeholders": {} + }, + "oopsSomethingWentWrong": "Упс, штосьці пайшло не так…", + "@oopsSomethingWentWrong": { + "type": "String", + "placeholders": {} + }, + "openAppToReadMessages": "Адкройце праграму, каб прачытаць паведамленні", + "@openAppToReadMessages": { + "type": "String", + "placeholders": {} + }, + "openCamera": "Адкрыць камеру", + "@openCamera": { + "type": "String", + "placeholders": {} + }, + "openVideoCamera": "Адкрыць камеру для відэа", + "@openVideoCamera": { + "type": "String", + "placeholders": {} + }, + "oneClientLoggedOut": "Адзін з вашых кліентаў выйшаў", + "@oneClientLoggedOut": {}, + "addAccount": "Дадаць уліковы запіс", + "@addAccount": {}, + "editBundlesForAccount": "Змяніць пакеты для гэтага ўліковага запісу", + "@editBundlesForAccount": {}, + "addToBundle": "Дадаць у пакет", + "@addToBundle": {}, + "removeFromBundle": "Выдаліць з пакета", + "@removeFromBundle": {}, + "bundleName": "Назва пакета", + "@bundleName": {}, + "openInMaps": "Адкрыць на картах", + "@openInMaps": { + "type": "String", + "placeholders": {} + }, + "link": "Спасылка", + "@link": {}, + "serverRequiresEmail": "Гэты сервер павінен спраўдзіць ваш email для рэгістрацыі.", + "@serverRequiresEmail": {}, + "or": "Ці", + "@or": { + "type": "String", + "placeholders": {} + }, + "participant": "Удзельнік", + "@participant": { + "type": "String", + "placeholders": {} + }, + "passphraseOrKey": "фраза-пароль ці ключ аднаўлення", + "@passphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "password": "Пароль", + "@password": { + "type": "String", + "placeholders": {} + }, + "passwordForgotten": "Не памятаю пароль", + "@passwordForgotten": { + "type": "String", + "placeholders": {} + }, + "passwordHasBeenChanged": "Пароль быў зменены", + "@passwordHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "hideMemberChangesInPublicChats": "Хаваць змяненні ўдзельнікаў у публічных чатах", + "@hideMemberChangesInPublicChats": {}, + "hideMemberChangesInPublicChatsBody": "Не паказваць далучэнні і выхады з чату ў шкале часу для лепшай чытальнасці.", + "@hideMemberChangesInPublicChatsBody": {}, + "overview": "Агляд", + "@overview": {}, + "notifyMeFor": "Паведамляць мяне пра", + "@notifyMeFor": {}, + "passwordRecoverySettings": "Налады скіду пароля", + "@passwordRecoverySettings": {}, + "passwordRecovery": "Аднаўленне пароля", + "@passwordRecovery": { + "type": "String", + "placeholders": {} + }, + "people": "Людзі", + "@people": { + "type": "String", + "placeholders": {} + }, + "pickImage": "Абраць выяву", + "@pickImage": { + "type": "String", + "placeholders": {} + }, + "pin": "Замацаваць", + "@pin": { + "type": "String", + "placeholders": {} + }, + "play": "Прайграць {fileName}", + "@play": { + "type": "String", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "pleaseChoose": "Калі ласка, выберыце", + "@pleaseChoose": { + "type": "String", + "placeholders": {} + }, + "pleaseChooseAPasscode": "Калі ласка, выберыце код доступу", + "@pleaseChooseAPasscode": { + "type": "String", + "placeholders": {} + }, + "pleaseClickOnLink": "Калі ласка, націсніце на спасылку ў лісце на email, каб працягнуць.", + "@pleaseClickOnLink": { + "type": "String", + "placeholders": {} + }, + "pleaseEnter4Digits": "Калі ласка, увядзіце 4 лічбы ці пакіньце пустым, каб адключыць код-пароль.", + "@pleaseEnter4Digits": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterRecoveryKey": "Увядзіце ключ аднаўлення:", + "@pleaseEnterRecoveryKey": {}, + "pleaseEnterYourPassword": "Калі ласка, увядзіце ваш пароль", + "@pleaseEnterYourPassword": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterYourPin": "Калі ласка, увядзіце пін-код", + "@pleaseEnterYourPin": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterYourUsername": "Калі ласка, увядзіце імя карыстальніка", + "@pleaseEnterYourUsername": { + "type": "String", + "placeholders": {} + }, + "pleaseFollowInstructionsOnWeb": "Калі ласка, сачыце за інстуркцыямі ў браўзеры і націсніце \"Далей\".", + "@pleaseFollowInstructionsOnWeb": { + "type": "String", + "placeholders": {} + }, + "privacy": "Прыватнасць", + "@privacy": { + "type": "String", + "placeholders": {} + }, + "publicRooms": "Публічныя пакоі", + "@publicRooms": { + "type": "String", + "placeholders": {} + }, + "pushRules": "Правілы пушэй", + "@pushRules": { + "type": "String", + "placeholders": {} + }, + "reason": "Прычына", + "@reason": { + "type": "String", + "placeholders": {} + }, + "recording": "Запіс", + "@recording": { + "type": "String", + "placeholders": {} + }, + "redactedBy": "Адрэдагавана {username}", + "@redactedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "directChat": "Асобны чат", + "@directChat": {}, + "redactedByBecause": "Адрэдагавана {username}, прычына: \"{reason}\"", + "@redactedByBecause": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "reason": { + "type": "String" + } + } + }, + "redactedAnEvent": "{username} адрэдагаваў(-ла) падзею", + "@redactedAnEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "redactMessage": "Адрэдагаваць паведамленне", + "@redactMessage": { + "type": "String", + "placeholders": {} + }, + "register": "Зарэгістравацца", + "@register": { + "type": "String", + "placeholders": {} + }, + "reject": "Адмовіць", + "@reject": { + "type": "String", + "placeholders": {} + }, + "rejectedTheInvitation": "{username} адмовіў запрашэнне", + "@rejectedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "rejoin": "Далучыцца зноў", + "@rejoin": { + "type": "String", + "placeholders": {} + }, + "removeAllOtherDevices": "Выдаліць усе іншыя прылады", + "@removeAllOtherDevices": { + "type": "String", + "placeholders": {} + }, + "removedBy": "Выдалена {username}", + "@removedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "removeDevice": "Выдаліць прыладу", + "@removeDevice": { + "type": "String", + "placeholders": {} + }, + "unbanFromChat": "Разблакіраваць у чаце", + "@unbanFromChat": { + "type": "String", + "placeholders": {} + }, + "removeYourAvatar": "Выдаліць выяву ўліковага запісу", + "@removeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "replaceRoomWithNewerVersion": "Замяніць пакой навейшай версіяй", + "@replaceRoomWithNewerVersion": { + "type": "String", + "placeholders": {} + }, + "reply": "Адказаць", + "@reply": { + "type": "String", + "placeholders": {} + }, + "reportMessage": "Паведаміць пра паведамленне", + "@reportMessage": { + "type": "String", + "placeholders": {} + }, + "requestPermission": "Запрасіць дазвол", + "@requestPermission": { + "type": "String", + "placeholders": {} + }, + "roomHasBeenUpgraded": "Пакой быў абноўлены", + "@roomHasBeenUpgraded": { + "type": "String", + "placeholders": {} + }, + "roomVersion": "Версія пакою", + "@roomVersion": { + "type": "String", + "placeholders": {} + }, + "saveFile": "Захаваць файл", + "@saveFile": { + "type": "String", + "placeholders": {} + }, + "search": "Пошук", + "@search": { + "type": "String", + "placeholders": {} + }, + "security": "Бяспека", + "@security": { + "type": "String", + "placeholders": {} + }, + "recoveryKey": "Ключ аднаўлення", + "@recoveryKey": {}, + "recoveryKeyLost": "Ключ абнаўлення страчаны?", + "@recoveryKeyLost": {}, + "seenByUser": "Прагледжана {username}", + "@seenByUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "send": "Даслаць", + "@send": { + "type": "String", + "placeholders": {} + }, + "sendAMessage": "Даслаць паведамленне", + "@sendAMessage": { + "type": "String", + "placeholders": {} + }, + "sendAsText": "Даслаць як тэкст", + "@sendAsText": { + "type": "String" + }, + "sendAudio": "Даслаць аўдыё", + "@sendAudio": { + "type": "String", + "placeholders": {} + }, + "sendFile": "Даслаць файл", + "@sendFile": { + "type": "String", + "placeholders": {} + }, + "sendImage": "Даслаць выяву", + "@sendImage": { + "type": "String", + "placeholders": {} + }, + "sendImages": "Даслаць {count} выяў(-вы)", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "sendMessages": "Даслаць паведамленні", + "@sendMessages": { + "type": "String", + "placeholders": {} + }, + "sendOriginal": "Даслаць арыгінал", + "@sendOriginal": { + "type": "String", + "placeholders": {} + }, + "sendSticker": "Даслаць стыкер", + "@sendSticker": { + "type": "String", + "placeholders": {} + }, + "sendVideo": "Даслаць відэа", + "@sendVideo": { + "type": "String", + "placeholders": {} + }, + "sentAFile": "📁 {username} даслаў файл", + "@sentAFile": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAnAudio": "🎤 {username} даслаў аўдыё", + "@sentAnAudio": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAPicture": "🖼️ {username} даслаў выяву", + "@sentAPicture": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentASticker": "😊 {username} даслаў стыкер", + "@sentASticker": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAVideo": "🎥 {username} даслаў відэа", + "@sentAVideo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentCallInformations": "{senderName} даслаў інфрамацыю пра выклік", + "@sentCallInformations": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "separateChatTypes": "Падзяляць асобныя чаты і групы", + "@separateChatTypes": { + "type": "String", + "placeholders": {} + }, + "setAsCanonicalAlias": "Задаць як асноўны псеўданім", + "@setAsCanonicalAlias": { + "type": "String", + "placeholders": {} + }, + "setCustomEmotes": "Задаць карыстальніцкі эмодзі", + "@setCustomEmotes": { + "type": "String", + "placeholders": {} + }, + "toggleUnread": "Пазначыць прачытаным/непрачытаным", + "@toggleUnread": { + "type": "String", + "placeholders": {} + }, + "tooManyRequestsWarning": "Занадта шмат запытаў. Паспрабуйце пазней!", + "@tooManyRequestsWarning": { + "type": "String", + "placeholders": {} + }, + "transferFromAnotherDevice": "Перанесці з іншай прылады", + "@transferFromAnotherDevice": { + "type": "String", + "placeholders": {} + }, + "tryToSendAgain": "Паспрабуйце даслаць зноў", + "@tryToSendAgain": { + "type": "String", + "placeholders": {} + }, + "unavailable": "Недаступна", + "@unavailable": { + "type": "String", + "placeholders": {} + }, + "unbannedUser": "{username} разблакіраваў {targetName}", + "@unbannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "unblockDevice": "Разблакіраваць прыладу", + "@unblockDevice": { + "type": "String", + "placeholders": {} + }, + "unknownDevice": "Невядомая прылада", + "@unknownDevice": { + "type": "String", + "placeholders": {} + }, + "unknownEncryptionAlgorithm": "Невядомы алгарытм шыфравання", + "@unknownEncryptionAlgorithm": { + "type": "String", + "placeholders": {} + }, + "unknownEvent": "Невядомая падзея '{type}'", + "@unknownEvent": { + "type": "String", + "placeholders": { + "type": { + "type": "String" + } + } + }, + "unmuteChat": "Уключыць павдеамленні чата", + "@unmuteChat": { + "type": "String", + "placeholders": {} + }, + "unpin": "Адмацаваць", + "@unpin": { + "type": "String", + "placeholders": {} + }, + "unreadChats": "{unreadCount, plural, =1{1 непрачытаны чат} other{{unreadCount} непрачытаных чатаў}}", + "@unreadChats": { + "type": "String", + "placeholders": { + "unreadCount": { + "type": "int" + } + } + }, + "userAndOthersAreTyping": "{username} і {count} іншых удзельнікаў пішуць…", + "@userAndOthersAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "userAndUserAreTyping": "{username} і {username2} пішуць…", + "@userAndUserAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "username2": { + "type": "String" + } + } + }, + "userIsTyping": "{username} піша…", + "@userIsTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "userLeftTheChat": "🚪 {username} пакінуў(-ла) чат", + "@userLeftTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "username": "Імя карыстальніка", + "@username": { + "type": "String", + "placeholders": {} + }, + "userSentUnknownEvent": "{username} даслаў падзею {type}", + "@userSentUnknownEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "type": { + "type": "String" + } + } + }, + "unverified": "Не спраўджана", + "@unverified": {}, + "verified": "Спраўджана", + "@verified": { + "type": "String", + "placeholders": {} + }, + "verify": "Спраўдзіць", + "@verify": { + "type": "String", + "placeholders": {} + }, + "verifyStart": "Пачаць спраўджванне", + "@verifyStart": { + "type": "String", + "placeholders": {} + }, + "verifySuccess": "Вы паспяхова спраўджаны!", + "@verifySuccess": { + "type": "String", + "placeholders": {} + }, + "verifyTitle": "Спраўдзіць іншы ўліковы запіс", + "@verifyTitle": { + "type": "String", + "placeholders": {} + }, + "videoCall": "Відэа выклік", + "@videoCall": { + "type": "String", + "placeholders": {} + }, + "verifyOtherUserDescription": "Калі вы спраўдзілі іншага карыстальніка, вы можаце быць упэўненым з кім вы сапраўды перапісваецеся.💪\n\nКалі вы пачнеце спраўджванне, вы і іншы карыстальнік, убачыце ўсплывальнае акно ў праграме. У ім вы ўбачыце некалькі эмодзі ці лічб, якія вы павінны параўнаць адзін з адным.\n\nЛепшы метад зрабіць гэта - пачаць відэа выклік. 👭", + "@verifyOtherUserDescription": {}, + "pleaseEnterANumber": "Калі ласка, увядзіце лічбу большую за 0", + "@pleaseEnterANumber": {}, + "verifyOtherDeviceDescription": "Калі вы спраўдзіце другую прыладу, яны абмяняюцца ключамі, якія ўзмоцняць вашу бяспеку. 💪 Калі вы пачнеце спраўджванне, у праграмах прылад з'явіцца ўсплывальнае паведамленне. Потым, вы ўбачыце некалькі эмодзі ці лічбаў, якія вы павінны параўнаць паміж сабой. Прасцей за ўсё гэта зрабіць, маючы дзве прылады побач. 🤳", + "@verifyOtherDeviceDescription": {}, + "verifyOtherUser": "🔐 Спраўдзіць іншага карыстальніка", + "@verifyOtherUser": {}, + "verifyOtherDevice": "🔐 Спраўдзіць іншую прыладу", + "@verifyOtherDevice": {}, + "changeTheCanonicalRoomAlias": "Змяніць публічны адрас чату", + "@changeTheCanonicalRoomAlias": {}, + "wrongRecoveryKey": "Прабачце... гэта не выглядае як ключ аднаўлення.", + "@wrongRecoveryKey": {}, + "restoreSessionBody": "Праграма спрабуе аднавіць вашу сесію з рэзервовай копіі. Калі ласка, паведаміце пра памылку распрацоўшчыкам па спасылцы {url}. Паведамленне памылкі: {error}", + "@restoreSessionBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "homeserverDescription": "Вашыя даныя захоўваюцца на дамашнім серверы, як у правайдара электронай пошты. Вы можаце самастойна абраць дамашні сервер, захоўвая пры тым магчымасць размаўляць. Даведайцеся болей на https://matrix.org.", + "@homeserverDescription": {}, + "longPressToRecordVoiceMessage": "Доўга цісніце, каб запісаць галасавое паведамленне.", + "@longPressToRecordVoiceMessage": {}, + "visibilityOfTheChatHistory": "Бачнасць гісторыі чату", + "@visibilityOfTheChatHistory": { + "type": "String", + "placeholders": {} + }, + "visibleForAllParticipants": "Бачна для ўсіх удзельнікаў", + "@visibleForAllParticipants": { + "type": "String", + "placeholders": {} + }, + "pleaseTryAgainLaterOrChooseDifferentServer": "Калі ласка, паспрабуйце пазней ці абярыце іншы сервер.", + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "profileNotFound": "Карыстальнік не знойдзены на гэтым серверы. Гэта можа быць памылка сеткі ці карыстальніка не існуе.", + "@profileNotFound": {}, + "setTheme": "Тэма:", + "@setTheme": {}, + "setColorTheme": "Каляровая схема:", + "@setColorTheme": {}, + "invite": "Запрасіць", + "@invite": {}, + "inviteGroupChat": "📨 Запрашэнне ў групавы чат", + "@inviteGroupChat": {}, + "invitePrivateChat": "📨 Запрашэнне ў асобны чат", + "@invitePrivateChat": {}, + "invalidInput": "Недапушчальны ўвод!", + "@invalidInput": {}, + "wrongPinEntered": "Няверны пін-код! Паспрабуйце праз {seconds} секунд...", + "@wrongPinEntered": { + "type": "String", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "archiveRoomDescription": "Чат перамясціцца ў архіў. Іншыя карыстальнікі будуць бачыць гэта так, быццам вы выйшлі з чату.", + "@archiveRoomDescription": {}, + "roomUpgradeDescription": "Чат будзе пераствораны з новай версіяй пакою. Усе ўдзельнікі будуць паведамлены пра неабходнасць перайсці ў новы чат. Вы можаце даведацца пра версіі пакояў тут: https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "removeDevicesDescription": "Вы выйдзеце з гэтай прылады і больш не будзеце атрымліваць паведамленні.", + "@removeDevicesDescription": {}, + "sendTypingNotificationsDescription": "Іншыя ўдзельнікі чату могуць бачыць, калі вы пішаце новае паведамленне.", + "@sendTypingNotificationsDescription": {}, + "continueText": "Працягнуць", + "@continueText": {}, + "banUserDescription": "Карыстальнік будзе заблакіраваны з чату і больш не зможа ўвайсці, пакуль вы яго не разблакіруеце.", + "@banUserDescription": {}, + "unbanUserDescription": "Карыстальнік зможа зноў далучыцца да чату.", + "@unbanUserDescription": {}, + "kickUserDescription": "Карыстальнік будзе выгнаны, але не заблакіраваны. У публічных чатах, ён зможа далучыцца зноў у любы час.", + "@kickUserDescription": {}, + "makeAdminDescription": "Калі вы зробіце карыстальніка адміністратарам, вы не зможаце адмяніць гэта дзеянне, бо ён будзе мець такія ж правы, як і вы.", + "@makeAdminDescription": {}, + "pushNotificationsNotAvailable": "Пуш-паведамленні недаступны", + "@pushNotificationsNotAvailable": {}, + "learnMore": "Даведацца больш", + "@learnMore": {}, + "yourGlobalUserIdIs": "Ваш глабальны ID-карыстальніка: ", + "@yourGlobalUserIdIs": {}, + "noUsersFoundWithQuery": "На жаль, мы не змаглі знайсці карыстальніка з імём \"{query}\". Калі ласка, праверце наяўнасць памылак.", + "@noUsersFoundWithQuery": { + "type": "String", + "placeholders": { + "query": { + "type": "String" + } + } + }, + "knocking": "Грукацца", + "@knocking": {}, + "knockRestricted": "Грук абмежаваны", + "@knockRestricted": {}, + "spaceMemberOfCanKnock": "Удзельнікі прасторы з {spaces} могуць грукацца", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "chatCanBeDiscoveredViaSearchOnServer": "Чат можа быць знойдзены праз пошук у {server}", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "searchChatsRooms": "Пошук #чатаў, @карыстальнікаў...", + "@searchChatsRooms": {}, + "nothingFound": "Нічога не знойдзена...", + "@nothingFound": {}, + "groupName": "Назва групы", + "@groupName": {}, + "createGroupAndInviteUsers": "Стварыць групу і запрасіць карыстальнікаў", + "@createGroupAndInviteUsers": {}, + "groupCanBeFoundViaSearch": "Група можа быць знойдзена праз пошук", + "@groupCanBeFoundViaSearch": {}, + "startConversation": "Пачаць размову", + "@startConversation": {}, + "commandHint_sendraw": "Даслаць толькі json", + "@commandHint_sendraw": {}, + "databaseMigrationTitle": "База даных аптымізавана", + "@databaseMigrationTitle": {}, + "databaseMigrationBody": "Калі ласка, пачакайце. Гэта можа заняць некаторы час.", + "@databaseMigrationBody": {}, + "leaveEmptyToClearStatus": "Пакіньце пустым, каб ачысціць свой статус.", + "@leaveEmptyToClearStatus": {}, + "select": "Выбраць", + "@select": {}, + "searchForUsers": "Пошук @карыстальнікаў...", + "@searchForUsers": {}, + "pleaseEnterYourCurrentPassword": "Калі ласка, увядзіце свой бягучы пароль", + "@pleaseEnterYourCurrentPassword": {}, + "newPassword": "Новы пароль", + "@newPassword": {}, + "pleaseChooseAStrongPassword": "Калі ласка, падбярыце больш надзейны пароль", + "@pleaseChooseAStrongPassword": {}, + "passwordsDoNotMatch": "Паролі не супадаюць", + "@passwordsDoNotMatch": {}, + "passwordIsWrong": "Вы ўвялі няверны пароль", + "@passwordIsWrong": {}, + "publicLink": "Публічная спасылка", + "@publicLink": {}, + "publicChatAddresses": "Публічныя адрасы чату", + "@publicChatAddresses": {}, + "createNewAddress": "Стварыць новы адрас", + "@createNewAddress": {}, + "joinSpace": "Далучыцца да прасторы", + "@joinSpace": {}, + "publicSpaces": "Публічныя прасторы", + "@publicSpaces": {}, + "addChatOrSubSpace": "Дадаць чат ці пад-прастору", + "@addChatOrSubSpace": {}, + "subspace": "Пад-прастора", + "@subspace": {}, + "decline": "Адхіліць", + "@decline": {}, + "thisDevice": "Гэта прылада:", + "@thisDevice": {}, + "initAppError": "Адбылася памылка пры ініцыялізацыі праграмы", + "@initAppError": {}, + "userRole": "Роль карыстальніка", + "@userRole": {}, + "minimumPowerLevel": "{level} - мінімальны ўзровень дазволаў.", + "@minimumPowerLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "String" + } + } + }, + "searchIn": "Пошук у чаце \"{chat}\"...", + "@searchIn": { + "type": "String", + "placeholders": { + "chat": { + "type": "String" + } + } + }, + "searchMore": "Шукаць яшчэ...", + "@searchMore": {}, + "gallery": "Галерэя", + "@gallery": {}, + "files": "Файлы", + "@files": {}, + "databaseBuildErrorBody": "Немагчыма стварыць базу даных SQlite. Праграма спрабуе выкарыстоўваць састарэлую базу. Калі ласка, паведаміце распрацоўшчыкам пра гэта: {url}. Паведамленне памылкі: {error}", + "@databaseBuildErrorBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "sessionLostBody": "Ваш сеанс страчаны. Калі ласка, паведаміце пра гэта распрацоўшчыкам: {url}. Паведамленне памылкі: {error}", + "@sessionLostBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "forwardMessageTo": "Пераслаць паведамленне ў {roomName}?", + "@forwardMessageTo": { + "type": "String", + "placeholders": { + "roomName": { + "type": "String" + } + } + }, + "sendReadReceipts": "Дасылаць адзнаку аб чытанні", + "@sendReadReceipts": {}, + "sendReadReceiptsDescription": "Іншыя карыстальнікі чатаў будуць бачыць, калі вы прачыталі паведамленні.", + "@sendReadReceiptsDescription": {}, + "formattedMessages": "Фармаціраваныя паведамленні", + "@formattedMessages": {}, + "formattedMessagesDescription": "Адлюстроўваць пашыраныя паведамленні разметкай markdown.", + "@formattedMessagesDescription": {}, + "acceptedKeyVerification": "{sender} прыняў(-ла) спраўджванне ключэй", + "@acceptedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "canceledKeyVerification": "{sender} адхіліў(-ла) спраўджванне ключэй", + "@canceledKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "completedKeyVerification": "{sender} скочныў(-ла) спраўджванне ключэй", + "@completedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "isReadyForKeyVerification": "{sender} гатовы(-а) да спраўджвання ключэй", + "@isReadyForKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "requestedKeyVerification": "{sender} запрасіў(-ла) спраўджванне ключэй", + "@requestedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "startedKeyVerification": "{sender} пачаў(-ла) спраўджванне ключэй", + "@startedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "transparent": "Празрысты", + "@transparent": {}, + "incomingMessages": "Уваходныя паведамленні", + "@incomingMessages": {}, + "stickers": "Стыкеры", + "@stickers": {}, + "discover": "Даследаваць", + "@discover": {}, + "commandHint_ignore": "Ігнараваць дадзены matrix ID", + "@commandHint_ignore": {}, + "commandHint_unignore": "Перастаць ігнараваць дадзены matrix ID", + "@commandHint_unignore": {}, + "unreadChatsInApp": "{appname}: {unread} непрачытаных чатаў", + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "noDatabaseEncryption": "Шыфраванне базы даных не падтрымліваецца гэтай платформай", + "@noDatabaseEncryption": {}, + "thereAreCountUsersBlocked": "На гэты момант, {count} карыстальнікаў заблакіравана.", + "@thereAreCountUsersBlocked": { + "type": "String", + "count": {} + }, + "restricted": "Абмежавана", + "@restricted": {}, + "goToSpace": "Перайсці да прасторы: {space}", + "@goToSpace": { + "type": "String", + "space": {} + }, + "markAsUnread": "Адзначыць як непрачытанае", + "@markAsUnread": {}, + "userLevel": "{level} - Карыстальнік", + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "moderatorLevel": "{level} - Мадэратар", + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "adminLevel": "{level} - Адміністратар", + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "changeGeneralChatSettings": "Змяніць агульныя налады чату", + "@changeGeneralChatSettings": {}, + "inviteOtherUsers": "Запрасіць іншых карыстальнікаў у гэты чат", + "@inviteOtherUsers": {}, + "changeTheChatPermissions": "Змяніць дазволы чату", + "@changeTheChatPermissions": {}, + "changeTheVisibilityOfChatHistory": "Змяніць бачнасць гісторыі чату", + "@changeTheVisibilityOfChatHistory": {}, + "sendRoomNotifications": "Дасылаць паведамленні @room", + "@sendRoomNotifications": {}, + "changeTheDescriptionOfTheGroup": "Змяніць апісанне чату", + "@changeTheDescriptionOfTheGroup": {}, + "chatPermissionsDescription": "Задаць узровень дазволаў, які неабходны для некаторых дзеянняў у чаце. Узроўні 0, 50 і 100 звычайна адлюстроўваюць карыстальнікаў, мадэратараў і адміністратараў, але любая градацыя магчыма.", + "@chatPermissionsDescription": {}, + "updateInstalled": "🎉 Абнаўленне {version} усталявана!", + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "changelog": "Спіс змен", + "@changelog": {}, + "sendCanceled": "Адпраўка скасавана", + "@sendCanceled": {}, + "loginWithMatrixId": "Увайсці з Matrix-ID", + "@loginWithMatrixId": {}, + "discoverHomeservers": "Даследаваць дамашнія сервера", + "@discoverHomeservers": {}, + "whatIsAHomeserver": "Што такое дамашні сервер?", + "@whatIsAHomeserver": {}, + "doesNotSeemToBeAValidHomeserver": "Гэта не выглядае як дамашні сервер. Няслушны URL?", + "@doesNotSeemToBeAValidHomeserver": {}, + "calculatingFileSize": "Вылічэнне памеру файла...", + "@calculatingFileSize": {}, + "prepareSendingAttachment": "Падрыхтоўка адпраўкі прыкладання...", + "@prepareSendingAttachment": {}, + "sendingAttachment": "Адпраўка прыкладання...", + "@sendingAttachment": {}, + "generatingVideoThumbnail": "Стварэнне вокладкі відэа...", + "@generatingVideoThumbnail": {}, + "compressVideo": "Сцісканне відэа...", + "@compressVideo": {}, + "sendingAttachmentCountOfCount": "Адпраўляецца прыкладанне {index} з {length}...", + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } + } + }, + "serverLimitReached": "Дасягнуты серверны ліміт! Пачакайце {seconds} секунд...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "oneOfYourDevicesIsNotVerified": "Адна з вашых прылад не спраўджана", + "@oneOfYourDevicesIsNotVerified": {}, + "noticeChatBackupDeviceVerification": "Заўвага: Калі вы падключыце ўсе свае прылады да рэзервовага капіравання, яны аўтаматычна спраўдзяцца.", + "@noticeChatBackupDeviceVerification": {}, + "welcomeText": "Вітаначкі 👋 Гэта FluffyChat. Вы можаце ўвайсці на любы дамашні сервер, што сумяшчальны з https://matrix.org, а потым паразмаўляць з кім-небудзь. Гэта вялізная дэцэнтралізаваная сетка абмену паведамленнямі!", + "@welcomeText": {}, + "blur": "Размыццё:", + "@blur": {}, + "opacity": "Празрыстасць:", + "@opacity": {}, + "setWallpaper": "Задаць шпалеры", + "@setWallpaper": {}, + "notificationRuleMemberEvent": "Падзеі ўдзельніцтва", + "@notificationRuleMemberEvent": {}, + "notificationRuleMemberEventDescription": "Спыніць усе паведамленні пра ўдзельніцтва.", + "@notificationRuleMemberEventDescription": {}, + "notificationRuleIsUserMention": "Згадванні карыстальніка", + "@notificationRuleIsUserMention": {}, + "notificationRuleIsUserMentionDescription": "Паведамляе, калі карыстальніка згадалі ў паведамленні.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleContainsDisplayName": "Мае адлюстроўваемае імя", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleContainsDisplayNameDescription": "Паведамляе, калі паведамленне мае іх адлюстроўваемае імя.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsRoomMention": "Згадванні пакою", + "@notificationRuleIsRoomMention": {}, + "notificationRuleIsRoomMentionDescription": "Паведамляе карыстальніка, калі згадваюць пакой.", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomnotif": "Паведамленні пакою", + "@notificationRuleRoomnotif": {}, + "notificationRuleRoomnotifDescription": "Паведамляе пра згадванні '@room'.", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleTombstone": "Помнік", + "@notificationRuleTombstone": {}, + "notificationRuleTombstoneDescription": "Паведамляе пра дэактывацыю пакою.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleReaction": "Рэакцыя", + "@notificationRuleReaction": {}, + "notificationRuleReactionDescription": "Адключыць усе паведамленні пра рэакцыі.", + "@notificationRuleReactionDescription": {}, + "notificationRuleRoomServerAcl": "ACL сервера пакою", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomServerAclDescription": "Адключыць паведамленні пра серверныя спісы кантролю пакою (ACL).", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleSuppressEdits": "Заглушыць змены", + "@notificationRuleSuppressEdits": {}, + "notificationRuleSuppressEditsDescription": "Заглушыць паведамленні пра адрэдагаваныя паведамленні.", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleCall": "Выклік", + "@notificationRuleCall": {}, + "notificationRuleCallDescription": "Паведамляе пра выклікі.", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "Шыфраваны пакой One-to-One", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Паведамляе пра паведамленні ў шыфраваных one-to-one пакоях.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "Пакой One-to-One", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleRoomOneToOneDescription": "Паведамляе пра паведамленні ў пакоях one-to-one.", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleMessage": "Паведамленне", + "@notificationRuleMessage": {}, + "notificationRuleMessageDescription": "Паведамляе пра звычайныя паведамленні.", + "@notificationRuleMessageDescription": {}, + "notificationRuleEncrypted": "Зашыфравана", + "@notificationRuleEncrypted": {}, + "notificationRuleEncryptedDescription": "Паведамляе пра паведамленні ў зашыфраваных пакоях.", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsi": "Jitsi", + "@notificationRuleJitsi": {}, + "notificationRuleJitsiDescription": "Паведамляе пра падзеі віджэту Jitsi.", + "@notificationRuleJitsiDescription": {}, + "notificationRuleServerAcl": "Заглушыць серверныя падзеі ACL", + "@notificationRuleServerAcl": {}, + "notificationRuleServerAclDescription": "Заглушыць паведамленні пра серверныя падзеі ACL.", + "@notificationRuleServerAclDescription": {}, + "unknownPushRule": "Невядомае правіла пуша '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "sentVoiceMessage": "🎙️{duration} - Галасавое паведамленне ад {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "Калі вы выдаліце гэтыя налады паведамленняў, гэта не можа быць адменена.", + "@deletePushRuleCanNotBeUndone": {}, + "more": "Больш", + "@more": {}, + "shareKeysWith": "Падзяліцца ключамі з...", + "@shareKeysWith": {}, + "shareKeysWithDescription": "Якім прыладам вы давяраеце настолькі, каб яны маглі чытаць вашыя зашыфраваныя паведамленні?", + "@shareKeysWithDescription": {}, + "pause": "Паўза", + "@pause": {}, + "resume": "Працягнуць", + "@resume": {}, + "newSubSpace": "Новая пад-прастора", + "@newSubSpace": {}, + "moveToDifferentSpace": "Перамясціцца ў іншую прастору", + "@moveToDifferentSpace": {}, + "moveUp": "Перамясціць вышэй", + "@moveUp": {}, + "moveDown": "Перамясціць ніжэй", + "@moveDown": {}, + "removeFromSpaceDescription": "Гэты чат будзе выдалены з прасторы, але з'явіцца ў вашым спісе чатаў.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} чатаў", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Удзельнік прасторы {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Даць грошы", + "@donate": {}, + "startedAPoll": "{username} пачаў апытанне.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Апытанне", + "@poll": {}, + "startPoll": "Пачаць апытанне", + "@startPoll": {}, + "endPoll": "Скончыць апытанне", + "@endPoll": {}, + "answersVisible": "Адказы бачны", + "@answersVisible": {}, + "answersHidden": "Адказы схаваны", + "@answersHidden": {}, + "pollQuestion": "Пытанне апытання", + "@pollQuestion": {}, + "answerOption": "Варыянт адказу", + "@answerOption": {}, + "addAnswerOption": "Дадаць варыянт адказу", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Дазволіць некалькі адказаў", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Апытанне было скончана", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Адзін голас} other{{count} галасы(-оў)}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Вынікі будуць бачны, калі апытанне скончыцца", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Адказаць у гутарку", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Адзін адказ} other{{count} адказа(-ў)}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Гутарка", + "@thread": {}, + "backToMainChat": "Вярнуцца ў галоўны чат", + "@backToMainChat": {}, + "saveChanges": "Захаваць змены", + "@saveChanges": {}, + "createSticker": "Стварыць стыкер ці эмадзі", + "@createSticker": {}, + "useAsSticker": "Ужыць як стыкер", + "@useAsSticker": {}, + "useAsEmoji": "Ужыць як эмадзі", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Назва набору стыкераў ужо існуе", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Новы набор стыкераў", + "@newStickerPack": {}, + "stickerPackName": "Назва набору стыкераў", + "@stickerPackName": {}, + "attribution": "Атрыбуцыя", + "@attribution": {}, + "skipChatBackup": "Прапусціць рэзервовае капіраванне чатаў", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Вы ўпэўнены? Без наладжвання рэзервовага капіравання чатаў, вы можаце згубіць доступ да ўсіх вашых чатаў, калі вы зменіце прыладу.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Загрузка паведамленняў", + "@loadingMessages": {}, + "setupChatBackup": "Наладзіць рэзервовае капіраванне чатаў", + "@setupChatBackup": {}, "ignore": "Блакаваць", "ignoredUsers": "Блакаваные карыстальнікі", "writeAMessageLangCodes": "Пішыце на {l1} або {l2}...", @@ -821,7 +3533,6 @@ "updateLanguage": "Мовы, якія я ведаю", "whatLanguageYouWantToLearn": "Якую мову вы хочаце вывучыць?", "whatIsYourBaseLanguage": "Якая ваша асноўная мова?", - "saveChanges": "Захаваць змены", "publicProfileTitle": "Дазволіць знаходзіць мой профіль у пошуку", "publicProfileDesc": "Уключыўшы гэта, вы дазваляеце іншым карыстальнікам знаходзіць ваш профіль у глабальнай пошукавай радку і адпраўляць запыты на чат. На гэтым этапе вы можаце выбраць прыняць або адхіліць запыт.", "errorDisableIT": "Дапамога з перакладам выключана.", @@ -1908,3710 +4619,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-02-09 15:31:34.877959", - "@alwaysUse24HourFormat": { - "type": "String", - "placeholders": {} - }, - "@repeatPassword": { - "type": "String", - "placeholders": {} - }, - "@notAnImage": { - "type": "String", - "placeholders": {} - }, - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@remove": { - "type": "String", - "placeholders": {} - }, - "@importNow": { - "type": "String", - "placeholders": {} - }, - "@importEmojis": { - "type": "String", - "placeholders": {} - }, - "@importFromZipFile": { - "type": "String", - "placeholders": {} - }, - "@exportEmotePack": { - "type": "String", - "placeholders": {} - }, - "@replace": { - "type": "String", - "placeholders": {} - }, - "@about": { - "type": "String", - "placeholders": {} - }, - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@accept": { - "type": "String", - "placeholders": {} - }, - "@acceptedTheInvitation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@account": { - "type": "String", - "placeholders": {} - }, - "@activatedEndToEndEncryption": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@addEmail": { - "type": "String", - "placeholders": {} - }, - "@confirmMatrixId": { - "type": "String", - "placeholders": {} - }, - "@supposedMxid": { - "type": "String", - "placeholders": { - "mxid": { - "type": "String" - } - } - }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, - "@addToSpace": { - "type": "String", - "placeholders": {} - }, - "@admin": { - "type": "String", - "placeholders": {} - }, - "@alias": { - "type": "String", - "placeholders": {} - }, - "@all": { - "type": "String", - "placeholders": {} - }, - "@allChats": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@commandHint_googly": { - "type": "String", - "placeholders": {} - }, - "@commandHint_cuddle": { - "type": "String", - "placeholders": {} - }, - "@commandHint_hug": { - "type": "String", - "placeholders": {} - }, - "@googlyEyesContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@cuddleContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@hugContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@answeredTheCall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@anyoneCanJoin": { - "type": "String", - "placeholders": {} - }, - "@appLock": { - "type": "String", - "placeholders": {} - }, - "@appLockDescription": { - "type": "String", - "placeholders": {} - }, - "@archive": { - "type": "String", - "placeholders": {} - }, - "@areGuestsAllowedToJoin": { - "type": "String", - "placeholders": {} - }, - "@areYouSure": { - "type": "String", - "placeholders": {} - }, - "@areYouSureYouWantToLogout": { - "type": "String", - "placeholders": {} - }, - "@askSSSSSign": { - "type": "String", - "placeholders": {} - }, - "@askVerificationRequest": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@autoplayImages": { - "type": "String", - "placeholders": {} - }, - "@badServerLoginTypesException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "@sendTypingNotifications": { - "type": "String", - "placeholders": {} - }, - "@swipeRightToLeftToReply": { - "type": "String", - "placeholders": {} - }, - "@sendOnEnter": { - "type": "String", - "placeholders": {} - }, - "@badServerVersionsException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" - }, - "participants": { - "type": "int" - } - } - }, - "@noMoreChatsFound": { - "type": "String", - "placeholders": {} - }, - "@noChatsFoundHere": { - "type": "String", - "placeholders": {} - }, - "@joinedChats": { - "type": "String", - "placeholders": {} - }, - "@unread": { - "type": "String", - "placeholders": {} - }, - "@space": { - "type": "String", - "placeholders": {} - }, - "@spaces": { - "type": "String", - "placeholders": {} - }, - "@banFromChat": { - "type": "String", - "placeholders": {} - }, - "@banned": { - "type": "String", - "placeholders": {} - }, - "@bannedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@blockDevice": { - "type": "String", - "placeholders": {} - }, - "@blocked": { - "type": "String", - "placeholders": {} - }, - "@botMessages": { - "type": "String", - "placeholders": {} - }, - "@cancel": { - "type": "String", - "placeholders": {} - }, - "@cantOpenUri": { - "type": "String", - "placeholders": { - "uri": { - "type": "String" - } - } - }, - "@changeDeviceName": { - "type": "String", - "placeholders": {} - }, - "@changedTheChatAvatar": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheChatDescriptionTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "description": { - "type": "String" - } - } - }, - "@changedTheChatNameTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "chatname": { - "type": "String" - } - } - }, - "@changedTheChatPermissions": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheDisplaynameTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "displayname": { - "type": "String" - } - } - }, - "@changedTheGuestAccessRules": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheGuestAccessRulesTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "rules": { - "type": "String" - } - } - }, - "@changedTheHistoryVisibility": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheHistoryVisibilityTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "rules": { - "type": "String" - } - } - }, - "@changedTheJoinRules": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheJoinRulesTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "joinRules": { - "type": "String" - } - } - }, - "@changedTheProfileAvatar": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheRoomAliases": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheRoomInvitationLink": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changePassword": { - "type": "String", - "placeholders": {} - }, - "@changeTheHomeserver": { - "type": "String", - "placeholders": {} - }, - "@changeTheme": { - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "@changeYourAvatar": { - "type": "String", - "placeholders": {} - }, - "@channelCorruptedDecryptError": { - "type": "String", - "placeholders": {} - }, - "@chat": { - "type": "String", - "placeholders": {} - }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, - "@chatBackup": { - "type": "String", - "placeholders": {} - }, - "@chatBackupDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDetails": { - "type": "String", - "placeholders": {} - }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, - "@chats": { - "type": "String", - "placeholders": {} - }, - "@chooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@clearArchive": { - "type": "String", - "placeholders": {} - }, - "@close": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasdm": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasgroup": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ban": { - "type": "String", - "placeholders": {} - }, - "@commandHint_clearcache": { - "type": "String", - "placeholders": {} - }, - "@commandHint_create": { - "type": "String", - "placeholders": {} - }, - "@commandHint_discardsession": { - "type": "String", - "placeholders": {} - }, - "@commandHint_dm": { - "type": "String", - "placeholders": {} - }, - "@commandHint_html": { - "type": "String", - "placeholders": {} - }, - "@commandHint_invite": { - "type": "String", - "placeholders": {} - }, - "@commandHint_join": { - "type": "String", - "placeholders": {} - }, - "@commandHint_kick": { - "type": "String", - "placeholders": {} - }, - "@commandHint_leave": { - "type": "String", - "placeholders": {} - }, - "@commandHint_me": { - "type": "String", - "placeholders": {} - }, - "@commandHint_myroomavatar": { - "type": "String", - "placeholders": {} - }, - "@commandHint_myroomnick": { - "type": "String", - "placeholders": {} - }, - "@commandHint_op": { - "type": "String", - "placeholders": {} - }, - "@commandHint_plain": { - "type": "String", - "placeholders": {} - }, - "@commandHint_react": { - "type": "String", - "placeholders": {} - }, - "@commandHint_send": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unban": { - "type": "String", - "placeholders": {} - }, - "@commandInvalid": { - "type": "String", - "placeholders": {} - }, - "@commandMissing": { - "type": "String", - "placeholders": { - "command": { - "type": "String" - } - } - }, - "@compareEmojiMatch": { - "type": "String", - "placeholders": {} - }, - "@compareNumbersMatch": { - "type": "String", - "placeholders": {} - }, - "@configureChat": { - "type": "String", - "placeholders": {} - }, - "@confirm": { - "type": "String", - "placeholders": {} - }, - "@connect": { - "type": "String", - "placeholders": {} - }, - "@contactHasBeenInvitedToTheGroup": { - "type": "String", - "placeholders": {} - }, - "@containsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@containsUserName": { - "type": "String", - "placeholders": {} - }, - "@contentHasBeenReported": { - "type": "String", - "placeholders": {} - }, - "@copiedToClipboard": { - "type": "String", - "placeholders": {} - }, - "@copy": { - "type": "String", - "placeholders": {} - }, - "@copyToClipboard": { - "type": "String", - "placeholders": {} - }, - "@couldNotDecryptMessage": { - "type": "String", - "placeholders": { - "error": { - "type": "String" - } - } - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@create": { - "type": "String", - "placeholders": {} - }, - "@createdTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, - "@createNewSpace": { - "type": "String", - "placeholders": {} - }, - "@currentlyActive": { - "type": "String", - "placeholders": {} - }, - "@darkTheme": { - "type": "String", - "placeholders": {} - }, - "@dateAndTimeOfDay": { - "type": "String", - "placeholders": { - "date": { - "type": "String" - }, - "timeOfDay": { - "type": "String" - } - } - }, - "@dateWithoutYear": { - "type": "String", - "placeholders": { - "month": { - "type": "String" - }, - "day": { - "type": "String" - } - } - }, - "@dateWithYear": { - "type": "String", - "placeholders": { - "year": { - "type": "String" - }, - "month": { - "type": "String" - }, - "day": { - "type": "String" - } - } - }, - "@deactivateAccountWarning": { - "type": "String", - "placeholders": {} - }, - "@defaultPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@delete": { - "type": "String", - "placeholders": {} - }, - "@deleteAccount": { - "type": "String", - "placeholders": {} - }, - "@deleteMessage": { - "type": "String", - "placeholders": {} - }, - "@device": { - "type": "String", - "placeholders": {} - }, - "@deviceId": { - "type": "String", - "placeholders": {} - }, - "@devices": { - "type": "String", - "placeholders": {} - }, - "@directChats": { - "type": "String", - "placeholders": {} - }, - "@allRooms": { - "type": "String", - "placeholders": {} - }, - "@displaynameHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@downloadFile": { - "type": "String", - "placeholders": {} - }, - "@edit": { - "type": "String", - "placeholders": {} - }, - "@editBlockedServers": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, - "@editDisplayname": { - "type": "String", - "placeholders": {} - }, - "@editRoomAliases": { - "type": "String", - "placeholders": {} - }, - "@editRoomAvatar": { - "type": "String", - "placeholders": {} - }, - "@emoteExists": { - "type": "String", - "placeholders": {} - }, - "@emoteInvalid": { - "type": "String", - "placeholders": {} - }, - "@emoteKeyboardNoRecents": { - "type": "String", - "placeholders": {} - }, - "@emotePacks": { - "type": "String", - "placeholders": {} - }, - "@emoteSettings": { - "type": "String", - "placeholders": {} - }, - "@globalChatId": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibility": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibilityDescription": { - "type": "String", - "placeholders": {} - }, - "@calls": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickers": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickersBody": { - "type": "String", - "placeholders": {} - }, - "@emoteShortcode": { - "type": "String", - "placeholders": {} - }, - "@emoteWarnNeedToPick": { - "type": "String", - "placeholders": {} - }, - "@emptyChat": { - "type": "String", - "placeholders": {} - }, - "@enableEmotesGlobally": { - "type": "String", - "placeholders": {} - }, - "@enableEncryption": { - "type": "String", - "placeholders": {} - }, - "@enableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@encrypted": { - "type": "String", - "placeholders": {} - }, - "@encryption": { - "type": "String", - "placeholders": {} - }, - "@encryptionNotEnabled": { - "type": "String", - "placeholders": {} - }, - "@endedTheCall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@enterAnEmailAddress": { - "type": "String", - "placeholders": {} - }, - "@homeserver": { - "type": "String", - "placeholders": {} - }, - "@enterYourHomeserver": { - "type": "String", - "placeholders": {} - }, - "@errorObtainingLocation": { - "type": "String", - "placeholders": { - "error": { - "type": "String" - } - } - }, - "@everythingReady": { - "type": "String", - "placeholders": {} - }, - "@extremeOffensive": { - "type": "String", - "placeholders": {} - }, - "@fileName": { - "type": "String", - "placeholders": {} - }, - "@fluffychat": { - "type": "String", - "placeholders": {} - }, - "@fontSize": { - "type": "String", - "placeholders": {} - }, - "@forward": { - "type": "String", - "placeholders": {} - }, - "@fromJoining": { - "type": "String", - "placeholders": {} - }, - "@fromTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@goToTheNewRoom": { - "type": "String", - "placeholders": {} - }, - "@group": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@groupIsPublic": { - "type": "String", - "placeholders": {} - }, - "@groups": { - "type": "String", - "placeholders": {} - }, - "@groupWith": { - "type": "String", - "placeholders": { - "displayname": { - "type": "String" - } - } - }, - "@guestsAreForbidden": { - "type": "String", - "placeholders": {} - }, - "@guestsCanJoin": { - "type": "String", - "placeholders": {} - }, - "@hasWithdrawnTheInvitationFor": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@help": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedEvents": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessages": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessagesBody": { - "type": "String", - "placeholders": {} - }, - "@hideInvalidOrUnknownMessageFormats": { - "type": "String", - "placeholders": {} - }, - "@howOffensiveIsThisContent": { - "type": "String", - "placeholders": {} - }, - "@id": { - "type": "String", - "placeholders": {} - }, - "@identity": { - "type": "String", - "placeholders": {} - }, - "@block": { - "type": "String", - "placeholders": {} - }, - "@blockedUsers": { - "type": "String", - "placeholders": {} - }, - "@blockListDescription": { - "type": "String", - "placeholders": {} - }, - "@blockUsername": { - "type": "String", - "placeholders": {} - }, - "@iHaveClickedOnLink": { - "type": "String", - "placeholders": {} - }, - "@incorrectPassphraseOrKey": { - "type": "String", - "placeholders": {} - }, - "@inoffensive": { - "type": "String", - "placeholders": {} - }, - "@inviteContact": { - "type": "String", - "placeholders": {} - }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, - "@inviteContactToGroup": { - "type": "String", - "placeholders": { - "groupName": { - "type": "String" - } - } - }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@invited": { - "type": "String", - "placeholders": {} - }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, - "@invitedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@invitedUsersOnly": { - "type": "String", - "placeholders": {} - }, - "@inviteForMe": { - "type": "String", - "placeholders": {} - }, - "@inviteText": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "link": { - "type": "String" - } - } - }, - "@isTyping": { - "type": "String", - "placeholders": {} - }, - "@joinedTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@joinRoom": { - "type": "String", - "placeholders": {} - }, - "@kicked": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@kickedAndBanned": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@kickFromChat": { - "type": "String", - "placeholders": {} - }, - "@lastActiveAgo": { - "type": "String", - "placeholders": { - "localizedTimeShort": { - "type": "String" - } - } - }, - "@leave": { - "type": "String", - "placeholders": {} - }, - "@leftTheChat": { - "type": "String", - "placeholders": {} - }, - "@license": { - "type": "String", - "placeholders": {} - }, - "@lightTheme": { - "type": "String", - "placeholders": {} - }, - "@loadCountMoreParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@loadingPleaseWait": { - "type": "String", - "placeholders": {} - }, - "@loadMore": { - "type": "String", - "placeholders": {} - }, - "@locationDisabledNotice": { - "type": "String", - "placeholders": {} - }, - "@locationPermissionDeniedNotice": { - "type": "String", - "placeholders": {} - }, - "@login": { - "type": "String", - "placeholders": {} - }, - "@logInTo": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@logout": { - "type": "String", - "placeholders": {} - }, - "@memberChanges": { - "type": "String", - "placeholders": {} - }, - "@mention": { - "type": "String", - "placeholders": {} - }, - "@messages": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, - "@moderator": { - "type": "String", - "placeholders": {} - }, - "@muteChat": { - "type": "String", - "placeholders": {} - }, - "@needPantalaimonWarning": { - "type": "String", - "placeholders": {} - }, - "@newChat": { - "type": "String", - "placeholders": {} - }, - "@newMessageInFluffyChat": { - "type": "String", - "placeholders": {} - }, - "@newVerificationRequest": { - "type": "String", - "placeholders": {} - }, - "@next": { - "type": "String", - "placeholders": {} - }, - "@no": { - "type": "String", - "placeholders": {} - }, - "@noConnectionToTheServer": { - "type": "String", - "placeholders": {} - }, - "@noEmotesFound": { - "type": "String", - "placeholders": {} - }, - "@noEncryptionForPublicRooms": { - "type": "String", - "placeholders": {} - }, - "@noGoogleServicesWarning": { - "type": "String", - "placeholders": {} - }, - "@noMatrixServer": { - "type": "String", - "placeholders": { - "server1": { - "type": "String" - }, - "server2": { - "type": "String" - } - } - }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@scanQrCode": { - "type": "String", - "placeholders": {} - }, - "@none": { - "type": "String", - "placeholders": {} - }, - "@noPasswordRecoveryDescription": { - "type": "String", - "placeholders": {} - }, - "@noPermission": { - "type": "String", - "placeholders": {} - }, - "@noRoomsFound": { - "type": "String", - "placeholders": {} - }, - "@notifications": { - "type": "String", - "placeholders": {} - }, - "@notificationsEnabledForThisAccount": { - "type": "String", - "placeholders": {} - }, - "@numUsersTyping": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@obtainingLocation": { - "type": "String", - "placeholders": {} - }, - "@offensive": { - "type": "String", - "placeholders": {} - }, - "@offline": { - "type": "String", - "placeholders": {} - }, - "@ok": { - "type": "String", - "placeholders": {} - }, - "@online": { - "type": "String", - "placeholders": {} - }, - "@onlineKeyBackupEnabled": { - "type": "String", - "placeholders": {} - }, - "@oopsPushError": { - "type": "String", - "placeholders": {} - }, - "@oopsSomethingWentWrong": { - "type": "String", - "placeholders": {} - }, - "@openAppToReadMessages": { - "type": "String", - "placeholders": {} - }, - "@openCamera": { - "type": "String", - "placeholders": {} - }, - "@openVideoCamera": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, - "@addAccount": { - "type": "String", - "placeholders": {} - }, - "@editBundlesForAccount": { - "type": "String", - "placeholders": {} - }, - "@addToBundle": { - "type": "String", - "placeholders": {} - }, - "@removeFromBundle": { - "type": "String", - "placeholders": {} - }, - "@bundleName": { - "type": "String", - "placeholders": {} - }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, - "@openInMaps": { - "type": "String", - "placeholders": {} - }, - "@link": { - "type": "String", - "placeholders": {} - }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@or": { - "type": "String", - "placeholders": {} - }, - "@participant": { - "type": "String", - "placeholders": {} - }, - "@passphraseOrKey": { - "type": "String", - "placeholders": {} - }, - "@password": { - "type": "String", - "placeholders": {} - }, - "@passwordForgotten": { - "type": "String", - "placeholders": {} - }, - "@passwordHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChats": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChatsBody": { - "type": "String", - "placeholders": {} - }, - "@overview": { - "type": "String", - "placeholders": {} - }, - "@notifyMeFor": { - "type": "String", - "placeholders": {} - }, - "@passwordRecoverySettings": { - "type": "String", - "placeholders": {} - }, - "@passwordRecovery": { - "type": "String", - "placeholders": {} - }, - "@people": { - "type": "String", - "placeholders": {} - }, - "@pickImage": { - "type": "String", - "placeholders": {} - }, - "@pin": { - "type": "String", - "placeholders": {} - }, - "@play": { - "type": "String", - "placeholders": { - "fileName": { - "type": "String" - } - } - }, - "@pleaseChoose": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAPasscode": { - "type": "String", - "placeholders": {} - }, - "@pleaseClickOnLink": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnter4Digits": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourPin": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourUsername": { - "type": "String", - "placeholders": {} - }, - "@pleaseFollowInstructionsOnWeb": { - "type": "String", - "placeholders": {} - }, - "@privacy": { - "type": "String", - "placeholders": {} - }, - "@publicRooms": { - "type": "String", - "placeholders": {} - }, - "@pushRules": { - "type": "String", - "placeholders": {} - }, - "@reason": { - "type": "String", - "placeholders": {} - }, - "@recording": { - "type": "String", - "placeholders": {} - }, - "@redactedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, - "@redactedByBecause": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "reason": { - "type": "String" - } - } - }, - "@redactedAnEvent": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@redactMessage": { - "type": "String", - "placeholders": {} - }, - "@register": { - "type": "String", - "placeholders": {} - }, - "@reject": { - "type": "String", - "placeholders": {} - }, - "@rejectedTheInvitation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@rejoin": { - "type": "String", - "placeholders": {} - }, - "@removeAllOtherDevices": { - "type": "String", - "placeholders": {} - }, - "@removedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@removeDevice": { - "type": "String", - "placeholders": {} - }, - "@unbanFromChat": { - "type": "String", - "placeholders": {} - }, - "@removeYourAvatar": { - "type": "String", - "placeholders": {} - }, - "@replaceRoomWithNewerVersion": { - "type": "String", - "placeholders": {} - }, - "@reply": { - "type": "String", - "placeholders": {} - }, - "@reportMessage": { - "type": "String", - "placeholders": {} - }, - "@requestPermission": { - "type": "String", - "placeholders": {} - }, - "@roomHasBeenUpgraded": { - "type": "String", - "placeholders": {} - }, - "@roomVersion": { - "type": "String", - "placeholders": {} - }, - "@saveFile": { - "type": "String", - "placeholders": {} - }, - "@search": { - "type": "String", - "placeholders": {} - }, - "@security": { - "type": "String", - "placeholders": {} - }, - "@recoveryKey": { - "type": "String", - "placeholders": {} - }, - "@recoveryKeyLost": { - "type": "String", - "placeholders": {} - }, - "@seenByUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@send": { - "type": "String", - "placeholders": {} - }, - "@sendAMessage": { - "type": "String", - "placeholders": {} - }, - "@sendAsText": { - "type": "String", - "placeholders": {} - }, - "@sendAudio": { - "type": "String", - "placeholders": {} - }, - "@sendFile": { - "type": "String", - "placeholders": {} - }, - "@sendImage": { - "type": "String", - "placeholders": {} - }, - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@sendMessages": { - "type": "String", - "placeholders": {} - }, - "@sendOriginal": { - "type": "String", - "placeholders": {} - }, - "@sendSticker": { - "type": "String", - "placeholders": {} - }, - "@sendVideo": { - "type": "String", - "placeholders": {} - }, - "@sentAFile": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@sentAnAudio": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@sentAPicture": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@sentASticker": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@sentAVideo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@sentCallInformations": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@separateChatTypes": { - "type": "String", - "placeholders": {} - }, - "@setAsCanonicalAlias": { - "type": "String", - "placeholders": {} - }, - "@setCustomEmotes": { - "type": "String", - "placeholders": {} - }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, - "@setInvitationLink": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevel": { - "type": "String", - "placeholders": {} - }, - "@setStatus": { - "type": "String", - "placeholders": {} - }, - "@settings": { - "type": "String", - "placeholders": {} - }, - "@share": { - "type": "String", - "placeholders": {} - }, - "@sharedTheLocation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@shareLocation": { - "type": "String", - "placeholders": {} - }, - "@showPassword": { - "type": "String", - "placeholders": {} - }, - "@presenceStyle": { - "type": "String", - "placeholders": {} - }, - "@presencesToggle": { - "type": "String", - "placeholders": {} - }, - "@singlesignon": { - "type": "String", - "placeholders": {} - }, - "@skip": { - "type": "String", - "placeholders": {} - }, - "@sourceCode": { - "type": "String", - "placeholders": {} - }, - "@spaceIsPublic": { - "type": "String", - "placeholders": {} - }, - "@spaceName": { - "type": "String", - "placeholders": {} - }, - "@startedACall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, - "@status": { - "type": "String", - "placeholders": {} - }, - "@statusExampleMessage": { - "type": "String", - "placeholders": {} - }, - "@submit": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWait": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, - "@systemTheme": { - "type": "String", - "placeholders": {} - }, - "@theyDontMatch": { - "type": "String", - "placeholders": {} - }, - "@theyMatch": { - "type": "String", - "placeholders": {} - }, - "@title": { - "type": "String", - "placeholders": {} - }, - "@toggleFavorite": { - "type": "String", - "placeholders": {} - }, - "@toggleMuted": { - "type": "String", - "placeholders": {} - }, - "@toggleUnread": { - "type": "String", - "placeholders": {} - }, - "@tooManyRequestsWarning": { - "type": "String", - "placeholders": {} - }, - "@transferFromAnotherDevice": { - "type": "String", - "placeholders": {} - }, - "@tryToSendAgain": { - "type": "String", - "placeholders": {} - }, - "@unavailable": { - "type": "String", - "placeholders": {} - }, - "@unbannedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@unblockDevice": { - "type": "String", - "placeholders": {} - }, - "@unknownDevice": { - "type": "String", - "placeholders": {} - }, - "@unknownEncryptionAlgorithm": { - "type": "String", - "placeholders": {} - }, - "@unknownEvent": { - "type": "String", - "placeholders": { - "type": { - "type": "String" - } - } - }, - "@unmuteChat": { - "type": "String", - "placeholders": {} - }, - "@unpin": { - "type": "String", - "placeholders": {} - }, - "@unreadChats": { - "type": "String", - "placeholders": { - "unreadCount": { - "type": "int" - } - } - }, - "@userAndOthersAreTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "count": { - "type": "int" - } - } - }, - "@userAndUserAreTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "username2": { - "type": "String" - } - } - }, - "@userIsTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@userLeftTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@username": { - "type": "String", - "placeholders": {} - }, - "@userSentUnknownEvent": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "type": { - "type": "String" - } - } - }, - "@unverified": { - "type": "String", - "placeholders": {} - }, - "@verified": { - "type": "String", - "placeholders": {} - }, - "@verify": { - "type": "String", - "placeholders": {} - }, - "@verifyStart": { - "type": "String", - "placeholders": {} - }, - "@verifySuccess": { - "type": "String", - "placeholders": {} - }, - "@verifyTitle": { - "type": "String", - "placeholders": {} - }, - "@videoCall": { - "type": "String", - "placeholders": {} - }, - "@visibilityOfTheChatHistory": { - "type": "String", - "placeholders": {} - }, - "@visibleForAllParticipants": { - "type": "String", - "placeholders": {} - }, - "@visibleForEveryone": { - "type": "String", - "placeholders": {} - }, - "@voiceMessage": { - "type": "String", - "placeholders": {} - }, - "@waitingPartnerAcceptRequest": { - "type": "String", - "placeholders": {} - }, - "@waitingPartnerEmoji": { - "type": "String", - "placeholders": {} - }, - "@waitingPartnerNumbers": { - "type": "String", - "placeholders": {} - }, - "@wallpaper": { - "type": "String", - "placeholders": {} - }, - "@warning": { - "type": "String", - "placeholders": {} - }, - "@weSentYouAnEmail": { - "type": "String", - "placeholders": {} - }, - "@whoCanPerformWhichAction": { - "type": "String", - "placeholders": {} - }, - "@whoIsAllowedToJoinThisGroup": { - "type": "String", - "placeholders": {} - }, - "@whyDoYouWantToReportThis": { - "type": "String", - "placeholders": {} - }, - "@wipeChatBackup": { - "type": "String", - "placeholders": {} - }, - "@withTheseAddressesRecoveryDescription": { - "type": "String", - "placeholders": {} - }, - "@writeAMessage": { - "type": "String", - "placeholders": {} - }, - "@yes": { - "type": "String", - "placeholders": {} - }, - "@you": { - "type": "String", - "placeholders": {} - }, - "@youAreNoLongerParticipatingInThisChat": { - "type": "String", - "placeholders": {} - }, - "@youHaveBeenBannedFromThisChat": { - "type": "String", - "placeholders": {} - }, - "@yourPublicKey": { - "type": "String", - "placeholders": {} - }, - "@messageInfo": { - "type": "String", - "placeholders": {} - }, - "@time": { - "type": "String", - "placeholders": {} - }, - "@messageType": { - "type": "String", - "placeholders": {} - }, - "@sender": { - "type": "String", - "placeholders": {} - }, - "@openGallery": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@start": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, - "@publish": { - "type": "String", - "placeholders": {} - }, - "@videoWithSize": { - "type": "String", - "placeholders": { - "size": { - "type": "String" - } - } - }, - "@openChat": { - "type": "String", - "placeholders": {} - }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, - "@reportUser": { - "type": "String", - "placeholders": {} - }, - "@dismiss": { - "type": "String", - "placeholders": {} - }, - "@reactedWith": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - }, - "reaction": { - "type": "String" - } - } - }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, - "@confirmEventUnpin": { - "type": "String", - "placeholders": {} - }, - "@emojis": { - "type": "String", - "placeholders": {} - }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, - "@voiceCall": { - "type": "String", - "placeholders": {} - }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorTitle": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorLong": { - "type": "String", - "placeholders": {} - }, - "@switchToAccount": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "@nextAccount": { - "type": "String", - "placeholders": {} - }, - "@previousAccount": { - "type": "String", - "placeholders": {} - }, - "@addWidget": { - "type": "String", - "placeholders": {} - }, - "@widgetVideo": { - "type": "String", - "placeholders": {} - }, - "@widgetEtherpad": { - "type": "String", - "placeholders": {} - }, - "@widgetJitsi": { - "type": "String", - "placeholders": {} - }, - "@widgetCustom": { - "type": "String", - "placeholders": {} - }, - "@widgetName": { - "type": "String", - "placeholders": {} - }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, - "@errorAddingWidget": { - "type": "String", - "placeholders": {} - }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@youBannedUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youHaveWithdrawnTheInvitationFor": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youInvitedToBy": { - "type": "String", - "placeholders": { - "alias": { - "type": "String" - } - } - }, - "@youInvitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@invitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youInvitedUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youKicked": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youKickedAndBanned": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youUnbannedUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@hasKnocked": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@usersMustKnock": { - "type": "String", - "placeholders": {} - }, - "@noOneCanJoin": { - "type": "String", - "placeholders": {} - }, - "@userWouldLikeToChangeTheChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@noPublicLinkHasBeenCreatedYet": { - "type": "String", - "placeholders": {} - }, - "@knock": { - "type": "String", - "placeholders": {} - }, - "@users": { - "type": "String", - "placeholders": {} - }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, - "@saveKeyManuallyDescription": { - "type": "String", - "placeholders": {} - }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@countFiles": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@user": { - "type": "String", - "placeholders": {} - }, - "@custom": { - "type": "String", - "placeholders": {} - }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, - "@screenSharingTitle": { - "type": "String", - "placeholders": {} - }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, - "@callingPermissions": { - "type": "String", - "placeholders": {} - }, - "@callingAccount": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} - }, - "@newGroup": { - "type": "String", - "placeholders": {} - }, - "@newSpace": { - "type": "String", - "placeholders": {} - }, - "@enterSpace": { - "type": "String", - "placeholders": {} - }, - "@enterRoom": { - "type": "String", - "placeholders": {} - }, - "@allSpaces": { - "type": "String", - "placeholders": {} - }, - "@numChats": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@hidePresences": { - "type": "String", - "placeholders": {} - }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, - "@wasDirectChatDisplayName": { - "type": "String", - "placeholders": { - "oldDisplayName": { - "type": "String" - } - } - }, - "@newSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "@fileHasBeenSavedAt": { - "type": "String", - "placeholders": { - "path": { - "type": "String" - } - } - }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} - }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, - "@report": { - "type": "String", - "placeholders": {} - }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, - "@wrongPinEntered": { - "type": "String", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, - "@yourGlobalUserIdIs": { - "type": "String", - "placeholders": {} - }, - "@noUsersFoundWithQuery": { - "type": "String", - "placeholders": { - "query": { - "type": "String" - } - } - }, - "@knocking": { - "type": "String", - "placeholders": {} - }, - "@chatCanBeDiscoveredViaSearchOnServer": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@searchChatsRooms": { - "type": "String", - "placeholders": {} - }, - "@nothingFound": { - "type": "String", - "placeholders": {} - }, - "@groupName": { - "type": "String", - "placeholders": {} - }, - "@createGroupAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@groupCanBeFoundViaSearch": { - "type": "String", - "placeholders": {} - }, - "@wrongRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@startConversation": { - "type": "String", - "placeholders": {} - }, - "@commandHint_sendraw": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationTitle": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationBody": { - "type": "String", - "placeholders": {} - }, - "@leaveEmptyToClearStatus": { - "type": "String", - "placeholders": {} - }, - "@select": { - "type": "String", - "placeholders": {} - }, - "@searchForUsers": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourCurrentPassword": { - "type": "String", - "placeholders": {} - }, - "@newPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@passwordsDoNotMatch": { - "type": "String", - "placeholders": {} - }, - "@passwordIsWrong": { - "type": "String", - "placeholders": {} - }, - "@publicLink": { - "type": "String", - "placeholders": {} - }, - "@publicChatAddresses": { - "type": "String", - "placeholders": {} - }, - "@createNewAddress": { - "type": "String", - "placeholders": {} - }, - "@joinSpace": { - "type": "String", - "placeholders": {} - }, - "@publicSpaces": { - "type": "String", - "placeholders": {} - }, - "@addChatOrSubSpace": { - "type": "String", - "placeholders": {} - }, - "@subspace": { - "type": "String", - "placeholders": {} - }, - "@decline": { - "type": "String", - "placeholders": {} - }, - "@thisDevice": { - "type": "String", - "placeholders": {} - }, - "@initAppError": { - "type": "String", - "placeholders": {} - }, - "@userRole": { - "type": "String", - "placeholders": {} - }, - "@minimumPowerLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "String" - } - } - }, - "@searchIn": { - "type": "String", - "placeholders": { - "chat": { - "type": "String" - } - } - }, - "@searchMore": { - "type": "String", - "placeholders": {} - }, - "@gallery": { - "type": "String", - "placeholders": {} - }, - "@files": { - "type": "String", - "placeholders": {} - }, - "@databaseBuildErrorBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@sessionLostBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@restoreSessionBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@forwardMessageTo": { - "type": "String", - "placeholders": { - "roomName": { - "type": "String" - } - } - }, - "@sendReadReceipts": { - "type": "String", - "placeholders": {} - }, - "@sendTypingNotificationsDescription": { - "type": "String", - "placeholders": {} - }, - "@sendReadReceiptsDescription": { - "type": "String", - "placeholders": {} - }, - "@formattedMessages": { - "type": "String", - "placeholders": {} - }, - "@formattedMessagesDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUser": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUserDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDevice": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDeviceDescription": { - "type": "String", - "placeholders": {} - }, - "@acceptedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@canceledKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@completedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@isReadyForKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@requestedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@startedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@transparent": { - "type": "String", - "placeholders": {} - }, - "@incomingMessages": { - "type": "String", - "placeholders": {} - }, - "@stickers": { - "type": "String", - "placeholders": {} - }, - "@discover": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ignore": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unignore": { - "type": "String", - "placeholders": {} - }, - "@unreadChatsInApp": { - "type": "String", - "placeholders": { - "appname": { - "type": "String" - }, - "unread": { - "type": "String" - } - } - }, - "@noDatabaseEncryption": { - "type": "String", - "placeholders": {} - }, - "@thereAreCountUsersBlocked": { - "type": "String", - "placeholders": { - "count": {} - } - }, - "@restricted": { - "type": "String", - "placeholders": {} - }, - "@knockRestricted": { - "type": "String", - "placeholders": {} - }, - "@goToSpace": { - "type": "String", - "placeholders": { - "space": {} - } - }, - "@markAsUnread": { - "type": "String", - "placeholders": {} - }, - "@userLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@moderatorLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@adminLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@changeGeneralChatSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsers": { - "type": "String", - "placeholders": {} - }, - "@changeTheChatPermissions": { - "type": "String", - "placeholders": {} - }, - "@changeTheVisibilityOfChatHistory": { - "type": "String", - "placeholders": {} - }, - "@changeTheCanonicalRoomAlias": { - "type": "String", - "placeholders": {} - }, - "@sendRoomNotifications": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescriptionOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "@chatPermissionsDescription": { - "type": "String", - "placeholders": {} - }, - "@updateInstalled": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - } - } - }, - "@changelog": { - "type": "String", - "placeholders": {} - }, - "@sendCanceled": { - "type": "String", - "placeholders": {} - }, - "@loginWithMatrixId": { - "type": "String", - "placeholders": {} - }, - "@discoverHomeservers": { - "type": "String", - "placeholders": {} - }, - "@whatIsAHomeserver": { - "type": "String", - "placeholders": {} - }, - "@homeserverDescription": { - "type": "String", - "placeholders": {} - }, - "@doesNotSeemToBeAValidHomeserver": { - "type": "String", - "placeholders": {} - }, - "@calculatingFileSize": { - "type": "String", - "placeholders": {} - }, - "@prepareSendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@generatingVideoThumbnail": { - "type": "String", - "placeholders": {} - }, - "@compressVideo": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachmentCountOfCount": { - "type": "integer", - "placeholders": { - "index": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@oneOfYourDevicesIsNotVerified": { - "type": "String", - "placeholders": {} - }, - "@noticeChatBackupDeviceVerification": { - "type": "String", - "placeholders": {} - }, - "@continueText": { - "type": "String", - "placeholders": {} - }, - "@welcomeText": { - "type": "String", - "placeholders": {} - }, - "@blur": { - "type": "String", - "placeholders": {} - }, - "@opacity": { - "type": "String", - "placeholders": {} - }, - "@setWallpaper": { - "type": "String", - "placeholders": {} - }, - "@manageAccount": { - "type": "String", - "placeholders": {} - }, - "@noContactInformationProvided": { - "type": "String", - "placeholders": {} - }, - "@contactServerAdmin": { - "type": "String", - "placeholders": {} - }, - "@contactServerSecurity": { - "type": "String", - "placeholders": {} - }, - "@supportPage": { - "type": "String", - "placeholders": {} - }, - "@serverInformation": { - "type": "String", - "placeholders": {} - }, - "@name": { - "type": "String", - "placeholders": {} - }, - "@version": { - "type": "String", - "placeholders": {} - }, - "@website": { - "type": "String", - "placeholders": {} - }, - "@compress": { - "type": "String", - "placeholders": {} - }, - "@boldText": { - "type": "String", - "placeholders": {} - }, - "@italicText": { - "type": "String", - "placeholders": {} - }, - "@strikeThrough": { - "type": "String", - "placeholders": {} - }, - "@pleaseFillOut": { - "type": "String", - "placeholders": {} - }, - "@invalidUrl": { - "type": "String", - "placeholders": {} - }, - "@addLink": { - "type": "String", - "placeholders": {} - }, - "@unableToJoinChat": { - "type": "String", - "placeholders": {} - }, - "@previous": { - "type": "String", - "placeholders": {} - }, - "@otherPartyNotLoggedIn": { - "type": "String", - "placeholders": {} - }, - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@appWantsToUseForLoginDescription": { - "type": "String", - "placeholders": {} - }, - "@open": { - "type": "String", - "placeholders": {} - }, - "@waitingForServer": { - "type": "String", - "placeholders": {} - }, - "@appIntroduction": { - "type": "String", - "placeholders": {} - }, - "@newChatRequest": { - "type": "String", - "placeholders": {} - }, - "@contentNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@generalNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@roomNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@userSpecificNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@otherNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMaster": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMasterDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNotices": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNoticesDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMe": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEvent": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEventDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReaction": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReactionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEditsDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCall": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCallDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessage": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncrypted": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsi": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@deletePushRuleCanNotBeUndone": { - "type": "String", - "placeholders": {} - }, - "@more": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWith": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@allDevices": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@takeAPhoto": { - "type": "String", - "placeholders": {} - }, - "@recordAVideo": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@enterNewChat": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, - "@youHaveKnocked": { - "type": "String", - "placeholders": {} - }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, + "@@last_modified": "2026-02-05 10:09:46.469770", "@ignore": { "type": "String", "placeholders": {} @@ -5795,10 +4803,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11836,6 +10840,8 @@ "congratulations": "Сардэчна віншуем!", "anotherRound": "Яшчэ адзін раунд", "noActivityRequest": "Няма бягучага запыту на актыўнасць.", + "quit": "Выйсці", + "congratulationsYouveCompletedPractice": "Сардэчна віншуем! Вы завяршылі сесію практыкі.", "mustHave10Words": "Вы павінны мець не менш за 10 слоў для практыкавання. Паспрабуйце пагаварыць з сябрам або Pangea Bot, каб даведацца больш!", "botSettings": "Налады бота", "activitySettingsOverrideWarning": "Мова і ўзровень мовы вызначаюцца планам актыўнасці", @@ -11884,6 +10890,14 @@ "type": "String", "placeholders": {} }, + "@quit": { + "type": "String", + "placeholders": {} + }, + "@congratulationsYouveCompletedPractice": { + "type": "String", + "placeholders": {} + }, "@mustHave10Words": { "type": "String", "placeholders": {} @@ -12059,11 +11073,6 @@ "type": "String", "placeholders": {} }, - "aboutMeHint": "Пра мяне", - "@aboutMeHint": { - "type": "String", - "placeholders": {} - }, "grammarCopyPOSidiom": "Ідыём", "grammarCopyPOSphrasalv": "Фразавы дзеяслоў", "grammarCopyPOScompn": "Складаны", @@ -12078,90 +11087,5 @@ "@grammarCopyPOScompn": { "type": "String", "placeholders": {} - }, - "perfectPractice": "Ідэальная практыка!", - "greatPractice": "Выдатная практыка!", - "usedNoHints": "Малайчына, што не карыстаўся падказкамі!", - "youveCompletedPractice": "Вы завяршылі практыку, працягвайце, каб стаць лепшым!", - "@perfectPractice": { - "type": "String", - "placeholders": {} - }, - "@greatPractice": { - "type": "String", - "placeholders": {} - }, - "@usedNoHints": { - "type": "String", - "placeholders": {} - }, - "@youveCompletedPractice": { - "type": "String", - "placeholders": {} - }, - "changeEmail": "Змяніць электронную пошту", - "withTheseAddressesDescription": "З гэтымі электроннымі адрасамі вы можаце ўвайсці, аднавіць свой пароль і кіраваць падпіскамі.", - "noAddressDescription": "Вы яшчэ не дабавілі ніводнага электроннага адрасу.", - "@changeEmail": { - "type": "String", - "placeholders": {} - }, - "@withTheseAddressesDescription": { - "type": "String", - "placeholders": {} - }, - "@noAddressDescription": { - "type": "String", - "placeholders": {} - }, - "spanTypeGrammar": "Граматыка", - "spanTypeWordChoice": "Выбар слоў", - "spanTypeSpelling": "Арфаграфія", - "spanTypePunctuation": "Пунктуацыя", - "spanTypeStyle": "Стыль", - "spanTypeFluency": "Флюентнасць", - "spanTypeAccents": "Акцэнты", - "spanTypeCapitalization": "Капіталізацыя", - "spanTypeCorrection": "Карэкцыя", - "spanFeedbackTitle": "Паведаміць пра праблему з карэкцыяй", - "@spanTypeGrammar": { - "type": "String", - "placeholders": {} - }, - "@spanTypeWordChoice": { - "type": "String", - "placeholders": {} - }, - "@spanTypeSpelling": { - "type": "String", - "placeholders": {} - }, - "@spanTypePunctuation": { - "type": "String", - "placeholders": {} - }, - "@spanTypeStyle": { - "type": "String", - "placeholders": {} - }, - "@spanTypeFluency": { - "type": "String", - "placeholders": {} - }, - "@spanTypeAccents": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCapitalization": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCorrection": { - "type": "String", - "placeholders": {} - }, - "@spanFeedbackTitle": { - "type": "String", - "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index d941e4704..8a17c23ed 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -24,6 +24,173 @@ "type": "String", "placeholders": {} }, + "alwaysUse24HourFormat": "না", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "repeatPassword": "পাসওয়ার্ড আবার লিখো", + "@repeatPassword": {}, + "notAnImage": "ইমেজ ফাইল না।", + "@notAnImage": {}, + "setCustomPermissionLevel": "কাস্টম অনুমতি লেভেল ঠিক করো", + "@setCustomPermissionLevel": {}, + "admin": "অ্যাডমিস", + "@admin": { + "type": "String", + "placeholders": {} + }, + "alias": "অ্যালিয়াস", + "@alias": { + "type": "String", + "placeholders": {} + }, + "archive": "আর্কাইভ", + "@archive": { + "type": "String", + "placeholders": {} + }, + "banned": "ব্যানড", + "@banned": { + "type": "String", + "placeholders": {} + }, + "cancel": "বাতিল", + "@cancel": { + "type": "String", + "placeholders": {} + }, + "chat": "চ্যাট", + "@chat": { + "type": "String", + "placeholders": {} + }, + "close": "বন্ধ", + "@close": { + "type": "String", + "placeholders": {} + }, + "confirm": "নিশ্চিত করো", + "@confirm": { + "type": "String", + "placeholders": {} + }, + "connect": "কানেক্ট", + "@connect": { + "type": "String", + "placeholders": {} + }, + "copy": "অনুলিপি", + "@copy": { + "type": "String", + "placeholders": {} + }, + "create": "তৈরি", + "@create": { + "type": "String", + "placeholders": {} + }, + "dateWithoutYear": "{month}-{day}", + "@dateWithoutYear": { + "type": "String", + "placeholders": { + "month": { + "type": "String" + }, + "day": { + "type": "String" + } + } + }, + "delete": "অপসারণ", + "@delete": { + "type": "String", + "placeholders": {} + }, + "device": "ডিভাইস", + "@device": { + "type": "String", + "placeholders": {} + }, + "devices": "ডিভাইস", + "@devices": { + "type": "String", + "placeholders": {} + }, + "notifications": "বিজ্ঞপ্তি", + "@notifications": { + "type": "String", + "placeholders": {} + }, + "edit": "সম্পাদন", + "@edit": { + "type": "String", + "placeholders": {} + }, + "encryption": "এনক্রিপশন", + "@encryption": { + "type": "String", + "placeholders": {} + }, + "encrypted": "এনক্রিপ্টকৃত", + "@encrypted": { + "type": "String", + "placeholders": {} + }, + "fluffychat": "ফ্লাফিচ্যাট", + "@fluffychat": { + "type": "String", + "placeholders": {} + }, + "forward": "ফরওয়ার্ড", + "@forward": { + "type": "String", + "placeholders": {} + }, + "group": "গ্রুপ", + "@group": { + "type": "String", + "placeholders": {} + }, + "help": "সাহায্য", + "@help": { + "type": "String", + "placeholders": {} + }, + "id": "আইডি", + "@id": { + "type": "String", + "placeholders": {} + }, + "identity": "পরিচয়", + "@identity": { + "type": "String", + "placeholders": {} + }, + "invited": "আমন্ত্রিত", + "@invited": { + "type": "String", + "placeholders": {} + }, + "leave": "ছেড়ে যাও", + "@leave": { + "type": "String", + "placeholders": {} + }, + "logout": "প্রস্থান", + "@logout": { + "type": "String", + "placeholders": {} + }, + "license": "অনুমতিপত্র (লাইসেন্স)", + "@license": { + "type": "String", + "placeholders": {} + }, + "login": "প্রবেশ", + "@login": { + "type": "String", + "placeholders": {} + }, "@showPassword": { "type": "String", "placeholders": {} @@ -52,14 +219,7 @@ "type": "String", "placeholders": {} }, - "@connect": { - "type": "String", - "placeholders": {} - }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, + "@jumpToLastReadMessage": {}, "@allRooms": { "type": "String", "placeholders": {} @@ -68,22 +228,13 @@ "type": "String", "placeholders": {} }, - "@commandHint_cuddle": { - "type": "String", - "placeholders": {} - }, + "@commandHint_cuddle": {}, "@chats": { "type": "String", "placeholders": {} }, - "@widgetVideo": { - "type": "String", - "placeholders": {} - }, - "@dismiss": { - "type": "String", - "placeholders": {} - }, + "@widgetVideo": {}, + "@dismiss": {}, "@unknownDevice": { "type": "String", "placeholders": {} @@ -96,14 +247,7 @@ "type": "String", "placeholders": {} }, - "@admin": { - "type": "String", - "placeholders": {} - }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, + "@reportErrorDescription": {}, "@directChats": { "type": "String", "placeholders": {} @@ -120,22 +264,12 @@ } } }, - "@addAccount": { - "type": "String", - "placeholders": {} - }, - "@close": { - "type": "String", - "placeholders": {} - }, + "@addAccount": {}, "@configureChat": { "type": "String", "placeholders": {} }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, + "@chatHasBeenAddedToThisSpace": {}, "@reply": { "type": "String", "placeholders": {} @@ -148,14 +282,7 @@ "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, - "@device": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersion": {}, "@blockDevice": { "type": "String", "placeholders": {} @@ -164,30 +291,14 @@ "type": "String", "description": "Usage hint for the command /html" }, - "@widgetJitsi": { - "type": "String", - "placeholders": {} - }, + "@widgetJitsi": {}, "@youAreNoLongerParticipatingInThisChat": { "type": "String", "placeholders": {} }, - "@encryption": { - "type": "String", - "placeholders": {} - }, - "@messageType": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorLong": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, + "@messageType": {}, + "@indexedDbErrorLong": {}, + "@oneClientLoggedOut": {}, "@toggleMuted": { "type": "String", "placeholders": {} @@ -232,14 +343,8 @@ "type": "String", "placeholders": {} }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, - "@callingAccount": { - "type": "String", - "placeholders": {} - }, + "@startFirstChat": {}, + "@callingAccount": {}, "@requestPermission": { "type": "String", "placeholders": {} @@ -252,18 +357,8 @@ } } }, - "@invited": { - "type": "String", - "placeholders": {} - }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@nextAccount": { - "type": "String", - "placeholders": {} - }, + "@setColorTheme": {}, + "@nextAccount": {}, "@commandHint_create": { "type": "String", "description": "Usage hint for the command /create" @@ -280,10 +375,7 @@ "type": "String", "placeholders": {} }, - "@allSpaces": { - "type": "String", - "placeholders": {} - }, + "@allSpaces": {}, "@supposedMxid": { "type": "String", "placeholders": { @@ -296,10 +388,7 @@ "type": "String", "placeholders": {} }, - "@user": { - "type": "String", - "placeholders": {} - }, + "@user": {}, "@roomVersion": { "type": "String", "placeholders": {} @@ -316,10 +405,7 @@ "type": "String", "placeholders": {} }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, + "@youAcceptedTheInvitation": {}, "@banFromChat": { "type": "String", "placeholders": {} @@ -351,8 +437,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@userIsTyping": { "type": "String", @@ -374,10 +459,7 @@ } } }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, + "@banUserDescription": {}, "@inviteContact": { "type": "String", "placeholders": {} @@ -386,10 +468,7 @@ "type": "String", "placeholders": {} }, - "@widgetEtherpad": { - "type": "String", - "placeholders": {} - }, + "@widgetEtherpad": {}, "@waitingPartnerAcceptRequest": { "type": "String", "placeholders": {} @@ -406,14 +485,7 @@ "type": "String", "placeholders": {} }, - "@id": { - "type": "String", - "placeholders": {} - }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, + "@removeDevicesDescription": {}, "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -437,10 +509,7 @@ "type": "String", "placeholders": {} }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, + "@tryAgain": {}, "@areGuestsAllowedToJoin": { "type": "String", "placeholders": {} @@ -454,28 +523,13 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@dateWithoutYear": { - "type": "String", - "placeholders": { - "month": { - "type": "String" - }, - "day": { - "type": "String" - } } }, "@removeDevice": { "type": "String", "placeholders": {} }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, + "@unbanUserDescription": {}, "@userAndUserAreTyping": { "type": "String", "placeholders": { @@ -495,10 +549,7 @@ "type": "String", "placeholders": {} }, - "@sendOnEnter": { - "type": "String", - "placeholders": {} - }, + "@sendOnEnter": {}, "@answeredTheCall": { "type": "String", "placeholders": { @@ -507,18 +558,9 @@ } } }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, + "@youRejectedTheInvitation": {}, + "@otherCallingPermissions": {}, + "@messagesStyle": {}, "@couldNotDecryptMessage": { "type": "String", "placeholders": { @@ -531,30 +573,12 @@ "type": "String", "placeholders": {} }, - "@link": { - "type": "String", - "placeholders": {} - }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, - "@newSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, + "@link": {}, + "@widgetUrlError": {}, + "@emailOrUsername": {}, + "@newSpaceDescription": {}, + "@chatDescription": {}, + "@callingAccountDetails": {}, "@next": { "type": "String", "placeholders": {} @@ -589,14 +613,8 @@ "type": "String", "placeholders": {} }, - "@enterSpace": { - "type": "String", - "placeholders": {} - }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, + "@enterSpace": {}, + "@encryptThisChat": {}, "@fileName": { "type": "String", "placeholders": {} @@ -605,10 +623,7 @@ "type": "String", "placeholders": {} }, - "@previousAccount": { - "type": "String", - "placeholders": {} - }, + "@previousAccount": {}, "@publicRooms": { "type": "String", "placeholders": {} @@ -629,18 +644,8 @@ "type": "String", "placeholders": {} }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@create": { - "type": "String", - "placeholders": {} - }, + "@reopenChat": {}, + "@pleaseEnterRecoveryKey": {}, "@toggleFavorite": { "type": "String", "placeholders": {} @@ -649,14 +654,7 @@ "type": "String", "placeholders": {} }, - "@alias": { - "type": "String", - "placeholders": {} - }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, + "@widgetNameError": {}, "@inoffensive": { "type": "String", "placeholders": {} @@ -665,10 +663,7 @@ "type": "String", "placeholders": {} }, - "@addToBundle": { - "type": "String", - "placeholders": {} - }, + "@addToBundle": {}, "@reportMessage": { "type": "String", "placeholders": {} @@ -677,10 +672,7 @@ "type": "String", "placeholders": {} }, - "@addWidget": { - "type": "String", - "placeholders": {} - }, + "@addWidget": {}, "@all": { "type": "String", "placeholders": {} @@ -698,13 +690,9 @@ "count": { "type": "int" } - }, - "type": "String" - }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} + } }, + "@noKeyForThisMessage": {}, "@enableEncryptionWarning": { "type": "String", "placeholders": {} @@ -728,10 +716,7 @@ "type": "String", "placeholders": {} }, - "@commandHint_markasgroup": { - "type": "String", - "placeholders": {} - }, + "@commandHint_markasgroup": {}, "@errorObtainingLocation": { "type": "String", "placeholders": { @@ -740,38 +725,20 @@ } } }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, + "@hydrateTor": {}, + "@pushNotificationsNotAvailable": {}, "@passwordRecovery": { "type": "String", "placeholders": {} }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, + "@storeInAppleKeyChain": {}, "@replaceRoomWithNewerVersion": { "type": "String", "placeholders": {} }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, + "@hydrate": {}, + "@invalidServerName": {}, + "@chatPermissions": {}, "@voiceMessage": { "type": "String", "placeholders": {} @@ -799,14 +766,8 @@ } } }, - "@sender": { - "type": "String", - "placeholders": {} - }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, + "@sender": {}, + "@storeInAndroidKeystore": {}, "@hideRedactedEvents": { "type": "String", "placeholders": {} @@ -815,10 +776,7 @@ "type": "String", "placeholders": {} }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, + "@signInWithPassword": {}, "@ignoredUsers": { "type": "String", "placeholders": {} @@ -854,14 +812,7 @@ "type": "String", "placeholders": {} }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, - "@edit": { - "type": "String", - "placeholders": {} - }, + "@makeAdminDescription": {}, "@loadMore": { "type": "String", "placeholders": {} @@ -898,30 +849,17 @@ "type": "String", "placeholders": {} }, - "@copy": { - "type": "String", - "placeholders": {} - }, - "@saveKeyManuallyDescription": { - "type": "String", - "placeholders": {} - }, + "@saveKeyManuallyDescription": {}, "@none": { "type": "String", "placeholders": {} }, - "@editBundlesForAccount": { - "type": "String", - "placeholders": {} - }, + "@editBundlesForAccount": {}, "@enableEncryption": { "type": "String", "placeholders": {} }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, + "@whyIsThisMessageEncrypted": {}, "@rejectedTheInvitation": { "type": "String", "placeholders": { @@ -930,10 +868,7 @@ } } }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, + "@setChatDescription": {}, "@userLeftTheChat": { "type": "String", "placeholders": { @@ -946,10 +881,7 @@ "type": "String", "placeholders": {} }, - "@importFromZipFile": { - "type": "String", - "placeholders": {} - }, + "@importFromZipFile": {}, "@toggleUnread": { "type": "String", "placeholders": {} @@ -958,18 +890,12 @@ "type": "String", "placeholders": {} }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, + "@dehydrateWarning": {}, "@sendOriginal": { "type": "String", "placeholders": {} }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, + "@noOtherDevicesFound": {}, "@whoIsAllowedToJoinThisGroup": { "type": "String", "placeholders": {} @@ -986,10 +912,7 @@ } } }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, + "@yourChatBackupHasBeenSetUp": {}, "@chatBackup": { "type": "String", "placeholders": {} @@ -1006,10 +929,7 @@ "type": "String", "placeholders": {} }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, + "@videoCallsBetaWarning": {}, "@unmuteChat": { "type": "String", "placeholders": {} @@ -1058,14 +978,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@username": { "type": "String", "placeholders": {} @@ -1078,22 +990,8 @@ } } }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "@homeserver": { - "type": "String", - "placeholders": {} - }, - "@help": { - "type": "String", - "placeholders": {} - }, + "@fileIsTooBigForServer": {}, + "@homeserver": {}, "@chatDetails": { "type": "String", "placeholders": {} @@ -1121,10 +1019,6 @@ "type": "String", "placeholders": {} }, - "@repeatPassword": { - "type": "String", - "placeholders": {} - }, "@setStatus": { "type": "String", "placeholders": {} @@ -1137,26 +1031,13 @@ } } }, - "@callingPermissions": { - "type": "String", - "placeholders": {} - }, - "@delete": { - "type": "String", - "placeholders": {} - }, + "@callingPermissions": {}, "@newMessageInFluffyChat": { "type": "String", "placeholders": {} }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@start": { - "type": "String", - "placeholders": {} - }, + "@readUpToHere": {}, + "@start": {}, "@downloadFile": { "type": "String", "placeholders": {} @@ -1169,14 +1050,7 @@ "type": "String", "placeholders": {} }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, - "@identity": { - "type": "String", - "placeholders": {} - }, + "@unlockOldMessages": {}, "@numChats": { "type": "number", "placeholders": { @@ -1216,10 +1090,7 @@ "type": "String", "placeholders": {} }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, + "@optionalRedactReason": {}, "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -1240,10 +1111,7 @@ "type": "String", "placeholders": {} }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, + "@dehydrate": {}, "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} @@ -1271,10 +1139,6 @@ "type": "String", "placeholders": {} }, - "@banned": { - "type": "String", - "placeholders": {} - }, "@sendAsText": { "type": "String" }, @@ -1282,14 +1146,8 @@ "type": "String", "placeholders": {} }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@exportEmotePack": { - "type": "String", - "placeholders": {} - }, + "@archiveRoomDescription": {}, + "@exportEmotePack": {}, "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -1348,10 +1206,6 @@ "type": "String", "placeholders": {} }, - "@notifications": { - "type": "String", - "placeholders": {} - }, "@commandHint_plain": { "type": "String", "description": "Usage hint for the command /plain" @@ -1360,18 +1214,12 @@ "type": "String", "placeholders": {} }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, + "@experimentalVideoCalls": {}, "@openCamera": { "type": "String", "placeholders": {} }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterRecoveryKeyDescription": {}, "@guestsAreForbidden": { "type": "String", "placeholders": {} @@ -1388,13 +1236,7 @@ "type": "String", "placeholders": {} }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, + "@inviteContactToGroupQuestion": {}, "@emoteExists": { "type": "String", "placeholders": {} @@ -1419,37 +1261,18 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@chat": { - "type": "String", - "placeholders": {} - }, - "@group": { - "type": "String", - "placeholders": {} - }, - "@leave": { - "type": "String", - "placeholders": {} + } }, "@skip": { "type": "String", "placeholders": {} }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, + "@appearOnTopDetails": {}, "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} }, - "@enterRoom": { - "type": "String", - "placeholders": {} - }, + "@enterRoom": {}, "@enableEmotesGlobally": { "type": "String", "placeholders": {} @@ -1478,10 +1301,7 @@ "type": "String", "placeholders": {} }, - "@reportUser": { - "type": "String", - "placeholders": {} - }, + "@reportUser": {}, "@commandHint_send": { "type": "String", "description": "Usage hint for the command /send" @@ -1501,10 +1321,7 @@ } } }, - "@confirmEventUnpin": { - "type": "String", - "placeholders": {} - }, + "@confirmEventUnpin": {}, "@badServerVersionsException": { "type": "String", "placeholders": { @@ -1521,8 +1338,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@kickedAndBanned": { "type": "String", @@ -1547,14 +1363,7 @@ } } }, - "@license": { - "type": "String", - "placeholders": {} - }, - "@addToSpace": { - "type": "String", - "placeholders": {} - }, + "@addToSpace": {}, "@commandMissing": { "type": "String", "placeholders": { @@ -1564,34 +1373,21 @@ }, "description": "State that {command} is not a valid /command." }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, + "@redactMessageDescription": {}, "@rejoin": { "type": "String", "placeholders": {} }, - "@recoveryKey": { - "type": "String", - "placeholders": {} - }, + "@recoveryKey": {}, "@redactMessage": { "type": "String", "placeholders": {} }, - "@forward": { - "type": "String", - "placeholders": {} - }, "@commandHint_discardsession": { "type": "String", "description": "Usage hint for the command /discardsession" }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, + "@invalidInput": {}, "@chooseAStrongPassword": { "type": "String", "placeholders": {} @@ -1600,10 +1396,7 @@ "type": "String", "placeholders": {} }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, + "@dehydrateTorLong": {}, "@yourPublicKey": { "type": "String", "placeholders": {} @@ -1639,10 +1432,7 @@ "type": "String", "placeholders": {} }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, + "@doNotShowAgain": {}, "@activatedEndToEndEncryption": { "type": "String", "placeholders": { @@ -1651,10 +1441,7 @@ } } }, - "@report": { - "type": "String", - "placeholders": {} - }, + "@report": {}, "@status": { "type": "String", "placeholders": {} @@ -1679,34 +1466,15 @@ "type": "String", "placeholders": {} }, - "@unverified": { - "type": "String", - "placeholders": {} - }, - "@fluffychat": { - "type": "String", - "placeholders": {} - }, + "@unverified": {}, "@howOffensiveIsThisContent": { "type": "String", "placeholders": {} }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@screenSharingTitle": { - "type": "String", - "placeholders": {} - }, - "@widgetCustom": { - "type": "String", - "placeholders": {} - }, + "@serverRequiresEmail": {}, + "@hideUnimportantStateEvents": {}, + "@screenSharingTitle": {}, + "@widgetCustom": {}, "@sentCallInformations": { "type": "String", "placeholders": { @@ -1715,10 +1483,7 @@ } } }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, + "@addToSpaceDescription": {}, "@googlyEyesContent": { "type": "String", "placeholders": { @@ -1732,8 +1497,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@theyDontMatch": { "type": "String", @@ -1747,10 +1511,7 @@ "type": "String", "placeholders": {} }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, + "@addChatDescription": {}, "@sentAnAudio": { "type": "String", "placeholders": { @@ -1763,10 +1524,6 @@ "type": "String", "placeholders": {} }, - "@encrypted": { - "type": "String", - "placeholders": {} - }, "@commandHint_leave": { "type": "String", "description": "Usage hint for the command /leave" @@ -1775,30 +1532,16 @@ "type": "String", "description": "Usage hint for the command /myroomavatar" }, - "@cancel": { - "type": "String", - "placeholders": {} - }, "@hasKnocked": { "placeholders": { "user": { "type": "String" } - }, - "type": "String" - }, - "@publish": { - "type": "String", - "placeholders": {} - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} - }, - "@clearArchive": { - "type": "String", - "placeholders": {} + } }, + "@publish": {}, + "@openLinkInBrowser": {}, + "@clearArchive": {}, "@appLock": { "type": "String", "placeholders": {} @@ -1823,18 +1566,9 @@ "type": "String", "placeholders": {} }, - "@messageInfo": { - "type": "String", - "placeholders": {} - }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, + "@messageInfo": {}, + "@disableEncryptionWarning": {}, + "@directChat": {}, "@encryptionNotEnabled": { "type": "String", "placeholders": {} @@ -1847,42 +1581,24 @@ } } }, - "@sendTypingNotifications": { - "type": "String", - "placeholders": {} - }, + "@sendTypingNotifications": {}, "@lightTheme": { "type": "String", "placeholders": {} }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, + "@inviteGroupChat": {}, + "@appearOnTop": {}, + "@invitePrivateChat": {}, "@verifyTitle": { "type": "String", "placeholders": {} }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, + "@foregroundServiceRunning": {}, "@enterAnEmailAddress": { "type": "String", "placeholders": {} }, - "@voiceCall": { - "type": "String", - "placeholders": {} - }, + "@voiceCall": {}, "@commandHint_kick": { "type": "String", "description": "Usage hint for the command /kick" @@ -1907,14 +1623,7 @@ "type": "String", "description": "Usage hint for the command /ban" }, - "@importEmojis": { - "type": "String", - "placeholders": {} - }, - "@confirm": { - "type": "String", - "placeholders": {} - }, + "@importEmojis": {}, "@wasDirectChatDisplayName": { "type": "String", "placeholders": { @@ -1923,18 +1632,12 @@ } } }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, + "@noChatDescriptionYet": {}, "@defaultPermissionLevel": { "type": "String", "placeholders": {} }, - "@removeFromBundle": { - "type": "String", - "placeholders": {} - }, + "@removeFromBundle": {}, "@numUsersTyping": { "type": "String", "placeholders": { @@ -1951,14 +1654,8 @@ "type": "String", "placeholders": {} }, - "@confirmMatrixId": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, + "@confirmMatrixId": {}, + "@learnMore": {}, "@iHaveClickedOnLink": { "type": "String", "placeholders": {} @@ -1967,42 +1664,17 @@ "type": "String", "placeholders": {} }, - "@notAnImage": { - "type": "String", - "placeholders": {} - }, - "@users": { - "type": "String", - "placeholders": {} - }, - "@openGallery": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, + "@users": {}, + "@openGallery": {}, + "@chatDescriptionHasBeenChanged": {}, "@search": { "type": "String", "placeholders": {} }, - "@newGroup": { - "type": "String", - "placeholders": {} - }, - "@bundleName": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, + "@newGroup": {}, + "@bundleName": {}, + "@dehydrateTor": {}, + "@removeFromSpace": {}, "@dateAndTimeOfDay": { "type": "String", "placeholders": { @@ -2026,10 +1698,7 @@ "type": "String", "placeholders": {} }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, + "@roomUpgradeDescription": {}, "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" @@ -2045,18 +1714,8 @@ } } }, - "@scanQrCode": { - "type": "String", - "placeholders": {} - }, - "@logout": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, + "@scanQrCode": {}, + "@pleaseEnterANumber": {}, "@contactHasBeenInvitedToTheGroup": { "type": "String", "placeholders": {} @@ -2066,8 +1725,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@areYouSureYouWantToLogout": { "type": "String", @@ -2081,14 +1739,8 @@ } } }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, + "@profileNotFound": {}, + "@jump": {}, "@groups": { "type": "String", "placeholders": {} @@ -2115,10 +1767,7 @@ } } }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, + "@sorryThatsNotPossible": {}, "@videoWithSize": { "type": "String", "placeholders": { @@ -2139,18 +1788,9 @@ } } }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasdm": { - "type": "String", - "placeholders": {} - }, - "@recoveryKeyLost": { - "type": "String", - "placeholders": {} - }, + "@shareInviteLink": {}, + "@commandHint_markasdm": {}, + "@recoveryKeyLost": {}, "@cuddleContent": { "type": "String", "placeholders": { @@ -2175,14 +1815,7 @@ "type": "String", "placeholders": {} }, - "@login": { - "type": "String", - "placeholders": {} - }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, + "@deviceKeys": {}, "@waitingPartnerNumbers": { "type": "String", "placeholders": {} @@ -2239,18 +1872,12 @@ "type": "String", "placeholders": {} }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, + "@setTheme": {}, "@changeTheHomeserver": { "type": "String", "placeholders": {} }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, + "@youJoinedTheChat": {}, "@wallpaper": { "type": "String", "placeholders": {} @@ -2287,18 +1914,12 @@ "type": "String", "placeholders": {} }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, + "@markAsRead": {}, "@sendAudio": { "type": "String", "placeholders": {} }, - "@widgetName": { - "type": "String", - "placeholders": {} - }, + "@widgetName": {}, "@sentASticker": { "type": "String", "placeholders": { @@ -2307,22 +1928,13 @@ } } }, - "@errorAddingWidget": { - "type": "String", - "placeholders": {} - }, + "@errorAddingWidget": {}, "@commandHint_dm": { "type": "String", "description": "Usage hint for the command /dm" }, - "@commandHint_hug": { - "type": "String", - "placeholders": {} - }, - "@replace": { - "type": "String", - "placeholders": {} - }, + "@commandHint_hug": {}, + "@replace": {}, "@reject": { "type": "String", "placeholders": {} @@ -2340,17 +1952,12 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@deactivateAccountWarning": { "type": "String", "placeholders": {} }, - "@archive": { - "type": "String", - "placeholders": {} - }, "@joinedTheChat": { "type": "String", "placeholders": { @@ -2367,18 +1974,11 @@ "type": "String", "placeholders": {} }, - "@newSpace": { - "type": "String", - "placeholders": {} - }, + "@newSpace": {}, "@changePassword": { "type": "String", "placeholders": {} }, - "@devices": { - "type": "String", - "placeholders": {} - }, "@unknownEvent": { "type": "String", "placeholders": { @@ -2387,10 +1987,7 @@ } } }, - "@emojis": { - "type": "String", - "placeholders": {} - }, + "@emojis": {}, "@pleaseEnterYourPin": { "type": "String", "placeholders": {} @@ -2403,18 +2000,9 @@ "type": "String", "placeholders": {} }, - "@commandHint_googly": { - "type": "String", - "placeholders": {} - }, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, + "@commandHint_googly": {}, + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "@createGroup": {}, "@privacy": { "type": "String", "placeholders": {} @@ -2427,14 +2015,8 @@ "type": "String", "placeholders": {} }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@time": { - "type": "String", - "placeholders": {} - }, + "@hydrateTorLong": {}, + "@time": {}, "@enterYourHomeserver": { "type": "String", "placeholders": {} @@ -2447,14 +2029,8 @@ "type": "String", "placeholders": {} }, - "@custom": { - "type": "String", - "placeholders": {} - }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, + "@custom": {}, + "@noBackupWarning": {}, "@fromJoining": { "type": "String", "placeholders": {} @@ -2467,18 +2043,9 @@ "type": "String", "placeholders": {} }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, - "@openChat": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, + "@storeInSecureStorageDescription": {}, + "@openChat": {}, + "@kickUserDescription": {}, "@sendAMessage": { "type": "String", "placeholders": {} @@ -2487,10 +2054,7 @@ "type": "String", "placeholders": {} }, - "@importNow": { - "type": "String", - "placeholders": {} - }, + "@importNow": {}, "@deleteAccount": { "type": "String", "placeholders": {} @@ -2499,22 +2063,13 @@ "type": "String", "placeholders": {} }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, + "@pinMessage": {}, "@muteChat": { "type": "String", "placeholders": {} }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, + "@invite": {}, + "@enableMultiAccounts": {}, "@anyoneCanJoin": { "type": "String", "placeholders": {} @@ -2523,10 +2078,7 @@ "type": "String", "placeholders": {} }, - "@indexedDbErrorTitle": { - "type": "String", - "placeholders": {} - }, + "@indexedDbErrorTitle": {}, "@endedTheCall": { "type": "String", "placeholders": { @@ -2535,14 +2087,8 @@ } } }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersionLong": {}, + "@storeSecurlyOnThisDevice": {}, "@ok": { "type": "String", "placeholders": {} @@ -2559,10 +2105,7 @@ "type": "String", "placeholders": {} }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, + "@screenSharingDetail": {}, "@changedTheDisplaynameTo": { "type": "String", "placeholders": { @@ -2586,18 +2129,11 @@ "type": "String", "placeholders": {} }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, + "@placeCall": {}, "@extremeOffensive": { "type": "String", "placeholders": {} }, - "alwaysUse24HourFormat": "মিথ্যা", - "repeatPassword": "পাসওয়ার্ড পুনরাবৃত্তি করুন", - "notAnImage": "ছবি ফাইল নয়।", - "setCustomPermissionLevel": "কাস্টম অনুমতি স্তর সেট করুন", "setPermissionsLevelDescription": "নীচে একটি নির্ধারিত ভূমিকা নির্বাচন করুন বা 0 থেকে 100 এর মধ্যে একটি কাস্টম অনুমতি স্তর প্রবেশ করুন।", "ignoreUser": "ব্যবহারকারী উপেক্ষা করুন", "normalUser": "সাধারণ ব্যবহারকারী", @@ -2614,8 +2150,6 @@ "supposedMxid": "এটি হওয়া উচিত {mxid}", "addChatDescription": "একটি চ্যাট বিবরণ যোগ করুন...", "addToSpace": "স্পেসে যোগ করুন", - "admin": "অ্যাডমিন", - "alias": "উপনাম", "all": "সকল", "allChats": "সকল চ্যাট", "commandHint_roomupgrade": "এই রুমটি নির্দিষ্ট রুম সংস্করণে আপগ্রেড করুন", @@ -2629,7 +2163,6 @@ "anyoneCanJoin": "কেউ যোগ দিতে পারে! তবে, অ্যাডমিন যে কেউকে বহিষ্কার ও নিষিদ্ধ করতে পারেন। যারা নিষিদ্ধ, তারা ফিরে আসতে পারবেন না!", "appLock": "অ্যাপ লক", "appLockDescription": "পিন কোড দিয়ে ব্যবহার না করলে অ্যাপটি লক করুন", - "archive": "আর্কাইভ", "areGuestsAllowedToJoin": "অতিথি ব্যবহারকারীরা যোগ দিতে পারবে?", "areYouSure": "আপনি কি নিশ্চিত?", "areYouSureYouWantToLogout": "আপনি কি নিশ্চিত যে আপনি লগ আউট করতে চান?", @@ -2649,12 +2182,10 @@ "space": "স্থান", "spaces": "স্থানসমূহ", "banFromChat": "চ্যাট থেকে নিষিদ্ধ করুন", - "banned": "নিষিদ্ধ", "bannedUser": "{username} {targetName} কে নিষিদ্ধ করেছেন", "blockDevice": "ডিভাইস ব্লক করুন", "blocked": "ব্লক করা হয়েছে", "botMessages": "বট বার্তা", - "cancel": "বাতিল করুন", "cantOpenUri": "URI {uri} খোলা যাচ্ছে না", "changeDeviceName": "ডিভাইসের নাম পরিবর্তন করুন", "changedTheChatAvatar": "{username} চ্যাটের অ্যাভাটার পরিবর্তন করেছেন", @@ -2677,7 +2208,6 @@ "changeTheNameOfTheGroup": "গ্রুপের নাম পরিবর্তন করুন", "changeYourAvatar": "আপনার অবতার পরিবর্তন করুন", "channelCorruptedDecryptError": "এনক্রিপশন ক্ষতিগ্রস্ত হয়েছে", - "chat": "চ্যাট", "yourChatBackupHasBeenSetUp": "আপনার চ্যাট ব্যাকআপ সেটআপ করা হয়েছে।", "chatBackup": "চ্যাট ব্যাকআপ", "chatBackupDescription": "আপনার পুরানো বার্তা গুলি একটি পুনরুদ্ধার কী দিয়ে সুরক্ষিত। দয়া করে নিশ্চিত করুন যে আপনি এটি হারাবেন না।", @@ -2686,7 +2216,6 @@ "chats": "চ্যাট", "chooseAStrongPassword": "একটি শক্তিশালী পাসওয়ার্ড নির্বাচন করুন", "clearArchive": "আর্কাইভ মুছে ফেলুন", - "close": "বন্ধ করুন", "commandHint_markasdm": "নির্দেশিত ম্যাট্রিক্স আইডির জন্য সরাসরি বার্তা রুম হিসেবে চিহ্নিত করুন", "commandHint_markasgroup": "গ্রুপ হিসেবে চিহ্নিত করুন", "commandHint_ban": "এই রুম থেকে নির্দিষ্ট ব্যবহারকারীকে নিষিদ্ধ করুন", @@ -2712,41 +2241,32 @@ "compareEmojiMatch": "অনুগ্রহ করে ইমোজিগুলি তুলনা করুন", "compareNumbersMatch": "অনুগ্রহ করে সংখ্যাগুলি তুলনা করুন", "configureChat": "চ্যাট কনফিগার করুন", - "confirm": "নিশ্চিত করুন", - "connect": "সংযোগ করুন", "contactHasBeenInvitedToTheGroup": "যোগাযোগকে গ্রুপে আমন্ত্রণ জানানো হয়েছে", "containsDisplayName": "প্রদর্শন নাম রয়েছে", "containsUserName": "ব্যবহারকারীর নাম রয়েছে", "contentHasBeenReported": "বিষয়বস্তু সার্ভার অ্যাডমিনদের কাছে রিপোর্ট করা হয়েছে", "copiedToClipboard": "ক্লিপবোর্ডে কপি করা হয়েছে", - "copy": "কপি", "copyToClipboard": "ক্লিপবোর্ডে কপি করুন", "couldNotDecryptMessage": "বার্তা ডিক্রিপ্ট করতে পারিনি: {error}", "checkList": "তালিকা পরীক্ষা করুন", "countParticipants": "{count} অংশগ্রহণকারী", "countInvited": "{count} আমন্ত্রিত", - "create": "তৈরি করুন", "createdTheChat": "💬 {username} চ্যাটটি তৈরি করেছেন", "createGroup": "গ্রুপ তৈরি করুন", "createNewSpace": "নতুন স্থান", "currentlyActive": "বর্তমানে সক্রিয়", "darkTheme": "অন্ধকার", "dateAndTimeOfDay": "{date}, {timeOfDay}", - "dateWithoutYear": "{month}-{day}", "dateWithYear": "{year}-{month}-{day}", "deactivateAccountWarning": "এটি আপনার ব্যবহারকারী অ্যাকাউন্ট নিষ্ক্রিয় করবে। এটি পূর্বাবস্থায় ফিরিয়ে আনা যাবে না! আপনি কি নিশ্চিত?", "defaultPermissionLevel": "নতুন ব্যবহারকারীদের জন্য ডিফল্ট অনুমতি স্তর", - "delete": "মুছে ফেলুন", "deleteAccount": "অ্যাকাউন্ট মুছে ফেলুন", "deleteMessage": "বার্তা মুছে ফেলুন", - "device": "ডিভাইস", "deviceId": "ডিভাইস আইডি", - "devices": "ডিভাইসসমূহ", "directChats": "সরাসরি চ্যাট", "allRooms": "সমস্ত গ্রুপ চ্যাট", "displaynameHasBeenChanged": "প্রদর্শন নাম পরিবর্তিত হয়েছে", "downloadFile": "ফাইল ডাউনলোড করুন", - "edit": "সম্পাদনা করুন", "editBlockedServers": "ব্লক করা সার্ভার সম্পাদনা করুন", "chatPermissions": "চ্যাট অনুমতিসমূহ", "editDisplayname": "প্রদর্শন নাম সম্পাদনা করুন", @@ -2769,8 +2289,6 @@ "enableEmotesGlobally": "গ্লোবালি ইমোট প্যাক সক্রিয় করুন", "enableEncryption": "এনক্রিপশন সক্রিয় করুন", "enableEncryptionWarning": "আপনি আর এনক্রিপশন বন্ধ করতে পারবেন না। আপনি কি নিশ্চিত?", - "encrypted": "এনক্রিপ্টেড", - "encryption": "এনক্রিপশন", "encryptionNotEnabled": "এনক্রিপশন সক্রিয় নয়", "endedTheCall": "{senderName} কল শেষ করেছেন", "enterAnEmailAddress": "একটি ইমেল ঠিকানা লিখুন", @@ -2780,13 +2298,10 @@ "everythingReady": "সব কিছু প্রস্তুত!", "extremeOffensive": "অত্যন্ত আপত্তিজনক", "fileName": "ফাইলের নাম", - "fluffychat": "ফ্লাফি চ্যাট", "fontSize": "ফন্টের আকার", - "forward": "ফরোয়ার্ড", "fromJoining": "যোগদান থেকে", "fromTheInvitation": "নিমন্ত্রণ থেকে", "goToTheNewRoom": "নতুন রুমে যান", - "group": "গ্রুপ", "chatDescription": "চ্যাট বিবরণ", "chatDescriptionHasBeenChanged": "চ্যাট বিবরণ পরিবর্তিত হয়েছে", "groupIsPublic": "গ্রুপটি পাবলিক", @@ -2795,14 +2310,11 @@ "guestsAreForbidden": "অতিথিদের নিষিদ্ধ করা হয়েছে", "guestsCanJoin": "অতিথিরা যোগ দিতে পারেন", "hasWithdrawnTheInvitationFor": "{username} আমন্ত্রণ প্রত্যাহার করেছেন {targetName} এর জন্য", - "help": "সাহায্য", "hideRedactedEvents": "সংকলিত ইভেন্টগুলো লুকান", "hideRedactedMessages": "সংকলিত বার্তা লুকান", "hideRedactedMessagesBody": "যদি কেউ একটি বার্তা সংকলন করে, তাহলে এই বার্তা আর চ্যাটে দেখা যাবে না।", "hideInvalidOrUnknownMessageFormats": "অবৈধ বা অজানা বার্তার ফর্ম্যাট লুকান", "howOffensiveIsThisContent": "এই বিষয়বস্তু কতটা আপত্তিজনক?", - "id": "আইডি", - "identity": "পরিচয়", "block": "ব্লক করুন", "blockedUsers": "ব্লক করা ব্যবহারকারীরা", "blockListDescription": "আপনি যারা আপনাকে বিরক্ত করছে তাদের ব্লক করতে পারেন। আপনি আপনার ব্যক্তিগত ব্লক তালিকায় থাকা ব্যবহারকারীদের থেকে কোনও বার্তা বা রুম আমন্ত্রণ পেতে পারবেন না।", @@ -2816,7 +2328,6 @@ "noChatDescriptionYet": "এখনো কোনও চ্যাট বিবরণ তৈরি হয়নি।", "tryAgain": "আবার চেষ্টা করুন", "invalidServerName": "অবৈধ সার্ভার নাম", - "invited": "আমন্ত্রণ জানানো হয়েছে", "redactMessageDescription": "এই বার্তা সমস্ত অংশগ্রহণকারীর জন্য সম্পাদিত হবে। এটি পূর্বাবস্থায় ফিরিয়ে আনা যাবে না।", "optionalRedactReason": "(ঐচ্ছিক) এই বার্তা সম্পাদনের কারণ...", "invitedUser": "📩 {username} {targetName} কে আমন্ত্রণ জানিয়েছেন", @@ -2830,9 +2341,7 @@ "kickedAndBanned": "🙅 {username} {targetName} কে বহিষ্কার ও নিষিদ্ধ করেছেন", "kickFromChat": "চ্যাট থেকে বহিষ্কার করুন", "lastActiveAgo": "শেষ সক্রিয়তা: {localizedTimeShort}", - "leave": "প্রস্থান করুন", "leftTheChat": "চ্যাট থেকে প্রস্থান করেছেন", - "license": "লাইসেন্স", "lightTheme": "আলো", "res": {}, "loadCountMoreParticipants": "আরও {count} অংশগ্রহণকারী লোড করুন", @@ -2847,9 +2356,7 @@ "loadMore": "আরও লোড করুন…", "locationDisabledNotice": "অবস্থান পরিষেবা নিষ্ক্রিয়। দয়া করে এগুলি সক্ষম করুন যাতে আপনি আপনার অবস্থান শেয়ার করতে পারেন।", "locationPermissionDeniedNotice": "অবস্থান অনুমতি অস্বীকৃত। দয়া করে অনুমতি দিন যাতে আপনি আপনার অবস্থান শেয়ার করতে পারেন।", - "login": "লগইন", "logInTo": "{homeserver} এ লগইন করুন", - "logout": "লগআউট", "memberChanges": "সদস্য পরিবর্তন", "mention": "উল্লেখ করুন", "messages": "বার্তা", @@ -2873,7 +2380,6 @@ "noPasswordRecoveryDescription": "আপনি এখনও আপনার পাসওয়ার্ড পুনরুদ্ধারের জন্য কোনও উপায় যোগ করেননি।", "noPermission": "অনুমতি নেই", "noRoomsFound": "কোন রুম পাওয়া যায়নি…", - "notifications": "নোটিফিকেশন", "notificationsEnabledForThisAccount": "এই অ্যাকাউন্টের জন্য নোটিফিকেশন সক্রিয়", "numUsersTyping": "{count} ব্যবহারকারী টাইপ করছে…", "obtainingLocation": "অবস্থান সংগ্রহ করা হচ্ছে…", @@ -3156,7 +2662,6 @@ "report": "রিপোর্ট করুন", "signInWithPassword": "পাসওয়ার্ড দিয়ে সাইন ইন করুন", "pleaseTryAgainLaterOrChooseDifferentServer": "অনুগ্রহ করে পরে আবার চেষ্টা করুন বা আলাদা সার্ভার নির্বাচন করুন।", - "signInWith": "{provider} এর সাথে সাইন ইন করুন", "profileNotFound": "সার্ভারে ব্যবহারকারী পাওয়া যায়নি। হয়তো সংযোগের সমস্যা বা ব্যবহারকারী অস্তিত্ব নেই।", "setTheme": "থিম সেট করুন:", "setColorTheme": "রঙের থিম সেট করুন:", @@ -4500,14 +4005,6 @@ "courseStartDesc": "প্যাঙ্গিয়া বট যে কোনও সময় প্রস্তুত!\n\n...কিন্তু বন্ধুদের সাথে শেখা আরও ভালো!", "@@locale": "bn", "inviteOtherUsersToRoom": "অন্য ব্যবহারকারীদের আমন্ত্রণ জানান", - "@alwaysUse24HourFormat": { - "type": "String", - "placeholders": {} - }, - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, "@setPermissionsLevelDescription": { "type": "String", "placeholders": {} @@ -5061,9 +4558,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12169,4 +11664,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index d44cbfa2b..a58aa28ce 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -31,10 +31,7 @@ "type": "String", "placeholders": {} }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, + "@jumpToLastReadMessage": {}, "@allRooms": { "type": "String", "placeholders": {} @@ -43,22 +40,13 @@ "type": "String", "placeholders": {} }, - "@commandHint_cuddle": { - "type": "String", - "placeholders": {} - }, + "@commandHint_cuddle": {}, "@chats": { "type": "String", "placeholders": {} }, - "@widgetVideo": { - "type": "String", - "placeholders": {} - }, - "@dismiss": { - "type": "String", - "placeholders": {} - }, + "@widgetVideo": {}, + "@dismiss": {}, "@unknownDevice": { "type": "String", "placeholders": {} @@ -75,10 +63,7 @@ "type": "String", "placeholders": {} }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, + "@reportErrorDescription": {}, "@directChats": { "type": "String", "placeholders": {} @@ -95,10 +80,7 @@ } } }, - "@addAccount": { - "type": "String", - "placeholders": {} - }, + "@addAccount": {}, "@close": { "type": "String", "placeholders": {} @@ -107,10 +89,7 @@ "type": "String", "placeholders": {} }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, + "@chatHasBeenAddedToThisSpace": {}, "@reply": { "type": "String", "placeholders": {} @@ -123,10 +102,7 @@ "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersion": {}, "@device": { "type": "String", "placeholders": {} @@ -139,10 +115,7 @@ "type": "String", "description": "Usage hint for the command /html" }, - "@widgetJitsi": { - "type": "String", - "placeholders": {} - }, + "@widgetJitsi": {}, "@youAreNoLongerParticipatingInThisChat": { "type": "String", "placeholders": {} @@ -151,26 +124,14 @@ "type": "String", "placeholders": {} }, - "@messageType": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorLong": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, + "@messageType": {}, + "@indexedDbErrorLong": {}, + "@oneClientLoggedOut": {}, "@toggleMuted": { "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersionLong": {}, "@kicked": { "type": "String", "placeholders": { @@ -211,14 +172,8 @@ "type": "String", "placeholders": {} }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, - "@callingAccount": { - "type": "String", - "placeholders": {} - }, + "@startFirstChat": {}, + "@callingAccount": {}, "@requestPermission": { "type": "String", "placeholders": {} @@ -246,14 +201,8 @@ } } }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@nextAccount": { - "type": "String", - "placeholders": {} - }, + "@setColorTheme": {}, + "@nextAccount": {}, "@commandHint_create": { "type": "String", "description": "Usage hint for the command /create" @@ -270,10 +219,7 @@ "type": "String", "placeholders": {} }, - "@allSpaces": { - "type": "String", - "placeholders": {} - }, + "@allSpaces": {}, "@supposedMxid": { "type": "String", "placeholders": { @@ -286,10 +232,7 @@ "type": "String", "placeholders": {} }, - "@user": { - "type": "String", - "placeholders": {} - }, + "@user": {}, "@roomVersion": { "type": "String", "placeholders": {} @@ -306,10 +249,7 @@ "type": "String", "placeholders": {} }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, + "@youAcceptedTheInvitation": {}, "@banFromChat": { "type": "String", "placeholders": {} @@ -341,8 +281,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@userIsTyping": { "type": "String", @@ -364,10 +303,7 @@ } } }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, + "@banUserDescription": {}, "@inviteContact": { "type": "String", "placeholders": {} @@ -376,10 +312,7 @@ "type": "String", "placeholders": {} }, - "@widgetEtherpad": { - "type": "String", - "placeholders": {} - }, + "@widgetEtherpad": {}, "@waitingPartnerAcceptRequest": { "type": "String", "placeholders": {} @@ -400,10 +333,7 @@ "type": "String", "placeholders": {} }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, + "@removeDevicesDescription": {}, "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -427,10 +357,7 @@ "type": "String", "placeholders": {} }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, + "@tryAgain": {}, "@areGuestsAllowedToJoin": { "type": "String", "placeholders": {} @@ -444,8 +371,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@dateWithoutYear": { "type": "String", @@ -462,10 +388,7 @@ "type": "String", "placeholders": {} }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, + "@unbanUserDescription": {}, "@userAndUserAreTyping": { "type": "String", "placeholders": { @@ -485,10 +408,7 @@ "type": "String", "placeholders": {} }, - "@sendOnEnter": { - "type": "String", - "placeholders": {} - }, + "@sendOnEnter": {}, "@pickImage": { "type": "String", "placeholders": {} @@ -501,18 +421,9 @@ } } }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, + "@youRejectedTheInvitation": {}, + "@otherCallingPermissions": {}, + "@messagesStyle": {}, "@couldNotDecryptMessage": { "type": "String", "placeholders": { @@ -525,30 +436,12 @@ "type": "String", "placeholders": {} }, - "@link": { - "type": "String", - "placeholders": {} - }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, - "@newSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, + "@link": {}, + "@widgetUrlError": {}, + "@emailOrUsername": {}, + "@newSpaceDescription": {}, + "@chatDescription": {}, + "@callingAccountDetails": {}, "@next": { "type": "String", "placeholders": {} @@ -583,14 +476,8 @@ "type": "String", "placeholders": {} }, - "@enterSpace": { - "type": "String", - "placeholders": {} - }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, + "@enterSpace": {}, + "@encryptThisChat": {}, "@fileName": { "type": "String", "placeholders": {} @@ -599,10 +486,7 @@ "type": "String", "placeholders": {} }, - "@previousAccount": { - "type": "String", - "placeholders": {} - }, + "@previousAccount": {}, "@publicRooms": { "type": "String", "placeholders": {} @@ -623,14 +507,8 @@ "type": "String", "placeholders": {} }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, + "@reopenChat": {}, + "@pleaseEnterRecoveryKey": {}, "@create": { "type": "String", "placeholders": {} @@ -647,10 +525,7 @@ "type": "String", "placeholders": {} }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, + "@widgetNameError": {}, "@inoffensive": { "type": "String", "placeholders": {} @@ -659,10 +534,7 @@ "type": "String", "placeholders": {} }, - "@addToBundle": { - "type": "String", - "placeholders": {} - }, + "@addToBundle": {}, "@reportMessage": { "type": "String", "placeholders": {} @@ -671,10 +543,7 @@ "type": "String", "placeholders": {} }, - "@addWidget": { - "type": "String", - "placeholders": {} - }, + "@addWidget": {}, "@all": { "type": "String", "placeholders": {} @@ -692,13 +561,9 @@ "count": { "type": "int" } - }, - "type": "String" - }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} + } }, + "@noKeyForThisMessage": {}, "@enableEncryptionWarning": { "type": "String", "placeholders": {} @@ -722,10 +587,7 @@ "type": "String", "placeholders": {} }, - "@commandHint_markasgroup": { - "type": "String", - "placeholders": {} - }, + "@commandHint_markasgroup": {}, "@errorObtainingLocation": { "type": "String", "placeholders": { @@ -734,38 +596,20 @@ } } }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, + "@hydrateTor": {}, + "@pushNotificationsNotAvailable": {}, "@passwordRecovery": { "type": "String", "placeholders": {} }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, + "@storeInAppleKeyChain": {}, "@replaceRoomWithNewerVersion": { "type": "String", "placeholders": {} }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, + "@hydrate": {}, + "@invalidServerName": {}, + "@chatPermissions": {}, "@voiceMessage": { "type": "String", "placeholders": {} @@ -793,14 +637,8 @@ } } }, - "@sender": { - "type": "String", - "placeholders": {} - }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, + "@sender": {}, + "@storeInAndroidKeystore": {}, "@hideRedactedEvents": { "type": "String", "placeholders": {} @@ -809,10 +647,7 @@ "type": "String", "placeholders": {} }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, + "@signInWithPassword": {}, "@ignoredUsers": { "type": "String", "placeholders": {} @@ -848,10 +683,7 @@ "type": "String", "placeholders": {} }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, + "@makeAdminDescription": {}, "@edit": { "type": "String", "placeholders": {} @@ -896,26 +728,17 @@ "type": "String", "placeholders": {} }, - "@saveKeyManuallyDescription": { - "type": "String", - "placeholders": {} - }, + "@saveKeyManuallyDescription": {}, "@none": { "type": "String", "placeholders": {} }, - "@editBundlesForAccount": { - "type": "String", - "placeholders": {} - }, + "@editBundlesForAccount": {}, "@enableEncryption": { "type": "String", "placeholders": {} }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, + "@whyIsThisMessageEncrypted": {}, "@unreadChats": { "type": "String", "placeholders": { @@ -932,10 +755,7 @@ } } }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, + "@setChatDescription": {}, "@userLeftTheChat": { "type": "String", "placeholders": { @@ -948,10 +768,7 @@ "type": "String", "placeholders": {} }, - "@importFromZipFile": { - "type": "String", - "placeholders": {} - }, + "@importFromZipFile": {}, "@toggleUnread": { "type": "String", "placeholders": {} @@ -960,18 +777,12 @@ "type": "String", "placeholders": {} }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, + "@dehydrateWarning": {}, "@sendOriginal": { "type": "String", "placeholders": {} }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, + "@noOtherDevicesFound": {}, "@whoIsAllowedToJoinThisGroup": { "type": "String", "placeholders": {} @@ -988,14 +799,8 @@ } } }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, + "@storeSecurlyOnThisDevice": {}, + "@yourChatBackupHasBeenSetUp": {}, "@chatBackup": { "type": "String", "placeholders": {} @@ -1012,10 +817,7 @@ "type": "String", "placeholders": {} }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, + "@videoCallsBetaWarning": {}, "@unmuteChat": { "type": "String", "placeholders": {} @@ -1064,14 +866,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@username": { "type": "String", "placeholders": {} @@ -1084,18 +878,8 @@ } } }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "@homeserver": { - "type": "String", - "placeholders": {} - }, + "@fileIsTooBigForServer": {}, + "@homeserver": {}, "@help": { "type": "String", "placeholders": {} @@ -1127,10 +911,7 @@ "type": "String", "placeholders": {} }, - "@repeatPassword": { - "type": "String", - "placeholders": {} - }, + "@repeatPassword": {}, "@setStatus": { "type": "String", "placeholders": {} @@ -1143,10 +924,7 @@ } } }, - "@callingPermissions": { - "type": "String", - "placeholders": {} - }, + "@callingPermissions": {}, "@delete": { "type": "String", "placeholders": {} @@ -1155,14 +933,8 @@ "type": "String", "placeholders": {} }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@start": { - "type": "String", - "placeholders": {} - }, + "@readUpToHere": {}, + "@start": {}, "@downloadFile": { "type": "String", "placeholders": {} @@ -1175,10 +947,7 @@ "type": "String", "placeholders": {} }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, + "@unlockOldMessages": {}, "@identity": { "type": "String", "placeholders": {} @@ -1222,10 +991,7 @@ "type": "String", "placeholders": {} }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, + "@optionalRedactReason": {}, "@acceptedTheInvitation": { "type": "String", "placeholders": { @@ -1258,10 +1024,7 @@ "type": "String", "placeholders": {} }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, + "@dehydrate": {}, "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} @@ -1300,14 +1063,8 @@ "type": "String", "placeholders": {} }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@exportEmotePack": { - "type": "String", - "placeholders": {} - }, + "@archiveRoomDescription": {}, + "@exportEmotePack": {}, "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -1350,10 +1107,7 @@ "type": "String", "placeholders": {} }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, + "@placeCall": {}, "@removedBy": { "type": "String", "placeholders": { @@ -1386,18 +1140,12 @@ "type": "String", "placeholders": {} }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, + "@experimentalVideoCalls": {}, "@openCamera": { "type": "String", "placeholders": {} }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterRecoveryKeyDescription": {}, "@guestsAreForbidden": { "type": "String", "placeholders": {} @@ -1414,13 +1162,7 @@ "type": "String", "placeholders": {} }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, + "@inviteContactToGroupQuestion": {}, "@emoteExists": { "type": "String", "placeholders": {} @@ -1445,8 +1187,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@chat": { "type": "String", @@ -1464,18 +1205,12 @@ "type": "String", "placeholders": {} }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, + "@appearOnTopDetails": {}, "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} }, - "@enterRoom": { - "type": "String", - "placeholders": {} - }, + "@enterRoom": {}, "@enableEmotesGlobally": { "type": "String", "placeholders": {} @@ -1504,10 +1239,7 @@ "type": "String", "placeholders": {} }, - "@reportUser": { - "type": "String", - "placeholders": {} - }, + "@reportUser": {}, "@sharedTheLocation": { "type": "String", "placeholders": { @@ -1535,10 +1267,7 @@ } } }, - "@confirmEventUnpin": { - "type": "String", - "placeholders": {} - }, + "@confirmEventUnpin": {}, "@badServerVersionsException": { "type": "String", "placeholders": { @@ -1555,14 +1284,16 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@kickedAndBanned": { "type": "String", "placeholders": { "username": { "type": "String" + }, + "targetName": { + "type": "String" } } }, @@ -1582,10 +1313,7 @@ "type": "String", "placeholders": {} }, - "@addToSpace": { - "type": "String", - "placeholders": {} - }, + "@addToSpace": {}, "@unbanFromChat": { "type": "String", "placeholders": {} @@ -1599,18 +1327,12 @@ }, "description": "State that {command} is not a valid /command." }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, + "@redactMessageDescription": {}, "@rejoin": { "type": "String", "placeholders": {} }, - "@recoveryKey": { - "type": "String", - "placeholders": {} - }, + "@recoveryKey": {}, "@redactMessage": { "type": "String", "placeholders": {} @@ -1623,10 +1345,7 @@ "type": "String", "description": "Usage hint for the command /discardsession" }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, + "@invalidInput": {}, "@about": { "type": "String", "placeholders": {} @@ -1639,10 +1358,7 @@ "type": "String", "placeholders": {} }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, + "@dehydrateTorLong": {}, "@yourPublicKey": { "type": "String", "placeholders": {} @@ -1678,10 +1394,7 @@ "type": "String", "placeholders": {} }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, + "@doNotShowAgain": {}, "@activatedEndToEndEncryption": { "type": "String", "placeholders": { @@ -1690,10 +1403,7 @@ } } }, - "@report": { - "type": "String", - "placeholders": {} - }, + "@report": {}, "@status": { "type": "String", "placeholders": {} @@ -1718,10 +1428,7 @@ "type": "String", "placeholders": {} }, - "@unverified": { - "type": "String", - "placeholders": {} - }, + "@unverified": {}, "@fluffychat": { "type": "String", "placeholders": {} @@ -1730,22 +1437,10 @@ "type": "String", "placeholders": {} }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@screenSharingTitle": { - "type": "String", - "placeholders": {} - }, - "@widgetCustom": { - "type": "String", - "placeholders": {} - }, + "@serverRequiresEmail": {}, + "@hideUnimportantStateEvents": {}, + "@screenSharingTitle": {}, + "@widgetCustom": {}, "@sentCallInformations": { "type": "String", "placeholders": { @@ -1754,10 +1449,7 @@ } } }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, + "@addToSpaceDescription": {}, "@googlyEyesContent": { "type": "String", "placeholders": { @@ -1771,8 +1463,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@theyDontMatch": { "type": "String", @@ -1786,10 +1477,7 @@ "type": "String", "placeholders": {} }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, + "@addChatDescription": {}, "@sentAnAudio": { "type": "String", "placeholders": { @@ -1823,21 +1511,11 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@publish": { - "type": "String", - "placeholders": {} - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} - }, - "@clearArchive": { - "type": "String", - "placeholders": {} + } }, + "@publish": {}, + "@openLinkInBrowser": {}, + "@clearArchive": {}, "@appLock": { "type": "String", "placeholders": {} @@ -1862,18 +1540,9 @@ "type": "String", "placeholders": {} }, - "@messageInfo": { - "type": "String", - "placeholders": {} - }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, + "@messageInfo": {}, + "@disableEncryptionWarning": {}, + "@directChat": {}, "@encryptionNotEnabled": { "type": "String", "placeholders": {} @@ -1886,42 +1555,24 @@ } } }, - "@sendTypingNotifications": { - "type": "String", - "placeholders": {} - }, + "@sendTypingNotifications": {}, "@lightTheme": { "type": "String", "placeholders": {} }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, + "@inviteGroupChat": {}, + "@appearOnTop": {}, + "@invitePrivateChat": {}, "@verifyTitle": { "type": "String", "placeholders": {} }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, + "@foregroundServiceRunning": {}, "@enterAnEmailAddress": { "type": "String", "placeholders": {} }, - "@voiceCall": { - "type": "String", - "placeholders": {} - }, + "@voiceCall": {}, "@commandHint_kick": { "type": "String", "description": "Usage hint for the command /kick" @@ -1946,10 +1597,7 @@ "type": "String", "description": "Usage hint for the command /ban" }, - "@importEmojis": { - "type": "String", - "placeholders": {} - }, + "@importEmojis": {}, "@confirm": { "type": "String", "placeholders": {} @@ -1962,18 +1610,12 @@ } } }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, + "@noChatDescriptionYet": {}, "@defaultPermissionLevel": { "type": "String", "placeholders": {} }, - "@removeFromBundle": { - "type": "String", - "placeholders": {} - }, + "@removeFromBundle": {}, "@numUsersTyping": { "type": "String", "placeholders": { @@ -1990,14 +1632,8 @@ "type": "String", "placeholders": {} }, - "@confirmMatrixId": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, + "@confirmMatrixId": {}, + "@learnMore": {}, "@iHaveClickedOnLink": { "type": "String", "placeholders": {} @@ -2006,42 +1642,18 @@ "type": "String", "placeholders": {} }, - "@notAnImage": { - "type": "String", - "placeholders": {} - }, - "@users": { - "type": "String", - "placeholders": {} - }, - "@openGallery": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, + "@notAnImage": {}, + "@users": {}, + "@openGallery": {}, + "@chatDescriptionHasBeenChanged": {}, "@search": { "type": "String", "placeholders": {} }, - "@newGroup": { - "type": "String", - "placeholders": {} - }, - "@bundleName": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, + "@newGroup": {}, + "@bundleName": {}, + "@dehydrateTor": {}, + "@removeFromSpace": {}, "@dateAndTimeOfDay": { "type": "String", "placeholders": { @@ -2065,10 +1677,7 @@ "type": "String", "placeholders": {} }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, + "@roomUpgradeDescription": {}, "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" @@ -2084,18 +1693,12 @@ } } }, - "@scanQrCode": { - "type": "String", - "placeholders": {} - }, + "@scanQrCode": {}, "@logout": { "type": "String", "placeholders": {} }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterANumber": {}, "@contactHasBeenInvitedToTheGroup": { "type": "String", "placeholders": {} @@ -2105,8 +1708,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@areYouSureYouWantToLogout": { "type": "String", @@ -2120,14 +1722,8 @@ } } }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, + "@profileNotFound": {}, + "@jump": {}, "@groups": { "type": "String", "placeholders": {} @@ -2154,10 +1750,7 @@ } } }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, + "@sorryThatsNotPossible": {}, "@videoWithSize": { "type": "String", "placeholders": { @@ -2178,18 +1771,9 @@ } } }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasdm": { - "type": "String", - "placeholders": {} - }, - "@recoveryKeyLost": { - "type": "String", - "placeholders": {} - }, + "@shareInviteLink": {}, + "@commandHint_markasdm": {}, + "@recoveryKeyLost": {}, "@cuddleContent": { "type": "String", "placeholders": { @@ -2218,10 +1802,7 @@ "type": "String", "placeholders": {} }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, + "@deviceKeys": {}, "@waitingPartnerNumbers": { "type": "String", "placeholders": {} @@ -2278,18 +1859,12 @@ "type": "String", "placeholders": {} }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, + "@setTheme": {}, "@changeTheHomeserver": { "type": "String", "placeholders": {} }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, + "@youJoinedTheChat": {}, "@wallpaper": { "type": "String", "placeholders": {} @@ -2326,18 +1901,12 @@ "type": "String", "placeholders": {} }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, + "@markAsRead": {}, "@sendAudio": { "type": "String", "placeholders": {} }, - "@widgetName": { - "type": "String", - "placeholders": {} - }, + "@widgetName": {}, "@sentASticker": { "type": "String", "placeholders": { @@ -2346,22 +1915,13 @@ } } }, - "@errorAddingWidget": { - "type": "String", - "placeholders": {} - }, + "@errorAddingWidget": {}, "@commandHint_dm": { "type": "String", "description": "Usage hint for the command /dm" }, - "@commandHint_hug": { - "type": "String", - "placeholders": {} - }, - "@replace": { - "type": "String", - "placeholders": {} - }, + "@commandHint_hug": {}, + "@replace": {}, "@reject": { "type": "String", "placeholders": {} @@ -2383,8 +1943,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@deactivateAccountWarning": { "type": "String", @@ -2410,10 +1969,7 @@ "type": "String", "placeholders": {} }, - "@newSpace": { - "type": "String", - "placeholders": {} - }, + "@newSpace": {}, "@changePassword": { "type": "String", "placeholders": {} @@ -2434,10 +1990,7 @@ } } }, - "@emojis": { - "type": "String", - "placeholders": {} - }, + "@emojis": {}, "@pleaseEnterYourPin": { "type": "String", "placeholders": {} @@ -2450,18 +2003,9 @@ "type": "String", "placeholders": {} }, - "@commandHint_googly": { - "type": "String", - "placeholders": {} - }, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, + "@commandHint_googly": {}, + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "@createGroup": {}, "@privacy": { "type": "String", "placeholders": {} @@ -2474,14 +2018,8 @@ "type": "String", "placeholders": {} }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@time": { - "type": "String", - "placeholders": {} - }, + "@hydrateTorLong": {}, + "@time": {}, "@enterYourHomeserver": { "type": "String", "placeholders": {} @@ -2494,14 +2032,8 @@ "type": "String", "placeholders": {} }, - "@custom": { - "type": "String", - "placeholders": {} - }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, + "@custom": {}, + "@noBackupWarning": {}, "@fromJoining": { "type": "String", "placeholders": {} @@ -2514,18 +2046,9 @@ "type": "String", "placeholders": {} }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, - "@openChat": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, + "@storeInSecureStorageDescription": {}, + "@openChat": {}, + "@kickUserDescription": {}, "@sendAMessage": { "type": "String", "placeholders": {} @@ -2534,10 +2057,7 @@ "type": "String", "placeholders": {} }, - "@importNow": { - "type": "String", - "placeholders": {} - }, + "@importNow": {}, "@deleteAccount": { "type": "String", "placeholders": {} @@ -2546,26 +2066,14 @@ "type": "String", "placeholders": {} }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, + "@pinMessage": {}, + "@screenSharingDetail": {}, "@muteChat": { "type": "String", "placeholders": {} }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, + "@invite": {}, + "@enableMultiAccounts": {}, "@anyoneCanJoin": { "type": "String", "placeholders": {} @@ -2574,10 +2082,7 @@ "type": "String", "placeholders": {} }, - "@indexedDbErrorTitle": { - "type": "String", - "placeholders": {} - }, + "@indexedDbErrorTitle": {}, "@endedTheCall": { "type": "String", "placeholders": { @@ -9499,7 +9004,6 @@ "report": "རེད་འདོད།", "signInWithPassword": "ནང་བསྐུར་ནས་སྤྱོད་འཇུག", "pleaseTryAgainLaterOrChooseDifferentServer": "ཧེ་མེད་ལོག་འགྱོ་འདི་ལས་འགོད་འབད་ནི། དེ་ལས་འགོད་འབད་ནི་ལས་འགོད་འབད་ནི།", - "signInWith": "ནང་བསྐུར་ནས {provider} ལས་འཇུག", "profileNotFound": "བརྗེ་འདེམས་སུ་མེད་པའི་བརྗེ་འདེམས་ལས་མེད། ཁྱེད་ཀྱིས་འདི་ལས་འགོད་འབད་ནི་ལས་འགོད་འབད་ནི།", "setTheme": "དབྱེ་བའི་དབྱེ་བ།", "setColorTheme": "རིགས་ལུས་དབྱེ་བ།", @@ -9643,9 +9147,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -10819,4 +10321,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index c06caf15f..a7e5b078d 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -2249,15 +2249,6 @@ "type": "String", "placeholders": {} }, - "signInWith": "Inicia sessió amb {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "fileIsTooBigForServer": "No s'ha pogut enviar! El servidor només accepta adjunts de fins a {max}.", "@fileIsTooBigForServer": {}, "homeserver": "Servidor", @@ -11089,4 +11080,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 8e963cf26..0b48e22a6 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -206,7 +206,7 @@ } } }, - "changedTheChatDescriptionTo": "{username} změnil popis chatu na: „{description}“", + "changedTheChatDescriptionTo": "{username} změnil/a popis konverzace na: „{description}“", "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -218,7 +218,7 @@ } } }, - "changedTheChatNameTo": "{username} změnil jméno chatu na: „{chatname}“", + "changedTheChatNameTo": "{username} změnil/a název konverzace na: „{chatname}“", "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -239,7 +239,7 @@ } } }, - "changedTheDisplaynameTo": "{username} změnili svoji přezdívku na: {displayname}", + "changedTheDisplaynameTo": "{username} změnil/a svoji přezdívku na: „{displayname}“", "@changedTheDisplaynameTo": { "type": "String", "placeholders": { @@ -381,7 +381,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "Záloha chatu je zabezpečena bezpečnostním klíčem. Ujistěte se, prosím, že klíč neztratíte.", + "chatBackupDescription": "Vaše zprávy jsou zabezpečeny bezpečnostním klíčem. Ujistěte se, prosím, že klíč neztratíte.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -2439,7 +2439,7 @@ "@allSpaces": {}, "noOtherDevicesFound": "Žádná ostatní zařízení nebyla nalezena", "@noOtherDevicesFound": {}, - "addChatDescription": "Přidejte popis konverzace", + "addChatDescription": "Přidat popis konverzace...", "@addChatDescription": {}, "chatDescription": "Popis konverzace", "@chatDescription": {}, @@ -2470,7 +2470,7 @@ } } }, - "newSpaceDescription": "Prostory umožňují organizovat vaše konverzace a vytvářet soukromé nebo veřejné komunity", + "newSpaceDescription": "Prostory umožňují organizovat Vaše konverzace a vytvářet soukromé nebo veřejné komunity.", "@newSpaceDescription": {}, "profileNotFound": "Uživatel nebyl na serveru nalezen. Možná je problém s připojením nebo uživatel neexistuje.", "@profileNotFound": {}, @@ -2507,7 +2507,7 @@ "@deviceKeys": {}, "reopenChat": "Znovu otevřít konverzaci", "@reopenChat": {}, - "fileIsTooBigForServer": "Server oznamuje že soubor je příliš velký na odeslání.", + "fileIsTooBigForServer": "Neodesláno! Server povoluje maximálně {max} příloh.", "@fileIsTooBigForServer": {}, "jump": "Skočit", "@jump": {}, @@ -2515,15 +2515,6 @@ "@openLinkInBrowser": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Prosím zkuste to znovu nebo si vyberte jiný server.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Přihlásit se pomocí {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "supposedMxid": "Tady by mělo být {mxid}", "@supposedMxid": { "type": "String", @@ -2670,79 +2661,180 @@ } } }, - "setCustomPermissionLevel": "Nastavit vlastní úroveň oprávnění", - "setPermissionsLevelDescription": "Vyberte níže předdefinovanou roli nebo zadejte vlastní úroveň oprávnění mezi 0 a 100.", "ignoreUser": "Ignorovat uživatele", - "normalUser": "Běžný uživatel", + "@ignoreUser": {}, + "normalUser": "Normalní uživatel", + "@normalUser": {}, + "countInvited": "{count} pozváno", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "invitedBy": "📩 Pozván {user}", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "hasKnocked": "🚪 {user} zaklepal", + "@hasKnocked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "usersMustKnock": "Uživatelé musí zaklepat", + "@usersMustKnock": {}, + "setCustomPermissionLevel": "Nastavit vlastní úroveň oprávnění", + "@setCustomPermissionLevel": {}, + "changedTheChatDescription": "{username} změnil/a popis konverzace", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} změnil/a název konverzace", + "@changedTheChatName": {}, + "checkList": "Kontrolní seznam", + "@checkList": {}, + "knock": "Zaklepat", + "@knock": {}, + "userWouldLikeToChangeTheChat": "{user} požádal/a o přidání do konverzace.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "enterSpace": "Vstoupit do prostoru", + "@enterSpace": {}, + "enterRoom": "Vstoupit do místnosti", + "@enterRoom": {}, + "hideUnimportantStateEvents": "Skrýt nedůležité stavové události", + "@hideUnimportantStateEvents": {}, + "hidePresences": "Skrýt seznam událostí?", + "@hidePresences": {}, + "noBackupWarning": "Pozor! Bez povolení zálohování konverzací ztratíte přístup k zašifrovaným zprávám. Důrazně doporučujeme zálohování konverzací před odhlášením povolit.", + "@noBackupWarning": {}, + "readUpToHere": "Čtěte až sem", + "@readUpToHere": {}, + "reportErrorDescription": "😭 Ale ne, něco se porouchalo. Pokud chcete, můžete tento bug nahlásit vývojářům.", + "@reportErrorDescription": {}, + "report": "hlášení", + "@report": {}, + "invite": "Pozvánka", + "@invite": {}, + "inviteGroupChat": "📨 Skupinová pozvánka", + "@inviteGroupChat": {}, + "invitePrivateChat": "📨 Soukromá pozvánka", + "@invitePrivateChat": {}, + "appearOnTopDetails": "Umožňuje zobrazit aplikaci nahoře (není nutné, pokud již máte Fluffychat nastaven jako účet volajícího)", + "@appearOnTopDetails": {}, + "noKeyForThisMessage": "K tomuto může dojít, pokud byla zpráva odeslána před přihlášením k účtu v tomto zařízení.\n\nJe také možné, že odesílatel zablokoval vaše zařízení nebo se něco pokazilo s internetovým připojením.\n\nJste schopni si zprávu přečíst v jiné relaci? Pak můžete zprávu přenést z něj! Přejděte do Nastavení > Zařízení a zkontrolujte, zda se Vaše zařízení vzájemně ověřila. Při příštím otevření místnosti, kdy budou obě relace v popředí, se klíče přenesou automaticky.\n\nNechcete klíče ztratit při odhlašování nebo přepínání zařízení? Ujistěte se, že jste v nastaveních povolili zálohování konverzací.", + "@noKeyForThisMessage": {}, + "invalidInput": "Nevhodný vstup!", + "@invalidInput": {}, + "wrongPinEntered": "Nespravný PIN! Zkuste to znovu za {seconds} vteřin...", + "@wrongPinEntered": { + "type": "String", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "pleaseEnterANumber": "Prosím, zadejte číslo větší než 0", + "@pleaseEnterANumber": {}, + "archiveRoomDescription": "Konverzace bude přesunuta do archivu. Ostatní uživatelé uvidí, že jste konverzaci opustil/a.", + "@archiveRoomDescription": {}, + "roomUpgradeDescription": "Konverzace bude vytvořena znovu s novou verzí místnosti. Všem účastníkům bude oznámeno, že se musí přesunout do nové konverzace. Více o verzích místností se dočtete na https://spec.matrix.org/latest/", + "@roomUpgradeDescription": {}, + "removeDevicesDescription": "Budete odhlášen/a z tohoto zařízení a nebudete nadále moci přijímat zprávy.", + "@removeDevicesDescription": {}, + "banUserDescription": "Uživatel bude vyhozen z konverzace a nebude se moci znovu připojit dokud nebude odblokován.", + "@banUserDescription": {}, + "unbanUserDescription": "Uživatel se bude moci vrátit do konverzace pokud se o to pokusí.", + "@unbanUserDescription": {}, + "makeAdminDescription": "Jestliže tohoto uživatele povýšíte na administrátora, nebude tak moci odčinit, protože bude mít stejná oprávnění jako Vy.", + "@makeAdminDescription": {}, + "pushNotificationsNotAvailable": "Notifikace nejsou dostupné", + "@pushNotificationsNotAvailable": {}, + "learnMore": "Dozvědět se více", + "@learnMore": {}, + "yourGlobalUserIdIs": "Vaše globální uživatelské ID (user-ID) je: ", + "@yourGlobalUserIdIs": {}, + "knocking": "Klepání", + "@knocking": {}, + "chatCanBeDiscoveredViaSearchOnServer": "Konverzaci naleznete vyhledáváním na {server}", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "searchChatsRooms": "Vyhledat #konverzace, @uživatele...", + "@searchChatsRooms": {}, + "nothingFound": "Nic nenalezeno...", + "@nothingFound": {}, + "groupName": "Název skupiny", + "@groupName": {}, + "createGroupAndInviteUsers": "Vytvořit skupinu a pozvat uživatele", + "@createGroupAndInviteUsers": {}, + "groupCanBeFoundViaSearch": "Skupinu naleznete vyhledávním", + "@groupCanBeFoundViaSearch": {}, + "startConversation": "Začít konverzaci", + "@startConversation": {}, + "commandHint_sendraw": "Odeslat soubor json", + "@commandHint_sendraw": {}, + "databaseMigrationTitle": "Databáze je optimalizována", + "@databaseMigrationTitle": {}, + "databaseMigrationBody": "Vydržte prosím. Bude to chvilku trvat.", + "@databaseMigrationBody": {}, + "leaveEmptyToClearStatus": "Zanechte prázdné pro smazání statusu.", + "@leaveEmptyToClearStatus": {}, + "select": "Vybrat", + "@select": {}, + "searchForUsers": "Vyhledat @uživatele...", + "@searchForUsers": {}, + "pleaseEnterYourCurrentPassword": "Prosím, zadejte Vaše současné heslo", + "@pleaseEnterYourCurrentPassword": {}, + "newPassword": "Nové heslo", + "@newPassword": {}, + "pleaseChooseAStrongPassword": "Prosím, zvolte si silné heslo", + "@pleaseChooseAStrongPassword": {}, + "passwordsDoNotMatch": "Hesla se neshodují", + "@passwordsDoNotMatch": {}, + "passwordIsWrong": "Zadané heslo je nesprávné", + "@passwordIsWrong": {}, + "publicLink": "Veřejný odkaz", + "@publicLink": {}, + "publicChatAddresses": "Adresy veřejných konverzací", + "@publicChatAddresses": {}, + "createNewAddress": "Vytvořit novou adresu", + "@createNewAddress": {}, + "thisDevice": "Toto zařzení:", + "@thisDevice": {}, + "setPermissionsLevelDescription": "Vyberte níže předdefinovanou roli nebo zadejte vlastní úroveň oprávnění mezi 0 a 100.", "commandHint_roomupgrade": "Upgradujte tento pokoj na danou verzi pokoje", - "checkList": "Seznam kontrol", - "countInvited": "{count} pozvaných", "sendImages": "Odeslat {count} obrázek", "synchronizingPleaseWaitCounter": "Synchronizuji… ({percentage}%)", - "invitedBy": "📩 Pozván od {user}", - "hasKnocked": "🚪 {user} zaklepal", - "usersMustKnock": "Uživatelé musí zaklepat", "noOneCanJoin": "Nikdo se nemůže připojit", - "userWouldLikeToChangeTheChat": "{user} by se rád připojil do chatu.", "noPublicLinkHasBeenCreatedYet": "Zatím nebyl vytvořen žádný veřejný odkaz", - "knock": "Zaklepat", "callingAccount": "Volající účet", - "appearOnTopDetails": "Umožňuje aplikaci zobrazit se navrchu (není potřeba, pokud již máte Fluffychat nastavený jako volající účet)", - "noKeyForThisMessage": "To se může stát, pokud byla zpráva odeslána dříve, než jste se přihlásili ke svému účtu na tomto zařízení.\n\nJe také možné, že odesílatel zablokoval vaše zařízení nebo došlo k chybě s internetovým připojením.\n\nJste schopni přečíst zprávu na jiné relaci? Pak ji můžete přenést z ní! Přejděte do Nastavení > Zařízení a ujistěte se, že vaše zařízení si vzájemně ověřila. Když příště otevřete místnost a obě relace budou v popředí, klíče budou automaticky přeneseny.\n\nNechcete-li při odhlášení nebo přepínání zařízení ztratit klíče, ujistěte se, že máte povolené zálohování chatu v nastavení.", - "enterSpace": "Vstoupit do prostoru", - "enterRoom": "Vstoupit do místnosti", - "hideUnimportantStateEvents": "Skrýt důležité události stavu", - "hidePresences": "Skrýt seznam stavu?", - "noBackupWarning": "Varování! Bez povolení zálohy chatu přijdete o přístup ke svým zašifrovaným zprávám. Doporučuje se nejprve povolit zálohu chatu před odhlášením.", - "readUpToHere": "Přečíst až sem", - "reportErrorDescription": "😞 Jejda. Něco se pokazilo. Pokud chcete, můžete tento problém nahlásit vývojářům.", - "report": "Nahlásit", - "invite": "Pozvat", - "inviteGroupChat": "📨 Pozvat do skupinového chatu", - "invitePrivateChat": "📨 Pozvat do soukromého chatu", - "invalidInput": "Neplatný vstup!", - "wrongPinEntered": "Zadali jste špatný PIN! Zkuste to znovu za {seconds} sekund...", - "pleaseEnterANumber": "Zadejte číslo větší než 0", - "archiveRoomDescription": "Chat bude přesunut do archivu. Ostatní uživatelé uvidí, že jste opustili chat.", - "roomUpgradeDescription": "Chat bude poté znovu vytvořen s novou verzí místnosti. Všichni účastníci budou upozorněni, že musí přejít na nový chat. Více o verzích místností najdete na https://spec.matrix.org/latest/rooms/", - "removeDevicesDescription": "Budete odhlášeni z tohoto zařízení a již nebudete moci přijímat zprávy.", - "banUserDescription": "Uživatel bude zablokován v chatu a nebude moci znovu vstoupit, dokud nebude odblokován.", - "unbanUserDescription": "Uživatel bude moci znovu vstoupit do chatu, pokud se pokusí.", "kickUserDescription": "Uživatel je vyhozen z chatu, ale není zablokován. Ve veřejných chatech se může kdykoli znovu připojit.", - "makeAdminDescription": "Jakmile tohoto uživatele udělíte správcem, možná nebudete moci toto změnit, protože bude mít stejná oprávnění jako vy.", - "pushNotificationsNotAvailable": "Oznámení push nejsou dostupná", - "learnMore": "Zjistit více", - "yourGlobalUserIdIs": "Váš globální uživatelský ID je: ", "noUsersFoundWithQuery": "Bohužel nebyl nalezen žádný uživatel s \"{query}\". Zkontrolujte, zda jste neudělali překlep.", - "knocking": "Klepání", - "chatCanBeDiscoveredViaSearchOnServer": "Chat lze najít přes vyhledávání na {server}", - "searchChatsRooms": "Hledat #chaty, @uživatele...", - "nothingFound": "Není nic nalezeno...", - "groupName": "Název skupiny", - "createGroupAndInviteUsers": "Vytvořit skupinu a pozvat uživatele", - "groupCanBeFoundViaSearch": "Skupinu lze najít přes vyhledávání", "wrongRecoveryKey": "Omlouváme se... zdá se, že toto není správný klíč pro obnovení.", - "startConversation": "Začít konverzaci", - "commandHint_sendraw": "Odeslat surový json", - "databaseMigrationTitle": "Databáze je optimalizována", - "databaseMigrationBody": "Prosím počkejte. To může chvíli trvat.", - "leaveEmptyToClearStatus": "Nechte prázdné pro vymazání vašeho stavu.", - "select": "Vybrat", - "searchForUsers": "Hledat @uživatele...", - "pleaseEnterYourCurrentPassword": "Zadejte prosím své aktuální heslo", - "newPassword": "Nové heslo", - "pleaseChooseAStrongPassword": "Vyberte si prosím silné heslo", - "passwordsDoNotMatch": "Hesla se neshodují", - "passwordIsWrong": "Zadané heslo je nesprávné", - "publicLink": "Veřejný odkaz", - "publicChatAddresses": "Veřejné adresy chatu", - "createNewAddress": "Vytvořit novou adresu", "joinSpace": "Připojit se ke prostoru", "publicSpaces": "Veřejné prostory", "addChatOrSubSpace": "Přidat chat nebo podprostor", "subspace": "Podprostor", "decline": "Odmítnout", - "thisDevice": "Toto zařízení:", "initAppError": "Při inicializaci aplikace došlo k chybě", "userRole": "Role uživatele", "minimumPowerLevel": "{level} je minimální úroveň oprávnění.", @@ -4034,38 +4126,14 @@ "inviteYourFriends": "Pozvěte své přátele", "playWithAI": "Hrajte zatím s AI", "courseStartDesc": "Pangea Bot je připraven kdykoliv vyrazit!\n\n...ale učení je lepší s přáteli!", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, "@setPermissionsLevelDescription": { "type": "String", "placeholders": {} }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, "@commandHint_roomupgrade": { "type": "String", "placeholders": {} }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, "@sendImages": { "type": "String", "placeholders": { @@ -4082,158 +4150,22 @@ } } }, - "@invitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@hasKnocked": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@usersMustKnock": { - "type": "String", - "placeholders": {} - }, "@noOneCanJoin": { "type": "String", "placeholders": {} }, - "@userWouldLikeToChangeTheChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, "@noPublicLinkHasBeenCreatedYet": { "type": "String", "placeholders": {} }, - "@knock": { - "type": "String", - "placeholders": {} - }, "@callingAccount": { "type": "String", "placeholders": {} }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} - }, - "@enterSpace": { - "type": "String", - "placeholders": {} - }, - "@enterRoom": { - "type": "String", - "placeholders": {} - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@hidePresences": { - "type": "String", - "placeholders": {} - }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, - "@report": { - "type": "String", - "placeholders": {} - }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, - "@wrongPinEntered": { - "type": "String", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, "@kickUserDescription": { "type": "String", "placeholders": {} }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, - "@yourGlobalUserIdIs": { - "type": "String", - "placeholders": {} - }, "@noUsersFoundWithQuery": { "type": "String", "placeholders": { @@ -4242,102 +4174,10 @@ } } }, - "@knocking": { - "type": "String", - "placeholders": {} - }, - "@chatCanBeDiscoveredViaSearchOnServer": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@searchChatsRooms": { - "type": "String", - "placeholders": {} - }, - "@nothingFound": { - "type": "String", - "placeholders": {} - }, - "@groupName": { - "type": "String", - "placeholders": {} - }, - "@createGroupAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@groupCanBeFoundViaSearch": { - "type": "String", - "placeholders": {} - }, "@wrongRecoveryKey": { "type": "String", "placeholders": {} }, - "@startConversation": { - "type": "String", - "placeholders": {} - }, - "@commandHint_sendraw": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationTitle": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationBody": { - "type": "String", - "placeholders": {} - }, - "@leaveEmptyToClearStatus": { - "type": "String", - "placeholders": {} - }, - "@select": { - "type": "String", - "placeholders": {} - }, - "@searchForUsers": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourCurrentPassword": { - "type": "String", - "placeholders": {} - }, - "@newPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@passwordsDoNotMatch": { - "type": "String", - "placeholders": {} - }, - "@passwordIsWrong": { - "type": "String", - "placeholders": {} - }, - "@publicLink": { - "type": "String", - "placeholders": {} - }, - "@publicChatAddresses": { - "type": "String", - "placeholders": {} - }, - "@createNewAddress": { - "type": "String", - "placeholders": {} - }, "@joinSpace": { "type": "String", "placeholders": {} @@ -4358,10 +4198,6 @@ "type": "String", "placeholders": {} }, - "@thisDevice": { - "type": "String", - "placeholders": {} - }, "@initAppError": { "type": "String", "placeholders": {} @@ -4564,9 +4400,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -11672,4 +11506,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index edd3ab152..dcdf6376b 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -583,7 +583,6 @@ "report": "rapporter", "signInWithPassword": "Log ind med adgangskode", "pleaseTryAgainLaterOrChooseDifferentServer": "Prøv igen senere eller vælg en anden server.", - "signInWith": "Log ind med {provider}", "profileNotFound": "Brugeren kunne ikke findes på serveren. Måske er der et forbindelsesproblem, eller brugeren findes ikke.", "setTheme": "Vælg tema:", "setColorTheme": "Vælg farvetema:", @@ -4593,14 +4592,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5011,9 +5002,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12126,4 +12115,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 009a38d51..b38c16454 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,11075 +1,11128 @@ { - "@@locale": "de", - "@@last_modified": "2026-02-09 15:31:16.861620", - "alwaysUse24HourFormat": "true", - "@alwaysUse24HourFormat": { - "description": "Set to true to always display time of day in 24 hour format." - }, - "about": "Über", - "@about": { - "type": "String", - "placeholders": {} - }, - "accept": "Annehmen", - "@accept": { - "type": "String", - "placeholders": {} - }, - "acceptedTheInvitation": "👍 {username} hat die Einladung angenommen", - "@acceptedTheInvitation": { - "type": "String", - "placeholders": { - "username": { + "@@locale": "de", + "@@last_modified": "2021-08-14 12:41:10.119255", + "alwaysUse24HourFormat": "true", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "about": "Über", + "@about": { + "type": "String", + "placeholders": {} + }, + "accept": "Annehmen", + "@accept": { + "type": "String", + "placeholders": {} + }, + "acceptedTheInvitation": "👍 {username} hat die Einladung angenommen", + "@acceptedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "account": "Konto", + "@account": { + "type": "String", + "placeholders": {} + }, + "activatedEndToEndEncryption": "🔐 {username} hat Ende-zu-Ende Verschlüsselung aktiviert", + "@activatedEndToEndEncryption": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "addEmail": "E-Mail hinzufügen", + "@addEmail": { + "type": "String", + "placeholders": {} + }, + "admin": "Admin", + "@admin": { + "type": "String", + "placeholders": {} + }, + "alias": "Alias", + "@alias": { + "type": "String", + "placeholders": {} + }, + "all": "Alle", + "@all": { + "type": "String", + "placeholders": {} + }, + "allChats": "Alle Chats", + "@allChats": { + "type": "String", + "placeholders": {} + }, + "answeredTheCall": "{senderName} hat den Anruf angenommen", + "@answeredTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "anyoneCanJoin": "Jeder darf beitreten", + "@anyoneCanJoin": { + "type": "String", + "placeholders": {} + }, + "appLock": "Anwendungssperre", + "@appLock": { + "type": "String", + "placeholders": {} + }, + "archive": "Archiv", + "@archive": { + "type": "String", + "placeholders": {} + }, + "areGuestsAllowedToJoin": "Dürfen Gäste beitreten", + "@areGuestsAllowedToJoin": { + "type": "String", + "placeholders": {} + }, + "areYouSure": "Bist du sicher?", + "@areYouSure": { + "type": "String", + "placeholders": {} + }, + "areYouSureYouWantToLogout": "Willst du dich wirklich abmelden?", + "@areYouSureYouWantToLogout": { + "type": "String", + "placeholders": {} + }, + "askSSSSSign": "Bitte gib, um die andere Person signieren zu können, dein Sicherheitsschlüssel oder Wiederherstellungsschlüssel ein.", + "@askSSSSSign": { + "type": "String", + "placeholders": {} + }, + "askVerificationRequest": "Diese Bestätigungsanfrage von {username} annehmen?", + "@askVerificationRequest": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "badServerLoginTypesException": "Der Homeserver unterstützt diese Anmelde-Typen:\n{serverVersions}\nAber diese App unterstützt nur:\n{supportedVersions}", + "@badServerLoginTypesException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "badServerVersionsException": "Der Homeserver unterstützt die Spec-Versionen:\n{serverVersions}\nAber diese App unterstützt nur:\n{supportedVersions}", + "@badServerVersionsException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "banFromChat": "Aus dem Chat verbannen", + "@banFromChat": { + "type": "String", + "placeholders": {} + }, + "banned": "Verbannt", + "@banned": { + "type": "String", + "placeholders": {} + }, + "bannedUser": "{username} hat {targetName} verbannt", + "@bannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "blockDevice": "Blockiere Gerät", + "@blockDevice": { + "type": "String", + "placeholders": {} + }, + "blocked": "Blockiert", + "@blocked": { + "type": "String", + "placeholders": {} + }, + "botMessages": "Bot-Nachrichten", + "@botMessages": { + "type": "String", + "placeholders": {} + }, + "cancel": "Abbrechen", + "@cancel": { + "type": "String", + "placeholders": {} + }, + "cantOpenUri": "Die URI {uri} kann nicht geöffnet werden", + "@cantOpenUri": { + "type": "String", + "placeholders": { + "uri": { + "type": "String" + } + } + }, + "changeDeviceName": "Gerätenamen ändern", + "@changeDeviceName": { + "type": "String", + "placeholders": {} + }, + "changedTheChatAvatar": "{username} hat den Chat-Avatar geändert", + "@changedTheChatAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheChatDescriptionTo": "{username} hat die Chatbeschreibung geändert in: '{description}'", + "@changedTheChatDescriptionTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "description": { + "type": "String" + } + } + }, + "changedTheChatNameTo": "{username} hat den Chatnamen geändert in: '{chatname}'", + "@changedTheChatNameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "chatname": { + "type": "String" + } + } + }, + "changedTheChatPermissions": "{username} hat die Chat-Berechtigungen geändert", + "@changedTheChatPermissions": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheDisplaynameTo": "{username} hat den Spitznamen geändert in: '{displayname}'", + "@changedTheDisplaynameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "displayname": { + "type": "String" + } + } + }, + "changedTheGuestAccessRules": "{username} hat die Zugangsregeln für Gäste geändert", + "@changedTheGuestAccessRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheGuestAccessRulesTo": "{username} hat die Zugangsregeln für Gäste geändert zu: {rules}", + "@changedTheGuestAccessRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, + "changedTheHistoryVisibility": "{username} hat die Sichtbarkeit des Chat-Verlaufs geändert", + "@changedTheHistoryVisibility": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheHistoryVisibilityTo": "{username} hat die Sichtbarkeit des Chat-Verlaufs geändert zu: {rules}", + "@changedTheHistoryVisibilityTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, + "changedTheJoinRules": "{username} hat die Zugangsregeln geändert", + "@changedTheJoinRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheJoinRulesTo": "{username} hat die Zugangsregeln geändert zu: {joinRules}", + "@changedTheJoinRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "joinRules": { + "type": "String" + } + } + }, + "changedTheProfileAvatar": "{username} hat das Profilbild geändert", + "@changedTheProfileAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheRoomAliases": "{username} hat die Raum-Aliasse geändert", + "@changedTheRoomAliases": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheRoomInvitationLink": "{username} hat den Einladungslink geändert", + "@changedTheRoomInvitationLink": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changePassword": "Passwort ändern", + "@changePassword": { + "type": "String", + "placeholders": {} + }, + "changeTheHomeserver": "Anderen Homeserver verwenden", + "@changeTheHomeserver": { + "type": "String", + "placeholders": {} + }, + "changeTheme": "Ändere Deinen Style", + "@changeTheme": { + "type": "String", + "placeholders": {} + }, + "changeTheNameOfTheGroup": "Gruppenname ändern", + "@changeTheNameOfTheGroup": { + "type": "String", + "placeholders": {} + }, + "changeYourAvatar": "Deinen Avatar ändern", + "@changeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "channelCorruptedDecryptError": "Die Verschlüsselung wurde korrumpiert", + "@channelCorruptedDecryptError": { + "type": "String", + "placeholders": {} + }, + "chat": "Chat", + "@chat": { + "type": "String", + "placeholders": {} + }, + "chatBackup": "Chat-Backup", + "@chatBackup": { + "type": "String", + "placeholders": {} + }, + "chatBackupDescription": "Deine Nachrichten sind mit einem Wiederherstellungsschlüssel gesichert. Bitte stelle sicher, dass du ihn nicht verlierst.", + "@chatBackupDescription": { + "type": "String", + "placeholders": {} + }, + "chatDetails": "Chatdetails", + "@chatDetails": { + "type": "String", + "placeholders": {} + }, + "chats": "Chats", + "@chats": { + "type": "String", + "placeholders": {} + }, + "chooseAStrongPassword": "Wähle ein sicheres Passwort", + "@chooseAStrongPassword": { + "type": "String", + "placeholders": {} + }, + "clearArchive": "Archiv leeren", + "close": "Schließen", + "@close": { + "type": "String", + "placeholders": {} + }, + "commandHint_ban": "Banne ausgewählten Benutzer aus diesen Raum", + "@commandHint_ban": { + "type": "String", + "description": "Usage hint for the command /ban" + }, + "commandHint_html": "Sende HTML-formatierten Text", + "@commandHint_html": { + "type": "String", + "description": "Usage hint for the command /html" + }, + "commandHint_invite": "Lade den Benutzer in diesen Raum ein", + "@commandHint_invite": { + "type": "String", + "description": "Usage hint for the command /invite" + }, + "commandHint_join": "Betritt den ausgewählten Raum", + "@commandHint_join": { + "type": "String", + "description": "Usage hint for the command /join" + }, + "commandHint_kick": "Entferne den übergebenen Benutzer aus diesem Raum", + "@commandHint_kick": { + "type": "String", + "description": "Usage hint for the command /kick" + }, + "commandHint_leave": "Diesen Raum verlassen", + "@commandHint_leave": { + "type": "String", + "description": "Usage hint for the command /leave" + }, + "commandHint_me": "Beschreibe dich selbst", + "@commandHint_me": { + "type": "String", + "description": "Usage hint for the command /me" + }, + "commandHint_myroomavatar": "Setze dein Profilbild nur für diesen Raum (MXC-Uri)", + "@commandHint_myroomavatar": { + "type": "String", + "description": "Usage hint for the command /myroomavatar" + }, + "commandHint_myroomnick": "Setze deinen Anzeigenamen nur für diesen Raum", + "@commandHint_myroomnick": { + "type": "String", + "description": "Usage hint for the command /myroomnick" + }, + "commandHint_op": "Setze den übergeben Powerlevel des Benutzers (Standard: 50)", + "@commandHint_op": { + "type": "String", + "description": "Usage hint for the command /op" + }, + "commandHint_plain": "Sende unformatierten Text", + "@commandHint_plain": { + "type": "String", + "description": "Usage hint for the command /plain" + }, + "commandHint_react": "Sende die Antwort als Reaktion", + "@commandHint_react": { + "type": "String", + "description": "Usage hint for the command /react" + }, + "commandHint_send": "Text senden", + "@commandHint_send": { + "type": "String", + "description": "Usage hint for the command /send" + }, + "commandHint_unban": "Hebe die Verbannung dieses Benutzers in diesem Raum auf", + "@commandHint_unban": { + "type": "String", + "description": "Usage hint for the command /unban" + }, + "commandInvalid": "Befehl ungültig", + "@commandInvalid": { "type": "String" - } - } - }, - "account": "Konto", - "@account": { - "type": "String", - "placeholders": {} - }, - "activatedEndToEndEncryption": "🔐 {username} hat Ende-zu-Ende Verschlüsselung aktiviert", - "@activatedEndToEndEncryption": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "addEmail": "E-Mail hinzufügen", - "@addEmail": { - "type": "String", - "placeholders": {} - }, - "admin": "Admin", - "@admin": { - "type": "String", - "placeholders": {} - }, - "alias": "Alias", - "@alias": { - "type": "String", - "placeholders": {} - }, - "all": "Alle", - "@all": { - "type": "String", - "placeholders": {} - }, - "allChats": "Alle Chats", - "@allChats": { - "type": "String", - "placeholders": {} - }, - "answeredTheCall": "{senderName} hat den Anruf angenommen", - "@answeredTheCall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "anyoneCanJoin": "Jeder darf beitreten", - "@anyoneCanJoin": { - "type": "String", - "placeholders": {} - }, - "appLock": "Anwendungssperre", - "@appLock": { - "type": "String", - "placeholders": {} - }, - "archive": "Archiv", - "@archive": { - "type": "String", - "placeholders": {} - }, - "areGuestsAllowedToJoin": "Dürfen Gäste beitreten", - "@areGuestsAllowedToJoin": { - "type": "String", - "placeholders": {} - }, - "areYouSure": "Bist du sicher?", - "@areYouSure": { - "type": "String", - "placeholders": {} - }, - "areYouSureYouWantToLogout": "Willst du dich wirklich abmelden?", - "@areYouSureYouWantToLogout": { - "type": "String", - "placeholders": {} - }, - "askSSSSSign": "Bitte gib, um die andere Person signieren zu können, dein Sicherheitsschlüssel oder Wiederherstellungsschlüssel ein.", - "@askSSSSSign": { - "type": "String", - "placeholders": {} - }, - "askVerificationRequest": "Diese Bestätigungsanfrage von {username} annehmen?", - "@askVerificationRequest": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "badServerLoginTypesException": "Der Homeserver unterstützt diese Anmelde-Typen:\n{serverVersions}\nAber diese App unterstützt nur:\n{supportedVersions}", - "@badServerLoginTypesException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "badServerVersionsException": "Der Homeserver unterstützt die Spec-Versionen:\n{serverVersions}\nAber diese App unterstützt nur:\n{supportedVersions}", - "@badServerVersionsException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "banFromChat": "Aus dem Chat verbannen", - "@banFromChat": { - "type": "String", - "placeholders": {} - }, - "banned": "Verbannt", - "@banned": { - "type": "String", - "placeholders": {} - }, - "bannedUser": "{username} hat {targetName} verbannt", - "@bannedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "blockDevice": "Blockiere Gerät", - "@blockDevice": { - "type": "String", - "placeholders": {} - }, - "blocked": "Blockiert", - "@blocked": { - "type": "String", - "placeholders": {} - }, - "botMessages": "Bot-Nachrichten", - "@botMessages": { - "type": "String", - "placeholders": {} - }, - "cancel": "Abbrechen", - "@cancel": { - "type": "String", - "placeholders": {} - }, - "cantOpenUri": "Die URI {uri} kann nicht geöffnet werden", - "@cantOpenUri": { - "type": "String", - "placeholders": { - "uri": { - "type": "String" - } - } - }, - "changeDeviceName": "Gerätenamen ändern", - "@changeDeviceName": { - "type": "String", - "placeholders": {} - }, - "changedTheChatAvatar": "{username} hat den Chat-Avatar geändert", - "@changedTheChatAvatar": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changedTheChatDescriptionTo": "{username} hat die Chatbeschreibung geändert in: '{description}'", - "@changedTheChatDescriptionTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "description": { - "type": "String" - } - } - }, - "changedTheChatNameTo": "{username} hat den Chatnamen geändert in: '{chatname}'", - "@changedTheChatNameTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "chatname": { - "type": "String" - } - } - }, - "changedTheChatPermissions": "{username} hat die Chat-Berechtigungen geändert", - "@changedTheChatPermissions": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changedTheDisplaynameTo": "{username} hat den Spitznamen geändert in: '{displayname}'", - "@changedTheDisplaynameTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "displayname": { - "type": "String" - } - } - }, - "changedTheGuestAccessRules": "{username} hat die Zugangsregeln für Gäste geändert", - "@changedTheGuestAccessRules": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changedTheGuestAccessRulesTo": "{username} hat die Zugangsregeln für Gäste geändert zu: {rules}", - "@changedTheGuestAccessRulesTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "rules": { - "type": "String" - } - } - }, - "changedTheHistoryVisibility": "{username} hat die Sichtbarkeit des Chat-Verlaufs geändert", - "@changedTheHistoryVisibility": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changedTheHistoryVisibilityTo": "{username} hat die Sichtbarkeit des Chat-Verlaufs geändert zu: {rules}", - "@changedTheHistoryVisibilityTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "rules": { - "type": "String" - } - } - }, - "changedTheJoinRules": "{username} hat die Zugangsregeln geändert", - "@changedTheJoinRules": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changedTheJoinRulesTo": "{username} hat die Zugangsregeln geändert zu: {joinRules}", - "@changedTheJoinRulesTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "joinRules": { - "type": "String" - } - } - }, - "changedTheProfileAvatar": "{username} hat das Profilbild geändert", - "@changedTheProfileAvatar": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changedTheRoomAliases": "{username} hat die Raum-Aliasse geändert", - "@changedTheRoomAliases": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changedTheRoomInvitationLink": "{username} hat den Einladungslink geändert", - "@changedTheRoomInvitationLink": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "changePassword": "Passwort ändern", - "@changePassword": { - "type": "String", - "placeholders": {} - }, - "changeTheHomeserver": "Anderen Homeserver verwenden", - "@changeTheHomeserver": { - "type": "String", - "placeholders": {} - }, - "changeTheme": "Ändere Deinen Style", - "@changeTheme": { - "type": "String", - "placeholders": {} - }, - "changeTheNameOfTheGroup": "Gruppenname ändern", - "@changeTheNameOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "changeYourAvatar": "Deinen Avatar ändern", - "@changeYourAvatar": { - "type": "String", - "placeholders": {} - }, - "channelCorruptedDecryptError": "Die Verschlüsselung wurde korrumpiert", - "@channelCorruptedDecryptError": { - "type": "String", - "placeholders": {} - }, - "chat": "Chat", - "@chat": { - "type": "String", - "placeholders": {} - }, - "chatBackup": "Chat-Backup", - "@chatBackup": { - "type": "String", - "placeholders": {} - }, - "chatBackupDescription": "Deine alten Nachrichten sind mit einem Wiederherstellungsschlüssel gesichert. Bitte stellen sicher, dass du ihn nicht verlierst.", - "@chatBackupDescription": { - "type": "String", - "placeholders": {} - }, - "chatDetails": "Chatdetails", - "@chatDetails": { - "type": "String", - "placeholders": {} - }, - "chats": "Chats", - "@chats": { - "type": "String", - "placeholders": {} - }, - "chooseAStrongPassword": "Wähle ein sicheres Passwort", - "@chooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "clearArchive": "Archiv leeren", - "@clearArchive": {}, - "close": "Schließen", - "@close": { - "type": "String", - "placeholders": {} - }, - "commandHint_ban": "Banne ausgewählten Benutzer aus diesen Raum", - "@commandHint_ban": { - "type": "String", - "description": "Usage hint for the command /ban" - }, - "commandHint_html": "Sende HTML-formatierten Text", - "@commandHint_html": { - "type": "String", - "description": "Usage hint for the command /html" - }, - "commandHint_invite": "Lade den Benutzer in diesen Raum ein", - "@commandHint_invite": { - "type": "String", - "description": "Usage hint for the command /invite" - }, - "commandHint_join": "Betritt den ausgewählten Raum", - "@commandHint_join": { - "type": "String", - "description": "Usage hint for the command /join" - }, - "commandHint_kick": "Entferne den übergebenen Benutzer aus diesem Raum", - "@commandHint_kick": { - "type": "String", - "description": "Usage hint for the command /kick" - }, - "commandHint_leave": "Diesen Raum verlassen", - "@commandHint_leave": { - "type": "String", - "description": "Usage hint for the command /leave" - }, - "commandHint_me": "Beschreibe dich selbst", - "@commandHint_me": { - "type": "String", - "description": "Usage hint for the command /me" - }, - "commandHint_myroomavatar": "Setze dein Profilbild nur für diesen Raum (MXC-Uri)", - "@commandHint_myroomavatar": { - "type": "String", - "description": "Usage hint for the command /myroomavatar" - }, - "commandHint_myroomnick": "Setze deinen Anzeigenamen nur für diesen Raum", - "@commandHint_myroomnick": { - "type": "String", - "description": "Usage hint for the command /myroomnick" - }, - "commandHint_op": "Setze den übergeben Powerlevel des Benutzers (Standard: 50)", - "@commandHint_op": { - "type": "String", - "description": "Usage hint for the command /op" - }, - "commandHint_plain": "Sende unformatierten Text", - "@commandHint_plain": { - "type": "String", - "description": "Usage hint for the command /plain" - }, - "commandHint_react": "Sende die Antwort als Reaction", - "@commandHint_react": { - "type": "String", - "description": "Usage hint for the command /react" - }, - "commandHint_send": "Text senden", - "@commandHint_send": { - "type": "String", - "description": "Usage hint for the command /send" - }, - "commandHint_unban": "Hebe die Verbannung dieses Benutzers in diesem Raum auf", - "@commandHint_unban": { - "type": "String", - "description": "Usage hint for the command /unban" - }, - "commandInvalid": "Befehl ungültig", - "@commandInvalid": { - "type": "String" - }, - "commandMissing": "{command} ist kein Befehl.", - "@commandMissing": { - "type": "String", - "placeholders": { - "command": { + }, + "commandMissing": "{command} ist kein Befehl.", + "@commandMissing": { + "type": "String", + "placeholders": { + "command": { + "type": "String" + } + }, + "description": "State that {command} is not a valid /command." + }, + "compareEmojiMatch": "Bitte vergleiche die Emojis", + "@compareEmojiMatch": { + "type": "String", + "placeholders": {} + }, + "compareNumbersMatch": "Bitte vergleiche die Zahlen", + "@compareNumbersMatch": { + "type": "String", + "placeholders": {} + }, + "configureChat": "Chat konfigurieren", + "@configureChat": { + "type": "String", + "placeholders": {} + }, + "confirm": "Bestätigen", + "@confirm": { + "type": "String", + "placeholders": {} + }, + "connect": "Verbinden", + "@connect": { + "type": "String", + "placeholders": {} + }, + "contactHasBeenInvitedToTheGroup": "Kontakt wurde in die Gruppe eingeladen", + "@contactHasBeenInvitedToTheGroup": { + "type": "String", + "placeholders": {} + }, + "containsDisplayName": "Enthält Anzeigenamen", + "@containsDisplayName": { + "type": "String", + "placeholders": {} + }, + "containsUserName": "Enthält Benutzernamen", + "@containsUserName": { + "type": "String", + "placeholders": {} + }, + "contentHasBeenReported": "Der Inhalt wurde den Serveradministratoren gemeldet", + "@contentHasBeenReported": { + "type": "String", + "placeholders": {} + }, + "copiedToClipboard": "Wurde in die Zwischenablage kopiert", + "@copiedToClipboard": { + "type": "String", + "placeholders": {} + }, + "copy": "Kopieren", + "@copy": { + "type": "String", + "placeholders": {} + }, + "copyToClipboard": "In Zwischenablage kopieren", + "@copyToClipboard": { + "type": "String", + "placeholders": {} + }, + "couldNotDecryptMessage": "Nachricht konnte nicht entschlüsselt werden: {error}", + "@couldNotDecryptMessage": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "countParticipants": "{count} Mitglieder", + "@countParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "create": "Erstellen", + "@create": { + "type": "String", + "placeholders": {} + }, + "createdTheChat": "💬 {username} hat den Chat erstellt", + "@createdTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "createNewSpace": "Neuer Space", + "@createNewSpace": { + "type": "String", + "placeholders": {} + }, + "currentlyActive": "Jetzt gerade online", + "@currentlyActive": { + "type": "String", + "placeholders": {} + }, + "darkTheme": "Dunkel", + "@darkTheme": { + "type": "String", + "placeholders": {} + }, + "dateAndTimeOfDay": "{date}, {timeOfDay}", + "@dateAndTimeOfDay": { + "type": "String", + "placeholders": { + "date": { + "type": "String" + }, + "timeOfDay": { + "type": "String" + } + } + }, + "dateWithoutYear": "{day}.{month}", + "@dateWithoutYear": { + "type": "String", + "placeholders": { + "month": { + "type": "String" + }, + "day": { + "type": "String" + } + } + }, + "dateWithYear": "{day}.{month}.{year}", + "@dateWithYear": { + "type": "String", + "placeholders": { + "year": { + "type": "String" + }, + "month": { + "type": "String" + }, + "day": { + "type": "String" + } + } + }, + "deactivateAccountWarning": "Dies deaktiviert dein Konto. Es kann nicht rückgängig gemacht werden! Bist du sicher?", + "@deactivateAccountWarning": { + "type": "String", + "placeholders": {} + }, + "defaultPermissionLevel": "Standardberechtigungsstufe für neue Benutzer", + "@defaultPermissionLevel": { + "type": "String", + "placeholders": {} + }, + "delete": "Löschen", + "@delete": { + "type": "String", + "placeholders": {} + }, + "deleteAccount": "Konto löschen", + "@deleteAccount": { + "type": "String", + "placeholders": {} + }, + "deleteMessage": "Nachricht löschen", + "@deleteMessage": { + "type": "String", + "placeholders": {} + }, + "device": "Gerät", + "@device": { + "type": "String", + "placeholders": {} + }, + "deviceId": "Geräte-ID", + "@deviceId": { + "type": "String", + "placeholders": {} + }, + "devices": "Geräte", + "@devices": { + "type": "String", + "placeholders": {} + }, + "directChats": "Direkte Chats", + "@directChats": { + "type": "String", + "placeholders": {} + }, + "displaynameHasBeenChanged": "Anzeigename wurde geändert", + "@displaynameHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "downloadFile": "Datei herunterladen", + "@downloadFile": { + "type": "String", + "placeholders": {} + }, + "edit": "Bearbeiten", + "@edit": { + "type": "String", + "placeholders": {} + }, + "editBlockedServers": "Blockierte Server einstellen", + "@editBlockedServers": { + "type": "String", + "placeholders": {} + }, + "editDisplayname": "Anzeigename ändern", + "@editDisplayname": { + "type": "String", + "placeholders": {} + }, + "editRoomAliases": "Raum-Aliase bearbeiten", + "@editRoomAliases": { + "type": "String", + "placeholders": {} + }, + "editRoomAvatar": "Raumavatar bearbeiten", + "@editRoomAvatar": { + "type": "String", + "placeholders": {} + }, + "emoteExists": "Emoticon existiert bereits!", + "@emoteExists": { + "type": "String", + "placeholders": {} + }, + "emoteInvalid": "Ungültiges Emoticon-Kürzel!", + "@emoteInvalid": { + "type": "String", + "placeholders": {} + }, + "emotePacks": "Emoticon-Bündel für Raum", + "@emotePacks": { + "type": "String", + "placeholders": {} + }, + "emoteSettings": "Emoticon-Einstellungen", + "@emoteSettings": { + "type": "String", + "placeholders": {} + }, + "emoteShortcode": "Emoticon-Kürzel", + "@emoteShortcode": { + "type": "String", + "placeholders": {} + }, + "emoteWarnNeedToPick": "Wähle ein Emoticon-Kürzel und ein Bild!", + "@emoteWarnNeedToPick": { + "type": "String", + "placeholders": {} + }, + "emptyChat": "Leerer Chat", + "@emptyChat": { + "type": "String", + "placeholders": {} + }, + "enableEmotesGlobally": "Aktiviere Emoticon-Bündel global", + "@enableEmotesGlobally": { + "type": "String", + "placeholders": {} + }, + "enableEncryption": "Verschlüsselung anschalten", + "@enableEncryption": { + "type": "String", + "placeholders": {} + }, + "enableEncryptionWarning": "Du wirst die Verschlüsselung nicht mehr ausstellen können. Bist Du sicher?", + "@enableEncryptionWarning": { + "type": "String", + "placeholders": {} + }, + "encrypted": "Verschlüsselt", + "@encrypted": { + "type": "String", + "placeholders": {} + }, + "encryption": "Verschlüsselung", + "@encryption": { + "type": "String", + "placeholders": {} + }, + "encryptionNotEnabled": "Verschlüsselung ist nicht aktiviert", + "@encryptionNotEnabled": { + "type": "String", + "placeholders": {} + }, + "endedTheCall": "{senderName} hat den Anruf beendet", + "@endedTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "enterAnEmailAddress": "Gib eine E-Mail-Adresse ein", + "@enterAnEmailAddress": { + "type": "String", + "placeholders": {} + }, + "enterYourHomeserver": "Gib Deinen Homeserver ein", + "@enterYourHomeserver": { + "type": "String", + "placeholders": {} + }, + "errorObtainingLocation": "Fehler beim Suchen des Standortes: {error}", + "@errorObtainingLocation": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "everythingReady": "Alles fertig!", + "@everythingReady": { + "type": "String", + "placeholders": {} + }, + "extremeOffensive": "Extrem beleidigend", + "@extremeOffensive": { + "type": "String", + "placeholders": {} + }, + "fileName": "Dateiname", + "@fileName": { + "type": "String", + "placeholders": {} + }, + "fluffychat": "FluffyChat", + "@fluffychat": { + "type": "String", + "placeholders": {} + }, + "fontSize": "Schriftgröße", + "@fontSize": { + "type": "String", + "placeholders": {} + }, + "forward": "Weiterleiten", + "@forward": { + "type": "String", + "placeholders": {} + }, + "fromJoining": "Ab dem Beitritt", + "@fromJoining": { + "type": "String", + "placeholders": {} + }, + "fromTheInvitation": "Ab der Einladung", + "@fromTheInvitation": { + "type": "String", + "placeholders": {} + }, + "goToTheNewRoom": "Zum neuen Raum wechseln", + "@goToTheNewRoom": { + "type": "String", + "placeholders": {} + }, + "group": "Gruppe", + "@group": { + "type": "String", + "placeholders": {} + }, + "groupIsPublic": "Öffentliche Gruppe", + "@groupIsPublic": { + "type": "String", + "placeholders": {} + }, + "groups": "Gruppen", + "@groups": { + "type": "String", + "placeholders": {} + }, + "groupWith": "Gruppe mit {displayname}", + "@groupWith": { + "type": "String", + "placeholders": { + "displayname": { + "type": "String" + } + } + }, + "guestsAreForbidden": "Gäste sind verboten", + "@guestsAreForbidden": { + "type": "String", + "placeholders": {} + }, + "guestsCanJoin": "Gäste dürfen beitreten", + "@guestsCanJoin": { + "type": "String", + "placeholders": {} + }, + "hasWithdrawnTheInvitationFor": "{username} hat die Einladung für {targetName} zurückgezogen", + "@hasWithdrawnTheInvitationFor": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "help": "Hilfe", + "@help": { + "type": "String", + "placeholders": {} + }, + "hideRedactedEvents": "Gelöschte Nachrichten ausblenden", + "@hideRedactedEvents": { + "type": "String", + "placeholders": {} + }, + "hideUnknownEvents": "Unbekannte Ereignisse ausblenden", + "@hideUnknownEvents": { + "type": "String", + "placeholders": {} + }, + "howOffensiveIsThisContent": "Wie beleidigend ist dieser Inhalt?", + "@howOffensiveIsThisContent": { + "type": "String", + "placeholders": {} + }, + "id": "ID", + "@id": { + "type": "String", + "placeholders": {} + }, + "identity": "Identität", + "@identity": { + "type": "String", + "placeholders": {} + }, + "ignore": "Ignorieren", + "@ignore": { + "type": "String", + "placeholders": {} + }, + "ignoredUsers": "Ignorierte Personen", + "@ignoredUsers": { + "type": "String", + "placeholders": {} + }, + "iHaveClickedOnLink": "Ich habe den Link angeklickt", + "@iHaveClickedOnLink": { + "type": "String", + "placeholders": {} + }, + "incorrectPassphraseOrKey": "Falsches Passwort oder Wiederherstellungsschlüssel", + "@incorrectPassphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "inoffensive": "Harmlos", + "@inoffensive": { + "type": "String", + "placeholders": {} + }, + "inviteContact": "Kontakt einladen", + "@inviteContact": { + "type": "String", + "placeholders": {} + }, + "inviteContactToGroup": "Kontakt in die Gruppe {groupName} einladen", + "@inviteContactToGroup": { + "type": "String", + "placeholders": { + "groupName": { + "type": "String" + } + } + }, + "invited": "Eingeladen", + "@invited": { + "type": "String", + "placeholders": {} + }, + "invitedUser": "📩 {username} hat {targetName} eingeladen", + "@invitedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "invitedUsersOnly": "Nur eingeladene Mitglieder", + "@invitedUsersOnly": { + "type": "String", + "placeholders": {} + }, + "inviteForMe": "Einladung für mich", + "@inviteForMe": { + "type": "String", + "placeholders": {} + }, + "inviteText": "{username} hat Dich zu FluffyChat eingeladen. \n1. Gehe auf fluffychat.im und installiere die App \n2. Melde Dich in der App an \n3. Öffne den Einladungslink: \n {link}", + "@inviteText": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "link": { + "type": "String" + } + } + }, + "isTyping": "schreibt …", + "@isTyping": { + "type": "String", + "placeholders": {} + }, + "joinedTheChat": "👋 {username} ist dem Chat beigetreten", + "@joinedTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "joinRoom": "Raum beitreten", + "@joinRoom": { + "type": "String", + "placeholders": {} + }, + "kicked": "👞 {username} hat {targetName} hinausgeworfen", + "@kicked": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "kickedAndBanned": "🙅 {username} hat {targetName} hinausgeworfen und verbannt", + "@kickedAndBanned": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "kickFromChat": "Aus dem Chat hinauswerfen", + "@kickFromChat": { + "type": "String", + "placeholders": {} + }, + "lastActiveAgo": "Zuletzt aktiv: {localizedTimeShort}", + "@lastActiveAgo": { + "type": "String", + "placeholders": { + "localizedTimeShort": { + "type": "String" + } + } + }, + "leave": "Verlassen", + "@leave": { + "type": "String", + "placeholders": {} + }, + "leftTheChat": "Hat den Chat verlassen", + "@leftTheChat": { + "type": "String", + "placeholders": {} + }, + "license": "Lizenz", + "@license": { + "type": "String", + "placeholders": {} + }, + "lightTheme": "Hell", + "@lightTheme": { + "type": "String", + "placeholders": {} + }, + "loadCountMoreParticipants": "{count} weitere Mitglieder laden", + "@loadCountMoreParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "loadingPleaseWait": "Lade … Bitte warten.", + "@loadingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "loadMore": "Mehr laden …", + "@loadMore": { + "type": "String", + "placeholders": {} + }, + "locationDisabledNotice": "Standort ist deaktiviert. Bitte aktivieren, um den Standort teilen zu können.", + "@locationDisabledNotice": { + "type": "String", + "placeholders": {} + }, + "locationPermissionDeniedNotice": "Standort-Berechtigung wurde abgelehnt. Bitte akzeptieren, um den Standort teilen zu können.", + "@locationPermissionDeniedNotice": { + "type": "String", + "placeholders": {} + }, + "login": "Anmelden", + "@login": { + "type": "String", + "placeholders": {} + }, + "logInTo": "Bei {homeserver} anmelden", + "@logInTo": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "logout": "Abmelden", + "@logout": { + "type": "String", + "placeholders": {} + }, + "memberChanges": "Änderungen der Mitglieder", + "@memberChanges": { + "type": "String", + "placeholders": {} + }, + "mention": "Erwähnen", + "@mention": { + "type": "String", + "placeholders": {} + }, + "messages": "Nachrichten", + "@messages": { + "type": "String", + "placeholders": {} + }, + "moderator": "Moderator", + "@moderator": { + "type": "String", + "placeholders": {} + }, + "muteChat": "Stummschalten", + "@muteChat": { + "type": "String", + "placeholders": {} + }, + "needPantalaimonWarning": "Bitte beachte, dass du Pantalaimon brauchst, um Ende-zu-Ende-Verschlüsselung benutzen zu können.", + "@needPantalaimonWarning": { + "type": "String", + "placeholders": {} + }, + "newChat": "Neuer Chat", + "@newChat": { + "type": "String", + "placeholders": {} + }, + "newMessageInFluffyChat": "💬 Neue Nachricht in FluffyChat", + "@newMessageInFluffyChat": { + "type": "String", + "placeholders": {} + }, + "newVerificationRequest": "Neue Verifikationsanfrage!", + "@newVerificationRequest": { + "type": "String", + "placeholders": {} + }, + "next": "Weiter", + "@next": { + "type": "String", + "placeholders": {} + }, + "no": "Nein", + "@no": { + "type": "String", + "placeholders": {} + }, + "noConnectionToTheServer": "Keine Verbindung zum Server", + "@noConnectionToTheServer": { + "type": "String", + "placeholders": {} + }, + "noEmotesFound": "Keine Emoticons gefunden. 😕", + "@noEmotesFound": { + "type": "String", + "placeholders": {} + }, + "noEncryptionForPublicRooms": "Du kannst die Verschlüsselung erst aktivieren, sobald dieser Raum nicht mehr öffentlich zugänglich ist.", + "@noEncryptionForPublicRooms": { + "type": "String", + "placeholders": {} + }, + "noGoogleServicesWarning": "Firebase Cloud Messaging scheint auf deinem Gerät nicht verfügbar zu sein. Um trotzdem Push-Benachrichtigungen zu erhalten, empfehlen wir die Installation von ntfy. Mit ntfy oder einem anderen Unified-Push-Anbieter kannst du Push-Benachrichtigungen datensicher empfangen. Du kannst ntfy im PlayStore oder bei F-Droid herunterladen.", + "@noGoogleServicesWarning": { + "type": "String", + "placeholders": {} + }, + "none": "Keiner", + "@none": { + "type": "String", + "placeholders": {} + }, + "noPasswordRecoveryDescription": "Du hast bisher keine Möglichkeit hinzugefügt, um dein Passwort zurückzusetzen.", + "@noPasswordRecoveryDescription": { + "type": "String", + "placeholders": {} + }, + "noPermission": "Keine Berechtigung", + "@noPermission": { + "type": "String", + "placeholders": {} + }, + "noRoomsFound": "Keine Räume gefunden …", + "@noRoomsFound": { + "type": "String", + "placeholders": {} + }, + "notifications": "Benachrichtigungen", + "@notifications": { + "type": "String", + "placeholders": {} + }, + "notificationsEnabledForThisAccount": "Benachrichtigungen für dieses Konto aktiviert", + "@notificationsEnabledForThisAccount": { + "type": "String", + "placeholders": {} + }, + "numUsersTyping": "{count} Mitglieder schreiben …", + "@numUsersTyping": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "obtainingLocation": "Standort wird ermittelt …", + "@obtainingLocation": { + "type": "String", + "placeholders": {} + }, + "offensive": "Beleidigend", + "@offensive": { + "type": "String", + "placeholders": {} + }, + "offline": "Offline", + "@offline": { + "type": "String", + "placeholders": {} + }, + "ok": "Ok", + "@ok": { + "type": "String", + "placeholders": {} + }, + "online": "Online", + "@online": { + "type": "String", + "placeholders": {} + }, + "onlineKeyBackupEnabled": "Online-Schlüsselsicherung ist aktiviert", + "@onlineKeyBackupEnabled": { + "type": "String", + "placeholders": {} + }, + "oopsPushError": "Hoppla! Leider ist beim Einrichten der Push-Benachrichtigungen ein Fehler aufgetreten.", + "@oopsPushError": { + "type": "String", + "placeholders": {} + }, + "oopsSomethingWentWrong": "Hoppla, da ist etwas schiefgelaufen…", + "@oopsSomethingWentWrong": { + "type": "String", + "placeholders": {} + }, + "openAppToReadMessages": "App öffnen, um Nachrichten zu lesen", + "@openAppToReadMessages": { + "type": "String", + "placeholders": {} + }, + "openCamera": "Kamera öffnen", + "@openCamera": { + "type": "String", + "placeholders": {} + }, + "openInMaps": "In Maps öffnen", + "@openInMaps": { + "type": "String", + "placeholders": {} + }, + "or": "Oder", + "@or": { + "type": "String", + "placeholders": {} + }, + "participant": "Mitglied", + "@participant": { + "type": "String", + "placeholders": {} + }, + "passphraseOrKey": "Passwort oder Wiederherstellungsschlüssel", + "@passphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "password": "Passwort", + "@password": { + "type": "String", + "placeholders": {} + }, + "passwordForgotten": "Passwort vergessen", + "@passwordForgotten": { + "type": "String", + "placeholders": {} + }, + "passwordHasBeenChanged": "Passwort wurde geändert", + "@passwordHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "passwordRecovery": "Passwort wiederherstellen", + "@passwordRecovery": { + "type": "String", + "placeholders": {} + }, + "people": "Personen", + "@people": { + "type": "String", + "placeholders": {} + }, + "pickImage": "Bild wählen", + "@pickImage": { + "type": "String", + "placeholders": {} + }, + "pin": "Anpinnen", + "@pin": { + "type": "String", + "placeholders": {} + }, + "play": "{fileName} abspielen", + "@play": { + "type": "String", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "pleaseChoose": "Bitte wählen", + "@pleaseChoose": { + "type": "String", + "placeholders": {} + }, + "pleaseChooseAPasscode": "Bitte einen Code festlegen", + "@pleaseChooseAPasscode": { + "type": "String", + "placeholders": {} + }, + "pleaseClickOnLink": "Bitte auf den Link in der E-Mail klicken und dann fortfahren.", + "@pleaseClickOnLink": { + "type": "String", + "placeholders": {} + }, + "pleaseEnter4Digits": "Bitte 4 Ziffern eingeben oder leer lassen, um die Anwendungssperre zu deaktivieren.", + "@pleaseEnter4Digits": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterYourPassword": "Bitte dein Passwort eingeben", + "@pleaseEnterYourPassword": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterYourPin": "Bitte gib deine Pin ein", + "@pleaseEnterYourPin": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterYourUsername": "Bitte deinen Benutzernamen eingeben", + "@pleaseEnterYourUsername": { + "type": "String", + "placeholders": {} + }, + "pleaseFollowInstructionsOnWeb": "Bitte folge den Anweisungen auf der Website und tippe auf Weiter.", + "@pleaseFollowInstructionsOnWeb": { + "type": "String", + "placeholders": {} + }, + "privacy": "Privatsphäre", + "@privacy": { + "type": "String", + "placeholders": {} + }, + "publicRooms": "Öffentliche Räume", + "@publicRooms": { + "type": "String", + "placeholders": {} + }, + "pushRules": "Push-Regeln", + "@pushRules": { + "type": "String", + "placeholders": {} + }, + "reason": "Grund", + "@reason": { + "type": "String", + "placeholders": {} + }, + "recording": "Aufnahme", + "@recording": { + "type": "String", + "placeholders": {} + }, + "redactedAnEvent": "{username} hat ein Ereignis gelöscht", + "@redactedAnEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "redactMessage": "Nachricht löschen", + "@redactMessage": { + "type": "String", + "placeholders": {} + }, + "register": "Registrieren", + "@register": { + "type": "String", + "placeholders": {} + }, + "reject": "Ablehnen", + "@reject": { + "type": "String", + "placeholders": {} + }, + "rejectedTheInvitation": "{username} hat die Einladung abgelehnt", + "@rejectedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "rejoin": "Wieder beitreten", + "@rejoin": { + "type": "String", + "placeholders": {} + }, + "remove": "Entfernen", + "@remove": { + "type": "String", + "placeholders": {} + }, + "removeAllOtherDevices": "Alle anderen Geräte entfernen", + "@removeAllOtherDevices": { + "type": "String", + "placeholders": {} + }, + "removedBy": "Entfernt von {username}", + "@removedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "removeDevice": "Gerät entfernen", + "@removeDevice": { + "type": "String", + "placeholders": {} + }, + "unbanFromChat": "Verbannung aufheben", + "@unbanFromChat": { + "type": "String", + "placeholders": {} + }, + "removeYourAvatar": "Deinen Avatar löschen", + "@removeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "replaceRoomWithNewerVersion": "Raum mit neuer Version ersetzen", + "@replaceRoomWithNewerVersion": { + "type": "String", + "placeholders": {} + }, + "reply": "Antworten", + "@reply": { + "type": "String", + "placeholders": {} + }, + "reportMessage": "Nachricht melden", + "@reportMessage": { + "type": "String", + "placeholders": {} + }, + "requestPermission": "Berechtigung anfragen", + "@requestPermission": { + "type": "String", + "placeholders": {} + }, + "roomHasBeenUpgraded": "Der Raum wurde ge-upgraded", + "@roomHasBeenUpgraded": { + "type": "String", + "placeholders": {} + }, + "roomVersion": "Raumversion", + "@roomVersion": { + "type": "String", + "placeholders": {} + }, + "saveFile": "Datei speichern", + "@saveFile": { + "type": "String", + "placeholders": {} + }, + "search": "Suchen", + "@search": { + "type": "String", + "placeholders": {} + }, + "security": "Sicherheit", + "@security": { + "type": "String", + "placeholders": {} + }, + "seenByUser": "Gelesen von {username}", + "@seenByUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "send": "Senden", + "@send": { + "type": "String", + "placeholders": {} + }, + "sendAMessage": "Nachricht schreiben", + "@sendAMessage": { + "type": "String", + "placeholders": {} + }, + "sendAsText": "Sende als Text", + "@sendAsText": { "type": "String" + }, + "sendAudio": "Sende Audiodatei", + "@sendAudio": { + "type": "String", + "placeholders": {} + }, + "sendFile": "Datei senden", + "@sendFile": { + "type": "String", + "placeholders": {} + }, + "sendImage": "Bild senden", + "@sendImage": { + "type": "String", + "placeholders": {} + }, + "sendMessages": "Nachrichten senden", + "@sendMessages": { + "type": "String", + "placeholders": {} + }, + "sendOriginal": "Sende Original", + "@sendOriginal": { + "type": "String", + "placeholders": {} + }, + "sendSticker": "Sticker senden", + "@sendSticker": { + "type": "String", + "placeholders": {} + }, + "sendVideo": "Sende Video", + "@sendVideo": { + "type": "String", + "placeholders": {} + }, + "sentAFile": "📁 {username} hat eine Datei gesendet", + "@sentAFile": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAnAudio": "🎤 {username} hat eine Audio-Datei gesendet", + "@sentAnAudio": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAPicture": "🖼️ {username} hat ein Bild gesendet", + "@sentAPicture": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentASticker": "😊 {username} hat einen Sticker gesendet", + "@sentASticker": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAVideo": "🎥 {username} hat ein Video gesendet", + "@sentAVideo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentCallInformations": "{senderName} hat Anrufinformationen geschickt", + "@sentCallInformations": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "setAsCanonicalAlias": "Als Haupt-Alias festlegen", + "@setAsCanonicalAlias": { + "type": "String", + "placeholders": {} + }, + "setCustomEmotes": "Eigene Emoticons einstellen", + "@setCustomEmotes": { + "type": "String", + "placeholders": {} + }, + "setInvitationLink": "Einladungslink festlegen", + "@setInvitationLink": { + "type": "String", + "placeholders": {} + }, + "setPermissionsLevel": "Berechtigungsstufe einstellen", + "@setPermissionsLevel": { + "type": "String", + "placeholders": {} + }, + "setStatus": "Status ändern", + "@setStatus": { + "type": "String", + "placeholders": {} + }, + "settings": "Einstellungen", + "@settings": { + "type": "String", + "placeholders": {} + }, + "share": "Teilen", + "@share": { + "type": "String", + "placeholders": {} + }, + "sharedTheLocation": "{username} hat den Standort geteilt", + "@sharedTheLocation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "shareLocation": "Standort teilen", + "@shareLocation": { + "type": "String", + "placeholders": {} + }, + "showPassword": "Passwort anzeigen", + "@showPassword": { + "type": "String", + "placeholders": {} + }, + "singlesignon": "Einmalige Anmeldung", + "@singlesignon": { + "type": "String", + "placeholders": {} + }, + "skip": "Überspringe", + "@skip": { + "type": "String", + "placeholders": {} + }, + "sourceCode": "Quellcode", + "@sourceCode": { + "type": "String", + "placeholders": {} + }, + "spaceIsPublic": "Space ist öffentlich", + "@spaceIsPublic": { + "type": "String", + "placeholders": {} + }, + "spaceName": "Space-Name", + "@spaceName": { + "type": "String", + "placeholders": {} + }, + "startedACall": "{senderName} hat einen Anruf getätigt", + "@startedACall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "status": "Status", + "@status": { + "type": "String", + "placeholders": {} + }, + "statusExampleMessage": "Wie geht es dir heute?", + "@statusExampleMessage": { + "type": "String", + "placeholders": {} + }, + "submit": "Absenden", + "@submit": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWait": "Synchronisiere... Bitte warten.", + "@synchronizingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "systemTheme": "System", + "@systemTheme": { + "type": "String", + "placeholders": {} + }, + "theyDontMatch": "Stimmen nicht überein", + "@theyDontMatch": { + "type": "String", + "placeholders": {} + }, + "theyMatch": "Stimmen überein", + "@theyMatch": { + "type": "String", + "placeholders": {} + }, + "title": "FluffyChat", + "@title": { + "description": "Title for the application", + "type": "String", + "placeholders": {} + }, + "toggleFavorite": "Favorite umschalten", + "@toggleFavorite": { + "type": "String", + "placeholders": {} + }, + "toggleMuted": "Stummgeschaltete umschalten", + "@toggleMuted": { + "type": "String", + "placeholders": {} + }, + "toggleUnread": "Markieren als gelesen/ungelesen", + "@toggleUnread": { + "type": "String", + "placeholders": {} + }, + "tooManyRequestsWarning": "Zu viele Anfragen. Bitte versuche es später noch einmal!", + "@tooManyRequestsWarning": { + "type": "String", + "placeholders": {} + }, + "transferFromAnotherDevice": "Von anderem Gerät übertragen", + "@transferFromAnotherDevice": { + "type": "String", + "placeholders": {} + }, + "tryToSendAgain": "Noch mal versuchen zu senden", + "@tryToSendAgain": { + "type": "String", + "placeholders": {} + }, + "unavailable": "Nicht verfügbar", + "@unavailable": { + "type": "String", + "placeholders": {} + }, + "unbannedUser": "{username} hat die Verbannung von {targetName} aufgehoben", + "@unbannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "unblockDevice": "Geräteblockierung aufheben", + "@unblockDevice": { + "type": "String", + "placeholders": {} + }, + "unknownDevice": "Unbekanntes Gerät", + "@unknownDevice": { + "type": "String", + "placeholders": {} + }, + "unknownEncryptionAlgorithm": "Unbekannter Verschlüsselungsalgorithmus", + "@unknownEncryptionAlgorithm": { + "type": "String", + "placeholders": {} + }, + "unknownEvent": "Unbekanntes Ereignis '{type}'", + "@unknownEvent": { + "type": "String", + "placeholders": { + "type": { + "type": "String" + } + } + }, + "unmuteChat": "Stumm aus", + "@unmuteChat": { + "type": "String", + "placeholders": {} + }, + "unpin": "Nicht mehr anpinnen", + "@unpin": { + "type": "String", + "placeholders": {} + }, + "unreadChats": "{unreadCount, plural, =1{1 ungelesene Unterhaltung} other{{unreadCount} ungelesene Unterhaltungen}}", + "@unreadChats": { + "type": "String", + "placeholders": { + "unreadCount": { + "type": "int" + } + } + }, + "userAndOthersAreTyping": "{username} und {count} andere schreiben …", + "@userAndOthersAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "userAndUserAreTyping": "{username} und {username2} schreiben …", + "@userAndUserAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "username2": { + "type": "String" + } + } + }, + "userIsTyping": "{username} schreibt …", + "@userIsTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "userLeftTheChat": "🚪 {username} hat den Chat verlassen", + "@userLeftTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "username": "Benutzername", + "@username": { + "type": "String", + "placeholders": {} + }, + "userSentUnknownEvent": "{username} hat ein {type}-Ereignis gesendet", + "@userSentUnknownEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "type": { + "type": "String" + } + } + }, + "verified": "Verifiziert", + "@verified": { + "type": "String", + "placeholders": {} + }, + "verify": "Verifizieren", + "@verify": { + "type": "String", + "placeholders": {} + }, + "verifyStart": "Starte Verifikation", + "@verifyStart": { + "type": "String", + "placeholders": {} + }, + "verifySuccess": "Erfolgreich verifiziert!", + "@verifySuccess": { + "type": "String", + "placeholders": {} + }, + "verifyTitle": "Anderes Konto wird verifiziert", + "@verifyTitle": { + "type": "String", + "placeholders": {} + }, + "videoCall": "Videoanruf", + "@videoCall": { + "type": "String", + "placeholders": {} + }, + "visibilityOfTheChatHistory": "Sichtbarkeit des Chat-Verlaufs", + "@visibilityOfTheChatHistory": { + "type": "String", + "placeholders": {} + }, + "visibleForAllParticipants": "Sichtbar für alle Mitglieder", + "@visibleForAllParticipants": { + "type": "String", + "placeholders": {} + }, + "visibleForEveryone": "Für jeden sichtbar", + "@visibleForEveryone": { + "type": "String", + "placeholders": {} + }, + "voiceMessage": "Sprachnachricht", + "@voiceMessage": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerAcceptRequest": "Warte darauf, dass der Partner die Anfrage annimmt …", + "@waitingPartnerAcceptRequest": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerEmoji": "Warte darauf, dass der Partner die Emoji annimmt …", + "@waitingPartnerEmoji": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerNumbers": "Warten, dass der Partner die Zahlen annimmt …", + "@waitingPartnerNumbers": { + "type": "String", + "placeholders": {} + }, + "wallpaper": "Hintergrund:", + "@wallpaper": { + "type": "String", + "placeholders": {} + }, + "warning": "Achtung!", + "@warning": { + "type": "String", + "placeholders": {} + }, + "weSentYouAnEmail": "Wir haben dir eine E-Mail gesendet", + "@weSentYouAnEmail": { + "type": "String", + "placeholders": {} + }, + "whoCanPerformWhichAction": "Wer kann welche Aktion ausführen", + "@whoCanPerformWhichAction": { + "type": "String", + "placeholders": {} + }, + "whoIsAllowedToJoinThisGroup": "Wer darf der Gruppe beitreten", + "@whoIsAllowedToJoinThisGroup": { + "type": "String", + "placeholders": {} + }, + "whyDoYouWantToReportThis": "Warum willst du dies melden?", + "@whyDoYouWantToReportThis": { + "type": "String", + "placeholders": {} + }, + "wipeChatBackup": "Den Chat-Backup löschen, um einen neuen Wiederherstellungsschlüssel zu erstellen?", + "@wipeChatBackup": { + "type": "String", + "placeholders": {} + }, + "withTheseAddressesRecoveryDescription": "Mit diesen Adressen kannst du dein Passwort wiederherstellen, wenn du es vergessen hast.", + "@withTheseAddressesRecoveryDescription": { + "type": "String", + "placeholders": {} + }, + "writeAMessage": "Schreibe eine Nachricht …", + "@writeAMessage": { + "type": "String", + "placeholders": {} + }, + "yes": "Ja", + "@yes": { + "type": "String", + "placeholders": {} + }, + "you": "Du", + "@you": { + "type": "String", + "placeholders": {} + }, + "youAreNoLongerParticipatingInThisChat": "Du bist kein Mitglied mehr in diesem Chat", + "@youAreNoLongerParticipatingInThisChat": { + "type": "String", + "placeholders": {} + }, + "youHaveBeenBannedFromThisChat": "Du wurdest aus dem Chat verbannt", + "@youHaveBeenBannedFromThisChat": { + "type": "String", + "placeholders": {} + }, + "yourPublicKey": "Dein öffentlicher Schlüssel", + "@yourPublicKey": { + "type": "String", + "placeholders": {} + }, + "noMatrixServer": "{server1} ist kein Matrix-Server, stattdessen {server2} benutzen?", + "@noMatrixServer": { + "type": "String", + "placeholders": { + "server1": { + "type": "String" + }, + "server2": { + "type": "String" + } + } + }, + "scanQrCode": "QR-Code scannen", + "chatHasBeenAddedToThisSpace": "Chat wurde zum Space hinzugefügt", + "autoplayImages": "Animierte Sticker und Emotes automatisch abspielen", + "@autoplayImages": { + "type": "String", + "placeholder": {} + }, + "addToSpace": "Zum Space hinzufügen", + "serverRequiresEmail": "Dieser Server muss deine E-Mail-Adresse für die Registrierung überprüfen.", + "enableMultiAccounts": "(BETA) Aktiviere Multi-Accounts für dieses Gerät", + "bundleName": "Name des Bundles", + "removeFromBundle": "Von diesem Bundle entfernen", + "addToBundle": "Zu einem Bundle hinzufügen", + "editBundlesForAccount": "Bundles für dieses Konto bearbeiten", + "addAccount": "Konto hinzufügen", + "oneClientLoggedOut": "Einer deiner Clients wurde abgemeldet", + "homeserver": "Homeserver", + "sendOnEnter": "Senden mit Enter", + "link": "Link", + "yourChatBackupHasBeenSetUp": "Dein Chat-Backup wurde eingerichtet.", + "unverified": "Unverifiziert", + "messageInfo": "Nachrichten-Info", + "time": "Zeit", + "messageType": "Nachrichtentyp", + "sender": "Absender:in", + "openGallery": "Galerie öffnen", + "removeFromSpace": "Aus dem Space entfernen", + "addToSpaceDescription": "Wähle einen Space aus, um diesen Chat hinzuzufügen.", + "start": "Start", + "repeatPassword": "Passwort wiederholen", + "commandHint_dm": "Starte einen direkten Chat\nBenutze --no-encryption, um die Verschlüsselung auszuschalten", + "@commandHint_dm": { + "type": "String", + "description": "Usage hint for the command /dm" + }, + "commandHint_discardsession": "Sitzung verwerfen", + "@commandHint_discardsession": { + "type": "String", + "description": "Usage hint for the command /discardsession" + }, + "commandHint_clearcache": "Zwischenspeicher löschen", + "@commandHint_clearcache": { + "type": "String", + "description": "Usage hint for the command /clearcache" + }, + "commandHint_create": "Erstelle ein leeren Gruppenchat\nBenutze --no-encryption, um die Verschlüsselung auszuschalten", + "@commandHint_create": { + "type": "String", + "description": "Usage hint for the command /create" + }, + "openVideoCamera": "Video aufnehmen", + "@openVideoCamera": { + "type": "String", + "placeholders": {} + }, + "videoWithSize": "Video ({size})", + "@videoWithSize": { + "type": "String", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "publish": "Veröffentlichen", + "pinMessage": "An Raum anheften", + "emojis": "Emojis", + "placeCall": "Anruf tätigen", + "voiceCall": "Sprachanruf", + "unsupportedAndroidVersion": "Nicht unterstützte Android-Version", + "videoCallsBetaWarning": "Bitte beachte, dass sich Videoanrufe derzeit in der Beta-Phase befinden. Sie funktionieren möglicherweise nicht wie erwartet oder überhaupt nicht auf allen Plattformen.", + "emailOrUsername": "E-Mail oder Benutzername", + "unsupportedAndroidVersionLong": "Diese Funktion erfordert eine neuere Android-Version. Bitte suche nach Updates oder prüfe die Lineage-OS-Unterstützung.", + "experimentalVideoCalls": "Experimentelle Videoanrufe", + "reactedWith": "{sender} reagierte mit {reaction}", + "@reactedWith": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "reaction": { + "type": "String" + } + } + }, + "markAsRead": "Als gelesen markiert", + "reportUser": "Benutzer melden", + "openChat": "Chat öffnen", + "confirmEventUnpin": "Möchtest du das Ereignis wirklich dauerhaft lösen?", + "dismiss": "Verwerfen", + "switchToAccount": "Zu Konto {number} wechseln", + "@switchToAccount": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "nextAccount": "Nächstes Konto", + "widgetJitsi": "Jitsi Meet", + "widgetCustom": "Angepasst", + "widgetEtherpad": "Textnotiz", + "addWidget": "Widget hinzufügen", + "widgetVideo": "Video", + "widgetName": "Name", + "widgetUrlError": "Das ist keine gültige URL.", + "errorAddingWidget": "Fehler beim Hinzufügen des Widgets.", + "previousAccount": "Vorheriges Konto", + "separateChatTypes": "Separate Direktchats und Gruppen", + "@separateChatTypes": { + "type": "String", + "placeholders": {} + }, + "widgetNameError": "Bitte gib einen Anzeigenamen an.", + "youKicked": "👞 Du hast {user} rausgeworfen", + "@youKicked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youKickedAndBanned": "🙅 Du hast {user} rausgeworfen und verbannt", + "@youKickedAndBanned": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youUnbannedUser": "Du hast die Verbannung von {user} rückgängig gemacht", + "@youUnbannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youRejectedTheInvitation": "Du hast die Einladung abgelehnt", + "youJoinedTheChat": "Du bist dem Chat beigetreten", + "youAcceptedTheInvitation": "👍 Du hast die Einladung angenommen", + "youBannedUser": "Du hast den {user} verbannt", + "@youBannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youHaveWithdrawnTheInvitationFor": "Du hast die Einladung für {user} zurückgezogen", + "@youHaveWithdrawnTheInvitationFor": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youInvitedBy": "📩 Du wurdest von {user} eingeladen", + "@youInvitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youInvitedUser": "📩 Du hast {user} eingeladen", + "@youInvitedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "recoveryKey": "Wiederherstellungs-Schlüssel", + "recoveryKeyLost": "Wiederherstellungsschlüssel verloren?", + "user": "Benutzer", + "custom": "Benutzerdefiniert", + "storeInAndroidKeystore": "Im Android KeyStore speichern", + "storeSecurlyOnThisDevice": "Auf diesem Gerät sicher speichern", + "dehydrate": "Sitzung exportieren und Gerät löschen", + "dehydrateWarning": "Diese Aktion kann nicht rückgängig gemacht werden. Stelle sicher, dass du die Sicherungsdatei sicher aufbewahrst.", + "dehydrateTor": "TOR-Benutzer: Sitzung exportieren", + "dehydrateTorLong": "Für TOR-Benutzer wird empfohlen, die Sitzung zu exportieren, bevor das Fenster geschlossen wird.", + "hydrateTor": "TOR-Benutzer: Session-Export importieren", + "hydrate": "Aus Sicherungsdatei wiederherstellen", + "indexedDbErrorTitle": "Probleme im Privatmodus", + "unlockOldMessages": "Entsperre alte Nachrichten", + "pleaseEnterRecoveryKeyDescription": "Um deine alten Nachrichten zu entsperren, gib bitte den Wiederherstellungsschlüssel ein, der in einer früheren Sitzung generiert wurde. Dein Wiederherstellungsschlüssel ist NICHT dein Passwort.", + "saveKeyManuallyDescription": "Speicher diesen Schlüssel manuell, indem du den Systemfreigabedialog oder die Zwischenablage auslöst.", + "hydrateTorLong": "Hast du deine Sitzung das letzte Mal auf TOR exportiert? Importiere sie schnell und chatte weiter.", + "pleaseEnterRecoveryKey": "Bitte gib deinen Wiederherstellungsschlüssel ein:", + "countFiles": "{count} Dateien", + "@countFiles": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "users": "Benutzer", + "storeInSecureStorageDescription": "Speicher den Wiederherstellungsschlüssel im sicheren Speicher dieses Geräts.", + "storeInAppleKeyChain": "Im Apple KeyChain speichern", + "indexedDbErrorLong": "Die Nachrichtenspeicherung ist im privaten Modus standardmäßig leider nicht aktiviert.\nBitte besuche\n- about:config\n- Setze dom.indexedDB.privateBrowsing.enabled auf true\nAndernfalls ist es nicht möglich, FluffyChat auszuführen.", + "confirmMatrixId": "Bitte bestätigen deine Matrix-ID, um dein Konto zu löschen.", + "supposedMxid": "das sollte sein {mxid}", + "@supposedMxid": { + "type": "String", + "placeholders": { + "mxid": { + "type": "String" + } + } + }, + "commandHint_markasdm": "Als Direktnachrichtenraum für die angegebene Matrix-ID markieren", + "commandHint_markasgroup": "Als Gruppe markieren", + "hideUnimportantStateEvents": "Blende unwichtige Zustandsereignisse aus", + "doNotShowAgain": "Nicht mehr anzeigen", + "appearOnTopDetails": "Ermöglicht, dass die App oben angezeigt wird (nicht erforderlich, wenn du Fluffychat bereits als Anrufkonto eingerichtet haben)", + "noKeyForThisMessage": "Dies kann passieren, wenn die Nachricht gesendet wurde, bevor du dich auf diesem Gerät bei deinem Konto angemeldet hast.\n\nEs ist auch möglich, dass der Absender dein Gerät blockiert hat oder etwas mit der Internetverbindung schief gelaufen ist.\n\nKannst du die Nachricht in einer anderen Sitzung lesen? Dann kannst du die Nachricht davon übertragen! Gehe zu den Einstellungen > Geräte und vergewissere dich, dass sich deine Geräte gegenseitig verifiziert haben. Wenn du den Raum das nächste Mal öffnest und beide Sitzungen im Vordergrund sind, werden die Schlüssel automatisch übertragen.\n\nDu möchtest die Schlüssel beim Abmelden oder Gerätewechsel nicht verlieren? Stelle sicher, dass du das Chat-Backup in den Einstellungen aktiviert hast.", + "foregroundServiceRunning": "Diese Benachrichtigung wird angezeigt, wenn der Vordergrunddienst ausgeführt wird.", + "screenSharingTitle": "Bildschirm teilen", + "callingPermissions": "Anrufberechtigungen", + "callingAccount": "Anrufkonto", + "callingAccountDetails": "Ermöglicht FluffyChat, die native Android-Dialer-App zu verwenden.", + "appearOnTop": "Oben erscheinen", + "otherCallingPermissions": "Mikrofon, Kamera und andere FluffyChat-Berechtigungen", + "whyIsThisMessageEncrypted": "Warum ist diese Nachricht nicht lesbar?", + "newGroup": "Neue Gruppe", + "newSpace": "Neuer Space", + "enterSpace": "Raum betreten", + "enterRoom": "Raum betreten", + "allSpaces": "Alle Spaces", + "screenSharingDetail": "Du teilst deinen Bildschirm in FuffyChat", + "numChats": "{number} Chats", + "@numChats": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "newSpaceDescription": "Mit Spaces kannst du deine Chats zusammenfassen und private oder öffentliche Communities aufbauen.", + "wasDirectChatDisplayName": "Leerer Chat (war {oldDisplayName})", + "@wasDirectChatDisplayName": { + "type": "String", + "placeholders": { + "oldDisplayName": { + "type": "String" + } + } + }, + "encryptThisChat": "Diesen Chat verschlüsseln", + "googlyEyesContent": "{senderName} hat dir Googly Eyes gesendet", + "@googlyEyesContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "startFirstChat": "Starte deinen ersten Chat", + "deviceKeys": "Geräteschlüssel:", + "commandHint_cuddle": "Umarmung senden", + "commandHint_hug": "Umarmung senden", + "cuddleContent": "{senderName} knuddelt dich", + "@cuddleContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "sorryThatsNotPossible": "Sorry ... das ist nicht möglich", + "hugContent": "{senderName} umarmt dich", + "@hugContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "commandHint_googly": "Glupschaugen senden", + "disableEncryptionWarning": "Aus Sicherheitsgründen kannst du die Verschlüsselung in einem Chat nicht deaktivieren, wo sie zuvor aktiviert wurde.", + "reopenChat": "Chat wieder eröffnen", + "noBackupWarning": "Achtung! Ohne Aktivierung des Chat-Backups verlierst du den Zugriff auf deine verschlüsselten Nachrichten. Vor dem Ausloggen wird dringend empfohlen, das Chat-Backup zu aktivieren.", + "noOtherDevicesFound": "Keine anderen Geräte anwesend", + "allRooms": "Alle Gruppenchats", + "@allRooms": { + "type": "String", + "placeholders": {} + }, + "fileHasBeenSavedAt": "Datei wurde gespeichert unter {path}", + "@fileHasBeenSavedAt": { + "type": "String", + "placeholders": { + "path": { + "type": "String" + } + } + }, + "jumpToLastReadMessage": "Zur letzten ungelesenen Nachricht", + "readUpToHere": "Bis hier gelesen", + "pleaseTryAgainLaterOrChooseDifferentServer": "Bitte versuche es später noch einmal oder wähle einen anderen Server.", + "jump": "Springen", + "openLinkInBrowser": "Link im Browser öffnen", + "reportErrorDescription": "😭 Oh nein. Etwas ist schief gelaufen. Wenn du möchtest, kannst du den Bug bei den Entwicklern melden.", + "report": "Melden", + "signInWithPassword": "Anmelden mit Passwort", + "signInWithLabel": "Anmelden mit:", + "importNow": "Jetzt importieren", + "importEmojis": "Emojis importieren", + "importFromZipFile": "Aus ZIP-Datei importieren", + "exportEmotePack": "Emote-Paket als ZIP-Datei exportieren", + "notAnImage": "Keine Bilddatei.", + "replace": "Ersetzen", + "sendTypingNotifications": "Tippbenachrichtigungen senden", + "profileNotFound": "Der Benutzer konnte auf dem Server nicht gefunden werden. Vielleicht gibt es ein Verbindungsproblem oder der Benutzer existiert nicht.", + "createGroup": "Gruppe erstellen", + "shareInviteLink": "Einladungslink teilen", + "inviteContactToGroupQuestion": "Willst du {contact} zum Chat {groupName} einladen?", + "tryAgain": "Neuer Versuch", + "redactMessageDescription": "Die Nachricht wird für alle Teilnehmer dieses Gesprächs gelöscht. Dies kann nicht rückgängig gemacht werden.", + "redactedBy": "Gelöscht von {username}", + "@redactedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "redactedByBecause": "Gelöscht von {username} weil: \"{reason}\"", + "@redactedByBecause": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "reason": { + "type": "String" + } + } + }, + "setTheme": "Design festlegen:", + "setColorTheme": "Farbdesign einstellen:", + "invite": "Einladen", + "optionalRedactReason": "(Optional) Grund für die Löschung dieser Nachricht...", + "messagesStyle": "Nachrichten:", + "chatPermissions": "Chatberechtigungen", + "chatDescription": "Chatbeschreibung", + "chatDescriptionHasBeenChanged": "Chatbeschreibung geändert", + "noChatDescriptionYet": "Noch keine Chatbeschreibung vorhanden.", + "invalidServerName": "Ungültiger Servername", + "directChat": "Privater Chat", + "addChatDescription": "Chatbeschreibung hinzufügen ...", + "setChatDescription": "Chatbeschreibung festlegen", + "inviteGroupChat": "📨 Einladungen zum Gruppenchat", + "invitePrivateChat": "📨 Einladungen zum privaten Chat", + "invalidInput": "Ungültige Eingabe!", + "hasKnocked": "🚪 {user} hat angeklopft", + "@hasKnocked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "wrongPinEntered": "Falsche PIN eingegeben! Bitte in {seconds} Sekunden erneut versuchen ...", + "@wrongPinEntered": { + "type": "String", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "pleaseEnterANumber": "Bitte eine Zahl größer 0 eingeben", + "emoteKeyboardNoRecents": "Kürzlich verwendete Emotes werden hier angezeigt ...", + "@emoteKeyboardNoRecents": { + "type": "String", + "placeholders": {} + }, + "banUserDescription": "Der Benutzer wird aus dem Chat gebannt und kann den Chat erst wieder betreten, wenn die Verbannung aufgehoben wird.", + "removeDevicesDescription": "Du wirst von diesem Gerät abgemeldet und kannst dann dort keine Nachrichten mehr empfangen.", + "unbanUserDescription": "Der Benutzer kann den Chat dann wieder betreten, wenn er es versucht.", + "pushNotificationsNotAvailable": "Push-Benachrichtigungen nicht verfügbar", + "makeAdminDescription": "Sobald du diesen Benutzer zum Administrator gemacht hast, kannst du das möglicherweise nicht mehr rückgängig machen, da er dann über dieselben Berechtigungen wie du verfügt.", + "archiveRoomDescription": "Der Chat wird in das Archiv verschoben. Andere Benutzer können sehen, dass du den Chat verlassen hast.", + "learnMore": "Erfahre mehr", + "roomUpgradeDescription": "Der Chat wird dann mit der neuen Raumversion neu erstellt. Alle Teilnehmer werden benachrichtigt, dass sie zum neuen Chat wechseln müssen. Mehr über Raumversionen erfährst du unter https://spec.matrix.org/latest/rooms/", + "kickUserDescription": "Der Benutzer wird aus dem Chat geworfen, aber nicht gebannt. In öffentlichen Chats kann der Benutzer jederzeit wieder beitreten.", + "blockListDescription": "Du kannst Benutzer blockieren, die dich stören. Von Benutzern auf deiner persönlichen Blocklierliste kannst du keine Nachrichten oder Raumeinladungen mehr erhalten.", + "createGroupAndInviteUsers": "Gruppe erstellen und Nutzer einladen", + "startConversation": "Unterhaltung starten", + "blockedUsers": "Blockierte Benutzer", + "groupCanBeFoundViaSearch": "Gruppe kann über die Suche gefunden werden", + "noUsersFoundWithQuery": "Leider konnte mit \"{query}\" kein Benutzer gefunden werden. Bitte schau nach, ob dir ein Tippfehler unterlaufen ist.", + "@noUsersFoundWithQuery": { + "type": "String", + "placeholders": { + "query": { + "type": "String" + } + } + }, + "block": "Blockieren", + "yourGlobalUserIdIs": "Deine globale Benutzer-ID ist: ", + "commandHint_sendraw": "Rohes JSON senden", + "wrongRecoveryKey": "Entschuldigung ... das scheint nicht der richtige Wiederherstellungsschlüssel zu sein.", + "blockUsername": "Blockiere Benutzername", + "groupName": "Gruppenname", + "searchChatsRooms": "Suche nach #Chats, @Nutzer ...", + "databaseMigrationTitle": "Datenbank wird optimiert", + "databaseMigrationBody": "Bitte warten. Dies kann einen Moment dauern.", + "thisDevice": "Dieses Gerät:", + "publicSpaces": "Öffentliche Spaces", + "passwordIsWrong": "Dein eingegebenes Passwort ist falsch", + "pleaseEnterYourCurrentPassword": "Bitte dein aktuelles Passwort eingeben", + "publicLink": "Öffentlicher Link", + "nothingFound": "Nichts gefunden ...", + "decline": "Ablehnen", + "newPassword": "Neues Passwort", + "passwordsDoNotMatch": "Passwörter stimmen nicht überein", + "subspace": "Sub-Space", + "select": "Auswählen", + "pleaseChooseAStrongPassword": "Bitte wähle ein starkes Passwort", + "addChatOrSubSpace": "Chat oder Sub-Space hinzufügen", + "leaveEmptyToClearStatus": "Leer lassen, um den Status zu löschen.", + "joinSpace": "Space beitreten", + "searchForUsers": "Suche nach @benutzer ...", + "initAppError": "Beim Starten der App ist ein Fehler aufgetreten", + "databaseBuildErrorBody": "Die SQlite-Datenbank kann nicht erstellt werden. Die App versucht vorerst, die Legacy-Datenbank zu verwenden. Bitte melde diesen Fehler an die Entwickler unter {url}. Die Fehlermeldung lautet: {error}", + "@databaseBuildErrorBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "sessionLostBody": "Die App versucht nun, deine Sitzung aus der Sicherung wiederherzustellen. Bitte melde diesen Fehler an die Entwickler unter {url}. Die Fehlermeldung lautet: {error}", + "@sessionLostBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "restoreSessionBody": "Die App versucht nun, deine Sitzung aus der Sicherung wiederherzustellen. Bitte melde diesen Fehler an die Entwickler unter {url}. Die Fehlermeldung lautet: {error}", + "@restoreSessionBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "youInvitedToBy": "📩 Du wurdest per Link eingeladen zu:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": { + "type": "String" + } + } + }, + "sendReadReceipts": "Lesebestätigungen senden", + "formattedMessages": "Formatierte Nachrichten", + "forwardMessageTo": "Nachricht weiterleiten an {roomName}?", + "@forwardMessageTo": { + "type": "String", + "placeholders": { + "roomName": { + "type": "String" + } + } + }, + "sendTypingNotificationsDescription": "Andere Teilnehmer in einem Chat können sehen, wenn du eine neue Nachricht tippst.", + "formattedMessagesDescription": "Formatierte Nachrichteninhalte wie fettgedruckten Text mit Markdown anzeigen.", + "verifyOtherUser": "🔐 Anderen Benutzer verifizieren", + "sendReadReceiptsDescription": "Andere Teilnehmer in einem Chat können sehen, ob du eine Nachricht gelesen hast.", + "transparent": "Transparent", + "verifyOtherDevice": "🔐 Anderes Gerät verifizieren", + "verifyOtherUserDescription": "Wenn du einen anderen Benutzer verifizierst, kannst du sicher sein, dass du weißt, an wen du wirklich schreibst. 💪\n\nWenn du eine Verifizierung startest, wird dir und dem anderen Nutzer ein Popup in der App angezeigt. Dort siehst du dann eine Reihe von Emojis oder Zahlen, die ihr miteinander vergleichen müsst.\n\nDas geht am besten, wenn man sich trifft oder einen Videoanruf startet. 👭", + "acceptedKeyVerification": "{sender} hat die Schlüsselverifikation akzeptiert", + "@acceptedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "canceledKeyVerification": "{sender} hat die Schlüsselverifikation abgebrochen", + "@canceledKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "completedKeyVerification": "{sender} hat die Schlüsselverifikation abgeschlossen", + "@completedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "isReadyForKeyVerification": "{sender} ist bereit für die Schlüsselverifikation", + "@isReadyForKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "requestedKeyVerification": "{sender} hat eine Schlüsselverifikation angefragt", + "@requestedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "startedKeyVerification": "{sender} hat die Schlüsselverifikation gestartet", + "@startedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "verifyOtherDeviceDescription": "Wenn du ein anderes Gerät verifizierst, können diese Geräte Schlüssel austauschen, was die Sicherheit insgesamt erhöht. 💪Sobald du eine Verifizierung startest, erscheint ein Pop-up in der App auf beiden Geräten. Dort siehst du dann eine Reihe von Emojis oder Zahlen, die du miteinander vergleichen musst. Am besten hältst du beide Geräte bereit, bevor du die Verifizierung startest. 🤳", + "presenceStyle": "Statusmeldungen:", + "@presenceStyle": { + "type": "String", + "placeholders": {} + }, + "presencesToggle": "Status-Nachrichten anderer Benutzer anzeigen", + "@presencesToggle": { + "type": "String", + "placeholders": {} + }, + "incomingMessages": "Eingehende Nachrichten", + "commandHint_unignore": "Angegebene Matrix-ID nicht mehr ignorieren", + "commandHint_ignore": "Angegebene Matrix-ID ignorieren", + "noDatabaseEncryption": "Datenbankverschlüsselung wird auf dieser Plattform nicht unterstützt", + "hidePresences": "Status-Liste verbergen?", + "stickers": "Sticker", + "discover": "Entdecken", + "unreadChatsInApp": "{appname}: {unread} ungelesene Chats", + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "customEmojisAndStickersBody": "Eigene Emojis oder Sticker zur Nutzung im Chat hinzufügen oder teilen.", + "globalChatId": "Globale Chat-ID", + "accessAndVisibility": "Zugang und Sichtbarkeit", + "hideMemberChangesInPublicChats": "Mitglieder-Änderungen in öffentlichen Chats ausblenden", + "accessAndVisibilityDescription": "Wer darf dem Chat beitreten und wie kann der Chat gefunden werden.", + "hideMemberChangesInPublicChatsBody": "Zeige keine Beitritt- oder Verlassen-Ereignisse von Mitgliedern in der Timeline an, um die Lesbarkeit in öffentlichen Chats zu verbessern.", + "userWouldLikeToChangeTheChat": "{user} würde dem Chat gerne beitreten.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "noPublicLinkHasBeenCreatedYet": "Es wurde noch kein öffentlicher Link erstellt", + "chatCanBeDiscoveredViaSearchOnServer": "Chat kann über die Suche auf {server} gefunden werden", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appLockDescription": "App mit einer PIN sperren, wenn sie nicht verwendet wird", + "calls": "Anrufe", + "customEmojisAndStickers": "Eigene Emojis und Sticker", + "hideRedactedMessages": "Geschwärzte Nachrichten verstecken", + "hideRedactedMessagesBody": "Wenn jemand eine Nachricht schwärzt/löscht, dann wird diese Nachricht im Chat nicht mehr sichtbar sein.", + "hideInvalidOrUnknownMessageFormats": "Ungültige und unbekannte Nachrichten-Formate ausblenden", + "overview": "Übersicht", + "notifyMeFor": "Benachrichtige mich für", + "passwordRecoverySettings": "Passwort-Wiederherstellungs-Einstellungen", + "knock": "Anklopfen", + "knocking": "Klopft", + "thereAreCountUsersBlocked": "Im Augenblick werden {count} Benutzer blockiert.", + "@thereAreCountUsersBlocked": { + "type": "String", + "count": {} + }, + "usersMustKnock": "Benutzer müssen anklopfen", + "noOneCanJoin": "Niemand kann beitreten", + "createNewAddress": "Neue Adresse erstellen", + "userRole": "Benutzerrolle", + "minimumPowerLevel": "{level} is das minimale Power-Level.", + "@minimumPowerLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "String" + } + } + }, + "publicChatAddresses": "Öffentliche Chat-Adressen", + "gallery": "Galerie", + "files": "Dateien", + "restricted": "Beschränkt", + "knockRestricted": "Anklopfen beschränkt", + "searchIn": "In Chat \"{chat}\" suchen ...", + "@searchIn": { + "type": "String", + "placeholders": { + "chat": { + "type": "String" + } + } + }, + "searchMore": "Weiter suchen ...", + "unread": "Ungelesen", + "noMoreChatsFound": "Keine weiteren Chats gefunden ...", + "joinedChats": "Beigetretene Chats", + "space": "Space", + "spaces": "Spaces", + "goToSpace": "Geh zum Space: {space}", + "@goToSpace": { + "type": "String", + "space": {} + }, + "markAsUnread": "Als ungelesen markieren", + "swipeRightToLeftToReply": "Wische von rechts nach links zum Antworten", + "countChatsAndCountParticipants": "{chats} Chats und {participants} Teilnehmer", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "changeGeneralChatSettings": "Allgemeine Chat-Einstellungen ändern", + "userLevel": "{level} - Benutzer", + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "moderatorLevel": "{level} - Moderator", + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "changeTheChatPermissions": "Ändere die Chat-Berechtigungen", + "changeTheVisibilityOfChatHistory": "Wechsele die Sichtbarkeit der Chat-Historie", + "chatPermissionsDescription": "Einstellen, welches Level für bestimmte Aktionen in diesem Chat erforderlich ist. Die Level 0, 50 und 100 stehen üblicherweise für Benutzer, Moderatoren und Admins, aber jede Abstufung ist möglich.", + "invitedBy": "📩 Eingeladen von {user}", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "adminLevel": "{level} - Administrator", + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "inviteOtherUsers": "Lade andere Benutzer in diesen Chat ein", + "changeTheCanonicalRoomAlias": "Ändern der Hauptadresse für den öffentlichen Chat", + "sendRoomNotifications": "Sende eine @room-Benachrichtigung", + "changeTheDescriptionOfTheGroup": "Chat-Beschreibung ändern", + "updateInstalled": "🎉 Update {version} installiert!", + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "changelog": "Änderungsprotokoll", + "sendCanceled": "Senden abgebrochen", + "noChatsFoundHere": "Hier wurden noch keine Chats gefunden. Starte einen neuen Chat mit jemandem, indem du die Schaltfläche unten verwenden. ⤵️", + "whatIsAHomeserver": "Was ist ein Homeserver?", + "doesNotSeemToBeAValidHomeserver": "Scheint kein kompatibler Homeserver zu sein. Falsche URL?", + "loginWithMatrixId": "Mit Matrix-ID anmelden", + "discoverHomeservers": "Server suchen", + "homeserverDescription": "Alle deine Daten werden auf einem Homeserver gespeichert, so wie bei einem E-Mail Anbieter. Du kannst aussuchen, welchen Homeserver du benutzen willst und kannst trotzdem mit allen kommunizieren. Erfahre mehr auf https://matrix.org.", + "sendingAttachment": "Anhang wird gesendet ...", + "generatingVideoThumbnail": "Generiere Video-Vorschaubild ...", + "serverLimitReached": "Server-Limit erreicht! Warte {seconds} Sekunden ...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "calculatingFileSize": "Dateigröße wird berechnet ...", + "prepareSendingAttachment": "Anhang zum Senden vorbereiten ...", + "compressVideo": "Video wird komprimiert ...", + "sendingAttachmentCountOfCount": "Sende Anhang {index} von {length} ...", + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } + } + }, + "fileIsTooBigForServer": "Kann nicht gesendet werden! Der Server unterstützt nur Anhänge bis höchstens {max}.", + "@fileIsTooBigForServer": { + "type": "String", + "placeholders": { + "max": { + "type": "String" + } + } + }, + "oneOfYourDevicesIsNotVerified": "Eines deiner Geräte ist nicht verifiziert", + "noticeChatBackupDeviceVerification": "Hinweis: Wenn du alle deine Geräte mit dem Chat-Backup verbindest, sind sie automatisch verifiziert.", + "setWallpaper": "Hintergrund ändern", + "opacity": "Deckkraft:", + "welcomeText": "Hey Hey 👋 Das ist FluffyChat. Du kannst sich bei jedem Homeserver anmelden, der mit https://matrix.org kompatibel ist. Und dann mit jedem chatten. Das hier ist ein riesiges dezentrales Nachrichtennetzwerk!", + "blur": "Verwischen:", + "manageAccount": "Konto verwalten", + "continueText": "Fortfahren", + "noContactInformationProvided": "Der Server stellt keine gültigen Kontaktinformationen bereit", + "contactServerAdmin": "Serveradministrator kontaktieren", + "name": "Name", + "version": "Version", + "website": "Website", + "aboutHomeserver": "Über {homeserver}", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "boldText": "Fetter Text", + "invalidUrl": "Ungültige URL", + "addLink": "Link hinzufügen", + "unableToJoinChat": "Chat kann nicht beigetreten werden. Möglicherweise hat die Gegenseite das Gespräch bereits beendet.", + "italicText": "Kursiver Text", + "strikeThrough": "Durchgestrichen", + "pleaseFillOut": "Bitte ausfüllen", + "sendImages": "Sende {count} Bilder", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "contactServerSecurity": "Server-Sicherheit kontaktieren", + "compress": "Komprimieren", + "supportPage": "Support-Seite", + "serverInformation": "Server-Informationen:", + "appIntroduction": "Mit FluffyChat kannst du über verschiedene Messenger hinweg mit deinen Freunden chatten. Erfahre mehr dazu auf https://matrix.org oder tippe einfach auf *Fortfahren*.", + "newChatRequest": "📩 Neue Chat-Anfrage", + "synchronizingPleaseWaitCounter": " Synchronisierung… ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "waitingForServer": "Auf Server warten...", + "previous": "Vorige", + "otherPartyNotLoggedIn": "Der User ist aktuell nicht eingeloggt und kann daher keine Nachrichten empfangen!", + "appWantsToUseForLogin": "Nutze '{server}' um dich einzuloggen", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appWantsToUseForLoginDescription": "Hiermit erlaubst du der App und der Website, Informationen über dich weiterzugeben.", + "open": "Offen", + "notificationRuleContainsUserName": "Enthält Benutzernamen", + "notificationRuleContainsUserNameDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht seinen Benutzernamen enthält.", + "notificationRuleMaster": "Alle Benachrichtigungen stummschalten", + "notificationRuleSuppressNotices": "Automatisierte Nachrichten unterdrücken", + "notificationRuleMasterDescription": "Setzt alle anderen Regeln außer Kraft und deaktiviert alle Benachrichtigungen.", + "generalNotificationSettings": "Allgemeine Benachrichtigungseinstellungen", + "otherNotificationSettings": "Andere Benachrichtigungseinstellungen", + "contentNotificationSettings": "Einstellungen für Inhaltsbenachrichtigungen", + "userSpecificNotificationSettings": "Benutzerspezifische Benachrichtigungseinstellungen", + "roomNotificationSettings": "Einstellungen für Raumbenachrichtigungen", + "notificationRuleSuppressNoticesDescription": "Unterdrückt Benachrichtigungen von automatisierten Clients wie Bots.", + "notificationRuleInviteForMe": "Einladung für mich", + "notificationRuleReaction": "Reaktion", + "notificationRuleReactionDescription": "Unterdrückt Benachrichtigungen für Reaktionen.", + "notificationRuleSuppressEditsDescription": "Unterdrückt Benachrichtigungen für bearbeitete Nachrichten.", + "notificationRuleCall": "Anruf", + "notificationRuleCallDescription": "Benachrichtigt den Benutzer über Anrufe.", + "notificationRuleEncrypted": "Verschlüsselt", + "more": "Mehr", + "notificationRuleSuppressEdits": "Unterdrückt Bearbeitungen", + "notificationRuleRoomServerAclDescription": "Unterdrückt Benachrichtigungen für Raumserver-Zugriffskontrolllisten (ACL).", + "notificationRuleMessage": "Nachricht", + "notificationRuleMessageDescription": "Informiert den Benutzer über allgemeine Nachrichten.", + "notificationRuleJitsi": "Jitsi", + "allDevices": "Alle Geräte", + "enterNewChat": "Neuen Chat betreten", + "shareKeysWith": "Schlüssel teilen mit...", + "shareKeysWithDescription": "Welchen Geräten sollte vertraut werden, damit sie deine Nachrichten in verschlüsselten Chats mitlesen können?", + "verifiedDevicesOnly": "Nur verifizierte Geräte", + "takeAPhoto": "Foto aufnehmen", + "recordAVideo": "Video aufnehmen", + "optionalMessage": "(Optionale) Nachricht...", + "notSupportedOnThisDevice": "Nicht unterstützt auf diesem Gerät", + "ignoreUser": "Nutzer ignorieren", + "notificationRuleEncryptedRoomOneToOneDescription": "Benachrichtigt den Benutzer über Nachrichten in verschlüsselten Eins-zu-Eins-Chats.", + "commandHint_roomupgrade": "Aktualisieren Sie diesen Raum auf die angegebene Raumversion", + "notificationRuleMemberEvent": "Mitgliederveranstaltung", + "notificationRuleInviteForMeDescription": "Benachrichtigt den Benutzer, wenn er in einen Raum eingeladen wird.", + "notificationRuleIsUserMentionDescription": "Benachrichtigt den Benutzer, wenn er in einer Nachricht direkt erwähnt wird.", + "notificationRuleRoomnotifDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht „@room“ enthält.", + "notificationRuleRoomOneToOneDescription": "Benachrichtigt den Benutzer über Nachrichten in Einzelchats.", + "notificationRuleEncryptedDescription": "Benachrichtigt den Benutzer über Nachrichten in verschlüsselten Räumen.", + "notificationRuleJitsiDescription": "Benachrichtigt den Benutzer über Jitsi-Widget-Ereignisse.", + "checkList": "Checkliste", + "countInvited": "{count} invited", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notificationRuleIsUserMention": "Benutzererwähnung", + "notificationRuleContainsDisplayName": "Enthält den Anzeigenamen", + "notificationRuleContainsDisplayNameDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht seinen Anzeigenamen enthält.", + "notificationRuleIsRoomMention": "Chat-Erwähnung", + "notificationRuleRoomnotif": "Chat-Benachritigung", + "notificationRuleTombstoneDescription": "Benachrichtigt den Benutzer über Nachrichten zur Raumdeaktivierung.", + "notificationRuleEncryptedRoomOneToOne": "Verschlüsselter Einzelchat", + "notificationRuleRoomOneToOne": "Einzelchat", + "notificationRuleServerAclDescription": "Unterdrückt Benachrichtigungen für Server-ACL-Ereignisse.", + "unknownPushRule": "Unbekannte Push-Regel '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "Wenn Sie diese Benachrichtigungseinstellung löschen, kann dies nicht rückgängig gemacht werden.", + "crossVerifiedDevices": "Cross-verifizierte Geräte", + "notificationRuleIsRoomMentionDescription": "Benachrichtigt den Benutzer, wenn ein Raum erwähnt wird.", + "notificationRuleRoomServerAcl": "Raumserver-ACL", + "crossVerifiedDevicesIfEnabled": "Cross-verifizierte Geräte, falls aktiviert", + "notificationRuleServerAcl": "Unterdrücken von Server-ACL-Ereignissen", + "notificationRuleMemberEventDescription": "Unterdrückt Benachrichtigungen zu Mitgliedschaftsereignissen.", + "sentVoiceMessage": "🎙️ {duration} - Sprachnachricht von {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "normalUser": "Normaler Benutzer", + "setCustomPermissionLevel": "Benutzerdefinierte Berechtigungsstufe festlegen", + "setPermissionsLevelDescription": "Bitte wählen Sie unten eine vordefinierte Rolle aus oder geben Sie eine benutzerdefinierte Berechtigungsstufe zwischen 0 und 100 ein.", + "approve": "Genehmigen", + "youHaveKnocked": "Du hast geklopft", + "pleaseWaitUntilInvited": "Bitte warte nun, bis dich jemand aus dem Raum auffordert.", + "notificationRuleTombstone": "Tombstone", + "commandHint_logout": "Aktuelles Gerät abmelden", + "commandHint_logoutall": "Alle aktiven Geräte abmelden", + "displayNavigationRail": "Navigationsleiste auf dem Smartphone anzeigen", + "customReaction": "Benutzerdefinierte Reaktion", + "moreEvents": "Weitere Ereignisse", + "declineInvitation": "Einladung ablehnen", + "noMessagesYet": "Noch keine Nachrichten", + "longPressToRecordVoiceMessage": "Lange drücken, um eine Sprachnachricht aufzunehmen.", + "pause": "Pause", + "newSubSpace": "Neuer Sub-Space", + "moveToDifferentSpace": "In einen anderen space wechseln", + "moveUp": "Nach oben", + "moveDown": "Nach unten", + "removeFromSpaceDescription": "Der Chat wird aus dem Space entfernt, erscheint aber weiterhin in Ihrer Chatliste.", + "countChats": "{chats} Chats", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Space-Mitglieder von {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Space-Mitglieder von {spaces} kann klopfen", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Spenden", + "resume": "Fortsetzen", + "startedAPoll": "{username} hat eine Umfrage gestartet.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Umfrage", + "startPoll": "Umfrage starten", + "endPoll": "Umfrage beenden", + "answersVisible": "Antworten sichtbar", + "answersHidden": "Antworten sind verborgen", + "pollQuestion": "Frage", + "answerOption": "Antwortmöglichkeit", + "addAnswerOption": "Antwortoption hinzufügen", + "allowMultipleAnswers": "Mehrere Antworten zulassen", + "pollHasBeenEnded": "Umfrage ist beendet", + "countVotes": "{count, plural, =1{Eine Stimme} other{{count} Stimmen}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Die Antworten werden nach Ende der Umfrage sichtbar sein", + "replyInThread": "Im Thread antworten", + "countReplies": "{count, plural, =1{Eine Antwort} other{{count} Antworten}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Thread", + "backToMainChat": "Zurück zum Hauptchat", + "changedTheChatDescription": "{username} hat die Chatbeschreibung geändert", + "changedTheChatName": "{username} den Chatnamen geändert", + "saveChanges": "Änderungen speichern", + "skipChatBackup": "Chatsicherung überspringen", + "skipChatBackupWarning": "Bist du sicher? Ohne die Chatsicherung zu aktivieren, kannst du den Zugriff auf deine Nachrichten verlieren, wenn du dein Gerät wechselst.", + "loadingMessages": "Nachrichten werden geladen", + "setupChatBackup": "Chatsicherung einrichten", + "createSticker": "Sticker oder Emoji erstellen", + "useAsSticker": "Als Sticker verwenden", + "useAsEmoji": "Als Emoji verwenden", + "stickerPackNameAlreadyExists": "Name des Sticker-Pakets existiert bereits", + "newStickerPack": "Neues Sticker-Paket", + "stickerPackName": "Name des Sticker-Pakets", + "noMoreResultsFound": "Keine weiteren Ergebnisse gefunden", + "chatSearchedUntil": "Chat durchsucht bis {time}", + "@chatSearchedUntil": { + "type": "String", + "placeholders": { + "time": { + "type": "String" + } + } + }, + "attribution": "Attribuierung", + "identityServer": "Identitätsserver:", + "versionWithNumber": "Version: {version}", + "@versionWithNumber": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "logs": "Protokolle", + "baseUrl": "Basis-URL", + "advancedConfigs": "Erweiterte Konfigurationen", + "advancedConfigurations": "Erweiterte Konfigurationen", + "@clearArchive": {}, + "@scanQrCode": {}, + "@chatHasBeenAddedToThisSpace": {}, + "@addToSpace": {}, + "@serverRequiresEmail": {}, + "@enableMultiAccounts": {}, + "@bundleName": {}, + "@removeFromBundle": {}, + "@addToBundle": {}, + "@editBundlesForAccount": {}, + "@addAccount": {}, + "@oneClientLoggedOut": {}, + "@homeserver": {}, + "@sendOnEnter": {}, + "@link": {}, + "@yourChatBackupHasBeenSetUp": {}, + "@unverified": {}, + "@messageInfo": {}, + "@time": {}, + "@messageType": {}, + "@sender": {}, + "@openGallery": {}, + "@removeFromSpace": {}, + "@addToSpaceDescription": {}, + "@start": {}, + "@repeatPassword": {}, + "@publish": {}, + "@pinMessage": {}, + "@emojis": {}, + "@placeCall": {}, + "@voiceCall": {}, + "@unsupportedAndroidVersion": {}, + "@videoCallsBetaWarning": {}, + "@emailOrUsername": {}, + "@unsupportedAndroidVersionLong": {}, + "@experimentalVideoCalls": {}, + "@markAsRead": {}, + "@reportUser": {}, + "@openChat": {}, + "@confirmEventUnpin": {}, + "@dismiss": {}, + "@nextAccount": {}, + "@widgetJitsi": {}, + "@widgetCustom": {}, + "@widgetEtherpad": {}, + "@addWidget": {}, + "@widgetVideo": {}, + "@widgetName": {}, + "@widgetUrlError": {}, + "@errorAddingWidget": {}, + "@previousAccount": {}, + "@widgetNameError": {}, + "@youRejectedTheInvitation": {}, + "@youJoinedTheChat": {}, + "@youAcceptedTheInvitation": {}, + "@recoveryKey": {}, + "@recoveryKeyLost": {}, + "@user": {}, + "@custom": {}, + "@storeInAndroidKeystore": {}, + "@storeSecurlyOnThisDevice": {}, + "@dehydrate": {}, + "@dehydrateWarning": {}, + "@dehydrateTor": {}, + "@dehydrateTorLong": {}, + "@hydrateTor": {}, + "@hydrate": {}, + "@indexedDbErrorTitle": {}, + "@unlockOldMessages": {}, + "@pleaseEnterRecoveryKeyDescription": {}, + "@saveKeyManuallyDescription": {}, + "@hydrateTorLong": {}, + "@pleaseEnterRecoveryKey": {}, + "@users": {}, + "@storeInSecureStorageDescription": {}, + "@storeInAppleKeyChain": {}, + "@indexedDbErrorLong": {}, + "@confirmMatrixId": {}, + "@commandHint_markasdm": {}, + "@commandHint_markasgroup": {}, + "@hideUnimportantStateEvents": {}, + "@doNotShowAgain": {}, + "@appearOnTopDetails": {}, + "@noKeyForThisMessage": {}, + "@foregroundServiceRunning": {}, + "@screenSharingTitle": {}, + "@callingPermissions": {}, + "@callingAccount": {}, + "@callingAccountDetails": {}, + "@appearOnTop": {}, + "@otherCallingPermissions": {}, + "@whyIsThisMessageEncrypted": {}, + "@newGroup": {}, + "@newSpace": {}, + "@enterSpace": {}, + "@enterRoom": {}, + "@allSpaces": {}, + "@screenSharingDetail": {}, + "@newSpaceDescription": {}, + "@encryptThisChat": {}, + "@startFirstChat": {}, + "@deviceKeys": {}, + "@commandHint_cuddle": {}, + "@commandHint_hug": {}, + "@sorryThatsNotPossible": {}, + "@commandHint_googly": {}, + "@disableEncryptionWarning": {}, + "@reopenChat": {}, + "@noBackupWarning": {}, + "@noOtherDevicesFound": {}, + "@jumpToLastReadMessage": {}, + "@readUpToHere": {}, + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "@jump": {}, + "@openLinkInBrowser": {}, + "@reportErrorDescription": {}, + "@report": {}, + "@signInWithPassword": {}, + "@importNow": {}, + "@importEmojis": {}, + "@importFromZipFile": {}, + "@exportEmotePack": {}, + "@notAnImage": {}, + "@replace": {}, + "@sendTypingNotifications": {}, + "@profileNotFound": {}, + "@createGroup": {}, + "@shareInviteLink": {}, + "@inviteContactToGroupQuestion": {}, + "@tryAgain": {}, + "@redactMessageDescription": {}, + "@setTheme": {}, + "@setColorTheme": {}, + "@invite": {}, + "@optionalRedactReason": {}, + "@messagesStyle": {}, + "@chatPermissions": {}, + "@chatDescription": {}, + "@chatDescriptionHasBeenChanged": {}, + "@noChatDescriptionYet": {}, + "@invalidServerName": {}, + "@directChat": {}, + "@addChatDescription": {}, + "@setChatDescription": {}, + "@inviteGroupChat": {}, + "@invitePrivateChat": {}, + "@invalidInput": {}, + "@pleaseEnterANumber": {}, + "@banUserDescription": {}, + "@removeDevicesDescription": {}, + "@unbanUserDescription": {}, + "@pushNotificationsNotAvailable": {}, + "@makeAdminDescription": {}, + "@archiveRoomDescription": {}, + "@learnMore": {}, + "@roomUpgradeDescription": {}, + "@kickUserDescription": {}, + "@blockListDescription": {}, + "@createGroupAndInviteUsers": {}, + "@startConversation": {}, + "@blockedUsers": {}, + "@groupCanBeFoundViaSearch": {}, + "@block": {}, + "@yourGlobalUserIdIs": {}, + "@commandHint_sendraw": {}, + "@wrongRecoveryKey": {}, + "@blockUsername": {}, + "@groupName": {}, + "@searchChatsRooms": {}, + "@databaseMigrationTitle": {}, + "@databaseMigrationBody": {}, + "@thisDevice": {}, + "@publicSpaces": {}, + "@passwordIsWrong": {}, + "@pleaseEnterYourCurrentPassword": {}, + "@publicLink": {}, + "@nothingFound": {}, + "@decline": {}, + "@newPassword": {}, + "@passwordsDoNotMatch": {}, + "@subspace": {}, + "@select": {}, + "@pleaseChooseAStrongPassword": {}, + "@addChatOrSubSpace": {}, + "@leaveEmptyToClearStatus": {}, + "@joinSpace": {}, + "@searchForUsers": {}, + "@initAppError": {}, + "@sendReadReceipts": {}, + "@formattedMessages": {}, + "@sendTypingNotificationsDescription": {}, + "@formattedMessagesDescription": {}, + "@verifyOtherUser": {}, + "@sendReadReceiptsDescription": {}, + "@transparent": {}, + "@verifyOtherDevice": {}, + "@verifyOtherUserDescription": {}, + "@verifyOtherDeviceDescription": {}, + "@incomingMessages": {}, + "@commandHint_unignore": {}, + "@commandHint_ignore": {}, + "@noDatabaseEncryption": {}, + "@hidePresences": {}, + "@stickers": {}, + "@discover": {}, + "@customEmojisAndStickersBody": {}, + "@globalChatId": {}, + "@accessAndVisibility": {}, + "@hideMemberChangesInPublicChats": {}, + "@accessAndVisibilityDescription": {}, + "@hideMemberChangesInPublicChatsBody": {}, + "@noPublicLinkHasBeenCreatedYet": {}, + "@appLockDescription": {}, + "@calls": {}, + "@customEmojisAndStickers": {}, + "@hideRedactedMessages": {}, + "@hideRedactedMessagesBody": {}, + "@hideInvalidOrUnknownMessageFormats": {}, + "@overview": {}, + "@notifyMeFor": {}, + "@passwordRecoverySettings": {}, + "@knock": {}, + "@knocking": {}, + "@usersMustKnock": {}, + "@noOneCanJoin": {}, + "@createNewAddress": {}, + "@userRole": {}, + "@publicChatAddresses": {}, + "@gallery": {}, + "@files": {}, + "@restricted": {}, + "@knockRestricted": {}, + "@searchMore": {}, + "@unread": {}, + "@noMoreChatsFound": {}, + "@joinedChats": {}, + "@space": {}, + "@spaces": {}, + "@markAsUnread": {}, + "@swipeRightToLeftToReply": {}, + "@changeGeneralChatSettings": {}, + "@changeTheChatPermissions": {}, + "@changeTheVisibilityOfChatHistory": {}, + "@chatPermissionsDescription": {}, + "@inviteOtherUsers": {}, + "@changeTheCanonicalRoomAlias": {}, + "@sendRoomNotifications": {}, + "@changeTheDescriptionOfTheGroup": {}, + "@changelog": {}, + "@sendCanceled": {}, + "@noChatsFoundHere": {}, + "@whatIsAHomeserver": {}, + "@doesNotSeemToBeAValidHomeserver": {}, + "@loginWithMatrixId": {}, + "@discoverHomeservers": {}, + "@homeserverDescription": {}, + "@sendingAttachment": {}, + "@generatingVideoThumbnail": {}, + "@calculatingFileSize": {}, + "@prepareSendingAttachment": {}, + "@compressVideo": {}, + "@oneOfYourDevicesIsNotVerified": {}, + "@noticeChatBackupDeviceVerification": {}, + "@setWallpaper": {}, + "@opacity": {}, + "@welcomeText": {}, + "@blur": {}, + "@manageAccount": {}, + "@continueText": {}, + "@noContactInformationProvided": {}, + "@contactServerAdmin": {}, + "@name": {}, + "@version": {}, + "@website": {}, + "@boldText": {}, + "@invalidUrl": {}, + "@addLink": {}, + "@unableToJoinChat": {}, + "@italicText": {}, + "@strikeThrough": {}, + "@pleaseFillOut": {}, + "@contactServerSecurity": {}, + "@compress": {}, + "@supportPage": {}, + "@serverInformation": {}, + "@appIntroduction": {}, + "@newChatRequest": {}, + "@waitingForServer": {}, + "@previous": {}, + "@otherPartyNotLoggedIn": {}, + "@appWantsToUseForLoginDescription": {}, + "@open": {}, + "@notificationRuleContainsUserName": {}, + "@notificationRuleContainsUserNameDescription": {}, + "@notificationRuleMaster": {}, + "@notificationRuleSuppressNotices": {}, + "@notificationRuleMasterDescription": {}, + "@generalNotificationSettings": {}, + "@otherNotificationSettings": {}, + "@contentNotificationSettings": {}, + "@userSpecificNotificationSettings": {}, + "@roomNotificationSettings": {}, + "@notificationRuleSuppressNoticesDescription": {}, + "@notificationRuleInviteForMe": {}, + "@notificationRuleReaction": {}, + "@notificationRuleReactionDescription": {}, + "@notificationRuleSuppressEditsDescription": {}, + "@notificationRuleCall": {}, + "@notificationRuleCallDescription": {}, + "@notificationRuleEncrypted": {}, + "@more": {}, + "@notificationRuleSuppressEdits": {}, + "@notificationRuleRoomServerAclDescription": {}, + "@notificationRuleMessage": {}, + "@notificationRuleMessageDescription": {}, + "@notificationRuleJitsi": {}, + "@allDevices": {}, + "@enterNewChat": {}, + "@shareKeysWith": {}, + "@shareKeysWithDescription": {}, + "@verifiedDevicesOnly": {}, + "@takeAPhoto": {}, + "@recordAVideo": {}, + "@optionalMessage": {}, + "@notSupportedOnThisDevice": {}, + "@ignoreUser": {}, + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "@commandHint_roomupgrade": {}, + "@notificationRuleMemberEvent": {}, + "@notificationRuleInviteForMeDescription": {}, + "@notificationRuleIsUserMentionDescription": {}, + "@notificationRuleRoomnotifDescription": {}, + "@notificationRuleRoomOneToOneDescription": {}, + "@notificationRuleEncryptedDescription": {}, + "@notificationRuleJitsiDescription": {}, + "@checkList": {}, + "@notificationRuleIsUserMention": {}, + "@notificationRuleContainsDisplayName": {}, + "@notificationRuleContainsDisplayNameDescription": {}, + "@notificationRuleIsRoomMention": {}, + "@notificationRuleRoomnotif": {}, + "@notificationRuleTombstoneDescription": {}, + "@notificationRuleEncryptedRoomOneToOne": {}, + "@notificationRuleRoomOneToOne": {}, + "@notificationRuleServerAclDescription": {}, + "@deletePushRuleCanNotBeUndone": {}, + "@crossVerifiedDevices": {}, + "@notificationRuleIsRoomMentionDescription": {}, + "@notificationRuleRoomServerAcl": {}, + "@crossVerifiedDevicesIfEnabled": {}, + "@notificationRuleServerAcl": {}, + "@notificationRuleMemberEventDescription": {}, + "@normalUser": {}, + "@setCustomPermissionLevel": {}, + "@setPermissionsLevelDescription": {}, + "@approve": {}, + "@youHaveKnocked": {}, + "@pleaseWaitUntilInvited": {}, + "@notificationRuleTombstone": {}, + "@commandHint_logout": {}, + "@commandHint_logoutall": {}, + "@displayNavigationRail": {}, + "@customReaction": {}, + "@moreEvents": {}, + "@declineInvitation": {}, + "@noMessagesYet": {}, + "@longPressToRecordVoiceMessage": {}, + "@pause": {}, + "@newSubSpace": {}, + "@moveToDifferentSpace": {}, + "@moveUp": {}, + "@moveDown": {}, + "@removeFromSpaceDescription": {}, + "@donate": {}, + "@resume": {}, + "@poll": {}, + "@startPoll": {}, + "@endPoll": {}, + "@answersVisible": {}, + "@answersHidden": {}, + "@pollQuestion": {}, + "@answerOption": {}, + "@addAnswerOption": {}, + "@allowMultipleAnswers": {}, + "@pollHasBeenEnded": {}, + "@answersWillBeVisibleWhenPollHasEnded": {}, + "@replyInThread": {}, + "@thread": {}, + "@backToMainChat": {}, + "@changedTheChatDescription": {}, + "@changedTheChatName": {}, + "@saveChanges": {}, + "@skipChatBackup": {}, + "@skipChatBackupWarning": {}, + "@loadingMessages": {}, + "@setupChatBackup": {}, + "@createSticker": {}, + "@useAsSticker": {}, + "@useAsEmoji": {}, + "@stickerPackNameAlreadyExists": {}, + "@newStickerPack": {}, + "@stickerPackName": {}, + "@noMoreResultsFound": {}, + "writeAMessageLangCodes": "Geben Sie in {l1} oder {l2} ein...", + "requests": "Anfragen", + "holdForInfo": "Klicken und halten für Wortinformationen.", + "greenFeedback": "Das würde ich eintragen!", + "yellowFeedback": "Hm, du kannst es versuchen und sehen, ob es funktioniert! Um dieses Wort zu verwenden, klicke es einfach erneut.", + "redFeedback": "Ich glaube nicht, dass das richtig ist...", + "itInstructionsTitle": "Ich kann dir beim Übersetzen helfen!", + "itInstructionsBody": "Sie können durch Tippen und Halten Informationen zum Wort erhalten.", + "gaTooltip": "L2 Verwendung mit Grammatikhilfe", + "taTooltip": "L2 Verwendung mit Übersetzungshilfe", + "unTooltip": "Andere", + "interactiveTranslatorSliderHeader": "Interaktiver Übersetzer", + "interactiveGrammarSliderHeader": "Interaktiver Grammatikprüfer", + "interactiveTranslatorNotAllowed": "Deaktiviert", + "interactiveTranslatorAllowed": "Schülerwahl", + "interactiveTranslatorRequired": "Erforderlich", + "notYetSet": "Noch nicht festgelegt", + "waTooltip": "L2 Verwendung ohne Hilfe", + "languageSettings": "Spracheinstellungen", + "interactiveTranslator": "Übersetzungshilfe", + "noIdenticalLanguages": "Bitte wählen Sie unterschiedliche Ausgangs- und Zielsprache", + "searchBy": "Suche nach Land und Sprachen", + "joinWithClassCode": "Kurs beitreten", + "languageLevelPreA1": "Anfänger Niedrig (Pre A1)", + "languageLevelA1": "Novize Mitte (A1)", + "languageLevelA2": "Anfänger Hoch (A2)", + "languageLevelB1": "Mittelstufe (B1)", + "languageLevelB2": "Fortgeschritten Niedrig (B2)", + "languageLevelC1": "Fortgeschritten Mittel (C1)", + "languageLevelC2": "Überlegen (C2)", + "changeTheNameOfTheClass": "Namen ändern", + "changeTheNameOfTheChat": "Den Namen des Chats ändern", + "sorryNoResults": "Entschuldigung, keine Ergebnisse.", + "ignoreInThisText": "Ignorieren", + "needsItMessage": "Warte, das ist nicht {targetLanguage}! Bist du bei der Übersetzung Hilfe nötig?", + "countryInformation": "Mein Land", + "targetLanguage": "Zielsprache", + "sourceLanguage": "Ausgangssprache", + "updateLanguage": "Meine Sprachen", + "whatLanguageYouWantToLearn": "Welche Sprache möchtest du lernen?", + "whatIsYourBaseLanguage": "Was ist deine Ausgangssprache?", + "publicProfileTitle": "Erlauben Sie, dass mein Profil in der Suche gefunden wird", + "publicProfileDesc": "Wenn Sie es einschalten, können andere Benutzer Ihr Profil in der globalen Suchleiste finden und Chat-Anfragen senden. An diesem Punkt können Sie die Anfrage akzeptieren oder ablehnen.", + "errorDisableIT": "Übersetzungshilfe ist deaktiviert.", + "errorDisableIGC": "Grammatikhilfe ist deaktiviert.", + "errorDisableLanguageAssistance": "Übersetzungs- und Grammatikhilfe sind deaktiviert.", + "errorDisableITUserDesc": "Klicken Sie hier, um die Einstellungen für Übersetzungshilfe zu aktualisieren", + "errorDisableIGCUserDesc": "Klicken Sie hier, um die Einstellungen für Grammatikhilfe zu aktualisieren", + "errorDisableLanguageAssistanceUserDesc": "Klicken Sie hier, um die Einstellungen für Übersetzungs- und Grammatikhilfe zu aktualisieren", + "errorDisableITClassDesc": "Die Übersetzungshilfe ist für den Kurs, in dem sich dieser Chat befindet, deaktiviert.", + "errorDisableIGCClassDesc": "Die Grammatikhilfe ist für den Kurs, in dem sich dieser Chat befindet, deaktiviert.", + "error405Title": "Sprachen nicht eingestellt", + "error405Desc": "Bitte stellen Sie Ihre Sprachen im Hauptmenü > Lerneinstellungen ein.", + "termsAndConditions": "Allgemeinen Geschäftsbedingungen", + "andCertifyIAmAtLeast13YearsOfAge": " zu und bestätigen, dass ich mindestens 16 Jahre alt bin.", + "error502504Title": "Wow, es sind viele Schüler online!", + "error502504Desc": "Übersetzungs- und Grammatiktools könnten langsam sein oder nicht verfügbar sein, während die Pangea-Bots aufholen.", + "error404Title": "Übersetzungsfehler!", + "error404Desc": "Der Pangea-Bot ist sich nicht sicher, wie das zu übersetzen ist...", + "errorPleaseRefresh": "Wir kümmern uns darum! Bitte laden Sie die Seite neu und versuchen Sie es erneut.", + "connectedToStaging": "Mit Staging verbunden", + "learningSettings": "Lerneinstellungen", + "participants": "Teilnehmer", + "clickMessageTitle": "Brauchen Sie Hilfe?", + "clickMessageBody": "Klicken Sie auf eine Nachricht für Sprachtools wie Übersetzung, Wiedergabe und mehr!", + "allDone": "Alles erledigt!", + "vocab": "Vokabular", + "low": "Wir haben Hinweise darauf, dass der Benutzer diese Wörter nicht versteht.", + "medium": "Diese Wörter wurden verwendet. Es ist unklar, ob die Wörter vollständig verstanden wurden oder nicht.", + "high": "Wir haben Hinweise darauf, dass der Benutzer diese Wörter versteht.", + "subscribe": "Abonnieren", + "getAccess": "Jetzt abonnieren!", + "subscriptionDesc": "Messaging ist kostenlos! Abonnieren Sie, um interaktive Übersetzungen, Grammatikprüfung und Lernanalysen freizuschalten.", + "subscriptionManagement": "Abonnementverwaltung", + "currentSubscription": "Aktuelles Abonnement", + "cancelSubscription": "Abonnement kündigen", + "selectYourPlan": "Wählen Sie Ihren Plan", + "subsciptionPlatformTooltip": "Bitte melden Sie sich auf Ihrem ursprünglichen Gerät an, um Ihren Abonnementplan zu verwalten", + "subscriptionManagementUnavailable": "Abonnementverwaltung nicht verfügbar", + "paymentMethod": "Zahlungsmethode", + "paymentHistory": "Zahlungsverlauf", + "emptyChatDownloadWarning": "Leeren Chat können nicht heruntergeladen werden", + "update": "Aktualisieren", + "toggleImmersionMode": "Immersionsmodus", + "toggleImmersionModeDesc": "Wenn aktiviert, werden alle Nachrichten in Ihrer Zielsprache angezeigt. Diese Einstellung ist in Sprachaustauschen am nützlichsten.", + "itToggleDescription": "Dieses Werkzeug zum Sprachenlernen erkennt Wörter in Ihrer Ausgangssprache und hilft Ihnen, sie in Ihre Zielsprache zu übersetzen. Obwohl selten, kann die KI Übersetzungsfehler machen.", + "igcToggleDescription": "Dieses Werkzeug zum Sprachenlernen erkennt häufige Rechtschreib-, Grammatik- und Interpunktionsfehler in Ihrer Nachricht und schlägt Korrekturen vor. Obwohl selten, kann die KI Korrekturfehler machen.", + "originalMessage": "Originalnachricht", + "sentMessage": "Gesendete Nachricht", + "useType": "Verwendungstyp", + "notAvailable": "Nicht verfügbar", + "taAndGaTooltip": "L2-Nutzung mit Übersetzungs- und Grammatikhilfe", + "definitionsToolName": "Wortdefinitionen", + "messageTranslationsToolName": "Nachrichtübersetzungen", + "definitionsToolDescription": "Wenn aktiviert, können Wörter, die blau unterstrichen sind, angeklickt werden, um Definitionen anzuzeigen. Klicken Sie auf Nachrichten, um Definitionen zu erhalten.", + "translationsToolDescrption": "Wenn aktiviert, klicken Sie auf eine Nachricht und auf das Übersetzungssymbol, um eine Nachricht in Ihrer Ausgangssprache zu sehen.", + "welcomeBack": "Willkommen zurück! Wenn Sie Teil des Pilotprojekts 2023-2024 waren, kontaktieren Sie uns bitte für Ihr spezielles Pilotabonnement. Wenn Sie Lehrer sind, der (oder dessen Institution) Lizenzen für Ihre Klasse gekauft hat, kontaktieren Sie uns für Ihr Lehrerausweis.", + "downloadTxtFile": "Textdatei herunterladen", + "downloadCSVFile": "CSV-Datei herunterladen", + "promotionalSubscriptionDesc": "Sie haben derzeit ein lebenslanges Aktionsabonnement. Kontaktieren Sie support@pangea.chat, um Hilfe bei der Änderung Ihres Abonnements zu erhalten.", + "originalSubscriptionPlatform": "Abonnement gekauft über {purchasePlatform}", + "oneWeekTrial": "Einwöchige Testversion", + "downloadXLSXFile": "Excel-Datei herunterladen", + "unkDisplayName": "Unbekannt", + "wwCountryDisplayName": "Weltweit", + "afCountryDisplayName": "Afghanistan", + "axCountryDisplayName": "Alandinseln", + "alCountryDisplayName": "Albanien", + "dzCountryDisplayName": "Algerien", + "asCountryDisplayName": "Amerikanisch-Samoa", + "adCountryDisplayName": "Andorra", + "aoCountryDisplayName": "Angola", + "aiCountryDisplayName": "Anguilla", + "agCountryDisplayName": "Antigua und Barbuda", + "arCountryDisplayName": "Argentinien", + "amCountryDisplayName": "Armenien", + "awCountryDisplayName": "Aruba", + "acCountryDisplayName": "Ascension-Insel", + "auCountryDisplayName": "Australien", + "atCountryDisplayName": "Österreich", + "azCountryDisplayName": "Aserbaidschan", + "bsCountryDisplayName": "Bahamas", + "bhCountryDisplayName": "Bahrain", + "bdCountryDisplayName": "Bangladesch", + "bbCountryDisplayName": "Barbados", + "byCountryDisplayName": "Weißrussland", + "beCountryDisplayName": "Belgien", + "bzCountryDisplayName": "Belize", + "bjCountryDisplayName": "Benin", + "bmCountryDisplayName": "Bermuda", + "btCountryDisplayName": "Bhutan", + "boCountryDisplayName": "Bolivien", + "baCountryDisplayName": "Bosnien und Herzegowina", + "bwCountryDisplayName": "Botswana", + "brCountryDisplayName": "Brasilien", + "ioCountryDisplayName": "Britisches Territorium im Indischen Ozean", + "vgCountryDisplayName": "Britische Jungferninseln", + "bnCountryDisplayName": "Brunei", + "bgCountryDisplayName": "Bulgarien", + "bfCountryDisplayName": "Burkina Faso", + "biCountryDisplayName": "Burundi", + "khCountryDisplayName": "Kambodscha", + "cmCountryDisplayName": "Kamerun", + "caCountryDisplayName": "Kanada", + "cvCountryDisplayName": "Kap Verde", + "bqCountryDisplayName": "Karibisches Niederlande", + "kyCountryDisplayName": "Kaimaninseln", + "cfCountryDisplayName": "Zentralafrikanische Republik", + "tdCountryDisplayName": "Tschad", + "clCountryDisplayName": "Chile", + "cnCountryDisplayName": "China", + "cxCountryDisplayName": "Weihnachtsinsel", + "ccCountryDisplayName": "Kokosinseln [Keeling]", + "coCountryDisplayName": "Kolumbien", + "kmCountryDisplayName": "Komoren", + "cdCountryDisplayName": "Demokratische Republik Kongo", + "cgCountryDisplayName": "Republik Kongo", + "ckCountryDisplayName": "Cookinseln", + "crCountryDisplayName": "Costa Rica", + "ciCountryDisplayName": "Elfenbeinküste", + "hrCountryDisplayName": "Kroatien", + "cuCountryDisplayName": "Kuba", + "cwCountryDisplayName": "Curaçao", + "cyCountryDisplayName": "Zypern", + "czCountryDisplayName": "Tschechische Republik", + "dkCountryDisplayName": "Dänemark", + "djCountryDisplayName": "Dschibuti", + "dmCountryDisplayName": "Dominica", + "doCountryDisplayName": "Dominikanische Republik", + "tlCountryDisplayName": "Osttimor", + "ecCountryDisplayName": "Ecuador", + "egCountryDisplayName": "Ägypten", + "svCountryDisplayName": "El Salvador", + "gqCountryDisplayName": "Äquatorialguinea", + "erCountryDisplayName": "Eritrea", + "eeCountryDisplayName": "Estland", + "szCountryDisplayName": "Eswatini", + "etCountryDisplayName": "Äthiopien", + "fkCountryDisplayName": "Falklandinseln", + "foCountryDisplayName": "Färöer-Inseln", + "fjCountryDisplayName": "Fidschi", + "fiCountryDisplayName": "Finnland", + "frCountryDisplayName": "Frankreich", + "gfCountryDisplayName": "Französisch-Guayana", + "pfCountryDisplayName": "Französisch-Polynesien", + "gaCountryDisplayName": "Gabun", + "gmCountryDisplayName": "Gambia", + "geCountryDisplayName": "Georgien", + "deCountryDisplayName": "Deutschland", + "ghCountryDisplayName": "Ghana", + "giCountryDisplayName": "Gibraltar", + "grCountryDisplayName": "Griechenland", + "glCountryDisplayName": "Grönland", + "gdCountryDisplayName": "Grenada", + "gpCountryDisplayName": "Guadeloupe", + "guCountryDisplayName": "Guam", + "gtCountryDisplayName": "Guatemala", + "ggCountryDisplayName": "Guernsey", + "gnCountryDisplayName": "Guinea Conakry", + "gwCountryDisplayName": "Guinea-Bissau", + "gyCountryDisplayName": "Guyana", + "htCountryDisplayName": "Haiti", + "hmCountryDisplayName": "Heard-Insel und McDonald-Inseln", + "hnCountryDisplayName": "Honduras", + "hkCountryDisplayName": "Hongkong", + "huCountryDisplayName": "Ungarn", + "isCountryDisplayName": "Island", + "inCountryDisplayName": "Indien", + "idCountryDisplayName": "Indonesien", + "irCountryDisplayName": "Iran", + "iqCountryDisplayName": "Irak", + "ieCountryDisplayName": "Irland", + "imCountryDisplayName": "Insel Man", + "ilCountryDisplayName": "Israel", + "itCountryDisplayName": "Italien", + "jmCountryDisplayName": "Jamaika", + "jpCountryDisplayName": "Japan", + "jeCountryDisplayName": "Jersey", + "joCountryDisplayName": "Jordanien", + "kzCountryDisplayName": "Kasachstan", + "keCountryDisplayName": "Kenia", + "kiCountryDisplayName": "Kiribati", + "xkCountryDisplayName": "Kosovo", + "kwCountryDisplayName": "Kuwait", + "kgCountryDisplayName": "Kirgisistan", + "laCountryDisplayName": "Laos", + "lvCountryDisplayName": "Lettland", + "lbCountryDisplayName": "Libanon", + "lsCountryDisplayName": "Lesotho", + "lrCountryDisplayName": "Liberia", + "lyCountryDisplayName": "Libyen", + "liCountryDisplayName": "Liechtenstein", + "ltCountryDisplayName": "Litauen", + "luCountryDisplayName": "Luxemburg", + "moCountryDisplayName": "Macau", + "mkCountryDisplayName": "Nordmazedonien", + "mgCountryDisplayName": "Madagaskar", + "mwCountryDisplayName": "Malawi", + "myCountryDisplayName": "Malaysia", + "mvCountryDisplayName": "Malediven", + "mlCountryDisplayName": "Mali", + "mtCountryDisplayName": "Malta", + "mhCountryDisplayName": "Marshallinseln", + "mqCountryDisplayName": "Martinique", + "mrCountryDisplayName": "Mauretanien", + "muCountryDisplayName": "Mauritius", + "ytCountryDisplayName": "Mayotte", + "mxCountryDisplayName": "Mexiko", + "fmCountryDisplayName": "Mikronesien", + "mdCountryDisplayName": "Moldawien", + "mcCountryDisplayName": "Monaco", + "mnCountryDisplayName": "Mongolei", + "meCountryDisplayName": "Montenegro", + "msCountryDisplayName": "Montserrat", + "maCountryDisplayName": "Marokko", + "mzCountryDisplayName": "Mosambik", + "mmCountryDisplayName": "Myanmar (Burma)", + "naCountryDisplayName": "Namibia", + "nrCountryDisplayName": "Nauru", + "npCountryDisplayName": "Nepal", + "nlCountryDisplayName": "Niederlande", + "ncCountryDisplayName": "Neukaledonien", + "nzCountryDisplayName": "Neuseeland", + "niCountryDisplayName": "Nicaragua", + "neCountryDisplayName": "Niger", + "ngCountryDisplayName": "Nigeria", + "nuCountryDisplayName": "Niue", + "nfCountryDisplayName": "Norfolkinsel", + "kpCountryDisplayName": "Nordkorea", + "mpCountryDisplayName": "Nördliche Marianen", + "noCountryDisplayName": "Norwegen", + "omCountryDisplayName": "Oman", + "pkCountryDisplayName": "Pakistan", + "pwCountryDisplayName": "Palau", + "psCountryDisplayName": "Palästinensische Gebiete", + "paCountryDisplayName": "Panama", + "pgCountryDisplayName": "Papua-Neuguinea", + "pyCountryDisplayName": "Paraguay", + "peCountryDisplayName": "Peru", + "phCountryDisplayName": "Philippinen", + "plCountryDisplayName": "Polen", + "ptCountryDisplayName": "Portugal", + "prCountryDisplayName": "Puerto Rico", + "qaCountryDisplayName": "Katar", + "reCountryDisplayName": "Réunion", + "roCountryDisplayName": "Rumänien", + "ruCountryDisplayName": "Russland", + "rwCountryDisplayName": "Ruanda", + "blCountryDisplayName": "Saint-Barthélemy", + "shCountryDisplayName": "St. Helena", + "knCountryDisplayName": "St. Kitts", + "lcCountryDisplayName": "St. Lucia", + "mfCountryDisplayName": "St. Martin", + "pmCountryDisplayName": "St. Pierre und Miquelon", + "vcCountryDisplayName": "St. Vincent", + "wsCountryDisplayName": "Samoa", + "smCountryDisplayName": "San Marino", + "stCountryDisplayName": "Sao Tomé und Príncipe", + "saCountryDisplayName": "Saudi-Arabien", + "snCountryDisplayName": "Senegal", + "rsCountryDisplayName": "Serbien", + "scCountryDisplayName": "Seychellen", + "slCountryDisplayName": "Sierra Leone", + "sgCountryDisplayName": "Singapur", + "sxCountryDisplayName": "Sint Maarten", + "skCountryDisplayName": "Slowakei", + "siCountryDisplayName": "Slowenien", + "sbCountryDisplayName": "Salomonen", + "soCountryDisplayName": "Somalia", + "zaCountryDisplayName": "Südafrika", + "gsCountryDisplayName": "Südgeorgien und die Südlichen Sandwichinseln", + "krCountryDisplayName": "Südkorea", + "ssCountryDisplayName": "Südsudan", + "esCountryDisplayName": "Spanien", + "lkCountryDisplayName": "Sri Lanka", + "sdCountryDisplayName": "Sudan", + "srCountryDisplayName": "Suriname", + "sjCountryDisplayName": "Svalbard und Jan Mayen", + "seCountryDisplayName": "Schweden", + "chCountryDisplayName": "Schweiz", + "syCountryDisplayName": "Syrien", + "twCountryDisplayName": "Taiwan", + "tjCountryDisplayName": "Tadschikistan", + "tzCountryDisplayName": "Tansania", + "thCountryDisplayName": "Thailand", + "tgCountryDisplayName": "Togo", + "tkCountryDisplayName": "Tokelau", + "toCountryDisplayName": "Tonga", + "ttCountryDisplayName": "Trinidad/Tobago", + "tnCountryDisplayName": "Tunesien", + "trCountryDisplayName": "Türkei", + "tmCountryDisplayName": "Turkmenistan", + "tcCountryDisplayName": "Turks- und Caicosinseln", + "tvCountryDisplayName": "Tuvalu", + "viCountryDisplayName": "Amerikanische Jungferninseln", + "ugCountryDisplayName": "Uganda", + "uaCountryDisplayName": "Ukraine", + "aeCountryDisplayName": "Vereinigte Arabische Emirate", + "gbCountryDisplayName": "Vereinigtes Königreich", + "usCountryDisplayName": "Vereinigte Staaten", + "uyCountryDisplayName": "Uruguay", + "uzCountryDisplayName": "Usbekistan", + "vuCountryDisplayName": "Vanuatu", + "vaCountryDisplayName": "Vatikanstadt", + "veCountryDisplayName": "Venezuela", + "vnCountryDisplayName": "Vietnam", + "wfCountryDisplayName": "Wallis und Futuna", + "ehCountryDisplayName": "Westsahara", + "yeCountryDisplayName": "Jemen", + "zmCountryDisplayName": "Sambia", + "zwCountryDisplayName": "Simbabwe", + "pay": "Zur Kasse", + "invitedToSpace": "{user} hat Sie eingeladen, an einem Kurs teilzunehmen: {space}! Möchten Sie annehmen?", + "youreInvited": "📩 Sie sind eingeladen!", + "invitedToChat": "{user} hat Sie eingeladen, an einem Chat teilzunehmen: {name}! Möchten Sie annehmen?", + "monthlySubscription": "Monatlich", + "yearlySubscription": "Jährlich", + "defaultSubscription": "Pangea Chat-Abonnement", + "freeTrial": "Kostenlose Testversion", + "total": "Gesamt: ", + "noDataFound": "Keine Daten gefunden", + "blurMeansTranslateTitle": "Warum ist die Nachricht verschwommen?", + "blurMeansTranslateBody": "Wenn der Immersionsmodus aktiviert ist, werden Nachrichten, die in Ihrer Basissprache gesendet werden, verschwommen dargestellt, während Pangea Bot sie in Ihre Zielsprache übersetzt. Der Immersionsmodus kann in den Einstellungen für einzelne Chats und Kurse umgeschaltet werden.", + "bestCorrectionFeedback": "Das ist richtig!", + "distractorFeedback": "Das ist nicht ganz richtig.", + "bestAnswerFeedback": "Das ist richtig!", + "definitionDefaultPrompt": "Was bedeutet dieses Wort?", + "practiceDefaultPrompt": "Was ist die beste Antwort?", + "correctionDefaultPrompt": "Was ist die beste Alternative?", + "acceptSelection": "Korrektur akzeptieren", + "why": "Warum?", + "definition": "Definition", + "exampleSentence": "Beispielsatz", + "reportToTeacher": "Wen möchten Sie diese Nachricht melden?", + "reportMessageTitle": "{reportingUserId} hat eine Nachricht von {reportedUserId} im Chat {roomName} gemeldet", + "reportMessageBody": "Nachricht: {reportedMessage}\nGrund: {reason}", + "noTeachersFound": "Keine Lehrer zum Melden gefunden", + "trialExpiration": "Ihre kostenlose Testphase läuft am {expiration} ab", + "freeTrialDesc": "Neue Nutzer erhalten eine einwöchige kostenlose Testphase von Pangea Chat", + "activateTrial": "Kostenlose 7-Tage-Testversion", + "successfullySubscribed": "Sie haben erfolgreich abonniert!", + "clickToManageSubscription": "Klicken Sie hier, um Ihr Abonnement zu verwalten.", + "signUp": "Registrieren", + "pleaseChooseAtLeastChars": "Bitte wählen Sie mindestens {min} Zeichen.", + "noEmailWarning": "Bitte geben Sie eine gültige E-Mail-Adresse ein. Andernfalls können Sie Ihr Passwort nicht zurücksetzen. Wenn Sie das nicht möchten, tippen Sie erneut auf die Schaltfläche, um fortzufahren.", + "pleaseEnterValidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein.", + "pleaseChooseAUsername": "Bitte wählen Sie einen Benutzernamen", + "define": "Definieren", + "listen": "Hören", + "trialPeriodExpired": "Ihre Testphase ist abgelaufen", + "translations": "Übersetzungen", + "messageAudio": "Nachrichten-Audio", + "definitions": "Definitionen", + "subscribedToUnlockTools": "Abonnieren Sie, um interaktive Übersetzungen und Grammatikprüfungen, Audio-Wiedergabe, personalisierte Übungsaktivitäten und Lernanalysen freizuschalten!", + "translationTooltip": "Übersetzen", + "speechToTextTooltip": "Transkript", + "kickBotWarning": "Das Entfernen des Pangea-Bots entfernt den Konversationsbot aus diesem Chat.", + "refresh": "Aktualisieren", + "messageAnalytics": "Nachrichtenanalyse", + "words": "Wörter", + "score": "Punktzahl", + "accuracy": "Genauigkeit", + "points": "Punkte", + "noPaymentInfo": "Keine Zahlungsinformationen erforderlich!", + "updatePhoneOS": "Sie müssen möglicherweise die OS-Version Ihres Geräts aktualisieren.", + "wordsPerMinute": "Wörter pro Minute", + "tooltipInstructionsTitle": "Nicht sicher, was das macht?", + "tooltipInstructionsMobileBody": "Tippen und halten Sie Elemente, um Tooltips anzuzeigen.", + "tooltipInstructionsBrowserBody": "Bewegen Sie den Mauszeiger über Elemente, um Tooltips anzuzeigen.", + "chatCapacity": "Chat-Kapazität", + "roomFull": "Dieser Raum ist bereits voll.", + "chatCapacityHasBeenChanged": "Chat-Kapazität wurde geändert", + "chatCapacitySetTooLow": "Die Chat-Kapazität muss mindestens {count} sein.", + "chatCapacityExplanation": "Die Chat-Kapazität begrenzt die Anzahl der Mitglieder, die in einem Chat erlaubt sind.", + "tooManyRequest": "Zu viele Anfragen, bitte versuchen Sie es später erneut.", + "enterNumber": "Bitte geben Sie eine ganze Zahl ein.", + "buildTranslation": "Erstellen Sie Ihre Übersetzung aus den oben genannten Optionen", + "practice": "Üben", + "noLanguagesSet": "Keine Sprachen festgelegt", + "speechToTextBody": "Bei Sprachnachrichten können Sie sowohl eine Transkription als auch die Wörter pro Minute des Sprechers sehen.", + "versionNotFound": "Version nicht gefunden", + "fetchingVersion": "Version wird abgerufen...", + "versionFetchError": "Fehler beim Abrufen der Version", + "versionText": "Version: {version}+{buildNumber}", + "l1TranslationBody": "Nachrichten in Ihrer Basissprache werden nicht übersetzt.", + "deleteSubscriptionWarningTitle": "Sie haben ein aktives Abonnement", + "deleteSubscriptionWarningBody": "Das Löschen Ihres Kontos wird Ihr Abonnement nicht automatisch kündigen.", + "manageSubscription": "Abonnement verwalten", + "error520Title": "Bitte versuchen Sie es erneut.", + "error520Desc": "Entschuldigung, wir konnten Ihre Nachricht nicht verstehen...", + "wordsUsed": "Verwendete Wörter", + "level": "Stufe", + "morphsUsed": "Verwendete Morphs", + "translationChoicesBody": "Klicken und halten Sie eine Option für einen Hinweis.", + "grammar": "Grammatik", + "contactHasBeenInvitedToTheChat": "Kontakt wurde zum Chat eingeladen", + "inviteChat": "📨 Chat einladen", + "chatName": "Chat-Name", + "reportContentIssueTitle": "Inhaltproblem melden", + "feedback": "Optionale Rückmeldung", + "reportContentIssueDescription": "Oh je! KI kann personalisierte Lernerfahrungen erleichtern, aber... halluziniert auch. Bitte geben Sie jegliches Feedback, das Sie haben, und wir versuchen es erneut.", + "clickTheWordAgainToDeselect": "Klicken Sie auf das ausgewählte Wort, um es abzuwählen.", + "l2SupportNa": "Nicht verfügbar", + "l2SupportAlpha": "Alpha", + "l2SupportBeta": "Beta", + "l2SupportFull": "Vollständig", + "missingVoiceTitle": "Stimme fehlt", + "voiceNotAvailable": "Sie haben keine Stimme für diese Sprache installiert.", + "openVoiceSettings": "Stimmeinstellungen öffnen", + "playAudio": "Abspielen", + "stop": "Stopp", + "grammarCopyPOSsconj": "Subordinierende Konjunktion", + "grammarCopyPOSnum": "Nummer", + "grammarCopyPOSverb": "Verb", + "grammarCopyPOSaffix": "Affix", + "grammarCopyPOSpart": "Partikel", + "grammarCopyPOSadj": "Adjektiv", + "grammarCopyPOScconj": "Koordinierende Konjunktion", + "grammarCopyPOSpunct": "Interpunktion", + "grammarCopyPOSadv": "Adverb", + "grammarCopyPOSaux": "Hilfsverb", + "grammarCopyPOSspace": "Leerzeichen", + "grammarCopyPOSsym": "Symbol", + "grammarCopyPOSdet": "Artikel", + "grammarCopyPOSpron": "Pronomen", + "grammarCopyPOSadp": "Adposition", + "grammarCopyPOSpropn": "Eigenname", + "grammarCopyPOSnoun": "Substantiv", + "grammarCopyPOSintj": "Interjektion", + "grammarCopyPOSx": "Andere", + "grammarCopyGENDERfem": "Weiblich", + "grammarCopyPERSON2": "Zweite Person", + "grammarCopyMOODimp": "Imperativ", + "grammarCopyPUNCTTYPEqest": "Frage", + "grammarCopyASPECTperf": "Perfekt", + "grammarCopyCASEaccnom": "Akkusativ, Nominativ", + "grammarCopyCASEobl": "Oblique", + "grammarCopyVOICEact": "Aktiv", + "grammarCopyPUNCTTYPEbrck": "Klammer", + "grammarCopyNOUNTYPEart": "Artikel", + "grammarCopyNUMBERsing": "Singular", + "grammarCopyGENDERmasc": "Maskulin", + "grammarCopyVERBTYPEmod": "Modal", + "grammarCopyADVTYPEadverbial": "Adverbial", + "grammarCopyTENSEperi": "Periphrastisch", + "grammarCopyNUMFORMdigit": "Ziffer", + "grammarCopyNOUNTYPEnot_proper": "Nicht Eigennamen", + "grammarCopyNUMTYPEcard": "Kardinalzahl", + "grammarCopyNOUNTYPEprop": "Eigenname", + "grammarCopyPUNCTTYPEdash": "Bindestrich", + "grammarCopyPUNCTTYPEyes": "Ja", + "grammarCopyPUNCTTYPEsemi": "Semikolon", + "grammarCopyPUNCTTYPEcomm": "Komma", + "grammarCopyMOODcnd": "Konditional", + "grammarCopyCASEacc": "Akkusativ", + "grammarCopyPARTTYPEpart": "Partitiv", + "grammarCopyTENSEpast": "Vergangenheit", + "grammarCopyDEGREEsup": "Superlativ", + "grammarCopyPUNCTTYPEcolo": "Doppelpunkt", + "grammarCopyPERSON3": "Dritte Person", + "grammarCopyNUMBERplur": "Plural", + "grammarCopyPRONTYPEnpr": "Eigenname", + "grammarCopyPRONTYPEinterrogative": "Fragewort", + "grammarCopyPOLITEinfm": "Informell", + "grammarCopyADVTYPEtim": "Zeit", + "grammarCopyPOLARITYneg": "Negativ", + "grammarCopyNUMTYPEtot": "Gesamt", + "grammarCopyADVTYPEadnomial": "Adnominal", + "grammarCopyASPECTprog": "Progressiv", + "grammarCopyMOODsub": "Subjunktiv", + "grammarCopyVERBFORMcomplementive": "Komplementiv", + "grammarCopyCASEnom": "Nominativ", + "grammarCopyTENSEfut": "Zukunft", + "grammarCopyCASEdat": "Dativ", + "grammarCopyTENSEpres": "Präsens", + "grammarCopyGENDERneut": "Neutrum", + "grammarCopyPRONTYPErel": "Relativpronomen", + "grammarCopyVERBFORMfinalEnding": "Endung", + "grammarCopyPRONTYPEdem": "Demonstrativpronomen", + "grammarCopyPREPCASEpre": "Präpositional", + "grammarCopyVERBFORMfin": "Finit", + "grammarCopyDEGREEpos": "Positiv", + "grammarCopyPUNCTTYPEquot": "Zitat", + "grammarCopyVERBFORMger": "Gerundium", + "grammarCopyVOICEpass": "Passiv", + "grammarCopyCASEgen": "Genitiv", + "grammarCopyTENSEprs": "Präsens", + "grammarCopyDEFINITEdef": "Bestimmt", + "grammarCopyNUMTYPEord": "Ordinal", + "grammarCopyCASEins": "Instrumental", + "grammarCopyVERBFORMinf": "Infinitiv", + "grammarCopyVERBFORMaux": "Hilfsverb", + "grammarCopyNUMFORMlong": "Lang", + "grammarCopyCASEloc": "Lokativer Fall", + "grammarCopyMOODind": "Indikativ", + "grammarCopyDEGREEcmp": "Komparativ", + "grammarCopyCASErelativeCase": "Relativ", + "grammarCopyPUNCTTYPEexcl": "Ausruf", + "grammarCopyPERSON1": "Erste Person", + "grammarCopyPUNCTSIDEini": "Anfang", + "grammarCopyGENDERperson": "Person", + "grammarCopyFOREIGNyes": "Fremd", + "grammarCopyVOICEvoice": "Stimme", + "grammarCopyVERBTYPEverbType": "Verb", + "grammarCopyPOSSpass": "Possessiv", + "grammarCopyPREPCASEprepCase": "Präpositional", + "grammarCopyNUMTYPEnumType": "Zahlwort", + "grammarCopyNOUNTYPEnounType": "Substantiv", + "grammarCopyREFLEXreflex": "Reflexiv", + "grammarCopyPRONTYPEpronType": "Pronomen", + "grammarCopyPUNCTSIDEpunctSide": "Satzzeichen-Seite", + "grammarCopyVERBFORMverbForm": "Verbform", + "grammarCopyGENDERgender": "Geschlecht", + "grammarCopyMOODmood": "Modus", + "grammarCopyASPECTaspect": "Aspekt", + "grammarCopyPUNCTTYPEpunctType": "Zeichensetzung", + "grammarCopyTENSEtense": "Zeitform", + "grammarCopyDEGREEdegree": "Grad", + "grammarCopyPOLITEpolite": "Höflichkeit", + "grammarCopyADVTYPEadvType": "Adverb", + "grammarCopyNUMFORMnumber": "Zahl", + "grammarCopyCONJTYPEconjType": "Konjunktion", + "grammarCopyPOLARITYpolarity": "Polarisierung", + "grammarCopyCASEcase": "Fall", + "grammarCopyDEFINITEdefinite": "Bestimmtheit", + "grammarCopyNUMFORMnumForm": "Zahlwort", + "grammarCopyPRONTYPEadn": "Adnominal", + "grammarCopyVOCvoc": "Vokativ", + "grammarCopyCMPLcmpl": "Komplementierer", + "grammarCopyADVadv": "Adverbial", + "grammarCopyMOODjus": "Jussiv", + "grammarCopyGENDERcom": "Gemeinsam", + "grammarCopyREFLEXrflx": "Reflexiv", + "grammarCopyPARTTYPEpar": "Partitiv", + "grammarCopySPCspc": "Spezifisch", + "grammarCopyTENSEpqp": "Plusquamperfekt", + "grammarCopyREFLEXref": "Reflexiv", + "grammarCopyPUNCTTYPEnshrt": "Kurz", + "grammarCopyNUMBERdual": "Dual", + "grammarCopyNUMFORMlng": "Lang", + "grammarCopyVOICEmid": "Mittelfeld", + "grammarCopyINTRELintRel": "Interrogativ, Relativ", + "grammarCopyINTint": "Interrogativ", + "grammarCopyVOICEcaus": "Kausativ", + "grammarCopyUnknown": "Unbekannt", + "grammarCopyEVIDENTevident": "Beweisbarkeit", + "grammarCopyNUMFORMnumberPsor": "Besitzernummer", + "grammarCopyASPECThab": "Gewohnheitsmäßig", + "grammarCopyCASEabl": "Ablativ", + "grammarCopyCASEall": "Allativ", + "grammarCopyCASEess": "Essiv", + "grammarCopyCASEtra": "Translativ", + "grammarCopyCASEequ": "Äquativ", + "grammarCopyCASEdis": "Distributiv", + "grammarCopyCASEabs": "Absolut", + "grammarCopyCASEerg": "Ergativ", + "grammarCopyCASEcau": "Kausal", + "grammarCopyCASEben": "Benefaktiv", + "grammarCopyCASEtem": "Temporär", + "grammarCopyCONJTYPEcoord": "Koordinierend", + "grammarCopyDEFINITEcons": "Konstruktionszustand", + "grammarCopyDEGREEabs": "Absoluter Grad", + "grammarCopyEVIDENTfh": "Faktische Evidentialität", + "grammarCopyEVIDENTnfh": "Nicht-faktische Evidentialität", + "grammarCopyMOODopt": "Optativ", + "grammarCopyMOODadm": "Admirativ", + "grammarCopyMOODdes": "Desiderativ", + "grammarCopyMOODnec": "Notwendig", + "grammarCopyMOODpot": "Potenzial", + "grammarCopyMOODprp": "Propositive", + "grammarCopyMOODqot": "Quotativ", + "grammarCopyNUMFORMword": "Wortform", + "grammarCopyNUMFORMroman": "Römische Zahl", + "grammarCopyNUMFORMletter": "Buchstabenform", + "grammarCopyNUMTYPEmult": "Multiplikativ", + "grammarCopyNUMTYPEfrac": "Bruchzahl", + "grammarCopyNUMTYPEsets": "Menge", + "grammarCopyNUMTYPErange": "Bereich", + "grammarCopyNUMTYPEdist": "Distributiv", + "grammarCopyNUMBERtri": "Versuch", + "grammarCopyNUMBERpauc": "Paukal", + "grammarCopyNUMBERgrpa": "Größerer Paukal", + "grammarCopyNUMBERgrpl": "Größerer Plural", + "grammarCopyNUMBERinv": "Inverse", + "grammarCopyPERSON0": "Null", + "grammarCopyPERSON4": "Vierte", + "grammarCopyPOLITEform": "Formell", + "grammarCopyPOLITEelev": "Hochgestochen", + "grammarCopyPOLITEhumb": "Demütig", + "grammarCopyPRONTYPEemp": "Betont", + "grammarCopyPRONTYPEexc": "Ausruf", + "grammarCopyPRONTYPErcp": "Reziprok", + "grammarCopyPRONTYPEintRelPronType": "Interrogativ-Relativ", + "grammarCopyTENSEaor": "Aorist", + "grammarCopyTENSEeps": "Epistemisch", + "grammarCopyTENSEprosp": "Prospektiv", + "grammarCopyVERBFORMpart": "Partizip", + "grammarCopyVERBFORMconv": "Converb", + "grammarCopyVERBFORMvnoun": "Verbalnomen", + "grammarCopyVOICEantip": "Antipassiv", + "grammarCopyVOICEcauVoice": "Kausativ", + "grammarCopyVOICedir": "Direkt", + "grammarCopyVOICEinvVoice": "Inverse", + "grammarCopyVOICErcpVoice": "Reziprok", + "grammarCopyPOS": "Wortart", + "grammarCopyGENDER": "Geschlecht", + "grammarCopyPERSON": "Person", + "grammarCopyMOOD": "Modus", + "grammarCopyPUNCTTYPE": "Zeichensetzungstyp", + "grammarCopyASPECT": "Aspekt", + "grammarCopyCASE": "Fall", + "grammarCopyVOICE": "Stimme", + "grammarCopyNOUNTYPE": "Substantivart", + "grammarCopyVERBTYPE": "Verbart", + "grammarCopyADVTYPE": "Adverbart", + "grammarCopyNUMFORM": "Numeralform", + "grammarCopyNUMTYPE": "Numeralart", + "grammarCopyNUMBER": "Nummer", + "grammarCopyDEFINITE": "Bestimmtheit", + "grammarCopyDEGREE": "Grad", + "grammarCopyEVIDENT": "Evidentialität", + "grammarCopyFOREIGN": "Fremd", + "grammarCopyPOLARITY": "Polarität", + "grammarCopyPOLITE": "Höflichkeit", + "grammarCopyPREPCASE": "Präpositionalfall", + "grammarCopyPRONTYPE": "Pronomenart", + "grammarCopyPUNCTSIDE": "Zeichensetzung", + "grammarCopyREFLEX": "Reflexiv", + "grammarCopyTENSE": "Zeitform", + "grammarCopyVERBFORM": "Verbform", + "grammarCopyCONJTYPE": "Konjunktionstyp", + "grammarCopySPC": "Spezifizität", + "grammarCopyPARTTYPE": "Partitivtyp", + "grammarCopyINTREL": "Interrogativ-Relativ", + "grammarCopyUNKNOWN": "Unbekannt", + "grammarCopyNUMBERPSOR": "Besitzers Nummer", + "grammarCopyPOSS": "Possessiv", + "grammarCopyASPECTimp": "Unvollendeter Aspekt", + "grammarCopyCASEvoc": "Vokativ", + "grammarCopyCASEcom": "Kommativ", + "grammarCopyCASEpar": "Partitiv", + "grammarCopyCASEadv": "Adverbial", + "grammarCopyCASEref": "Referenziell", + "grammarCopyCASErel": "Relativ", + "grammarCopyCASEsub": "Subessiv", + "grammarCopyCASEsup": "Superessiv", + "grammarCopyCASEaccdat": "Akkusativ-Dativ", + "grammarCopyCASEpre": "Präpositional", + "grammarCopyCONJTYPEsub": "Subordinierend", + "grammarCopyCONJTYPEcmp": "Komparativ", + "grammarCopyDEFINITEind": "Indefinit", + "grammarCopyMOODint": "Fragesatz", + "grammarCopyNOUNTYPEcomm": "Gemeines Substantiv", + "grammarCopyNUMBERPSORsing": "Possessors Singular", + "grammarCopyNUMBERPSORplur": "Possessors Plural", + "grammarCopyNUMBERPSORdual": "Possessors Dual", + "grammarCopyPOLARITYpos": "Positive Polarität", + "grammarCopyPOSSyes": "Possessiv", + "grammarCopyPREPCASEnpr": "Nicht-präpositional", + "grammarCopyPRONTYPEprs": "Personal", + "grammarCopyPRONTYPEint": "Interrogativ", + "grammarCopyPRONTYPEtot": "Total", + "grammarCopyPRONTYPEneg": "Negativ", + "grammarCopyPRONTYPEart": "Artikel", + "grammarCopyPRONTYPEind": "Indefinit", + "grammarCopyPRONTYPEintrel": "Interrogativ-Relativ", + "grammarCopyPUNCTSIDEfin": "Endzeichen", + "grammarCopyPUNCTTYPEperi": "Punkt", + "grammarCopyREFLEXyes": "Reflexiv", + "grammarCopyTENSEimp": "Unvollkommen", + "grammarCopyVERBFORMsup": "Supin", + "grammarCopyVERBFORMadn": "Adjektivisch", + "grammarCopyVERBFORMlng": "Lang", + "grammarCopyVERBFORMshrt": "Kurz", + "grammarCopyVERBTYPEcaus": "Kausatives Verb", + "grammarCopyVOICEcau": "Kausativ", + "grammarCopyVOICEdir": "Direkt", + "grammarCopyVOICEinv": "Inverse", + "grammarCopyVOICErcp": "Reziprok", + "other": "Andere", + "levelShort": "LVL {level}", + "clickBestOption": "Wählen Sie die besten Optionen, um Ihre Nachricht zu übersetzen!", + "completeActivitiesToUnlock": "Absolvieren Sie mindestens eine Aktivität, um die Übersetzung freizuschalten!", + "noCapacityLimit": "Keine Kapazitätsbegrenzung", + "downloadGroupText": "Gruppentext herunterladen", + "notificationsOn": "Benachrichtigungen aktiviert", + "notificationsOff": "Benachrichtigungen deaktiviert", + "createChatAndInviteUsers": "Chat erstellen und Benutzer einladen", + "updatedNewSpaceDescription": "Kurse ermöglichen es, Ihre Chats zu konsolidieren und private oder öffentliche Gemeinschaften aufzubauen.", + "joinWithCode": "Mit Code beitreten", + "enterCodeToJoin": "Code eingeben, um beizutreten", + "updateNow": "Jetzt aktualisieren", + "updateLater": "Später aktualisieren", + "constructUseWaDesc": "Ohne Hilfe verwendet", + "constructUseGaDesc": "Grammatikunterstützung", + "constructUseTaDesc": "Übersetzungshilfe", + "constructUseUnkDesc": "Unbekannt", + "constructUseCorITDesc": "Korrekt in Übersetzung", + "constructUseIgnITDesc": "Ignoriert in Übersetzung", + "constructUseIncITDesc": "Falsch in Übersetzung", + "constructUseIgnIGCDesc": "Ignoriert in Grammatikprüfung", + "constructUseCorIGCDesc": "Korrekt in Grammatikprüfung", + "constructUseIncIGCDesc": "Falsch in Grammatikprüfung", + "constructUseCorPADesc": "Korrekt in Wortbedeutungsaktivität", + "constructUseIgnPADesc": "Ignoriert in Wortbedeutungsaktivität", + "constructUseIncPADesc": "Falsch in Wortbedeutungsaktivität", + "constructUseCorWLDesc": "Korrekt in Worthöraktivität", + "constructUseIncWLDesc": "Falsch in Worthöraktivität", + "constructUseIngWLDesc": "Ignoriert in Worthöraktivität", + "constructUseCorHWLDesc": "Korrekt in verstecktem Wortspiel", + "constructUseIncHWLDesc": "Falsch in verstecktem Wortspiel", + "constructUseIgnHWLDesc": "Ignoriert in verstecktem Wortspiel", + "constructUseCorLDesc": "Korrekt in Lemma-Aktivität", + "constructUseIncLDesc": "Falsch in Lemma-Aktivität", + "constructUseIgnLDesc": "Ignoriert in Lemma-Aktivität", + "constructUseCorMDesc": "Korrekt in Grammatikaktivität", + "constructUseIncMDesc": "Falsch in Grammatikaktivität", + "constructUseIgnMDesc": "In der Grammatikaktivität ignoriert", + "constructUseEmojiDesc": "In der Emoji-Aktivität korrekt", + "constructUseCollected": "Im Chat gesammelt", + "constructUseNanDesc": "Nicht anwendbar", + "xpIntoLevel": "{currentXP} / {maxXP} XP", + "enableTTSToolName": "Text-zu-Sprache aktiviert", + "enableTTSToolDescription": "Erlauben Sie der App, Text-zu-Sprache-Ausgaben für Textteile in Ihrer Zielsprache zu generieren.", + "yourUsername": "Ihr Benutzername", + "yourEmail": "Ihre E-Mail", + "iWantToLearn": "Ich möchte lernen", + "pleaseEnterEmail": "Bitte gib eine gültige E-Mail-Adresse ein.", + "myBaseLanguage": "Meine Basissprache", + "meaningSectionHeader": "Bedeutung:", + "formSectionHeader": "In Chats verwendete Formen:", + "writingExercisesTooltip": "Schreiben", + "listeningExercisesTooltip": "Hören", + "readingExercisesTooltip": "Lesen", + "meaningNotFound": "Bedeutung konnte nicht gefunden werden.", + "chooseBaseForm": "Wähle die Grundform", + "notTheCodeError": "Entschuldigung, das ist nicht der Code!", + "totalXP": "Gesamt-XP", + "numLemmas": "Gesamtzahl der Lemmas", + "numLemmasUsedCorrectly": "Anzahl der Lemmas, die mindestens einmal korrekt verwendet wurden", + "numLemmasUsedIncorrectly": "Anzahl der Lemmas, die 0-mal korrekt verwendet wurden", + "numLemmasSmallXP": "Anzahl der Lemmas mit 0 - 30 XP", + "numLemmasMediumXP": "Anzahl der Lemmas mit 31 - 200 XP", + "numLemmasLargeXP": "Anzahl der Lemmas mit > 200 XP", + "numGrammarConcepts": "Anzahl der Grammatikbegriffe", + "listGrammarConcepts": "Grammatikbegriffe", + "listGrammarConceptsUsedCorrectly": "Grammatikbegriffe, die in Originalnachrichten mindestens 80 % der Zeit korrekt verwendet wurden", + "listGrammarConceptsUsedIncorrectly": "Grammatikbegriffe, die in Originalnachrichten weniger als 80 % der Zeit korrekt verwendet wurden", + "listGrammarConceptsUseCorrectlySystemGenerated": "Grammatikbegriffe, die aus systemgenerierten Vorschlägen mindestens 80 % der Zeit korrekt ausgewählt wurden", + "listGrammarConceptsUseIncorrectlySystemGenerated": "Grammatikbegriffe, die aus systemgenerierten Vorschlägen weniger als 80 % der Zeit korrekt ausgewählt wurden", + "listGrammarConceptsSmallXP": "Grammatikbegriffe mit 0-50 XP", + "listGrammarConceptsMediumXP": "Grammatikbegriffe mit 51-200 XP", + "listGrammarConceptsLargeXP": "Grammatikbegriffe mit 201-500 XP", + "listGrammarConceptsHugeXP": "Grammatikbegriffe mit > 500 XP", + "numMessagesSent": "Anzahl der gesendeten Nachrichten", + "numWordsTyped": "Anzahl der in Originalnachrichten getippten Wörter", + "numCorrectChoices": "Anzahl der korrekt gewählten Wörter aus systemgenerierten Vorschlägen", + "numIncorrectChoices": "Anzahl der falsch gewählten Wörter aus systemgenerierten Vorschlägen", + "commaSeparatedFile": "CSV", + "excelFile": "Excel", + "fileType": "Dateityp", + "download": "Herunterladen", + "analyticsNotAvailable": "Benutzeranalysen nicht verfügbar", + "downloading": "Wird heruntergeladen...", + "failedFetchUserAnalytics": "Fehler beim Herunterladen der Benutzeranalysen", + "downloadComplete": "Download abgeschlossen!", + "whatIsTheMorphTag": "Was ist das {morphologicalFeature} von '{wordForm}'?", + "dataAvailable": "Datenverfügbarkeit", + "available": "Verfügbar", + "pangeaBotIsFallible": "Pangea Bot macht auch Fehler!", + "whatIsMeaning": "Was bedeutet '{lemma}'?", + "pickAnEmoji": "Welches ist dein Lieblings-Emoji für '{lemma}'?", + "chooseLemmaMeaningInstructionsBody": "Ordne Bedeutungen den Wörtern in der Nachricht zu!", + "doubleClickToEdit": "Doppelklicken zum Bearbeiten.", + "activityPlannerTitle": "Aktivitätsplaner", + "topicLabel": "Thema", + "topicPlaceholder": "Wählen Sie ein Thema...", + "modeLabel": "Aktivitätstyp", + "modePlaceholder": "Wählen Sie einen Modus...", + "learningObjectiveLabel": "Lernziel", + "learningObjectivePlaceholder": "Wählen Sie ein Lernziel...", + "languageOfInstructionsLabel": "Sprache der Anweisungen", + "targetLanguageLabel": "Zielsprache", + "cefrLevelLabel": "CEFR-Niveau", + "generateActivitiesButton": "Aktivität generieren", + "launchActivityButton": "Aktivität starten", + "image": "Bild", + "video": "Video", + "nan": "Nicht zutreffend", + "activityPlannerOverviewInstructionsBody": "Wählen Sie ein Thema, einen Modus, ein Lernziel und generieren Sie eine Aktivität für den Chat!", + "activityTitle": "Titel der Aktivität", + "addVocabulary": "Vokabular hinzufügen", + "instructions": "Anweisungen", + "numberOfLearners": "Anzahl der Lernenden", + "mustBeInteger": "Muss eine ganze Zahl sein, z.B. 1, 2, 3, ...", + "constructUsePvmDesc": "In Sprachmitteilung produziert", + "leaveSpaceDescription": "Wenn Sie den Kurs verlassen, verlassen Sie alle Chats darin. Andere Nutzer sehen, dass Sie den Kurs verlassen haben.", + "constructUseCorMmDesc": "Korrekte Nachrichtenbedeutung", + "constructUseIncMmDesc": "Falsche Nachrichtenbedeutung", + "constructUseIgnMmDesc": "Ignorierte Nachrichtenbedeutung", + "clickForMeaningActivity": "Klicken Sie hier für eine Bedeutungsherausforderung", + "meaning": "Bedeutung", + "chatWith": "Gruppe mit {displayname}", + "clickOnEmailLink": "Bitte klicken Sie auf den Link in der E-Mail und fahren Sie fort.\n\nÜberprüfen Sie Ihren Spam-Ordner, falls die E-Mail nicht angekommen ist.", + "dontForgetPassword": "Vergessen Sie nicht Ihr Passwort!", + "enableAutocorrectToolName": "Gerätekorrektur aktivieren", + "enableAutocorrectDescription": "Wenn Ihr Gerät die Sprache unterstützt, die Sie lernen, können Sie die Gerätekorrektur aktivieren, um häufige Fehler beim Tippen zu korrigieren.", + "ttsDisbledTitle": "Text-zu-Sprache deaktiviert", + "ttsDisabledBody": "Sie können die Text-zu-Sprache-Funktion in Ihren Lerneinstellungen aktivieren", + "noSpaceDescriptionYet": "Noch keine Kursbeschreibung erstellt.", + "tooLargeToSend": "Diese Nachricht ist zu groß zum Senden", + "exitWithoutSaving": "Möchten Sie wirklich ohne Speichern verlassen?", + "enableAutocorrectPopupTitle": "Fügen Sie Ihre Zielsprachentastatur hinzu, indem Sie zu gehen:", + "enableAutocorrectPopupSteps": " • Einstellungen\n • Allgemein\n • Tastatur\n • Tastaturen\n • Neue Tastatur hinzufügen", + "enableAutocorrectPopupDescription": "Sobald die Sprache ausgewählt ist, können Sie auf das kleine Globus-Symbol unten links auf Ihrer Tastatur klicken, um die neu installierte Tastatur zu aktivieren.", + "downloadGboardTitle": "Laden Sie Gboard aus dem Google Play Store herunter, um Autokorrektur und andere Tastaturfunktionen zu aktivieren:", + "downloadGboardSteps": " • Gboard herunterladen\n • App öffnen\n • Sprachen\n • Tastatur hinzufügen\n • Sprache auswählen\n • Tastaturtyp auswählen\n • Fertig", + "downloadGboardDescription": "Sobald die Sprache ausgewählt ist, können Sie auf das kleine Globus-Symbol unten links auf Ihrer Tastatur klicken, um die neu installierte Tastatur zu aktivieren.", + "enableAutocorrectWarning": "Warnung! Erfordert das Hinzufügen Ihrer Zielsprachentastatur", + "displayName": "Anzeigename", + "leaveRoomDescription": "Sie sind dabei, diesen Chat zu verlassen. Andere Benutzer werden sehen, dass Sie den Chat verlassen haben.", + "confirmUserId": "Bitte bestätigen Sie Ihren Pangea-Chat-Benutzernamen, um Ihr Konto zu löschen.", + "startingToday": "Ab heute", + "oneWeekFreeTrial": "Eine Woche kostenlos testen", + "paidSubscriptionStarts": "Beginnt am {startDate}", + "cancelInSubscriptionSettings": "• Jederzeit in den Abonnement-Einstellungen kündigen", + "cancelToAvoidCharges": "• Vor {trialEnds} kündigen, um Gebühren zu vermeiden", + "downloadGboard": "Gboard herunterladen", + "autocorrectNotAvailable": "Leider wird diese Plattform derzeit nicht für diese Funktion unterstützt. Bleiben Sie dran für weitere Entwicklungen!", + "pleaseUpdateApp": "Bitte aktualisieren Sie die App, um fortzufahren.", + "chooseEmojiInstructionsBody": "Ordnen Sie Emojis den Wörtern zu, die sie am besten repräsentieren. Keine Sorge! Es gibt keine Punktabzüge bei Meinungsverschiedenheiten. 😅", + "analyticsVocabListBody": "Das ist Ihr gesamter Wortschatz! Während Sie XP für jedes Wort verdienen, entwickeln sie sich vom Keimling zur vollen Blüte. Klicken Sie auf ein Wort, um mehr Details zu sehen.", + "morphAnalyticsListBody": "Dies sind alle Grammatik-Konzepte in der Sprache, die Sie lernen! Sie werden sie freischalten, wenn Sie ihnen beim Chatten begegnen. Klicken Sie für Details.", + "knockSpaceSuccess": "Sie haben die Anfrage gestellt, an diesem Kurs teilzunehmen! Ein Administrator wird auf Ihre Anfrage antworten, sobald er sie erhält 😄", + "chooseWordAudioInstructionsBody": "Hören Sie sich die vollständige Nachricht an. Ordnen Sie dann die Audios den Wörtern zu.", + "chooseMorphsInstructionsBody": "Klicken Sie auf die Puzzleteile für Grammatikfragen!", + "pleaseEnterInt": "Bitte geben Sie eine Zahl ein", + "home": "Startseite", + "join": "Beitreten", + "readingAssistanceOverviewBody": "Klicken Sie auf die Buttons unten für Minispiele zum Zuordnen von Emojis, Audios, Wortbedeutungen und Grammatik-Konzepten. Oder klicken Sie auf ein Wort für Details.", + "levelSummaryPopupTitle": "Level {level} Zusammenfassung", + "resetInstructionTooltipsTitle": "Anleitungstooltips zurücksetzen", + "resetInstructionTooltipsDesc": "Klicke, um Anleitungstooltips wie für einen völlig neuen Benutzer anzuzeigen.", + "selectForGrammar": "Wähle ein Grammatik-Icon für Aktivitäten und Details.", + "randomize": "Zufällig", + "clear": "Löschen", + "makeYourOwnActivity": "Erstelle deine eigene Aktivität", + "featuredActivities": "Vorgestellt", + "save": "Speichern", + "startChat": "Chat starten", + "translationProblem": "Übersetzungsproblem", + "askToJoin": "Frage zum Beitritt", + "emptyChatWarningTitle": "Chat ist leer", + "emptyChatWarningDesc": "Du hast niemanden zu deinem Chat eingeladen. Gehe zu den Chat-Einstellungen, um deine Kontakte oder den Bot einzuladen. Du kannst das auch später tun.", + "areYouLikeMe": "Bist du wie ich?", + "tryAgainLater": "Zu viele Versuche unternommen. Bitte versuche es in 5 Minuten erneut.", + "enterSpaceCode": "Gib den Kurscode ein", + "shareSpaceLink": "Link teilen", + "byUsingPangeaChat": "Durch die Nutzung von Pangea Chat stimme ich den ", + "details": "Details", + "languageLevelPreA1Desc": "Ich habe die Sprache noch nie gelernt oder benutzt.", + "languageLevelA1Desc": "Ich kann einige vertraute alltägliche Ausdrücke und sehr grundlegende Sätze verstehen und verwenden.", + "languageLevelA2Desc": "Ich kann Sätze und häufig verwendete Ausdrücke verstehen, die sich auf Bereiche unmittelbarer Relevanz beziehen.", + "languageLevelB1Desc": "Ich kann mit den meisten vertrauten Situationen umgehen und einfache zusammenhängende Texte zu vertrauten Themen produzieren.", + "languageLevelB2Desc": "Ich kann die Hauptideen komplexer Texte verstehen und mit einem gewissen Maß an Flüssigkeit und Spontaneität interagieren.", + "languageLevelC1Desc": "Ich kann Ideen fließend und spontan ausdrücken, ohne große Mühe, und eine Vielzahl anspruchsvoller Texte verstehen.", + "languageLevelC2Desc": "Ich kann fast alles, was ich höre oder lese, verstehen und mich fließend und präzise ausdrücken.", + "newVocab": "Neues Vokabular", + "newGrammar": "Neue Grammatik-Konzepte", + "choosePracticeMode": "Klicken Sie auf eine der Schaltflächen oben, um eine Übungsaktivität zu starten", + "ban": "Ban", + "unban": "Entbannen", + "kick": "Kicken", + "lemma": "Lemma", + "grammarFeature": "Grammatikmerkmal", + "grammarTag": "Grammatik-Tag", + "forms": "Formen", + "exampleMessages": "Beispielnachrichten", + "timesUsedIndependently": "Anzahl der unabhängigen Verwendungen", + "timesUsedWithAssistance": "Anzahl der Verwendungen mit Unterstützung", + "shareInviteCode": "Einladungs-Code teilen: {code}", + "leaderboard": "Bestenliste", + "skipForNow": "Jetzt überspringen", + "permissions": "Berechtigungen", + "spaceChildPermission": "Wer kann neue Chats zu diesem Kurs hinzufügen", + "addEnvironmentOverride": "Umgebungsüberschreibung hinzufügen", + "defaultOption": "Standard", + "deleteChatDesc": "Möchten Sie diesen Chat wirklich löschen? Er wird für alle Teilnehmer gelöscht und alle Nachrichten im Chat sind nicht mehr für Übungen oder Lernanalysen verfügbar.", + "deleteSpaceDesc": "Der Kurs und alle ausgewählten Chats werden für alle Teilnehmer gelöscht und alle Nachrichten im Chat sind nicht mehr für Übungen oder Lernanalysen verfügbar. Diese Aktion kann nicht rückgängig gemacht werden.", + "launch": "Starten", + "searchChats": "Chats suchen", + "maxFifty": "Maximal 50", + "configureSpace": "Kurs konfigurieren", + "pinMessages": "Nachrichten anheften", + "setJoinRules": "Beitrittsregeln festlegen", + "changeGeneralSettings": "Allgemeine Einstellungen ändern", + "inviteOtherUsersToRoom": "Andere Benutzer einladen", + "changeTheNameOfTheSpace": "Den Namen des Kurses ändern", + "changeTheDescription": "Ändere die Beschreibung", + "changeThePermissions": "Ändere die Berechtigungen", + "introductions": "Vorstellungen", + "announcements": "Ankündigungen", + "activities": "Aktivitäten", + "access": "Zugang", + "activitySuggestionTimeoutMessage": "Wir arbeiten hart daran, mehr Aktivitäten für Sie zu generieren. Bitte schauen Sie in einer Minute wieder vorbei", + "howSpaceCanBeFound": "Wie dieser Kurs gefunden werden kann", + "private": "Privat", + "cannotBeFoundInSearch": "In der Suche nicht auffindbar", + "public": "Öffentlich", + "visibleToCommunity": "Sichtbar für die breitere Pangea-Chat-Community über \"Einen Kurs finden\"", + "howSpaceCanBeJoined": "Wie dieser Kurs beigetreten werden kann", + "canBeFoundVia": "Kann gefunden werden über:", + "canBeFoundViaInvitation": "• Einladung", + "canBeFoundViaCodeOrLink": "• Code oder Link", + "canBeFoundViaKnock": "• Anfrage zum Beitritt und Admin-Genehmigung", + "youHaveLeveledUp": "Du hast ein Level aufgestiegen!", + "sendActivities": "Aktivitäten senden", + "groupChat": "Gruppenchats", + "directMessage": "Direktnachricht", + "newDirectMessage": "Neue Direktnachricht", + "speakingExercisesTooltip": "Sprechen", + "noChatsFoundHereYet": "Hier wurden noch keine Chats gefunden", + "duration": "Dauer", + "transcriptionFailed": "Transkription des Audios fehlgeschlagen", + "aUserIsKnocking": "Ein Benutzer bittet, deinem Kurs beizutreten", + "usersAreKnocking": "{users} Benutzer bitten, deinem Kurs beizutreten", + "failedToFetchTranscription": "Transkription konnte nicht abgerufen werden", + "deleteEmptySpaceDesc": "Der Kurs wird für alle Teilnehmer gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.", + "regenerate": "Neu generieren", + "mySavedActivities": "Meine gespeicherten Aktivitäten", + "noSavedActivities": "Keine gespeicherten Aktivitäten", + "saveActivity": "Aktivität speichern", + "failedToPlayVideo": "Video konnte nicht abgespielt werden", + "done": "Fertig", + "inThisSpace": "In diesem Kurs", + "myContacts": "Meine Kontakte", + "inviteAllInSpace": "Alle in diesem Kurs einladen", + "spaceParticipantsHaveBeenInvitedToTheChat": "Alle Kursteilnehmer wurden zum Chat eingeladen", + "numKnocking": "{count} klopft", + "numInvited": "{count} eingeladen", + "saved": "Gespeichert", + "reset": "Zurücksetzen", + "errorGenerateActivityMessage": "Aktivität konnte nicht generiert werden", + "errorRegenerateActivityMessage": "Aktivität konnte nicht neu generiert werden", + "errorLaunchActivityMessage": "Aktivität konnte nicht gestartet werden", + "errorFetchingActivitiesMessage": "Fehler beim Abrufen der Aktivitäten", + "errorFetchingDefinition": "Fehler beim Abrufen der Definition", + "errorProcessAnalytics": "Fehler bei der Verarbeitung der Analysen", + "errorDownloading": "Download fehlgeschlagen", + "errorFetchingLevelSummary": "Fehler beim Abrufen der Level-Zusammenfassung", + "errorLoadingSpaceChildren": "Fehler beim Laden der Chats innerhalb dieses Kurses", + "unexpectedError": "Unerwarteter Fehler.", + "pleaseReload": "Bitte neu laden und erneut versuchen.", + "translationError": "Übersetzungsfehler", + "errorFetchingTranslation": "Fehler beim Abrufen der Übersetzung", + "errorFetchingActivity": "Fehler beim Abrufen der Aktivität", + "check": "Prüfen", + "unableToFindRoom": "Kein Chat oder Kurs mit diesem Code gefunden. Bitte versuchen Sie es erneut.", + "numCompletedActivities": "Anzahl der abgeschlossenen Aktivitäten", + "viewingAnalytics": "Anzeige von {visible}/{users} Analysen", + "request": "Anfrage", + "requestAll": "Alle anfordern", + "confirmMessageUnpin": "Möchten Sie diese Nachricht wirklich lösen?", + "createActivityPlan": "Einen neuen Aktivitätsplan erstellen", + "saveAndLaunch": "Speichern und starten", + "launchToSpace": "Starten Sie den Kurs", + "numberOfActivities": "Anzahl der Aktivitätssitzungen", + "maximumActivityParticipants": "Jede Aktivität kann maximal {count} Teilnehmer haben.", + "pending": "Ausstehend", + "inactive": "Inaktiv", + "confirmRole": "Rolle bestätigen", + "openRoleLabel": "OFFEN", + "joinedTheActivity": "👋 {username} ist als {role} beigetreten", + "finishedTheActivity": "🎯 {username} hat diese Aktivität beendet", + "archiveToAnalytics": "Zu meinen abgeschlossenen Aktivitäten hinzufügen", + "activitySummaryError": "Aktivitätszusammenfassungen nicht verfügbar", + "requestSummaries": "Zusammenfassungen anfordern", + "generatingNewActivities": "Sie sind der erste Benutzer dieses Sprachpaares! Bitte geben Sie uns eine Minute, wir bereiten Aktivitäten nur für Sie vor.", + "requestAccessTitle": "Zugriff auf Analysen anfordern?", + "requestAccessDesc": "Möchten Sie Zugriff auf die Teilnehmeranalyse beantragen?\n\nWenn die Teilnehmer zustimmen, können Administratoren dieses Kurses ihre:\n • Gesamtvokabular\n • Gesammtgrammatik-Konzepte\n • Anzahl der abgeschlossenen Aktivitätssitzungen\n • die verwendeten Grammatik-Konzepte, richtig und falsch\n\nSie werden nicht in der Lage sein, ihre:\n • Nachrichten in Chats außerhalb des Kurses\n • Vokabelliste", + "requestAccess": "Zugriff anfordern ({count})", + "analyticsInactiveTitle": "Anfragen an inaktive Benutzer konnten nicht gesendet werden", + "analyticsInactiveDesc": "Inaktive Benutzer, die sich seit der Einführung dieses Features nicht angemeldet haben, sehen Ihre Anfrage nicht.\n\nDer Button „Anfrage“ erscheint, sobald sie zurückkehren. Sie können die Anfrage später erneut senden, indem Sie auf den Button „Anfrage“ unter ihrem Namen klicken, wenn dieser verfügbar ist.", + "accessRequestedTitle": "Anfrage für Analytics-Zugriff", + "accessRequestedDesc": "Anfordernde(r) Admin(s): {admin} \n\nAdmins von “{space}” bitten darum, Ihre Lernanalysen einzusehen.\n\nWenn Sie zustimmen, können sie Folgendes einsehen:\n • gesamten Wortschatz\n • gesamte Grammatik Konzepte\n • insgesamt abgeschlossene Aktivitätssitzungen\n • die spezifischen Grammatik Konzepte, die korrekt und inkorrekt verwendet wurden\n\nSie werden nicht in der Lage sein, Folgendes einzusehen:\n • Nachrichten in Chats außerhalb des Kurses\n • Wortschatzliste", + "adminRequestedAccess": "Administratoren haben um Zugriff auf Ihre Analysen gebeten.", + "lastUpdated": "Aktualisiert\n{time}", + "activityFinishedMessage": "Alles erledigt!", + "endForAll": "Für alle beenden", + "newCourse": "Neuer Kurs", + "numModules": "{num} Module", + "coursePlan": "Kursplan", + "editCourseLater": "Sie können den Titel, die Beschreibungen und das Kursbild später bearbeiten.", + "createCourse": "Kurs erstellen", + "stats": "Statistiken", + "createGroupChat": "Gruppenchats erstellen", + "editCourse": "Kurs bearbeiten", + "inviteDesc": "Per Benutzername, Code oder Link", + "editCourseDesc": "Hier können Sie den Kurstitel, die Beschreibung usw. bearbeiten.", + "permissionsDesc": "Berechtigungen festlegen, z. B. wer Einladungen senden, Nachrichten schicken, Chats erstellen kann usw.", + "accessDesc": "Sie können Ihren Kurs für die Welt öffnen! Oder machen Sie Ihren Kurs privat und sicher.", + "createGroupChatDesc": "Während Aktivitätssitzungen starten und enden, bleiben Gruppenchats für die routinemäßige Kommunikation offen.", + "deleteDesc": "Nur Administratoren können einen Kurs löschen. Dies ist eine zerstörerische Aktion, die alle Benutzer entfernt und alle ausgewählten Chats im Kurs löscht. Bitte vorsichtig vorgehen.", + "noCourseFound": "Oh, dieser Kurs braucht einen Plan!\n\nKurspläne sind eine Abfolge von Themen und Gesprächsaktivitäten.", + "additionalParticipants": "+ {num} weitere", + "directMessages": "Direktnachrichten", + "whatNow": "Was jetzt?", + "chooseNextActivity": "Wählen Sie Ihre nächste Aktivität!", + "letsGo": "Los geht's", + "chooseRole": "Wähle eine Rolle!", + "chooseRoleToParticipate": "Wähle eine Rolle zur Teilnahme!", + "waitingToFillRole": "Warte darauf, {num} Rollen zu besetzen...", + "pingParticipants": "Benachrichtige Kursteilnehmer", + "playWithBot": "Mit Pangea Bot spielen", + "inviteFriends": "Freunde einladen", + "waitNotDone": "Warte, ich bin noch nicht fertig!", + "waitingForOthersToFinish": "Warte, bis die anderen fertig sind...", + "generatingSummary": "Chat wird analysiert und Ergebnisse werden generiert", + "findCourse": "Kurs finden", + "pingParticipantsNotification": "{user} sucht nach Nutzern, die an der Sitzung in {room} teilnehmen möchten", + "course": "Kurs", + "courses": "Kurse", + "courseName": "Kursname", + "createNewCourse": "Neuer Kurs", + "goToCourse": "Zum Kurs: {course}", + "activityComplete": "Diese Aktivität wurde abgeschlossen. Die Zusammenfassung der Aktivität sollte unten verfügbar sein.", + "startNewSession": "Neue Sitzung starten", + "joinOpenSession": "Offene Sitzung beitreten", + "less": "weniger", + "activityNotFound": "Aktivität nicht gefunden", + "levelUp": "Aufstieg", + "myActivities": "Meine Aktivitäten", + "openToJoin": "Offen zum Beitreten", + "results": "Ergebnisse", + "activityDone": "Aktivität abgeschlossen!", + "promoCodeInfo": "Promo-Codes können auf der nächsten Seite eingegeben werden", + "editsComingSoon": "Die Möglichkeit, Städte und Aktivitäten zu bearbeiten, kommt bald.", + "editing": "Bearbeiten", + "activityNeedsOneMember": "Oh je! Für diese Aktivität wird noch eine Person benötigt.", + "activityNeedsMembers": "Oh nein! Diese Aktivität benötigt {num} weitere Personen.", + "inviteFriendsToCourse": "Freunde zu meinem Kurs einladen", + "subscribeToUnlockActivitySummaries": "Abonnieren, um Aktivitätszusammenfassungen freizuschalten", + "subscribeToUnlockDefinitions": "Abonnieren, um Definitionen freizuschalten", + "subscribeToUnlockTranscriptions": "Abonnieren, um Transkriptionen freizuschalten", + "pingSent": "🔔 Kurs-Ping gesendet! 🔔", + "courseTitle": "Kurstitel", + "courseDesc": "Kursbeschreibung", + "courseSavedSuccessfully": "Kurs erfolgreich gespeichert", + "addCoursePlan": "Einen Kursplan hinzufügen", + "activityStatsButtonInstruction": "Klicken Sie hier, um Ihre Aktivitätsstatistiken anzuzeigen und die Aktivität bei Abschluss zu schließen", + "loginToAccount": "In mein Konto einloggen", + "appDescription": "Lerne eine Sprache\nwährend du deinen Freunden schreibst.", + "languages": "Sprachen", + "chooseLanguage": "Wählen Sie eine Zielsprache.", + "planTrip": "Plane deine Reise", + "howAreYouTraveling": "Wie reist du?", + "unlockPrivateTrip": "Einen privaten Trip freischalten", + "joinPublicTrip": "Einem öffentlichen Trip beitreten", + "startOwnTrip": "Eigenen Trip starten", + "tripPlanDesc": "Trips sind Kurse. Jeder hat 8-10 sequenzierte Themen mit einer Vielzahl von aufgabenbasierten Sprachlernaktivitäten.", + "unlockPrivateTripTitle": "Privaten Trip freischalten", + "browsePublicTrips": "Öffentliche Trips durchsuchen", + "startOwnTripTitle": "Eigenen Trip starten", + "courseCode": "Was ist das geheime Passwort?", + "courseCodeHint": "Reisecode oder Link", + "unlockMyTrip": "Meinen Trip freischalten", + "signupOption": "Wie möchten Sie sich anmelden?", + "withApple": "Mit Apple", + "withGoogle": "Mit Google", + "withEmail": "Mit E-Mail", + "createAccount": "Konto erstellen", + "loginWithEmail": "Mit E-Mail anmelden", + "usernameOrEmail": "Benutzername oder E-Mail", + "email": "E-Mail", + "forgotPassword": "Passwort vergessen?", + "endActivity": "Aktivität beenden", + "allLanguages": "Alle Sprachen", + "chatListTooltip": "Hier finden Sie Ihre Direktnachrichten! Klicken Sie auf das Avatar eines Nutzers und “Unterhaltung starten”, um eine DM zu senden.", + "directMessageBotTitle": "Direktnachricht Pangea Bot", + "feedbackTitle": "Aktivitätsfeedback", + "feedbackHint": "Ihr Feedback", + "feedbackButton": "Feedback absenden", + "directMessageBotDesc": "Mit Menschen zu sprechen macht mehr Spaß, aber... KI ist immer bereit!", + "inviteYourFriends": "Laden Sie Ihre Freunde ein", + "playWithAI": "Jetzt mit KI spielen", + "courseStartDesc": "Pangea Bot ist jederzeit einsatzbereit!\n\n...aber Lernen macht mit Freunden mehr Spaß!", + "@writeAMessageLangCodes": { + "type": "String", + "placeholders": { + "l1": { + "type": "String" + }, + "l2": { + "type": "String" + } } }, - "description": "State that {command} is not a valid /command." - }, - "compareEmojiMatch": "Bitte vergleiche die Emojis", - "@compareEmojiMatch": { - "type": "String", - "placeholders": {} - }, - "compareNumbersMatch": "Bitte vergleiche die Zahlen", - "@compareNumbersMatch": { - "type": "String", - "placeholders": {} - }, - "configureChat": "Chat konfigurieren", - "@configureChat": { - "type": "String", - "placeholders": {} - }, - "confirm": "Bestätigen", - "@confirm": { - "type": "String", - "placeholders": {} - }, - "connect": "Verbinden", - "@connect": { - "type": "String", - "placeholders": {} - }, - "contactHasBeenInvitedToTheGroup": "Kontakt wurde in die Gruppe eingeladen", - "@contactHasBeenInvitedToTheGroup": { - "type": "String", - "placeholders": {} - }, - "containsDisplayName": "Enthält Anzeigenamen", - "@containsDisplayName": { - "type": "String", - "placeholders": {} - }, - "containsUserName": "Enthält Benutzernamen", - "@containsUserName": { - "type": "String", - "placeholders": {} - }, - "contentHasBeenReported": "Der Inhalt wurde den Serveradministratoren gemeldet", - "@contentHasBeenReported": { - "type": "String", - "placeholders": {} - }, - "copiedToClipboard": "Wurde in die Zwischenablage kopiert", - "@copiedToClipboard": { - "type": "String", - "placeholders": {} - }, - "copy": "Kopieren", - "@copy": { - "type": "String", - "placeholders": {} - }, - "copyToClipboard": "In Zwischenablage kopieren", - "@copyToClipboard": { - "type": "String", - "placeholders": {} - }, - "couldNotDecryptMessage": "Nachricht konnte nicht entschlüsselt werden: {error}", - "@couldNotDecryptMessage": { - "type": "String", - "placeholders": { - "error": { - "type": "String" - } - } - }, - "countParticipants": "{count} Mitglieder", - "@countParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "create": "Erstellen", - "@create": { - "type": "String", - "placeholders": {} - }, - "createdTheChat": "💬 {username} hat den Chat erstellt", - "@createdTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "createNewSpace": "Neuer Space", - "@createNewSpace": { - "type": "String", - "placeholders": {} - }, - "currentlyActive": "Jetzt gerade online", - "@currentlyActive": { - "type": "String", - "placeholders": {} - }, - "darkTheme": "Dunkel", - "@darkTheme": { - "type": "String", - "placeholders": {} - }, - "dateAndTimeOfDay": "{date}, {timeOfDay}", - "@dateAndTimeOfDay": { - "type": "String", - "placeholders": { - "date": { - "type": "String" - }, - "timeOfDay": { - "type": "String" - } - } - }, - "dateWithoutYear": "{day}.{month}", - "@dateWithoutYear": { - "type": "String", - "placeholders": { - "month": { - "type": "String" - }, - "day": { - "type": "String" - } - } - }, - "dateWithYear": "{day}.{month}.{year}", - "@dateWithYear": { - "type": "String", - "placeholders": { - "year": { - "type": "String" - }, - "month": { - "type": "String" - }, - "day": { - "type": "String" - } - } - }, - "deactivateAccountWarning": "Dies deaktiviert dein Konto. Es kann nicht rückgängig gemacht werden! Bist du sicher?", - "@deactivateAccountWarning": { - "type": "String", - "placeholders": {} - }, - "defaultPermissionLevel": "Standardberechtigungsstufe für neue Benutzer", - "@defaultPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "delete": "Löschen", - "@delete": { - "type": "String", - "placeholders": {} - }, - "deleteAccount": "Konto löschen", - "@deleteAccount": { - "type": "String", - "placeholders": {} - }, - "deleteMessage": "Nachricht löschen", - "@deleteMessage": { - "type": "String", - "placeholders": {} - }, - "device": "Gerät", - "@device": { - "type": "String", - "placeholders": {} - }, - "deviceId": "Geräte-ID", - "@deviceId": { - "type": "String", - "placeholders": {} - }, - "devices": "Geräte", - "@devices": { - "type": "String", - "placeholders": {} - }, - "directChats": "Direkte Chats", - "@directChats": { - "type": "String", - "placeholders": {} - }, - "displaynameHasBeenChanged": "Anzeigename wurde geändert", - "@displaynameHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "downloadFile": "Datei herunterladen", - "@downloadFile": { - "type": "String", - "placeholders": {} - }, - "edit": "Bearbeiten", - "@edit": { - "type": "String", - "placeholders": {} - }, - "editBlockedServers": "Blockierte Server einstellen", - "@editBlockedServers": { - "type": "String", - "placeholders": {} - }, - "editDisplayname": "Anzeigename ändern", - "@editDisplayname": { - "type": "String", - "placeholders": {} - }, - "editRoomAliases": "Raum-Aliase bearbeiten", - "@editRoomAliases": { - "type": "String", - "placeholders": {} - }, - "editRoomAvatar": "Raumavatar bearbeiten", - "@editRoomAvatar": { - "type": "String", - "placeholders": {} - }, - "emoteExists": "Emoticon existiert bereits!", - "@emoteExists": { - "type": "String", - "placeholders": {} - }, - "emoteInvalid": "Ungültiges Emoticon-Kürzel!", - "@emoteInvalid": { - "type": "String", - "placeholders": {} - }, - "emotePacks": "Emoticon-Bündel für Raum", - "@emotePacks": { - "type": "String", - "placeholders": {} - }, - "emoteSettings": "Emoticon-Einstellungen", - "@emoteSettings": { - "type": "String", - "placeholders": {} - }, - "emoteShortcode": "Emoticon-Kürzel", - "@emoteShortcode": { - "type": "String", - "placeholders": {} - }, - "emoteWarnNeedToPick": "Wähle ein Emoticon-Kürzel und ein Bild!", - "@emoteWarnNeedToPick": { - "type": "String", - "placeholders": {} - }, - "emptyChat": "Leerer Chat", - "@emptyChat": { - "type": "String", - "placeholders": {} - }, - "enableEmotesGlobally": "Aktiviere Emoticon-Bündel global", - "@enableEmotesGlobally": { - "type": "String", - "placeholders": {} - }, - "enableEncryption": "Verschlüsselung anschalten", - "@enableEncryption": { - "type": "String", - "placeholders": {} - }, - "enableEncryptionWarning": "Du wirst die Verschlüsselung nicht mehr ausstellen können. Bist Du sicher?", - "@enableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "encrypted": "Verschlüsselt", - "@encrypted": { - "type": "String", - "placeholders": {} - }, - "encryption": "Verschlüsselung", - "@encryption": { - "type": "String", - "placeholders": {} - }, - "encryptionNotEnabled": "Verschlüsselung ist nicht aktiviert", - "@encryptionNotEnabled": { - "type": "String", - "placeholders": {} - }, - "endedTheCall": "{senderName} hat den Anruf beendet", - "@endedTheCall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "enterAnEmailAddress": "Gib eine E-Mail-Adresse ein", - "@enterAnEmailAddress": { - "type": "String", - "placeholders": {} - }, - "enterYourHomeserver": "Gib Deinen Homeserver ein", - "@enterYourHomeserver": { - "type": "String", - "placeholders": {} - }, - "errorObtainingLocation": "Fehler beim Suchen des Standortes: {error}", - "@errorObtainingLocation": { - "type": "String", - "placeholders": { - "error": { - "type": "String" - } - } - }, - "everythingReady": "Alles fertig!", - "@everythingReady": { - "type": "String", - "placeholders": {} - }, - "extremeOffensive": "Extrem beleidigend", - "@extremeOffensive": { - "type": "String", - "placeholders": {} - }, - "fileName": "Dateiname", - "@fileName": { - "type": "String", - "placeholders": {} - }, - "fluffychat": "FluffyChat", - "@fluffychat": { - "type": "String", - "placeholders": {} - }, - "fontSize": "Schriftgröße", - "@fontSize": { - "type": "String", - "placeholders": {} - }, - "forward": "Weiterleiten", - "@forward": { - "type": "String", - "placeholders": {} - }, - "fromJoining": "Ab dem Beitritt", - "@fromJoining": { - "type": "String", - "placeholders": {} - }, - "fromTheInvitation": "Ab der Einladung", - "@fromTheInvitation": { - "type": "String", - "placeholders": {} - }, - "goToTheNewRoom": "Zum neuen Raum wechseln", - "@goToTheNewRoom": { - "type": "String", - "placeholders": {} - }, - "group": "Gruppe", - "@group": { - "type": "String", - "placeholders": {} - }, - "groupIsPublic": "Öffentliche Gruppe", - "@groupIsPublic": { - "type": "String", - "placeholders": {} - }, - "groups": "Gruppen", - "@groups": { - "type": "String", - "placeholders": {} - }, - "groupWith": "Gruppe mit {displayname}", - "@groupWith": { - "type": "String", - "placeholders": { - "displayname": { - "type": "String" - } - } - }, - "guestsAreForbidden": "Gäste sind verboten", - "@guestsAreForbidden": { - "type": "String", - "placeholders": {} - }, - "guestsCanJoin": "Gäste dürfen beitreten", - "@guestsCanJoin": { - "type": "String", - "placeholders": {} - }, - "hasWithdrawnTheInvitationFor": "{username} hat die Einladung für {targetName} zurückgezogen", - "@hasWithdrawnTheInvitationFor": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "help": "Hilfe", - "@help": { - "type": "String", - "placeholders": {} - }, - "hideRedactedEvents": "Gelöschte Nachrichten ausblenden", - "@hideRedactedEvents": { - "type": "String", - "placeholders": {} - }, - "hideUnknownEvents": "Unbekannte Ereignisse ausblenden", - "@hideUnknownEvents": { - "type": "String", - "placeholders": {} - }, - "howOffensiveIsThisContent": "Wie beleidigend ist dieser Inhalt?", - "@howOffensiveIsThisContent": { - "type": "String", - "placeholders": {} - }, - "id": "ID", - "@id": { - "type": "String", - "placeholders": {} - }, - "identity": "Identität", - "@identity": { - "type": "String", - "placeholders": {} - }, - "ignore": "Ignorieren", - "@ignore": { - "type": "String", - "placeholders": {} - }, - "ignoredUsers": "Ignorierte Personen", - "@ignoredUsers": { - "type": "String", - "placeholders": {} - }, - "iHaveClickedOnLink": "Ich habe den Link angeklickt", - "@iHaveClickedOnLink": { - "type": "String", - "placeholders": {} - }, - "incorrectPassphraseOrKey": "Falsches Passwort oder Wiederherstellungsschlüssel", - "@incorrectPassphraseOrKey": { - "type": "String", - "placeholders": {} - }, - "inoffensive": "Harmlos", - "@inoffensive": { - "type": "String", - "placeholders": {} - }, - "inviteContact": "Kontakt einladen", - "@inviteContact": { - "type": "String", - "placeholders": {} - }, - "inviteContactToGroup": "Kontakt in die Gruppe {groupName} einladen", - "@inviteContactToGroup": { - "type": "String", - "placeholders": { - "groupName": { - "type": "String" - } - } - }, - "invited": "Eingeladen", - "@invited": { - "type": "String", - "placeholders": {} - }, - "invitedUser": "📩 {username} hat {targetName} eingeladen", - "@invitedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "invitedUsersOnly": "Nur eingeladene Mitglieder", - "@invitedUsersOnly": { - "type": "String", - "placeholders": {} - }, - "inviteForMe": "Einladung für mich", - "@inviteForMe": { - "type": "String", - "placeholders": {} - }, - "inviteText": "{username} hat Dich zu FluffyChat eingeladen. \n1. Gehe auf fluffychat.im und installiere die App \n2. Melde Dich in der App an \n3. Öffne den Einladungslink: \n {link}", - "@inviteText": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "link": { - "type": "String" - } - } - }, - "isTyping": "schreibt …", - "@isTyping": { - "type": "String", - "placeholders": {} - }, - "joinedTheChat": "👋 {username} ist dem Chat beigetreten", - "@joinedTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "joinRoom": "Raum beitreten", - "@joinRoom": { - "type": "String", - "placeholders": {} - }, - "kicked": "👞 {username} hat {targetName} hinausgeworfen", - "@kicked": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "kickedAndBanned": "🙅 {username} hat {targetName} hinausgeworfen und verbannt", - "@kickedAndBanned": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "kickFromChat": "Aus dem Chat hinauswerfen", - "@kickFromChat": { - "type": "String", - "placeholders": {} - }, - "lastActiveAgo": "Zuletzt aktiv: {localizedTimeShort}", - "@lastActiveAgo": { - "type": "String", - "placeholders": { - "localizedTimeShort": { - "type": "String" - } - } - }, - "leave": "Verlassen", - "@leave": { - "type": "String", - "placeholders": {} - }, - "leftTheChat": "Hat den Chat verlassen", - "@leftTheChat": { - "type": "String", - "placeholders": {} - }, - "license": "Lizenz", - "@license": { - "type": "String", - "placeholders": {} - }, - "lightTheme": "Hell", - "@lightTheme": { - "type": "String", - "placeholders": {} - }, - "loadCountMoreParticipants": "{count} weitere Mitglieder laden", - "@loadCountMoreParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "loadingPleaseWait": "Lade … Bitte warten.", - "@loadingPleaseWait": { - "type": "String", - "placeholders": {} - }, - "loadMore": "Mehr laden …", - "@loadMore": { - "type": "String", - "placeholders": {} - }, - "locationDisabledNotice": "Standort ist deaktiviert. Bitte aktivieren, um den Standort teilen zu können.", - "@locationDisabledNotice": { - "type": "String", - "placeholders": {} - }, - "locationPermissionDeniedNotice": "Standort-Berechtigung wurde abgelehnt. Bitte akzeptieren, um den Standort teilen zu können.", - "@locationPermissionDeniedNotice": { - "type": "String", - "placeholders": {} - }, - "login": "Anmelden", - "@login": { - "type": "String", - "placeholders": {} - }, - "logInTo": "Bei {homeserver} anmelden", - "@logInTo": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "logout": "Abmelden", - "@logout": { - "type": "String", - "placeholders": {} - }, - "memberChanges": "Änderungen der Mitglieder", - "@memberChanges": { - "type": "String", - "placeholders": {} - }, - "mention": "Erwähnen", - "@mention": { - "type": "String", - "placeholders": {} - }, - "messages": "Nachrichten", - "@messages": { - "type": "String", - "placeholders": {} - }, - "moderator": "Moderator", - "@moderator": { - "type": "String", - "placeholders": {} - }, - "muteChat": "Stummschalten", - "@muteChat": { - "type": "String", - "placeholders": {} - }, - "needPantalaimonWarning": "Bitte beachte, dass du Pantalaimon brauchst, um Ende-zu-Ende-Verschlüsselung benutzen zu können.", - "@needPantalaimonWarning": { - "type": "String", - "placeholders": {} - }, - "newChat": "Neuer Chat", - "@newChat": { - "type": "String", - "placeholders": {} - }, - "newMessageInFluffyChat": "💬 Neue Nachricht in FluffyChat", - "@newMessageInFluffyChat": { - "type": "String", - "placeholders": {} - }, - "newVerificationRequest": "Neue Verifikationsanfrage!", - "@newVerificationRequest": { - "type": "String", - "placeholders": {} - }, - "next": "Weiter", - "@next": { - "type": "String", - "placeholders": {} - }, - "no": "Nein", - "@no": { - "type": "String", - "placeholders": {} - }, - "noConnectionToTheServer": "Keine Verbindung zum Server", - "@noConnectionToTheServer": { - "type": "String", - "placeholders": {} - }, - "noEmotesFound": "Keine Emoticons gefunden. 😕", - "@noEmotesFound": { - "type": "String", - "placeholders": {} - }, - "noEncryptionForPublicRooms": "Du kannst die Verschlüsselung erst aktivieren, sobald dieser Raum nicht mehr öffentlich zugänglich ist.", - "@noEncryptionForPublicRooms": { - "type": "String", - "placeholders": {} - }, - "noGoogleServicesWarning": "Firebase Cloud Messaging scheint auf deinem Gerät nicht verfügbar zu sein. Um trotzdem Push-Benachrichtigungen zu erhalten, empfehlen wir die Installation von ntfy. Mit ntfy oder einem anderen Unified-Push-Anbieter kannst du Push-Benachrichtigungen datensicher empfangen. Du kannst ntfy im PlayStore oder bei F-Droid herunterladen.", - "@noGoogleServicesWarning": { - "type": "String", - "placeholders": {} - }, - "none": "Keiner", - "@none": { - "type": "String", - "placeholders": {} - }, - "noPasswordRecoveryDescription": "Du hast bisher keine Möglichkeit hinzugefügt, um dein Passwort zurückzusetzen.", - "@noPasswordRecoveryDescription": { - "type": "String", - "placeholders": {} - }, - "noPermission": "Keine Berechtigung", - "@noPermission": { - "type": "String", - "placeholders": {} - }, - "noRoomsFound": "Keine Räume gefunden …", - "@noRoomsFound": { - "type": "String", - "placeholders": {} - }, - "notifications": "Benachrichtigungen", - "@notifications": { - "type": "String", - "placeholders": {} - }, - "notificationsEnabledForThisAccount": "Benachrichtigungen für dieses Konto aktiviert", - "@notificationsEnabledForThisAccount": { - "type": "String", - "placeholders": {} - }, - "numUsersTyping": "{count} Mitglieder schreiben …", - "@numUsersTyping": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "obtainingLocation": "Standort wird ermittelt …", - "@obtainingLocation": { - "type": "String", - "placeholders": {} - }, - "offensive": "Beleidigend", - "@offensive": { - "type": "String", - "placeholders": {} - }, - "offline": "Offline", - "@offline": { - "type": "String", - "placeholders": {} - }, - "ok": "Ok", - "@ok": { - "type": "String", - "placeholders": {} - }, - "online": "Online", - "@online": { - "type": "String", - "placeholders": {} - }, - "onlineKeyBackupEnabled": "Online-Schlüsselsicherung ist aktiviert", - "@onlineKeyBackupEnabled": { - "type": "String", - "placeholders": {} - }, - "oopsPushError": "Hoppla! Leider ist beim Einrichten der Push-Benachrichtigungen ein Fehler aufgetreten.", - "@oopsPushError": { - "type": "String", - "placeholders": {} - }, - "oopsSomethingWentWrong": "Hoppla, da ist etwas schiefgelaufen…", - "@oopsSomethingWentWrong": { - "type": "String", - "placeholders": {} - }, - "openAppToReadMessages": "App öffnen, um Nachrichten zu lesen", - "@openAppToReadMessages": { - "type": "String", - "placeholders": {} - }, - "openCamera": "Kamera öffnen", - "@openCamera": { - "type": "String", - "placeholders": {} - }, - "openInMaps": "In Maps öffnen", - "@openInMaps": { - "type": "String", - "placeholders": {} - }, - "or": "Oder", - "@or": { - "type": "String", - "placeholders": {} - }, - "participant": "Mitglied", - "@participant": { - "type": "String", - "placeholders": {} - }, - "passphraseOrKey": "Passwort oder Wiederherstellungsschlüssel", - "@passphraseOrKey": { - "type": "String", - "placeholders": {} - }, - "password": "Passwort", - "@password": { - "type": "String", - "placeholders": {} - }, - "passwordForgotten": "Passwort vergessen", - "@passwordForgotten": { - "type": "String", - "placeholders": {} - }, - "passwordHasBeenChanged": "Passwort wurde geändert", - "@passwordHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "passwordRecovery": "Passwort wiederherstellen", - "@passwordRecovery": { - "type": "String", - "placeholders": {} - }, - "people": "Personen", - "@people": { - "type": "String", - "placeholders": {} - }, - "pickImage": "Bild wählen", - "@pickImage": { - "type": "String", - "placeholders": {} - }, - "pin": "Anpinnen", - "@pin": { - "type": "String", - "placeholders": {} - }, - "play": "{fileName} abspielen", - "@play": { - "type": "String", - "placeholders": { - "fileName": { - "type": "String" - } - } - }, - "pleaseChoose": "Bitte wählen", - "@pleaseChoose": { - "type": "String", - "placeholders": {} - }, - "pleaseChooseAPasscode": "Bitte einen Code festlegen", - "@pleaseChooseAPasscode": { - "type": "String", - "placeholders": {} - }, - "pleaseClickOnLink": "Bitte auf den Link in der E-Mail klicken und dann fortfahren.", - "@pleaseClickOnLink": { - "type": "String", - "placeholders": {} - }, - "pleaseEnter4Digits": "Bitte 4 Ziffern eingeben oder leer lassen, um die Anwendungssperre zu deaktivieren.", - "@pleaseEnter4Digits": { - "type": "String", - "placeholders": {} - }, - "pleaseEnterYourPassword": "Bitte dein Passwort eingeben", - "@pleaseEnterYourPassword": { - "type": "String", - "placeholders": {} - }, - "pleaseEnterYourPin": "Bitte gib deine Pin ein", - "@pleaseEnterYourPin": { - "type": "String", - "placeholders": {} - }, - "pleaseEnterYourUsername": "Bitte deinen Benutzernamen eingeben", - "@pleaseEnterYourUsername": { - "type": "String", - "placeholders": {} - }, - "pleaseFollowInstructionsOnWeb": "Bitte folge den Anweisungen auf der Website und tippe auf Weiter.", - "@pleaseFollowInstructionsOnWeb": { - "type": "String", - "placeholders": {} - }, - "privacy": "Privatsphäre", - "@privacy": { - "type": "String", - "placeholders": {} - }, - "publicRooms": "Öffentliche Räume", - "@publicRooms": { - "type": "String", - "placeholders": {} - }, - "pushRules": "Push-Regeln", - "@pushRules": { - "type": "String", - "placeholders": {} - }, - "reason": "Grund", - "@reason": { - "type": "String", - "placeholders": {} - }, - "recording": "Aufnahme", - "@recording": { - "type": "String", - "placeholders": {} - }, - "redactedAnEvent": "{username} hat ein Ereignis gelöscht", - "@redactedAnEvent": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "redactMessage": "Nachricht löschen", - "@redactMessage": { - "type": "String", - "placeholders": {} - }, - "register": "Registrieren", - "@register": { - "type": "String", - "placeholders": {} - }, - "reject": "Ablehnen", - "@reject": { - "type": "String", - "placeholders": {} - }, - "rejectedTheInvitation": "{username} hat die Einladung abgelehnt", - "@rejectedTheInvitation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "rejoin": "Wieder beitreten", - "@rejoin": { - "type": "String", - "placeholders": {} - }, - "remove": "Entfernen", - "@remove": { - "type": "String", - "placeholders": {} - }, - "removeAllOtherDevices": "Alle anderen Geräte entfernen", - "@removeAllOtherDevices": { - "type": "String", - "placeholders": {} - }, - "removedBy": "Entfernt von {username}", - "@removedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "removeDevice": "Gerät entfernen", - "@removeDevice": { - "type": "String", - "placeholders": {} - }, - "unbanFromChat": "Verbannung aufheben", - "@unbanFromChat": { - "type": "String", - "placeholders": {} - }, - "removeYourAvatar": "Deinen Avatar löschen", - "@removeYourAvatar": { - "type": "String", - "placeholders": {} - }, - "replaceRoomWithNewerVersion": "Raum mit neuer Version ersetzen", - "@replaceRoomWithNewerVersion": { - "type": "String", - "placeholders": {} - }, - "reply": "Antworten", - "@reply": { - "type": "String", - "placeholders": {} - }, - "reportMessage": "Nachricht melden", - "@reportMessage": { - "type": "String", - "placeholders": {} - }, - "requestPermission": "Berechtigung anfragen", - "@requestPermission": { - "type": "String", - "placeholders": {} - }, - "roomHasBeenUpgraded": "Der Raum wurde ge-upgraded", - "@roomHasBeenUpgraded": { - "type": "String", - "placeholders": {} - }, - "roomVersion": "Raumversion", - "@roomVersion": { - "type": "String", - "placeholders": {} - }, - "saveFile": "Datei speichern", - "@saveFile": { - "type": "String", - "placeholders": {} - }, - "search": "Suchen", - "@search": { - "type": "String", - "placeholders": {} - }, - "security": "Sicherheit", - "@security": { - "type": "String", - "placeholders": {} - }, - "seenByUser": "Gelesen von {username}", - "@seenByUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "send": "Senden", - "@send": { - "type": "String", - "placeholders": {} - }, - "sendAMessage": "Nachricht schreiben", - "@sendAMessage": { - "type": "String", - "placeholders": {} - }, - "sendAsText": "Sende als Text", - "@sendAsText": { - "type": "String" - }, - "sendAudio": "Sende Audiodatei", - "@sendAudio": { - "type": "String", - "placeholders": {} - }, - "sendFile": "Datei senden", - "@sendFile": { - "type": "String", - "placeholders": {} - }, - "sendImage": "Bild senden", - "@sendImage": { - "type": "String", - "placeholders": {} - }, - "sendMessages": "Nachrichten senden", - "@sendMessages": { - "type": "String", - "placeholders": {} - }, - "sendOriginal": "Sende Original", - "@sendOriginal": { - "type": "String", - "placeholders": {} - }, - "sendSticker": "Sticker senden", - "@sendSticker": { - "type": "String", - "placeholders": {} - }, - "sendVideo": "Sende Video", - "@sendVideo": { - "type": "String", - "placeholders": {} - }, - "sentAFile": "📁 {username} hat eine Datei gesendet", - "@sentAFile": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "sentAnAudio": "🎤 {username} hat eine Audio-Datei gesendet", - "@sentAnAudio": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "sentAPicture": "🖼️ {username} hat ein Bild gesendet", - "@sentAPicture": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "sentASticker": "😊 {username} hat einen Sticker gesendet", - "@sentASticker": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "sentAVideo": "🎥 {username} hat ein Video gesendet", - "@sentAVideo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "sentCallInformations": "{senderName} hat Anrufinformationen geschickt", - "@sentCallInformations": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "setAsCanonicalAlias": "Als Haupt-Alias festlegen", - "@setAsCanonicalAlias": { - "type": "String", - "placeholders": {} - }, - "setCustomEmotes": "Eigene Emoticons einstellen", - "@setCustomEmotes": { - "type": "String", - "placeholders": {} - }, - "setInvitationLink": "Einladungslink festlegen", - "@setInvitationLink": { - "type": "String", - "placeholders": {} - }, - "setPermissionsLevel": "Berechtigungsstufe einstellen", - "@setPermissionsLevel": { - "type": "String", - "placeholders": {} - }, - "setStatus": "Status ändern", - "@setStatus": { - "type": "String", - "placeholders": {} - }, - "settings": "Einstellungen", - "@settings": { - "type": "String", - "placeholders": {} - }, - "share": "Teilen", - "@share": { - "type": "String", - "placeholders": {} - }, - "sharedTheLocation": "{username} hat den Standort geteilt", - "@sharedTheLocation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "shareLocation": "Standort teilen", - "@shareLocation": { - "type": "String", - "placeholders": {} - }, - "showPassword": "Passwort anzeigen", - "@showPassword": { - "type": "String", - "placeholders": {} - }, - "singlesignon": "Einmalige Anmeldung", - "@singlesignon": { - "type": "String", - "placeholders": {} - }, - "skip": "Überspringe", - "@skip": { - "type": "String", - "placeholders": {} - }, - "sourceCode": "Quellcode", - "@sourceCode": { - "type": "String", - "placeholders": {} - }, - "spaceIsPublic": "Space ist öffentlich", - "@spaceIsPublic": { - "type": "String", - "placeholders": {} - }, - "spaceName": "Space-Name", - "@spaceName": { - "type": "String", - "placeholders": {} - }, - "startedACall": "{senderName} hat einen Anruf getätigt", - "@startedACall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "status": "Status", - "@status": { - "type": "String", - "placeholders": {} - }, - "statusExampleMessage": "Wie geht es dir heute?", - "@statusExampleMessage": { - "type": "String", - "placeholders": {} - }, - "submit": "Absenden", - "@submit": { - "type": "String", - "placeholders": {} - }, - "synchronizingPleaseWait": "Synchronisiere... Bitte warten.", - "@synchronizingPleaseWait": { - "type": "String", - "placeholders": {} - }, - "systemTheme": "System", - "@systemTheme": { - "type": "String", - "placeholders": {} - }, - "theyDontMatch": "Stimmen nicht überein", - "@theyDontMatch": { - "type": "String", - "placeholders": {} - }, - "theyMatch": "Stimmen überein", - "@theyMatch": { - "type": "String", - "placeholders": {} - }, - "title": "FluffyChat", - "@title": { - "description": "Title for the application", - "type": "String", - "placeholders": {} - }, - "toggleFavorite": "Favorite umschalten", - "@toggleFavorite": { - "type": "String", - "placeholders": {} - }, - "toggleMuted": "Stummgeschaltete umschalten", - "@toggleMuted": { - "type": "String", - "placeholders": {} - }, - "toggleUnread": "Markieren als gelesen/ungelesen", - "@toggleUnread": { - "type": "String", - "placeholders": {} - }, - "tooManyRequestsWarning": "Zu viele Anfragen. Bitte versuche es später noch einmal!", - "@tooManyRequestsWarning": { - "type": "String", - "placeholders": {} - }, - "transferFromAnotherDevice": "Von anderem Gerät übertragen", - "@transferFromAnotherDevice": { - "type": "String", - "placeholders": {} - }, - "tryToSendAgain": "Noch mal versuchen zu senden", - "@tryToSendAgain": { - "type": "String", - "placeholders": {} - }, - "unavailable": "Nicht verfügbar", - "@unavailable": { - "type": "String", - "placeholders": {} - }, - "unbannedUser": "{username} hat die Verbannung von {targetName} aufgehoben", - "@unbannedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "unblockDevice": "Geräteblockierung aufheben", - "@unblockDevice": { - "type": "String", - "placeholders": {} - }, - "unknownDevice": "Unbekanntes Gerät", - "@unknownDevice": { - "type": "String", - "placeholders": {} - }, - "unknownEncryptionAlgorithm": "Unbekannter Verschlüsselungsalgorithmus", - "@unknownEncryptionAlgorithm": { - "type": "String", - "placeholders": {} - }, - "unknownEvent": "Unbekanntes Ereignis '{type}'", - "@unknownEvent": { - "type": "String", - "placeholders": { - "type": { - "type": "String" - } - } - }, - "unmuteChat": "Stumm aus", - "@unmuteChat": { - "type": "String", - "placeholders": {} - }, - "unpin": "Nicht mehr anpinnen", - "@unpin": { - "type": "String", - "placeholders": {} - }, - "unreadChats": "{unreadCount, plural, =1{1 ungelesene Unterhaltung} other{{unreadCount} ungelesene Unterhaltungen}}", - "@unreadChats": { - "type": "String", - "placeholders": { - "unreadCount": { - "type": "int" - } - } - }, - "userAndOthersAreTyping": "{username} und {count} andere schreiben …", - "@userAndOthersAreTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "count": { - "type": "int" - } - } - }, - "userAndUserAreTyping": "{username} und {username2} schreiben …", - "@userAndUserAreTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "username2": { - "type": "String" - } - } - }, - "userIsTyping": "{username} schreibt …", - "@userIsTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "userLeftTheChat": "🚪 {username} hat den Chat verlassen", - "@userLeftTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "username": "Benutzername", - "@username": { - "type": "String", - "placeholders": {} - }, - "userSentUnknownEvent": "{username} hat ein {type}-Ereignis gesendet", - "@userSentUnknownEvent": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "type": { - "type": "String" - } - } - }, - "verified": "Verifiziert", - "@verified": { - "type": "String", - "placeholders": {} - }, - "verify": "Verifizieren", - "@verify": { - "type": "String", - "placeholders": {} - }, - "verifyStart": "Starte Verifikation", - "@verifyStart": { - "type": "String", - "placeholders": {} - }, - "verifySuccess": "Erfolgreich verifiziert!", - "@verifySuccess": { - "type": "String", - "placeholders": {} - }, - "verifyTitle": "Anderes Konto wird verifiziert", - "@verifyTitle": { - "type": "String", - "placeholders": {} - }, - "videoCall": "Videoanruf", - "@videoCall": { - "type": "String", - "placeholders": {} - }, - "visibilityOfTheChatHistory": "Sichtbarkeit des Chat-Verlaufs", - "@visibilityOfTheChatHistory": { - "type": "String", - "placeholders": {} - }, - "visibleForAllParticipants": "Sichtbar für alle Mitglieder", - "@visibleForAllParticipants": { - "type": "String", - "placeholders": {} - }, - "visibleForEveryone": "Für jeden sichtbar", - "@visibleForEveryone": { - "type": "String", - "placeholders": {} - }, - "voiceMessage": "Sprachnachricht", - "@voiceMessage": { - "type": "String", - "placeholders": {} - }, - "waitingPartnerAcceptRequest": "Warte darauf, dass der Partner die Anfrage annimmt …", - "@waitingPartnerAcceptRequest": { - "type": "String", - "placeholders": {} - }, - "waitingPartnerEmoji": "Warte darauf, dass der Partner die Emoji annimmt …", - "@waitingPartnerEmoji": { - "type": "String", - "placeholders": {} - }, - "waitingPartnerNumbers": "Warten, dass der Partner die Zahlen annimmt …", - "@waitingPartnerNumbers": { - "type": "String", - "placeholders": {} - }, - "wallpaper": "Hintergrund:", - "@wallpaper": { - "type": "String", - "placeholders": {} - }, - "warning": "Achtung!", - "@warning": { - "type": "String", - "placeholders": {} - }, - "weSentYouAnEmail": "Wir haben dir eine E-Mail gesendet", - "@weSentYouAnEmail": { - "type": "String", - "placeholders": {} - }, - "whoCanPerformWhichAction": "Wer kann welche Aktion ausführen", - "@whoCanPerformWhichAction": { - "type": "String", - "placeholders": {} - }, - "whoIsAllowedToJoinThisGroup": "Wer darf der Gruppe beitreten", - "@whoIsAllowedToJoinThisGroup": { - "type": "String", - "placeholders": {} - }, - "whyDoYouWantToReportThis": "Warum willst du dies melden?", - "@whyDoYouWantToReportThis": { - "type": "String", - "placeholders": {} - }, - "wipeChatBackup": "Den Chat-Backup löschen, um einen neuen Wiederherstellungsschlüssel zu erstellen?", - "@wipeChatBackup": { - "type": "String", - "placeholders": {} - }, - "withTheseAddressesRecoveryDescription": "Mit diesen Adressen kannst du dein Passwort wiederherstellen, wenn du es vergessen hast.", - "@withTheseAddressesRecoveryDescription": { - "type": "String", - "placeholders": {} - }, - "writeAMessage": "Schreibe eine Nachricht …", - "@writeAMessage": { - "type": "String", - "placeholders": {} - }, - "yes": "Ja", - "@yes": { - "type": "String", - "placeholders": {} - }, - "you": "Du", - "@you": { - "type": "String", - "placeholders": {} - }, - "youAreNoLongerParticipatingInThisChat": "Du bist kein Mitglied mehr in diesem Chat", - "@youAreNoLongerParticipatingInThisChat": { - "type": "String", - "placeholders": {} - }, - "youHaveBeenBannedFromThisChat": "Du wurdest aus dem Chat verbannt", - "@youHaveBeenBannedFromThisChat": { - "type": "String", - "placeholders": {} - }, - "yourPublicKey": "Dein öffentlicher Schlüssel", - "@yourPublicKey": { - "type": "String", - "placeholders": {} - }, - "noMatrixServer": "{server1} ist kein Matrix-Server, stattdessen {server2} benutzen?", - "@noMatrixServer": { - "type": "String", - "placeholders": { - "server1": { - "type": "String" - }, - "server2": { - "type": "String" - } - } - }, - "scanQrCode": "QR-Code scannen", - "@scanQrCode": {}, - "chatHasBeenAddedToThisSpace": "Chat wurde zum Space hinzugefügt", - "@chatHasBeenAddedToThisSpace": {}, - "autoplayImages": "Animierte Sticker und Emotes automatisch abspielen", - "@autoplayImages": { - "type": "String", - "placeholder": {} - }, - "addToSpace": "Zum Space hinzufügen", - "@addToSpace": {}, - "serverRequiresEmail": "Dieser Server muss deine E-Mail-Adresse für die Registrierung überprüfen.", - "@serverRequiresEmail": {}, - "enableMultiAccounts": "(BETA) Aktiviere Multi-Accounts für dieses Gerät", - "@enableMultiAccounts": {}, - "bundleName": "Name des Bundles", - "@bundleName": {}, - "removeFromBundle": "Von diesem Bundle entfernen", - "@removeFromBundle": {}, - "addToBundle": "Zu einem Bundle hinzufügen", - "@addToBundle": {}, - "editBundlesForAccount": "Bundles für dieses Konto bearbeiten", - "@editBundlesForAccount": {}, - "addAccount": "Konto hinzufügen", - "@addAccount": {}, - "oneClientLoggedOut": "Einer deiner Clients wurde abgemeldet", - "@oneClientLoggedOut": {}, - "homeserver": "Homeserver", - "@homeserver": {}, - "sendOnEnter": "Senden mit Enter", - "@sendOnEnter": {}, - "link": "Link", - "@link": {}, - "yourChatBackupHasBeenSetUp": "Dein Chat-Backup wurde eingerichtet.", - "@yourChatBackupHasBeenSetUp": {}, - "unverified": "Unverifiziert", - "@unverified": {}, - "messageInfo": "Nachrichten-Info", - "@messageInfo": {}, - "time": "Zeit", - "@time": {}, - "messageType": "Nachrichtentyp", - "@messageType": {}, - "sender": "Absender:in", - "@sender": {}, - "openGallery": "Galerie öffnen", - "@openGallery": {}, - "removeFromSpace": "Aus dem Space entfernen", - "@removeFromSpace": {}, - "addToSpaceDescription": "Wähle einen Space aus, um diesen Chat hinzuzufügen.", - "@addToSpaceDescription": {}, - "start": "Start", - "@start": {}, - "repeatPassword": "Passwort wiederholen", - "@repeatPassword": {}, - "commandHint_dm": "Starte einen direkten Chat\nBenutze --no-encryption, um die Verschlüsselung auszuschalten", - "@commandHint_dm": { - "type": "String", - "description": "Usage hint for the command /dm" - }, - "commandHint_discardsession": "Sitzung verwerfen", - "@commandHint_discardsession": { - "type": "String", - "description": "Usage hint for the command /discardsession" - }, - "commandHint_clearcache": "Zwischenspeicher löschen", - "@commandHint_clearcache": { - "type": "String", - "description": "Usage hint for the command /clearcache" - }, - "commandHint_create": "Erstelle ein leeren Gruppenchat\nBenutze --no-encryption, um die Verschlüsselung auszuschalten", - "@commandHint_create": { - "type": "String", - "description": "Usage hint for the command /create" - }, - "openVideoCamera": "Video aufnehmen", - "@openVideoCamera": { - "type": "String", - "placeholders": {} - }, - "videoWithSize": "Video ({size})", - "@videoWithSize": { - "type": "String", - "placeholders": { - "size": { - "type": "String" - } - } - }, - "publish": "Veröffentlichen", - "@publish": {}, - "pinMessage": "An Raum anheften", - "@pinMessage": {}, - "emojis": "Emojis", - "@emojis": {}, - "placeCall": "Anruf tätigen", - "@placeCall": {}, - "voiceCall": "Sprachanruf", - "@voiceCall": {}, - "unsupportedAndroidVersion": "Nicht unterstützte Android-Version", - "@unsupportedAndroidVersion": {}, - "videoCallsBetaWarning": "Bitte beachte, dass sich Videoanrufe derzeit in der Beta-Phase befinden. Sie funktionieren möglicherweise nicht wie erwartet oder überhaupt nicht auf allen Plattformen.", - "@videoCallsBetaWarning": {}, - "emailOrUsername": "E-Mail oder Benutzername", - "@emailOrUsername": {}, - "unsupportedAndroidVersionLong": "Diese Funktion erfordert eine neuere Android-Version. Bitte suche nach Updates oder prüfe die Lineage-OS-Unterstützung.", - "@unsupportedAndroidVersionLong": {}, - "experimentalVideoCalls": "Experimentelle Videoanrufe", - "@experimentalVideoCalls": {}, - "reactedWith": "{sender} reagierte mit {reaction}", - "@reactedWith": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - }, - "reaction": { - "type": "String" - } - } - }, - "markAsRead": "Als gelesen markiert", - "@markAsRead": {}, - "reportUser": "Benutzer melden", - "@reportUser": {}, - "openChat": "Chat öffnen", - "@openChat": {}, - "confirmEventUnpin": "Möchtest du das Ereignis wirklich dauerhaft lösen?", - "@confirmEventUnpin": {}, - "dismiss": "Verwerfen", - "@dismiss": {}, - "switchToAccount": "Zu Konto {number} wechseln", - "@switchToAccount": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "nextAccount": "Nächstes Konto", - "@nextAccount": {}, - "widgetJitsi": "Jitsi Meet", - "@widgetJitsi": {}, - "widgetCustom": "Angepasst", - "@widgetCustom": {}, - "widgetEtherpad": "Textnotiz", - "@widgetEtherpad": {}, - "addWidget": "Widget hinzufügen", - "@addWidget": {}, - "widgetVideo": "Video", - "@widgetVideo": {}, - "widgetName": "Name", - "@widgetName": {}, - "widgetUrlError": "Das ist keine gültige URL.", - "@widgetUrlError": {}, - "errorAddingWidget": "Fehler beim Hinzufügen des Widgets.", - "@errorAddingWidget": {}, - "previousAccount": "Vorheriges Konto", - "@previousAccount": {}, - "separateChatTypes": "Separate Direktchats und Gruppen", - "@separateChatTypes": { - "type": "String", - "placeholders": {} - }, - "widgetNameError": "Bitte gib einen Anzeigenamen an.", - "@widgetNameError": {}, - "youKicked": "👞 Du hast {user} rausgeworfen", - "@youKicked": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "youKickedAndBanned": "🙅 Du hast {user} rausgeworfen und verbannt", - "@youKickedAndBanned": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "youUnbannedUser": "Du hast die Verbannung von {user} rückgängig gemacht", - "@youUnbannedUser": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "youRejectedTheInvitation": "Du hast die Einladung abgelehnt", - "@youRejectedTheInvitation": {}, - "youJoinedTheChat": "Du bist dem Chat beigetreten", - "@youJoinedTheChat": {}, - "youAcceptedTheInvitation": "👍 Du hast die Einladung angenommen", - "@youAcceptedTheInvitation": {}, - "youBannedUser": "Du hast den {user} verbannt", - "@youBannedUser": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "youHaveWithdrawnTheInvitationFor": "Du hast die Einladung für {user} zurückgezogen", - "@youHaveWithdrawnTheInvitationFor": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "youInvitedBy": "📩 Du wurdest von {user} eingeladen", - "@youInvitedBy": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "youInvitedUser": "📩 Du hast {user} eingeladen", - "@youInvitedUser": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "recoveryKey": "Wiederherstellungs-Schlüssel", - "@recoveryKey": {}, - "recoveryKeyLost": "Wiederherstellungsschlüssel verloren?", - "@recoveryKeyLost": {}, - "user": "Benutzer", - "@user": {}, - "custom": "Benutzerdefiniert", - "@custom": {}, - "storeInAndroidKeystore": "Im Android KeyStore speichern", - "@storeInAndroidKeystore": {}, - "storeSecurlyOnThisDevice": "Auf diesem Gerät sicher speichern", - "@storeSecurlyOnThisDevice": {}, - "dehydrate": "Sitzung exportieren und Gerät löschen", - "@dehydrate": {}, - "dehydrateWarning": "Diese Aktion kann nicht rückgängig gemacht werden. Stelle sicher, dass du die Sicherungsdatei sicher aufbewahrst.", - "@dehydrateWarning": {}, - "dehydrateTor": "TOR-Benutzer: Sitzung exportieren", - "@dehydrateTor": {}, - "dehydrateTorLong": "Für TOR-Benutzer wird empfohlen, die Sitzung zu exportieren, bevor das Fenster geschlossen wird.", - "@dehydrateTorLong": {}, - "hydrateTor": "TOR-Benutzer: Session-Export importieren", - "@hydrateTor": {}, - "hydrate": "Aus Sicherungsdatei wiederherstellen", - "@hydrate": {}, - "indexedDbErrorTitle": "Probleme im Privatmodus", - "@indexedDbErrorTitle": {}, - "unlockOldMessages": "Entsperre alte Nachrichten", - "@unlockOldMessages": {}, - "pleaseEnterRecoveryKeyDescription": "Um deine alten Nachrichten zu entsperren, gib bitte den Wiederherstellungsschlüssel ein, der in einer früheren Sitzung generiert wurde. Dein Wiederherstellungsschlüssel ist NICHT dein Passwort.", - "@pleaseEnterRecoveryKeyDescription": {}, - "saveKeyManuallyDescription": "Speicher diesen Schlüssel manuell, indem du den Systemfreigabedialog oder die Zwischenablage auslöst.", - "@saveKeyManuallyDescription": {}, - "hydrateTorLong": "Hast du deine Sitzung das letzte Mal auf TOR exportiert? Importiere sie schnell und chatte weiter.", - "@hydrateTorLong": {}, - "pleaseEnterRecoveryKey": "Bitte gib deinen Wiederherstellungsschlüssel ein:", - "@pleaseEnterRecoveryKey": {}, - "countFiles": "{count} Dateien", - "@countFiles": { - "placeholders": { - "count": { - "type": "int" - } - } - }, - "users": "Benutzer", - "@users": {}, - "storeInSecureStorageDescription": "Speicher den Wiederherstellungsschlüssel im sicheren Speicher dieses Geräts.", - "@storeInSecureStorageDescription": {}, - "storeInAppleKeyChain": "Im Apple KeyChain speichern", - "@storeInAppleKeyChain": {}, - "indexedDbErrorLong": "Die Nachrichtenspeicherung ist im privaten Modus standardmäßig leider nicht aktiviert.\nBitte besuche\n- about:config\n- Setze dom.indexedDB.privateBrowsing.enabled auf true\nAndernfalls ist es nicht möglich, FluffyChat auszuführen.", - "@indexedDbErrorLong": {}, - "confirmMatrixId": "Bitte bestätigen deine Matrix-ID, um dein Konto zu löschen.", - "@confirmMatrixId": {}, - "supposedMxid": "das sollte sein {mxid}", - "@supposedMxid": { - "type": "String", - "placeholders": { - "mxid": { - "type": "String" - } - } - }, - "commandHint_markasdm": "Als Direktnachrichtenraum für die angegebene Matrix-ID markieren", - "@commandHint_markasdm": {}, - "commandHint_markasgroup": "Als Gruppe markieren", - "@commandHint_markasgroup": {}, - "hideUnimportantStateEvents": "Blende unwichtige Zustandsereignisse aus", - "@hideUnimportantStateEvents": {}, - "doNotShowAgain": "Nicht mehr anzeigen", - "@doNotShowAgain": {}, - "appearOnTopDetails": "Ermöglicht, dass die App oben angezeigt wird (nicht erforderlich, wenn du Fluffychat bereits als Anrufkonto eingerichtet haben)", - "@appearOnTopDetails": {}, - "noKeyForThisMessage": "Dies kann passieren, wenn die Nachricht gesendet wurde, bevor du dich auf diesem Gerät bei deinem Konto angemeldet hast.\n\nEs ist auch möglich, dass der Absender dein Gerät blockiert hat oder etwas mit der Internetverbindung schief gelaufen ist.\n\nKannst du die Nachricht in einer anderen Sitzung lesen? Dann kannst du die Nachricht davon übertragen! Gehe zu den Einstellungen > Geräte und vergewissere dich, dass sich deine Geräte gegenseitig verifiziert haben. Wenn du den Raum das nächste Mal öffnest und beide Sitzungen im Vordergrund sind, werden die Schlüssel automatisch übertragen.\n\nDu möchtest die Schlüssel beim Abmelden oder Gerätewechsel nicht verlieren? Stelle sicher, dass du das Chat-Backup in den Einstellungen aktiviert hast.", - "@noKeyForThisMessage": {}, - "foregroundServiceRunning": "Diese Benachrichtigung wird angezeigt, wenn der Vordergrunddienst ausgeführt wird.", - "@foregroundServiceRunning": {}, - "screenSharingTitle": "Bildschirm teilen", - "@screenSharingTitle": {}, - "callingPermissions": "Anrufberechtigungen", - "@callingPermissions": {}, - "callingAccount": "Anrufkonto", - "@callingAccount": {}, - "callingAccountDetails": "Ermöglicht FluffyChat, die native Android-Dialer-App zu verwenden.", - "@callingAccountDetails": {}, - "appearOnTop": "Oben erscheinen", - "@appearOnTop": {}, - "otherCallingPermissions": "Mikrofon, Kamera und andere FluffyChat-Berechtigungen", - "@otherCallingPermissions": {}, - "whyIsThisMessageEncrypted": "Warum ist diese Nachricht nicht lesbar?", - "@whyIsThisMessageEncrypted": {}, - "newGroup": "Neue Gruppe", - "@newGroup": {}, - "newSpace": "Neuer Space", - "@newSpace": {}, - "enterSpace": "Raum betreten", - "@enterSpace": {}, - "enterRoom": "Raum betreten", - "@enterRoom": {}, - "allSpaces": "Alle Spaces", - "@allSpaces": {}, - "screenSharingDetail": "Du teilst deinen Bildschirm in FuffyChat", - "@screenSharingDetail": {}, - "numChats": "{number} Chats", - "@numChats": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "newSpaceDescription": "Mit Spaces kannst du deine Chats zusammenfassen und private oder öffentliche Communities aufbauen.", - "@newSpaceDescription": {}, - "wasDirectChatDisplayName": "Leerer Chat (war {oldDisplayName})", - "@wasDirectChatDisplayName": { - "type": "String", - "placeholders": { - "oldDisplayName": { - "type": "String" - } - } - }, - "encryptThisChat": "Diesen Chat verschlüsseln", - "@encryptThisChat": {}, - "googlyEyesContent": "{senderName} hat dir Googly Eyes gesendet", - "@googlyEyesContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "startFirstChat": "Starte deinen ersten Chat", - "@startFirstChat": {}, - "deviceKeys": "Geräteschlüssel:", - "@deviceKeys": {}, - "commandHint_cuddle": "Umarmung senden", - "@commandHint_cuddle": {}, - "commandHint_hug": "Umarmung senden", - "@commandHint_hug": {}, - "cuddleContent": "{senderName} knuddelt dich", - "@cuddleContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "sorryThatsNotPossible": "Sorry ... das ist nicht möglich", - "@sorryThatsNotPossible": {}, - "hugContent": "{senderName} umarmt dich", - "@hugContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "commandHint_googly": "Glupschaugen senden", - "@commandHint_googly": {}, - "disableEncryptionWarning": "Aus Sicherheitsgründen kannst du die Verschlüsselung in einem Chat nicht deaktivieren, wo sie zuvor aktiviert wurde.", - "@disableEncryptionWarning": {}, - "reopenChat": "Chat wieder eröffnen", - "@reopenChat": {}, - "noBackupWarning": "Achtung! Ohne Aktivierung des Chat-Backups verlierst du den Zugriff auf deine verschlüsselten Nachrichten. Vor dem Ausloggen wird dringend empfohlen, das Chat-Backup zu aktivieren.", - "@noBackupWarning": {}, - "noOtherDevicesFound": "Keine anderen Geräte anwesend", - "@noOtherDevicesFound": {}, - "allRooms": "Alle Gruppenchats", - "@allRooms": { - "type": "String", - "placeholders": {} - }, - "fileHasBeenSavedAt": "Datei wurde gespeichert unter {path}", - "@fileHasBeenSavedAt": { - "type": "String", - "placeholders": { - "path": { - "type": "String" - } - } - }, - "jumpToLastReadMessage": "Zur letzten ungelesenen Nachricht", - "@jumpToLastReadMessage": {}, - "readUpToHere": "Bis hier gelesen", - "@readUpToHere": {}, - "pleaseTryAgainLaterOrChooseDifferentServer": "Bitte versuche es später noch einmal oder wähle einen anderen Server.", - "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "jump": "Springen", - "@jump": {}, - "openLinkInBrowser": "Link im Browser öffnen", - "@openLinkInBrowser": {}, - "reportErrorDescription": "😭 Oh nein. Etwas ist schief gelaufen. Wenn du möchtest, kannst du den Bug bei den Entwicklern melden.", - "@reportErrorDescription": {}, - "report": "Melden", - "@report": {}, - "signInWithPassword": "Anmelden mit Passwort", - "@signInWithPassword": {}, - "signInWith": "Anmelden mit {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, - "importNow": "Jetzt importieren", - "@importNow": {}, - "importEmojis": "Emojis importieren", - "@importEmojis": {}, - "importFromZipFile": "Aus ZIP-Datei importieren", - "@importFromZipFile": {}, - "exportEmotePack": "Emote-Paket als ZIP-Datei exportieren", - "@exportEmotePack": {}, - "notAnImage": "Keine Bilddatei.", - "@notAnImage": {}, - "replace": "Ersetzen", - "@replace": {}, - "sendTypingNotifications": "Tippbenachrichtigungen senden", - "@sendTypingNotifications": {}, - "profileNotFound": "Der Benutzer konnte auf dem Server nicht gefunden werden. Vielleicht gibt es ein Verbindungsproblem oder der Benutzer existiert nicht.", - "@profileNotFound": {}, - "createGroup": "Gruppe erstellen", - "@createGroup": {}, - "shareInviteLink": "Einladungslink teilen", - "@shareInviteLink": {}, - "inviteContactToGroupQuestion": "Willst du {contact} zum Chat {groupName} einladen?", - "@inviteContactToGroupQuestion": {}, - "tryAgain": "Neuer Versuch", - "@tryAgain": {}, - "redactMessageDescription": "Die Nachricht wird für alle Teilnehmer dieses Gesprächs gelöscht. Dies kann nicht rückgängig gemacht werden.", - "@redactMessageDescription": {}, - "redactedBy": "Gelöscht von {username}", - "@redactedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "redactedByBecause": "Gelöscht von {username} weil: \"{reason}\"", - "@redactedByBecause": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "reason": { - "type": "String" - } - } - }, - "setTheme": "Design festlegen:", - "@setTheme": {}, - "setColorTheme": "Farbdesign einstellen:", - "@setColorTheme": {}, - "invite": "Einladen", - "@invite": {}, - "optionalRedactReason": "(Optional) Grund für die Löschung dieser Nachricht...", - "@optionalRedactReason": {}, - "messagesStyle": "Nachrichten:", - "@messagesStyle": {}, - "chatPermissions": "Chatberechtigungen", - "@chatPermissions": {}, - "chatDescription": "Chatbeschreibung", - "@chatDescription": {}, - "chatDescriptionHasBeenChanged": "Chatbeschreibung geändert", - "@chatDescriptionHasBeenChanged": {}, - "noChatDescriptionYet": "Noch keine Chatbeschreibung vorhanden.", - "@noChatDescriptionYet": {}, - "invalidServerName": "Ungültiger Servername", - "@invalidServerName": {}, - "directChat": "Privater Chat", - "@directChat": {}, - "addChatDescription": "Chatbeschreibung hinzufügen ...", - "@addChatDescription": {}, - "setChatDescription": "Chatbeschreibung festlegen", - "@setChatDescription": {}, - "inviteGroupChat": "📨 Einladungen zum Gruppenchat", - "@inviteGroupChat": {}, - "invitePrivateChat": "📨 Einladungen zum privaten Chat", - "@invitePrivateChat": {}, - "invalidInput": "Ungültige Eingabe!", - "@invalidInput": {}, - "hasKnocked": "🚪 {user} hat angeklopft", - "@hasKnocked": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "wrongPinEntered": "Falsche PIN eingegeben! Bitte in {seconds} Sekunden erneut versuchen ...", - "@wrongPinEntered": { - "type": "String", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "pleaseEnterANumber": "Bitte eine Zahl größer 0 eingeben", - "@pleaseEnterANumber": {}, - "emoteKeyboardNoRecents": "Kürzlich verwendete Emotes werden hier angezeigt ...", - "@emoteKeyboardNoRecents": { - "type": "String", - "placeholders": {} - }, - "banUserDescription": "Der Benutzer wird aus dem Chat gebannt und kann den Chat erst wieder betreten, wenn die Verbannung aufgehoben wird.", - "@banUserDescription": {}, - "removeDevicesDescription": "Du wirst von diesem Gerät abgemeldet und kannst dann dort keine Nachrichten mehr empfangen.", - "@removeDevicesDescription": {}, - "unbanUserDescription": "Der Benutzer kann den Chat dann wieder betreten, wenn er es versucht.", - "@unbanUserDescription": {}, - "pushNotificationsNotAvailable": "Push-Benachrichtigungen nicht verfügbar", - "@pushNotificationsNotAvailable": {}, - "makeAdminDescription": "Sobald du diesen Benutzer zum Administrator gemacht hast, kannst du das möglicherweise nicht mehr rückgängig machen, da er dann über dieselben Berechtigungen wie du verfügt.", - "@makeAdminDescription": {}, - "archiveRoomDescription": "Der Chat wird in das Archiv verschoben. Andere Benutzer können sehen, dass du den Chat verlassen hast.", - "@archiveRoomDescription": {}, - "learnMore": "Erfahre mehr", - "@learnMore": {}, - "roomUpgradeDescription": "Der Chat wird dann mit der neuen Raumversion neu erstellt. Alle Teilnehmer werden benachrichtigt, dass sie zum neuen Chat wechseln müssen. Mehr über Raumversionen erfährst du unter https://spec.matrix.org/latest/rooms/", - "@roomUpgradeDescription": {}, - "kickUserDescription": "Der Benutzer wird aus dem Chat geworfen, aber nicht gebannt. In öffentlichen Chats kann der Benutzer jederzeit wieder beitreten.", - "@kickUserDescription": {}, - "blockListDescription": "Du kannst Benutzer blockieren, die dich stören. Von Benutzern auf deiner persönlichen Blocklierliste kannst du keine Nachrichten oder Raumeinladungen mehr erhalten.", - "@blockListDescription": {}, - "createGroupAndInviteUsers": "Gruppe erstellen und Nutzer einladen", - "@createGroupAndInviteUsers": {}, - "startConversation": "Unterhaltung starten", - "@startConversation": {}, - "blockedUsers": "Blockierte Benutzer", - "@blockedUsers": {}, - "groupCanBeFoundViaSearch": "Gruppe kann über die Suche gefunden werden", - "@groupCanBeFoundViaSearch": {}, - "noUsersFoundWithQuery": "Leider konnte mit \"{query}\" kein Benutzer gefunden werden. Bitte schau nach, ob dir ein Tippfehler unterlaufen ist.", - "@noUsersFoundWithQuery": { - "type": "String", - "placeholders": { - "query": { - "type": "String" - } - } - }, - "block": "Blockieren", - "@block": {}, - "yourGlobalUserIdIs": "Deine globale Benutzer-ID ist: ", - "@yourGlobalUserIdIs": {}, - "commandHint_sendraw": "Rohes JSON senden", - "@commandHint_sendraw": {}, - "wrongRecoveryKey": "Entschuldigung ... das scheint nicht der richtige Wiederherstellungsschlüssel zu sein.", - "@wrongRecoveryKey": {}, - "blockUsername": "Blockiere Benutzername", - "@blockUsername": {}, - "groupName": "Gruppenname", - "@groupName": {}, - "searchChatsRooms": "Suche nach #Chats, @Nutzer ...", - "@searchChatsRooms": {}, - "databaseMigrationTitle": "Datenbank wird optimiert", - "@databaseMigrationTitle": {}, - "databaseMigrationBody": "Bitte warten. Dies kann einen Moment dauern.", - "@databaseMigrationBody": {}, - "thisDevice": "Dieses Gerät:", - "@thisDevice": {}, - "publicSpaces": "Öffentliche Spaces", - "@publicSpaces": {}, - "passwordIsWrong": "Dein eingegebenes Passwort ist falsch", - "@passwordIsWrong": {}, - "pleaseEnterYourCurrentPassword": "Bitte dein aktuelles Passwort eingeben", - "@pleaseEnterYourCurrentPassword": {}, - "publicLink": "Öffentlicher Link", - "@publicLink": {}, - "nothingFound": "Nichts gefunden ...", - "@nothingFound": {}, - "decline": "Ablehnen", - "@decline": {}, - "newPassword": "Neues Passwort", - "@newPassword": {}, - "passwordsDoNotMatch": "Passwörter stimmen nicht überein", - "@passwordsDoNotMatch": {}, - "subspace": "Sub-Space", - "@subspace": {}, - "select": "Auswählen", - "@select": {}, - "pleaseChooseAStrongPassword": "Bitte wähle ein starkes Passwort", - "@pleaseChooseAStrongPassword": {}, - "addChatOrSubSpace": "Chat oder Sub-Space hinzufügen", - "@addChatOrSubSpace": {}, - "leaveEmptyToClearStatus": "Leer lassen, um den Status zu löschen.", - "@leaveEmptyToClearStatus": {}, - "joinSpace": "Space beitreten", - "@joinSpace": {}, - "searchForUsers": "Suche nach @benutzer ...", - "@searchForUsers": {}, - "initAppError": "Beim Starten der App ist ein Fehler aufgetreten", - "@initAppError": {}, - "databaseBuildErrorBody": "Die SQlite-Datenbank kann nicht erstellt werden. Die App versucht vorerst, die Legacy-Datenbank zu verwenden. Bitte melde diesen Fehler an die Entwickler unter {url}. Die Fehlermeldung lautet: {error}", - "@databaseBuildErrorBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "sessionLostBody": "Die App versucht nun, deine Sitzung aus der Sicherung wiederherzustellen. Bitte melde diesen Fehler an die Entwickler unter {url}. Die Fehlermeldung lautet: {error}", - "@sessionLostBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "restoreSessionBody": "Die App versucht nun, deine Sitzung aus der Sicherung wiederherzustellen. Bitte melde diesen Fehler an die Entwickler unter {url}. Die Fehlermeldung lautet: {error}", - "@restoreSessionBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "youInvitedToBy": "📩 Du wurdest per Link eingeladen zu:\n{alias}", - "@youInvitedToBy": { - "placeholders": { - "alias": { - "type": "String" - } - } - }, - "sendReadReceipts": "Lesebestätigungen senden", - "@sendReadReceipts": {}, - "formattedMessages": "Formatierte Nachrichten", - "@formattedMessages": {}, - "forwardMessageTo": "Nachricht weiterleiten an {roomName}?", - "@forwardMessageTo": { - "type": "String", - "placeholders": { - "roomName": { - "type": "String" - } - } - }, - "sendTypingNotificationsDescription": "Andere Teilnehmer in einem Chat können sehen, wenn du eine neue Nachricht tippst.", - "@sendTypingNotificationsDescription": {}, - "formattedMessagesDescription": "Formatierte Nachrichteninhalte wie fettgedruckten Text mit Markdown anzeigen.", - "@formattedMessagesDescription": {}, - "verifyOtherUser": "🔐 Anderen Benutzer verifizieren", - "@verifyOtherUser": {}, - "sendReadReceiptsDescription": "Andere Teilnehmer in einem Chat können sehen, ob du eine Nachricht gelesen hast.", - "@sendReadReceiptsDescription": {}, - "transparent": "Transparent", - "@transparent": {}, - "verifyOtherDevice": "🔐 Anderes Gerät verifizieren", - "@verifyOtherDevice": {}, - "verifyOtherUserDescription": "Wenn du einen anderen Benutzer verifizierst, kannst du sicher sein, dass du weißt, an wen du wirklich schreibst. 💪\n\nWenn du eine Verifizierung startest, wird dir und dem anderen Nutzer ein Popup in der App angezeigt. Dort siehst du dann eine Reihe von Emojis oder Zahlen, die ihr miteinander vergleichen müsst.\n\nDas geht am besten, wenn man sich trifft oder einen Videoanruf startet. 👭", - "@verifyOtherUserDescription": {}, - "acceptedKeyVerification": "{sender} hat die Schlüsselverifikation akzeptiert", - "@acceptedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "canceledKeyVerification": "{sender} hat die Schlüsselverifikation abgebrochen", - "@canceledKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "completedKeyVerification": "{sender} hat die Schlüsselverifikation abgeschlossen", - "@completedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "isReadyForKeyVerification": "{sender} ist bereit für die Schlüsselverifikation", - "@isReadyForKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "requestedKeyVerification": "{sender} hat eine Schlüsselverifikation angefragt", - "@requestedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "startedKeyVerification": "{sender} hat die Schlüsselverifikation gestartet", - "@startedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "verifyOtherDeviceDescription": "Wenn du ein anderes Gerät verifizieren, können diese Geräteschlüssel austauschen, was die Sicherheit insgesamt erhöht. 💪\n\nSobald du eine Verifizierung starten, erscheint ein Pop-up in der App auf beiden Geräten. Dort siehst du dann eine Reihe von Emojis oder Zahlen, die du miteinander vergleichen musst.\n\nAm besten hältst du beide Geräte bereit, bevor du die Verifizierung startest. 🤳", - "@verifyOtherDeviceDescription": {}, - "presenceStyle": "Statusmeldungen:", - "@presenceStyle": { - "type": "String", - "placeholders": {} - }, - "presencesToggle": "Status-Nachrichten anderer Benutzer anzeigen", - "@presencesToggle": { - "type": "String", - "placeholders": {} - }, - "incomingMessages": "Eingehende Nachrichten", - "@incomingMessages": {}, - "commandHint_unignore": "Angegebene Matrix-ID nicht mehr ignorieren", - "@commandHint_unignore": {}, - "commandHint_ignore": "Angegebene Matrix-ID ignorieren", - "@commandHint_ignore": {}, - "noDatabaseEncryption": "Datenbankverschlüsselung wird auf dieser Plattform nicht unterstützt", - "@noDatabaseEncryption": {}, - "hidePresences": "Status-Liste verbergen?", - "@hidePresences": {}, - "stickers": "Sticker", - "@stickers": {}, - "discover": "Entdecken", - "@discover": {}, - "unreadChatsInApp": "{appname}: {unread} ungelesene Chats", - "@unreadChatsInApp": { - "type": "String", - "placeholders": { - "appname": { - "type": "String" - }, - "unread": { - "type": "String" - } - } - }, - "customEmojisAndStickersBody": "Eigene Emojis oder Sticker zur Nutzung im Chat hinzufügen oder teilen.", - "@customEmojisAndStickersBody": {}, - "globalChatId": "Globale Chat-ID", - "@globalChatId": {}, - "accessAndVisibility": "Zugang und Sichtbarkeit", - "@accessAndVisibility": {}, - "hideMemberChangesInPublicChats": "Mitglieder-Änderungen in öffentlichen Chats ausblenden", - "@hideMemberChangesInPublicChats": {}, - "accessAndVisibilityDescription": "Wer darf dem Chat beitreten und wie kann der Chat gefunden werden.", - "@accessAndVisibilityDescription": {}, - "hideMemberChangesInPublicChatsBody": "Zeige keine Beitritt- oder Verlassen-Ereignisse von Mitgliedern in der Timeline an, um die Lesbarkeit in öffentlichen Chats zu verbessern.", - "@hideMemberChangesInPublicChatsBody": {}, - "userWouldLikeToChangeTheChat": "{user} würde dem Chat gerne beitreten.", - "@userWouldLikeToChangeTheChat": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "noPublicLinkHasBeenCreatedYet": "Es wurde noch kein öffentlicher Link erstellt", - "@noPublicLinkHasBeenCreatedYet": {}, - "chatCanBeDiscoveredViaSearchOnServer": "Chat kann über die Suche auf {server} gefunden werden", - "@chatCanBeDiscoveredViaSearchOnServer": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "appLockDescription": "App mit einer PIN sperren, wenn sie nicht verwendet wird", - "@appLockDescription": {}, - "calls": "Anrufe", - "@calls": {}, - "customEmojisAndStickers": "Eigene Emojis und Sticker", - "@customEmojisAndStickers": {}, - "hideRedactedMessages": "Geschwärzte Nachrichten verstecken", - "@hideRedactedMessages": {}, - "hideRedactedMessagesBody": "Wenn jemand eine Nachricht schwärzt/löscht, dann wird diese Nachricht im Chat nicht mehr sichtbar sein.", - "@hideRedactedMessagesBody": {}, - "hideInvalidOrUnknownMessageFormats": "Ungültige und unbekannte Nachrichten-Formate ausblenden", - "@hideInvalidOrUnknownMessageFormats": {}, - "overview": "Übersicht", - "@overview": {}, - "notifyMeFor": "Benachrichtige mich für", - "@notifyMeFor": {}, - "passwordRecoverySettings": "Passwort-Wiederherstellungs-Einstellungen", - "@passwordRecoverySettings": {}, - "knock": "Anklopfen", - "@knock": {}, - "knocking": "Klopft", - "@knocking": {}, - "thereAreCountUsersBlocked": "Im Augenblick werden {count} Benutzer blockiert.", - "@thereAreCountUsersBlocked": { - "type": "String", - "count": {} - }, - "usersMustKnock": "Benutzer müssen anklopfen", - "@usersMustKnock": {}, - "noOneCanJoin": "Niemand kann beitreten", - "@noOneCanJoin": {}, - "createNewAddress": "Neue Adresse erstellen", - "@createNewAddress": {}, - "userRole": "Benutzerrolle", - "@userRole": {}, - "minimumPowerLevel": "{level} is das minimale Power-Level.", - "@minimumPowerLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "String" - } - } - }, - "publicChatAddresses": "Öffentliche Chat-Adressen", - "@publicChatAddresses": {}, - "gallery": "Galerie", - "@gallery": {}, - "files": "Dateien", - "@files": {}, - "restricted": "Beschränkt", - "@restricted": {}, - "knockRestricted": "Anklopfen beschränkt", - "@knockRestricted": {}, - "searchIn": "In Chat \"{chat}\" suchen ...", - "@searchIn": { - "type": "String", - "placeholders": { - "chat": { - "type": "String" - } - } - }, - "searchMore": "Weiter suchen ...", - "@searchMore": {}, - "unread": "Ungelesen", - "@unread": {}, - "noMoreChatsFound": "Keine weiteren Chats gefunden ...", - "@noMoreChatsFound": {}, - "joinedChats": "Beigetretene Chats", - "@joinedChats": {}, - "space": "Space", - "@space": {}, - "spaces": "Spaces", - "@spaces": {}, - "goToSpace": "Geh zum Space: {space}", - "@goToSpace": { - "type": "String", - "space": {} - }, - "markAsUnread": "Als ungelesen markieren", - "@markAsUnread": {}, - "swipeRightToLeftToReply": "Wische von rechts nach links zum Antworten", - "@swipeRightToLeftToReply": {}, - "countChatsAndCountParticipants": "{chats} Chats und {participants} Teilnehmer", - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" - }, - "participants": { - "type": "int" - } - } - }, - "changeGeneralChatSettings": "Allgemeine Chat-Einstellungen ändern", - "@changeGeneralChatSettings": {}, - "userLevel": "{level} - Benutzer", - "@userLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "moderatorLevel": "{level} - Moderator", - "@moderatorLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "changeTheChatPermissions": "Ändere die Chat-Berechtigungen", - "@changeTheChatPermissions": {}, - "changeTheVisibilityOfChatHistory": "Wechsele die Sichtbarkeit der Chat-Historie", - "@changeTheVisibilityOfChatHistory": {}, - "chatPermissionsDescription": "Einstellen, welches Level für bestimmte Aktionen in diesem Chat erforderlich ist. Die Level 0, 50 und 100 stehen üblicherweise für Benutzer, Moderatoren und Admins, aber jede Abstufung ist möglich.", - "@chatPermissionsDescription": {}, - "invitedBy": "📩 Eingeladen von {user}", - "@invitedBy": { - "placeholders": { - "user": { - "type": "String" - } - } - }, - "adminLevel": "{level} - Administrator", - "@adminLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "inviteOtherUsers": "Lade andere Benutzer in diesen Chat ein", - "@inviteOtherUsers": {}, - "changeTheCanonicalRoomAlias": "Ändern der Hauptadresse für den öffentlichen Chat", - "@changeTheCanonicalRoomAlias": {}, - "sendRoomNotifications": "Sende eine @room-Benachrichtigung", - "@sendRoomNotifications": {}, - "changeTheDescriptionOfTheGroup": "Chat-Beschreibung ändern", - "@changeTheDescriptionOfTheGroup": {}, - "updateInstalled": "🎉 Update {version} installiert!", - "@updateInstalled": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - } - } - }, - "changelog": "Änderungsprotokoll", - "@changelog": {}, - "sendCanceled": "Senden abgebrochen", - "@sendCanceled": {}, - "noChatsFoundHere": "Hier wurden noch keine Chats gefunden. Starte einen neuen Chat mit jemandem, indem du die Schaltfläche unten verwenden. ⤵️", - "@noChatsFoundHere": {}, - "whatIsAHomeserver": "Was ist ein Homeserver?", - "@whatIsAHomeserver": {}, - "doesNotSeemToBeAValidHomeserver": "Scheint kein kompatibler Homeserver zu sein. Falsche URL?", - "@doesNotSeemToBeAValidHomeserver": {}, - "loginWithMatrixId": "Mit Matrix-ID anmelden", - "@loginWithMatrixId": {}, - "discoverHomeservers": "Server suchen", - "@discoverHomeservers": {}, - "homeserverDescription": "Alle deine Daten werden auf einem Homeserver gespeichert, so wie bei einem E-Mail Anbieter. Du kannst aussuchen, welchen Homeserver du benutzen willst und kannst trotzdem mit allen kommunizieren. Erfahre mehr auf https://matrix.org.", - "@homeserverDescription": {}, - "sendingAttachment": "Anhang wird gesendet ...", - "@sendingAttachment": {}, - "generatingVideoThumbnail": "Generiere Video-Vorschaubild ...", - "@generatingVideoThumbnail": {}, - "serverLimitReached": "Server-Limit erreicht! Warte {seconds} Sekunden ...", - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "calculatingFileSize": "Dateigröße wird berechnet ...", - "@calculatingFileSize": {}, - "prepareSendingAttachment": "Anhang zum Senden vorbereiten ...", - "@prepareSendingAttachment": {}, - "compressVideo": "Video wird komprimiert ...", - "@compressVideo": {}, - "sendingAttachmentCountOfCount": "Sende Anhang {index} von {length} ...", - "@sendingAttachmentCountOfCount": { - "type": "integer", - "placeholders": { - "index": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "fileIsTooBigForServer": "Kann nicht gesendet werden! Der Server unterstützt nur Anhänge bis höchstens {max}.", - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "oneOfYourDevicesIsNotVerified": "Eines deiner Geräte ist nicht verifiziert", - "@oneOfYourDevicesIsNotVerified": {}, - "noticeChatBackupDeviceVerification": "Hinweis: Wenn du alle deine Geräte mit dem Chat-Backup verbindest, sind sie automatisch verifiziert.", - "@noticeChatBackupDeviceVerification": {}, - "setWallpaper": "Hintergrund ändern", - "@setWallpaper": {}, - "opacity": "Deckkraft:", - "@opacity": {}, - "welcomeText": "Hey Hey 👋 Das ist FluffyChat. Du kannst sich bei jedem Homeserver anmelden, der mit https://matrix.org kompatibel ist. Und dann mit jedem chatten. Das hier ist ein riesiges dezentrales Nachrichtennetzwerk!", - "@welcomeText": {}, - "blur": "Verwischen:", - "@blur": {}, - "manageAccount": "Konto verwalten", - "@manageAccount": {}, - "continueText": "Fortfahren", - "@continueText": {}, - "noContactInformationProvided": "Der Server stellt keine gültigen Kontaktinformationen bereit", - "@noContactInformationProvided": {}, - "contactServerAdmin": "Serveradministrator kontaktieren", - "@contactServerAdmin": {}, - "name": "Name", - "@name": {}, - "version": "Version", - "@version": {}, - "website": "Website", - "@website": {}, - "aboutHomeserver": "Über {homeserver}", - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "boldText": "Fetter Text", - "@boldText": {}, - "invalidUrl": "Ungültige URL", - "@invalidUrl": {}, - "addLink": "Link hinzufügen", - "@addLink": {}, - "unableToJoinChat": "Chat kann nicht beigetreten werden. Möglicherweise hat die Gegenseite das Gespräch bereits beendet.", - "@unableToJoinChat": {}, - "italicText": "Kursiver Text", - "@italicText": {}, - "strikeThrough": "Durchgestrichen", - "@strikeThrough": {}, - "pleaseFillOut": "Bitte ausfüllen", - "@pleaseFillOut": {}, - "sendImages": "Sende {count} Bilder", - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "contactServerSecurity": "Server-Sicherheit kontaktieren", - "@contactServerSecurity": {}, - "compress": "Komprimieren", - "@compress": {}, - "supportPage": "Support-Seite", - "@supportPage": {}, - "serverInformation": "Server-Informationen:", - "@serverInformation": {}, - "appIntroduction": "Mit FluffyChat kannst du über verschiedene Messenger hinweg mit deinen Freunden chatten. Erfahre mehr dazu auf https://matrix.org oder tippe einfach auf *Fortfahren*.", - "@appIntroduction": {}, - "newChatRequest": "📩 Neue Chat-Anfrage", - "@newChatRequest": {}, - "synchronizingPleaseWaitCounter": " Synchronisierung… ({percentage}%)", - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, - "waitingForServer": "Auf Server warten...", - "@waitingForServer": {}, - "previous": "Vorige", - "@previous": {}, - "otherPartyNotLoggedIn": "Der User ist aktuell nicht eingeloggt und kann daher keine Nachrichten empfangen!", - "@otherPartyNotLoggedIn": {}, - "appWantsToUseForLogin": "Nutze '{server}' um dich einzuloggen", - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "appWantsToUseForLoginDescription": "Hiermit erlaubst du der App und der Website, Informationen über dich weiterzugeben.", - "@appWantsToUseForLoginDescription": {}, - "open": "Offen", - "@open": {}, - "notificationRuleContainsUserName": "Enthält Benutzernamen", - "@notificationRuleContainsUserName": {}, - "notificationRuleContainsUserNameDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht seinen Benutzernamen enthält.", - "@notificationRuleContainsUserNameDescription": {}, - "notificationRuleMaster": "Alle Benachrichtigungen stummschalten", - "@notificationRuleMaster": {}, - "notificationRuleSuppressNotices": "Automatisierte Nachrichten unterdrücken", - "@notificationRuleSuppressNotices": {}, - "notificationRuleMasterDescription": "Setzt alle anderen Regeln außer Kraft und deaktiviert alle Benachrichtigungen.", - "@notificationRuleMasterDescription": {}, - "generalNotificationSettings": "Allgemeine Benachrichtigungseinstellungen", - "@generalNotificationSettings": {}, - "otherNotificationSettings": "Andere Benachrichtigungseinstellungen", - "@otherNotificationSettings": {}, - "contentNotificationSettings": "Einstellungen für Inhaltsbenachrichtigungen", - "@contentNotificationSettings": {}, - "userSpecificNotificationSettings": "Benutzerspezifische Benachrichtigungseinstellungen", - "@userSpecificNotificationSettings": {}, - "roomNotificationSettings": "Einstellungen für Raumbenachrichtigungen", - "@roomNotificationSettings": {}, - "notificationRuleSuppressNoticesDescription": "Unterdrückt Benachrichtigungen von automatisierten Clients wie Bots.", - "@notificationRuleSuppressNoticesDescription": {}, - "notificationRuleInviteForMe": "Einladung für mich", - "@notificationRuleInviteForMe": {}, - "notificationRuleReaction": "Reaktion", - "@notificationRuleReaction": {}, - "notificationRuleReactionDescription": "Unterdrückt Benachrichtigungen für Reaktionen.", - "@notificationRuleReactionDescription": {}, - "notificationRuleSuppressEditsDescription": "Unterdrückt Benachrichtigungen für bearbeitete Nachrichten.", - "@notificationRuleSuppressEditsDescription": {}, - "notificationRuleCall": "Anruf", - "@notificationRuleCall": {}, - "notificationRuleCallDescription": "Benachrichtigt den Benutzer über Anrufe.", - "@notificationRuleCallDescription": {}, - "notificationRuleEncrypted": "Verschlüsselt", - "@notificationRuleEncrypted": {}, - "more": "Mehr", - "@more": {}, - "notificationRuleSuppressEdits": "Unterdrückt Bearbeitungen", - "@notificationRuleSuppressEdits": {}, - "notificationRuleRoomServerAclDescription": "Unterdrückt Benachrichtigungen für Raumserver-Zugriffskontrolllisten (ACL).", - "@notificationRuleRoomServerAclDescription": {}, - "notificationRuleMessage": "Nachricht", - "@notificationRuleMessage": {}, - "notificationRuleMessageDescription": "Informiert den Benutzer über allgemeine Nachrichten.", - "@notificationRuleMessageDescription": {}, - "notificationRuleJitsi": "Jitsi", - "@notificationRuleJitsi": {}, - "allDevices": "Alle Geräte", - "@allDevices": {}, - "enterNewChat": "Neuen Chat betreten", - "@enterNewChat": {}, - "shareKeysWith": "Schlüssel teilen mit...", - "@shareKeysWith": {}, - "shareKeysWithDescription": "Welchen Geräten sollte vertraut werden, damit sie deine Nachrichten in verschlüsselten Chats mitlesen können?", - "@shareKeysWithDescription": {}, - "verifiedDevicesOnly": "Nur verifizierte Geräte", - "@verifiedDevicesOnly": {}, - "takeAPhoto": "Foto aufnehmen", - "@takeAPhoto": {}, - "recordAVideo": "Video aufnehmen", - "@recordAVideo": {}, - "optionalMessage": "(Optionale) Nachricht...", - "@optionalMessage": {}, - "notSupportedOnThisDevice": "Nicht unterstützt auf diesem Gerät", - "@notSupportedOnThisDevice": {}, - "ignoreUser": "Nutzer ignorieren", - "@ignoreUser": {}, - "notificationRuleEncryptedRoomOneToOneDescription": "Benachrichtigt den Benutzer über Nachrichten in verschlüsselten Eins-zu-Eins-Chats.", - "@notificationRuleEncryptedRoomOneToOneDescription": {}, - "commandHint_roomupgrade": "Aktualisieren Sie diesen Raum auf die angegebene Raumversion", - "@commandHint_roomupgrade": {}, - "notificationRuleMemberEvent": "Mitgliederveranstaltung", - "@notificationRuleMemberEvent": {}, - "notificationRuleInviteForMeDescription": "Benachrichtigt den Benutzer, wenn er in einen Raum eingeladen wird.", - "@notificationRuleInviteForMeDescription": {}, - "notificationRuleIsUserMentionDescription": "Benachrichtigt den Benutzer, wenn er in einer Nachricht direkt erwähnt wird.", - "@notificationRuleIsUserMentionDescription": {}, - "notificationRuleRoomnotifDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht „@room“ enthält.", - "@notificationRuleRoomnotifDescription": {}, - "notificationRuleRoomOneToOneDescription": "Benachrichtigt den Benutzer über Nachrichten in Einzelchats.", - "@notificationRuleRoomOneToOneDescription": {}, - "notificationRuleEncryptedDescription": "Benachrichtigt den Benutzer über Nachrichten in verschlüsselten Räumen.", - "@notificationRuleEncryptedDescription": {}, - "notificationRuleJitsiDescription": "Benachrichtigt den Benutzer über Jitsi-Widget-Ereignisse.", - "@notificationRuleJitsiDescription": {}, - "checkList": "Checkliste", - "@checkList": {}, - "countInvited": "{count} invited", - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "notificationRuleIsUserMention": "Benutzererwähnung", - "@notificationRuleIsUserMention": {}, - "notificationRuleContainsDisplayName": "Enthält den Anzeigenamen", - "@notificationRuleContainsDisplayName": {}, - "notificationRuleContainsDisplayNameDescription": "Benachrichtigt den Benutzer, wenn eine Nachricht seinen Anzeigenamen enthält.", - "@notificationRuleContainsDisplayNameDescription": {}, - "notificationRuleIsRoomMention": "Chat-Erwähnung", - "@notificationRuleIsRoomMention": {}, - "notificationRuleRoomnotif": "Chat-Benachritigung", - "@notificationRuleRoomnotif": {}, - "notificationRuleTombstoneDescription": "Benachrichtigt den Benutzer über Nachrichten zur Raumdeaktivierung.", - "@notificationRuleTombstoneDescription": {}, - "notificationRuleEncryptedRoomOneToOne": "Verschlüsselter Einzelchat", - "@notificationRuleEncryptedRoomOneToOne": {}, - "notificationRuleRoomOneToOne": "Einzelchat", - "@notificationRuleRoomOneToOne": {}, - "notificationRuleServerAclDescription": "Unterdrückt Benachrichtigungen für Server-ACL-Ereignisse.", - "@notificationRuleServerAclDescription": {}, - "unknownPushRule": "Unbekannte Push-Regel '{rule}'", - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "deletePushRuleCanNotBeUndone": "Wenn Sie diese Benachrichtigungseinstellung löschen, kann dies nicht rückgängig gemacht werden.", - "@deletePushRuleCanNotBeUndone": {}, - "crossVerifiedDevices": "Cross-verifizierte Geräte", - "@crossVerifiedDevices": {}, - "notificationRuleIsRoomMentionDescription": "Benachrichtigt den Benutzer, wenn ein Raum erwähnt wird.", - "@notificationRuleIsRoomMentionDescription": {}, - "notificationRuleRoomServerAcl": "Raumserver-ACL", - "@notificationRuleRoomServerAcl": {}, - "crossVerifiedDevicesIfEnabled": "Cross-verifizierte Geräte, falls aktiviert", - "@crossVerifiedDevicesIfEnabled": {}, - "notificationRuleServerAcl": "Unterdrücken von Server-ACL-Ereignissen", - "@notificationRuleServerAcl": {}, - "notificationRuleMemberEventDescription": "Unterdrückt Benachrichtigungen zu Mitgliedschaftsereignissen.", - "@notificationRuleMemberEventDescription": {}, - "sentVoiceMessage": "🎙️ {duration} - {sender}", - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - }, - "duration": { - "type": "String" - } - } - }, - "normalUser": "Normaler Benutzer", - "@normalUser": {}, - "setCustomPermissionLevel": "Benutzerdefinierte Berechtigungsstufe festlegen", - "@setCustomPermissionLevel": {}, - "setPermissionsLevelDescription": "Bitte wählen Sie unten eine vordefinierte Rolle aus oder geben Sie eine benutzerdefinierte Berechtigungsstufe zwischen 0 und 100 ein.", - "@setPermissionsLevelDescription": {}, - "approve": "Genehmigen", - "@approve": {}, - "youHaveKnocked": "Du hast geklopft", - "@youHaveKnocked": {}, - "pleaseWaitUntilInvited": "Bitte warte nun, bis dich jemand aus dem Raum auffordert.", - "@pleaseWaitUntilInvited": {}, - "notificationRuleTombstone": "Tombstone", - "@notificationRuleTombstone": {}, - "commandHint_logout": "Abmelden von deinem aktuellen Gerät", - "commandHint_logoutall": "Alle aktiven Geräte abmelden", - "displayNavigationRail": "Navigationsleiste auf Mobilgeräten anzeigen", - "customReaction": "Benutzerdefinierte Reaktion", - "writeAMessageLangCodes": "Geben Sie in {l1} oder {l2} ein...", - "requests": "Anfragen", - "holdForInfo": "Klicken und halten für Wortinformationen.", - "greenFeedback": "Das würde ich eintragen!", - "yellowFeedback": "Hm, du kannst es versuchen und sehen, ob es funktioniert! Um dieses Wort zu verwenden, klicke es einfach erneut.", - "redFeedback": "Ich glaube nicht, dass das richtig ist...", - "itInstructionsTitle": "Ich kann dir beim Übersetzen helfen!", - "itInstructionsBody": "Sie können durch Tippen und Halten Informationen zum Wort erhalten.", - "gaTooltip": "L2 Verwendung mit Grammatikhilfe", - "taTooltip": "L2 Verwendung mit Übersetzungshilfe", - "unTooltip": "Andere", - "interactiveTranslatorSliderHeader": "Interaktiver Übersetzer", - "interactiveGrammarSliderHeader": "Interaktiver Grammatikprüfer", - "interactiveTranslatorNotAllowed": "Deaktiviert", - "interactiveTranslatorAllowed": "Schülerwahl", - "interactiveTranslatorRequired": "Erforderlich", - "notYetSet": "Noch nicht festgelegt", - "waTooltip": "L2 Verwendung ohne Hilfe", - "languageSettings": "Spracheinstellungen", - "interactiveTranslator": "Übersetzungshilfe", - "noIdenticalLanguages": "Bitte wählen Sie unterschiedliche Ausgangs- und Zielsprache", - "searchBy": "Suche nach Land und Sprachen", - "joinWithClassCode": "Kurs beitreten", - "languageLevelPreA1": "Anfänger Niedrig (Pre A1)", - "languageLevelA1": "Novize Mitte (A1)", - "languageLevelA2": "Anfänger Hoch (A2)", - "languageLevelB1": "Mittelstufe (B1)", - "languageLevelB2": "Fortgeschritten Niedrig (B2)", - "languageLevelC1": "Fortgeschritten Mittel (C1)", - "languageLevelC2": "Überlegen (C2)", - "changeTheNameOfTheClass": "Namen ändern", - "changeTheNameOfTheChat": "Den Namen des Chats ändern", - "sorryNoResults": "Entschuldigung, keine Ergebnisse.", - "ignoreInThisText": "Ignorieren", - "needsItMessage": "Warte, das ist nicht {targetLanguage}! Bist du bei der Übersetzung Hilfe nötig?", - "countryInformation": "Mein Land", - "targetLanguage": "Zielsprache", - "sourceLanguage": "Ausgangssprache", - "updateLanguage": "Meine Sprachen", - "whatLanguageYouWantToLearn": "Welche Sprache möchtest du lernen?", - "whatIsYourBaseLanguage": "Was ist deine Ausgangssprache?", - "saveChanges": "Änderungen speichern", - "publicProfileTitle": "Erlauben Sie, dass mein Profil in der Suche gefunden wird", - "publicProfileDesc": "Wenn Sie es einschalten, können andere Benutzer Ihr Profil in der globalen Suchleiste finden und Chat-Anfragen senden. An diesem Punkt können Sie die Anfrage akzeptieren oder ablehnen.", - "errorDisableIT": "Übersetzungshilfe ist deaktiviert.", - "errorDisableIGC": "Grammatikhilfe ist deaktiviert.", - "errorDisableLanguageAssistance": "Übersetzungs- und Grammatikhilfe sind deaktiviert.", - "errorDisableITUserDesc": "Klicken Sie hier, um die Einstellungen für Übersetzungshilfe zu aktualisieren", - "errorDisableIGCUserDesc": "Klicken Sie hier, um die Einstellungen für Grammatikhilfe zu aktualisieren", - "errorDisableLanguageAssistanceUserDesc": "Klicken Sie hier, um die Einstellungen für Übersetzungs- und Grammatikhilfe zu aktualisieren", - "errorDisableITClassDesc": "Die Übersetzungshilfe ist für den Kurs, in dem sich dieser Chat befindet, deaktiviert.", - "errorDisableIGCClassDesc": "Die Grammatikhilfe ist für den Kurs, in dem sich dieser Chat befindet, deaktiviert.", - "error405Title": "Sprachen nicht eingestellt", - "error405Desc": "Bitte stellen Sie Ihre Sprachen im Hauptmenü > Lerneinstellungen ein.", - "termsAndConditions": "Allgemeinen Geschäftsbedingungen", - "andCertifyIAmAtLeast13YearsOfAge": " zu und bestätigen, dass ich mindestens 16 Jahre alt bin.", - "error502504Title": "Wow, es sind viele Schüler online!", - "error502504Desc": "Übersetzungs- und Grammatiktools könnten langsam sein oder nicht verfügbar sein, während die Pangea-Bots aufholen.", - "error404Title": "Übersetzungsfehler!", - "error404Desc": "Der Pangea-Bot ist sich nicht sicher, wie das zu übersetzen ist...", - "errorPleaseRefresh": "Wir kümmern uns darum! Bitte laden Sie die Seite neu und versuchen Sie es erneut.", - "connectedToStaging": "Mit Staging verbunden", - "learningSettings": "Lerneinstellungen", - "participants": "Teilnehmer", - "clickMessageTitle": "Brauchen Sie Hilfe?", - "clickMessageBody": "Klicken Sie auf eine Nachricht für Sprachtools wie Übersetzung, Wiedergabe und mehr!", - "allDone": "Alles erledigt!", - "vocab": "Vokabular", - "low": "Wir haben Hinweise darauf, dass der Benutzer diese Wörter nicht versteht.", - "medium": "Diese Wörter wurden verwendet. Es ist unklar, ob die Wörter vollständig verstanden wurden oder nicht.", - "high": "Wir haben Hinweise darauf, dass der Benutzer diese Wörter versteht.", - "subscribe": "Abonnieren", - "getAccess": "Jetzt abonnieren!", - "subscriptionDesc": "Messaging ist kostenlos! Abonnieren Sie, um interaktive Übersetzungen, Grammatikprüfung und Lernanalysen freizuschalten.", - "subscriptionManagement": "Abonnementverwaltung", - "currentSubscription": "Aktuelles Abonnement", - "cancelSubscription": "Abonnement kündigen", - "selectYourPlan": "Wählen Sie Ihren Plan", - "subsciptionPlatformTooltip": "Bitte melden Sie sich auf Ihrem ursprünglichen Gerät an, um Ihren Abonnementplan zu verwalten", - "subscriptionManagementUnavailable": "Abonnementverwaltung nicht verfügbar", - "paymentMethod": "Zahlungsmethode", - "paymentHistory": "Zahlungsverlauf", - "emptyChatDownloadWarning": "Leeren Chat können nicht heruntergeladen werden", - "update": "Aktualisieren", - "toggleImmersionMode": "Immersionsmodus", - "toggleImmersionModeDesc": "Wenn aktiviert, werden alle Nachrichten in Ihrer Zielsprache angezeigt. Diese Einstellung ist in Sprachaustauschen am nützlichsten.", - "itToggleDescription": "Dieses Werkzeug zum Sprachenlernen erkennt Wörter in Ihrer Ausgangssprache und hilft Ihnen, sie in Ihre Zielsprache zu übersetzen. Obwohl selten, kann die KI Übersetzungsfehler machen.", - "igcToggleDescription": "Dieses Werkzeug zum Sprachenlernen erkennt häufige Rechtschreib-, Grammatik- und Interpunktionsfehler in Ihrer Nachricht und schlägt Korrekturen vor. Obwohl selten, kann die KI Korrekturfehler machen.", - "originalMessage": "Originalnachricht", - "sentMessage": "Gesendete Nachricht", - "useType": "Verwendungstyp", - "notAvailable": "Nicht verfügbar", - "taAndGaTooltip": "L2-Nutzung mit Übersetzungs- und Grammatikhilfe", - "definitionsToolName": "Wortdefinitionen", - "messageTranslationsToolName": "Nachrichtübersetzungen", - "definitionsToolDescription": "Wenn aktiviert, können Wörter, die blau unterstrichen sind, angeklickt werden, um Definitionen anzuzeigen. Klicken Sie auf Nachrichten, um Definitionen zu erhalten.", - "translationsToolDescrption": "Wenn aktiviert, klicken Sie auf eine Nachricht und auf das Übersetzungssymbol, um eine Nachricht in Ihrer Ausgangssprache zu sehen.", - "welcomeBack": "Willkommen zurück! Wenn Sie Teil des Pilotprojekts 2023-2024 waren, kontaktieren Sie uns bitte für Ihr spezielles Pilotabonnement. Wenn Sie Lehrer sind, der (oder dessen Institution) Lizenzen für Ihre Klasse gekauft hat, kontaktieren Sie uns für Ihr Lehrerausweis.", - "downloadTxtFile": "Textdatei herunterladen", - "downloadCSVFile": "CSV-Datei herunterladen", - "promotionalSubscriptionDesc": "Sie haben derzeit ein lebenslanges Aktionsabonnement. Kontaktieren Sie support@pangea.chat, um Hilfe bei der Änderung Ihres Abonnements zu erhalten.", - "originalSubscriptionPlatform": "Abonnement gekauft über {purchasePlatform}", - "oneWeekTrial": "Einwöchige Testversion", - "downloadXLSXFile": "Excel-Datei herunterladen", - "unkDisplayName": "Unbekannt", - "wwCountryDisplayName": "Weltweit", - "afCountryDisplayName": "Afghanistan", - "axCountryDisplayName": "Alandinseln", - "alCountryDisplayName": "Albanien", - "dzCountryDisplayName": "Algerien", - "asCountryDisplayName": "Amerikanisch-Samoa", - "adCountryDisplayName": "Andorra", - "aoCountryDisplayName": "Angola", - "aiCountryDisplayName": "Anguilla", - "agCountryDisplayName": "Antigua und Barbuda", - "arCountryDisplayName": "Argentinien", - "amCountryDisplayName": "Armenien", - "awCountryDisplayName": "Aruba", - "acCountryDisplayName": "Ascension-Insel", - "auCountryDisplayName": "Australien", - "atCountryDisplayName": "Österreich", - "azCountryDisplayName": "Aserbaidschan", - "bsCountryDisplayName": "Bahamas", - "bhCountryDisplayName": "Bahrain", - "bdCountryDisplayName": "Bangladesch", - "bbCountryDisplayName": "Barbados", - "byCountryDisplayName": "Weißrussland", - "beCountryDisplayName": "Belgien", - "bzCountryDisplayName": "Belize", - "bjCountryDisplayName": "Benin", - "bmCountryDisplayName": "Bermuda", - "btCountryDisplayName": "Bhutan", - "boCountryDisplayName": "Bolivien", - "baCountryDisplayName": "Bosnien und Herzegowina", - "bwCountryDisplayName": "Botswana", - "brCountryDisplayName": "Brasilien", - "ioCountryDisplayName": "Britisches Territorium im Indischen Ozean", - "vgCountryDisplayName": "Britische Jungferninseln", - "bnCountryDisplayName": "Brunei", - "bgCountryDisplayName": "Bulgarien", - "bfCountryDisplayName": "Burkina Faso", - "biCountryDisplayName": "Burundi", - "khCountryDisplayName": "Kambodscha", - "cmCountryDisplayName": "Kamerun", - "caCountryDisplayName": "Kanada", - "cvCountryDisplayName": "Kap Verde", - "bqCountryDisplayName": "Karibisches Niederlande", - "kyCountryDisplayName": "Kaimaninseln", - "cfCountryDisplayName": "Zentralafrikanische Republik", - "tdCountryDisplayName": "Tschad", - "clCountryDisplayName": "Chile", - "cnCountryDisplayName": "China", - "cxCountryDisplayName": "Weihnachtsinsel", - "ccCountryDisplayName": "Kokosinseln [Keeling]", - "coCountryDisplayName": "Kolumbien", - "kmCountryDisplayName": "Komoren", - "cdCountryDisplayName": "Demokratische Republik Kongo", - "cgCountryDisplayName": "Republik Kongo", - "ckCountryDisplayName": "Cookinseln", - "crCountryDisplayName": "Costa Rica", - "ciCountryDisplayName": "Elfenbeinküste", - "hrCountryDisplayName": "Kroatien", - "cuCountryDisplayName": "Kuba", - "cwCountryDisplayName": "Curaçao", - "cyCountryDisplayName": "Zypern", - "czCountryDisplayName": "Tschechische Republik", - "dkCountryDisplayName": "Dänemark", - "djCountryDisplayName": "Dschibuti", - "dmCountryDisplayName": "Dominica", - "doCountryDisplayName": "Dominikanische Republik", - "tlCountryDisplayName": "Osttimor", - "ecCountryDisplayName": "Ecuador", - "egCountryDisplayName": "Ägypten", - "svCountryDisplayName": "El Salvador", - "gqCountryDisplayName": "Äquatorialguinea", - "erCountryDisplayName": "Eritrea", - "eeCountryDisplayName": "Estland", - "szCountryDisplayName": "Eswatini", - "etCountryDisplayName": "Äthiopien", - "fkCountryDisplayName": "Falklandinseln", - "foCountryDisplayName": "Färöer-Inseln", - "fjCountryDisplayName": "Fidschi", - "fiCountryDisplayName": "Finnland", - "frCountryDisplayName": "Frankreich", - "gfCountryDisplayName": "Französisch-Guayana", - "pfCountryDisplayName": "Französisch-Polynesien", - "gaCountryDisplayName": "Gabun", - "gmCountryDisplayName": "Gambia", - "geCountryDisplayName": "Georgien", - "deCountryDisplayName": "Deutschland", - "ghCountryDisplayName": "Ghana", - "giCountryDisplayName": "Gibraltar", - "grCountryDisplayName": "Griechenland", - "glCountryDisplayName": "Grönland", - "gdCountryDisplayName": "Grenada", - "gpCountryDisplayName": "Guadeloupe", - "guCountryDisplayName": "Guam", - "gtCountryDisplayName": "Guatemala", - "ggCountryDisplayName": "Guernsey", - "gnCountryDisplayName": "Guinea Conakry", - "gwCountryDisplayName": "Guinea-Bissau", - "gyCountryDisplayName": "Guyana", - "htCountryDisplayName": "Haiti", - "hmCountryDisplayName": "Heard-Insel und McDonald-Inseln", - "hnCountryDisplayName": "Honduras", - "hkCountryDisplayName": "Hongkong", - "huCountryDisplayName": "Ungarn", - "isCountryDisplayName": "Island", - "inCountryDisplayName": "Indien", - "idCountryDisplayName": "Indonesien", - "irCountryDisplayName": "Iran", - "iqCountryDisplayName": "Irak", - "ieCountryDisplayName": "Irland", - "imCountryDisplayName": "Insel Man", - "ilCountryDisplayName": "Israel", - "itCountryDisplayName": "Italien", - "jmCountryDisplayName": "Jamaika", - "jpCountryDisplayName": "Japan", - "jeCountryDisplayName": "Jersey", - "joCountryDisplayName": "Jordanien", - "kzCountryDisplayName": "Kasachstan", - "keCountryDisplayName": "Kenia", - "kiCountryDisplayName": "Kiribati", - "xkCountryDisplayName": "Kosovo", - "kwCountryDisplayName": "Kuwait", - "kgCountryDisplayName": "Kirgisistan", - "laCountryDisplayName": "Laos", - "lvCountryDisplayName": "Lettland", - "lbCountryDisplayName": "Libanon", - "lsCountryDisplayName": "Lesotho", - "lrCountryDisplayName": "Liberia", - "lyCountryDisplayName": "Libyen", - "liCountryDisplayName": "Liechtenstein", - "ltCountryDisplayName": "Litauen", - "luCountryDisplayName": "Luxemburg", - "moCountryDisplayName": "Macau", - "mkCountryDisplayName": "Nordmazedonien", - "mgCountryDisplayName": "Madagaskar", - "mwCountryDisplayName": "Malawi", - "myCountryDisplayName": "Malaysia", - "mvCountryDisplayName": "Malediven", - "mlCountryDisplayName": "Mali", - "mtCountryDisplayName": "Malta", - "mhCountryDisplayName": "Marshallinseln", - "mqCountryDisplayName": "Martinique", - "mrCountryDisplayName": "Mauretanien", - "muCountryDisplayName": "Mauritius", - "ytCountryDisplayName": "Mayotte", - "mxCountryDisplayName": "Mexiko", - "fmCountryDisplayName": "Mikronesien", - "mdCountryDisplayName": "Moldawien", - "mcCountryDisplayName": "Monaco", - "mnCountryDisplayName": "Mongolei", - "meCountryDisplayName": "Montenegro", - "msCountryDisplayName": "Montserrat", - "maCountryDisplayName": "Marokko", - "mzCountryDisplayName": "Mosambik", - "mmCountryDisplayName": "Myanmar (Burma)", - "naCountryDisplayName": "Namibia", - "nrCountryDisplayName": "Nauru", - "npCountryDisplayName": "Nepal", - "nlCountryDisplayName": "Niederlande", - "ncCountryDisplayName": "Neukaledonien", - "nzCountryDisplayName": "Neuseeland", - "niCountryDisplayName": "Nicaragua", - "neCountryDisplayName": "Niger", - "ngCountryDisplayName": "Nigeria", - "nuCountryDisplayName": "Niue", - "nfCountryDisplayName": "Norfolkinsel", - "kpCountryDisplayName": "Nordkorea", - "mpCountryDisplayName": "Nördliche Marianen", - "noCountryDisplayName": "Norwegen", - "omCountryDisplayName": "Oman", - "pkCountryDisplayName": "Pakistan", - "pwCountryDisplayName": "Palau", - "psCountryDisplayName": "Palästinensische Gebiete", - "paCountryDisplayName": "Panama", - "pgCountryDisplayName": "Papua-Neuguinea", - "pyCountryDisplayName": "Paraguay", - "peCountryDisplayName": "Peru", - "phCountryDisplayName": "Philippinen", - "plCountryDisplayName": "Polen", - "ptCountryDisplayName": "Portugal", - "prCountryDisplayName": "Puerto Rico", - "qaCountryDisplayName": "Katar", - "reCountryDisplayName": "Réunion", - "roCountryDisplayName": "Rumänien", - "ruCountryDisplayName": "Russland", - "rwCountryDisplayName": "Ruanda", - "blCountryDisplayName": "Saint-Barthélemy", - "shCountryDisplayName": "St. Helena", - "knCountryDisplayName": "St. Kitts", - "lcCountryDisplayName": "St. Lucia", - "mfCountryDisplayName": "St. Martin", - "pmCountryDisplayName": "St. Pierre und Miquelon", - "vcCountryDisplayName": "St. Vincent", - "wsCountryDisplayName": "Samoa", - "smCountryDisplayName": "San Marino", - "stCountryDisplayName": "Sao Tomé und Príncipe", - "saCountryDisplayName": "Saudi-Arabien", - "snCountryDisplayName": "Senegal", - "rsCountryDisplayName": "Serbien", - "scCountryDisplayName": "Seychellen", - "slCountryDisplayName": "Sierra Leone", - "sgCountryDisplayName": "Singapur", - "sxCountryDisplayName": "Sint Maarten", - "skCountryDisplayName": "Slowakei", - "siCountryDisplayName": "Slowenien", - "sbCountryDisplayName": "Salomonen", - "soCountryDisplayName": "Somalia", - "zaCountryDisplayName": "Südafrika", - "gsCountryDisplayName": "Südgeorgien und die Südlichen Sandwichinseln", - "krCountryDisplayName": "Südkorea", - "ssCountryDisplayName": "Südsudan", - "esCountryDisplayName": "Spanien", - "lkCountryDisplayName": "Sri Lanka", - "sdCountryDisplayName": "Sudan", - "srCountryDisplayName": "Suriname", - "sjCountryDisplayName": "Svalbard und Jan Mayen", - "seCountryDisplayName": "Schweden", - "chCountryDisplayName": "Schweiz", - "syCountryDisplayName": "Syrien", - "twCountryDisplayName": "Taiwan", - "tjCountryDisplayName": "Tadschikistan", - "tzCountryDisplayName": "Tansania", - "thCountryDisplayName": "Thailand", - "tgCountryDisplayName": "Togo", - "tkCountryDisplayName": "Tokelau", - "toCountryDisplayName": "Tonga", - "ttCountryDisplayName": "Trinidad/Tobago", - "tnCountryDisplayName": "Tunesien", - "trCountryDisplayName": "Türkei", - "tmCountryDisplayName": "Turkmenistan", - "tcCountryDisplayName": "Turks- und Caicosinseln", - "tvCountryDisplayName": "Tuvalu", - "viCountryDisplayName": "Amerikanische Jungferninseln", - "ugCountryDisplayName": "Uganda", - "uaCountryDisplayName": "Ukraine", - "aeCountryDisplayName": "Vereinigte Arabische Emirate", - "gbCountryDisplayName": "Vereinigtes Königreich", - "usCountryDisplayName": "Vereinigte Staaten", - "uyCountryDisplayName": "Uruguay", - "uzCountryDisplayName": "Usbekistan", - "vuCountryDisplayName": "Vanuatu", - "vaCountryDisplayName": "Vatikanstadt", - "veCountryDisplayName": "Venezuela", - "vnCountryDisplayName": "Vietnam", - "wfCountryDisplayName": "Wallis und Futuna", - "ehCountryDisplayName": "Westsahara", - "yeCountryDisplayName": "Jemen", - "zmCountryDisplayName": "Sambia", - "zwCountryDisplayName": "Simbabwe", - "pay": "Zur Kasse", - "invitedToSpace": "{user} hat Sie eingeladen, an einem Kurs teilzunehmen: {space}! Möchten Sie annehmen?", - "youreInvited": "📩 Sie sind eingeladen!", - "invitedToChat": "{user} hat Sie eingeladen, an einem Chat teilzunehmen: {name}! Möchten Sie annehmen?", - "monthlySubscription": "Monatlich", - "yearlySubscription": "Jährlich", - "defaultSubscription": "Pangea Chat-Abonnement", - "freeTrial": "Kostenlose Testversion", - "total": "Gesamt: ", - "noDataFound": "Keine Daten gefunden", - "blurMeansTranslateTitle": "Warum ist die Nachricht verschwommen?", - "blurMeansTranslateBody": "Wenn der Immersionsmodus aktiviert ist, werden Nachrichten, die in Ihrer Basissprache gesendet werden, verschwommen dargestellt, während Pangea Bot sie in Ihre Zielsprache übersetzt. Der Immersionsmodus kann in den Einstellungen für einzelne Chats und Kurse umgeschaltet werden.", - "bestCorrectionFeedback": "Das ist richtig!", - "distractorFeedback": "Das ist nicht ganz richtig.", - "bestAnswerFeedback": "Das ist richtig!", - "definitionDefaultPrompt": "Was bedeutet dieses Wort?", - "practiceDefaultPrompt": "Was ist die beste Antwort?", - "correctionDefaultPrompt": "Was ist die beste Alternative?", - "acceptSelection": "Korrektur akzeptieren", - "why": "Warum?", - "definition": "Definition", - "exampleSentence": "Beispielsatz", - "reportToTeacher": "Wen möchten Sie diese Nachricht melden?", - "reportMessageTitle": "{reportingUserId} hat eine Nachricht von {reportedUserId} im Chat {roomName} gemeldet", - "reportMessageBody": "Nachricht: {reportedMessage}\nGrund: {reason}", - "noTeachersFound": "Keine Lehrer zum Melden gefunden", - "trialExpiration": "Ihre kostenlose Testphase läuft am {expiration} ab", - "freeTrialDesc": "Neue Nutzer erhalten eine einwöchige kostenlose Testphase von Pangea Chat", - "activateTrial": "Kostenlose 7-Tage-Testversion", - "successfullySubscribed": "Sie haben erfolgreich abonniert!", - "clickToManageSubscription": "Klicken Sie hier, um Ihr Abonnement zu verwalten.", - "signUp": "Registrieren", - "pleaseChooseAtLeastChars": "Bitte wählen Sie mindestens {min} Zeichen.", - "noEmailWarning": "Bitte geben Sie eine gültige E-Mail-Adresse ein. Andernfalls können Sie Ihr Passwort nicht zurücksetzen. Wenn Sie das nicht möchten, tippen Sie erneut auf die Schaltfläche, um fortzufahren.", - "pleaseEnterValidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein.", - "pleaseChooseAUsername": "Bitte wählen Sie einen Benutzernamen", - "define": "Definieren", - "listen": "Hören", - "trialPeriodExpired": "Ihre Testphase ist abgelaufen", - "translations": "Übersetzungen", - "messageAudio": "Nachrichten-Audio", - "definitions": "Definitionen", - "subscribedToUnlockTools": "Abonnieren Sie, um interaktive Übersetzungen und Grammatikprüfungen, Audio-Wiedergabe, personalisierte Übungsaktivitäten und Lernanalysen freizuschalten!", - "translationTooltip": "Übersetzen", - "speechToTextTooltip": "Transkript", - "kickBotWarning": "Das Entfernen des Pangea-Bots entfernt den Konversationsbot aus diesem Chat.", - "refresh": "Aktualisieren", - "messageAnalytics": "Nachrichtenanalyse", - "words": "Wörter", - "score": "Punktzahl", - "accuracy": "Genauigkeit", - "points": "Punkte", - "noPaymentInfo": "Keine Zahlungsinformationen erforderlich!", - "updatePhoneOS": "Sie müssen möglicherweise die OS-Version Ihres Geräts aktualisieren.", - "wordsPerMinute": "Wörter pro Minute", - "tooltipInstructionsTitle": "Nicht sicher, was das macht?", - "tooltipInstructionsMobileBody": "Tippen und halten Sie Elemente, um Tooltips anzuzeigen.", - "tooltipInstructionsBrowserBody": "Bewegen Sie den Mauszeiger über Elemente, um Tooltips anzuzeigen.", - "chatCapacity": "Chat-Kapazität", - "roomFull": "Dieser Raum ist bereits voll.", - "chatCapacityHasBeenChanged": "Chat-Kapazität wurde geändert", - "chatCapacitySetTooLow": "Die Chat-Kapazität muss mindestens {count} sein.", - "chatCapacityExplanation": "Die Chat-Kapazität begrenzt die Anzahl der Mitglieder, die in einem Chat erlaubt sind.", - "tooManyRequest": "Zu viele Anfragen, bitte versuchen Sie es später erneut.", - "enterNumber": "Bitte geben Sie eine ganze Zahl ein.", - "buildTranslation": "Erstellen Sie Ihre Übersetzung aus den oben genannten Optionen", - "practice": "Üben", - "noLanguagesSet": "Keine Sprachen festgelegt", - "speechToTextBody": "Bei Sprachnachrichten können Sie sowohl eine Transkription als auch die Wörter pro Minute des Sprechers sehen.", - "versionNotFound": "Version nicht gefunden", - "fetchingVersion": "Version wird abgerufen...", - "versionFetchError": "Fehler beim Abrufen der Version", - "versionText": "Version: {version}+{buildNumber}", - "l1TranslationBody": "Nachrichten in Ihrer Basissprache werden nicht übersetzt.", - "deleteSubscriptionWarningTitle": "Sie haben ein aktives Abonnement", - "deleteSubscriptionWarningBody": "Das Löschen Ihres Kontos wird Ihr Abonnement nicht automatisch kündigen.", - "manageSubscription": "Abonnement verwalten", - "error520Title": "Bitte versuchen Sie es erneut.", - "error520Desc": "Entschuldigung, wir konnten Ihre Nachricht nicht verstehen...", - "wordsUsed": "Verwendete Wörter", - "level": "Stufe", - "morphsUsed": "Verwendete Morphs", - "translationChoicesBody": "Klicken und halten Sie eine Option für einen Hinweis.", - "grammar": "Grammatik", - "contactHasBeenInvitedToTheChat": "Kontakt wurde zum Chat eingeladen", - "inviteChat": "📨 Chat einladen", - "chatName": "Chat-Name", - "reportContentIssueTitle": "Inhaltproblem melden", - "feedback": "Optionale Rückmeldung", - "reportContentIssueDescription": "Oh je! KI kann personalisierte Lernerfahrungen erleichtern, aber... halluziniert auch. Bitte geben Sie jegliches Feedback, das Sie haben, und wir versuchen es erneut.", - "clickTheWordAgainToDeselect": "Klicken Sie auf das ausgewählte Wort, um es abzuwählen.", - "l2SupportNa": "Nicht verfügbar", - "l2SupportAlpha": "Alpha", - "l2SupportBeta": "Beta", - "l2SupportFull": "Vollständig", - "missingVoiceTitle": "Stimme fehlt", - "voiceNotAvailable": "Sie haben keine Stimme für diese Sprache installiert.", - "openVoiceSettings": "Stimmeinstellungen öffnen", - "playAudio": "Abspielen", - "stop": "Stopp", - "grammarCopyPOSsconj": "Subordinierende Konjunktion", - "grammarCopyPOSnum": "Nummer", - "grammarCopyPOSverb": "Verb", - "grammarCopyPOSaffix": "Affix", - "grammarCopyPOSpart": "Partikel", - "grammarCopyPOSadj": "Adjektiv", - "grammarCopyPOScconj": "Koordinierende Konjunktion", - "grammarCopyPOSpunct": "Interpunktion", - "grammarCopyPOSadv": "Adverb", - "grammarCopyPOSaux": "Hilfsverb", - "grammarCopyPOSspace": "Leerzeichen", - "grammarCopyPOSsym": "Symbol", - "grammarCopyPOSdet": "Artikel", - "grammarCopyPOSpron": "Pronomen", - "grammarCopyPOSadp": "Adposition", - "grammarCopyPOSpropn": "Eigenname", - "grammarCopyPOSnoun": "Substantiv", - "grammarCopyPOSintj": "Interjektion", - "grammarCopyPOSx": "Andere", - "grammarCopyGENDERfem": "Weiblich", - "grammarCopyPERSON2": "Zweite Person", - "grammarCopyMOODimp": "Imperativ", - "grammarCopyPUNCTTYPEqest": "Frage", - "grammarCopyASPECTperf": "Perfekt", - "grammarCopyCASEaccnom": "Akkusativ, Nominativ", - "grammarCopyCASEobl": "Oblique", - "grammarCopyVOICEact": "Aktiv", - "grammarCopyPUNCTTYPEbrck": "Klammer", - "grammarCopyNOUNTYPEart": "Artikel", - "grammarCopyNUMBERsing": "Singular", - "grammarCopyGENDERmasc": "Maskulin", - "grammarCopyVERBTYPEmod": "Modal", - "grammarCopyADVTYPEadverbial": "Adverbial", - "grammarCopyTENSEperi": "Periphrastisch", - "grammarCopyNUMFORMdigit": "Ziffer", - "grammarCopyNOUNTYPEnot_proper": "Nicht Eigennamen", - "grammarCopyNUMTYPEcard": "Kardinalzahl", - "grammarCopyNOUNTYPEprop": "Eigenname", - "grammarCopyPUNCTTYPEdash": "Bindestrich", - "grammarCopyPUNCTTYPEyes": "Ja", - "grammarCopyPUNCTTYPEsemi": "Semikolon", - "grammarCopyPUNCTTYPEcomm": "Komma", - "grammarCopyMOODcnd": "Konditional", - "grammarCopyCASEacc": "Akkusativ", - "grammarCopyPARTTYPEpart": "Partitiv", - "grammarCopyTENSEpast": "Vergangenheit", - "grammarCopyDEGREEsup": "Superlativ", - "grammarCopyPUNCTTYPEcolo": "Doppelpunkt", - "grammarCopyPERSON3": "Dritte Person", - "grammarCopyNUMBERplur": "Plural", - "grammarCopyPRONTYPEnpr": "Eigenname", - "grammarCopyPRONTYPEinterrogative": "Fragewort", - "grammarCopyPOLITEinfm": "Informell", - "grammarCopyADVTYPEtim": "Zeit", - "grammarCopyPOLARITYneg": "Negativ", - "grammarCopyNUMTYPEtot": "Gesamt", - "grammarCopyADVTYPEadnomial": "Adnominal", - "grammarCopyASPECTprog": "Progressiv", - "grammarCopyMOODsub": "Subjunktiv", - "grammarCopyVERBFORMcomplementive": "Komplementiv", - "grammarCopyCASEnom": "Nominativ", - "grammarCopyTENSEfut": "Zukunft", - "grammarCopyCASEdat": "Dativ", - "grammarCopyTENSEpres": "Präsens", - "grammarCopyGENDERneut": "Neutrum", - "grammarCopyPRONTYPErel": "Relativpronomen", - "grammarCopyVERBFORMfinalEnding": "Endung", - "grammarCopyPRONTYPEdem": "Demonstrativpronomen", - "grammarCopyPREPCASEpre": "Präpositional", - "grammarCopyVERBFORMfin": "Finit", - "grammarCopyDEGREEpos": "Positiv", - "grammarCopyPUNCTTYPEquot": "Zitat", - "grammarCopyVERBFORMger": "Gerundium", - "grammarCopyVOICEpass": "Passiv", - "grammarCopyCASEgen": "Genitiv", - "grammarCopyTENSEprs": "Präsens", - "grammarCopyDEFINITEdef": "Bestimmt", - "grammarCopyNUMTYPEord": "Ordinal", - "grammarCopyCASEins": "Instrumental", - "grammarCopyVERBFORMinf": "Infinitiv", - "grammarCopyVERBFORMaux": "Hilfsverb", - "grammarCopyNUMFORMlong": "Lang", - "grammarCopyCASEloc": "Lokativer Fall", - "grammarCopyMOODind": "Indikativ", - "grammarCopyDEGREEcmp": "Komparativ", - "grammarCopyCASErelativeCase": "Relativ", - "grammarCopyPUNCTTYPEexcl": "Ausruf", - "grammarCopyPERSON1": "Erste Person", - "grammarCopyPUNCTSIDEini": "Anfang", - "grammarCopyGENDERperson": "Person", - "grammarCopyFOREIGNyes": "Fremd", - "grammarCopyVOICEvoice": "Stimme", - "grammarCopyVERBTYPEverbType": "Verb", - "grammarCopyPOSSpass": "Possessiv", - "grammarCopyPREPCASEprepCase": "Präpositional", - "grammarCopyNUMTYPEnumType": "Zahlwort", - "grammarCopyNOUNTYPEnounType": "Substantiv", - "grammarCopyREFLEXreflex": "Reflexiv", - "grammarCopyPRONTYPEpronType": "Pronomen", - "grammarCopyPUNCTSIDEpunctSide": "Satzzeichen-Seite", - "grammarCopyVERBFORMverbForm": "Verbform", - "grammarCopyGENDERgender": "Geschlecht", - "grammarCopyMOODmood": "Modus", - "grammarCopyASPECTaspect": "Aspekt", - "grammarCopyPUNCTTYPEpunctType": "Zeichensetzung", - "grammarCopyTENSEtense": "Zeitform", - "grammarCopyDEGREEdegree": "Grad", - "grammarCopyPOLITEpolite": "Höflichkeit", - "grammarCopyADVTYPEadvType": "Adverb", - "grammarCopyNUMFORMnumber": "Zahl", - "grammarCopyCONJTYPEconjType": "Konjunktion", - "grammarCopyPOLARITYpolarity": "Polarisierung", - "grammarCopyCASEcase": "Fall", - "grammarCopyDEFINITEdefinite": "Bestimmtheit", - "grammarCopyNUMFORMnumForm": "Zahlwort", - "grammarCopyPRONTYPEadn": "Adnominal", - "grammarCopyVOCvoc": "Vokativ", - "grammarCopyCMPLcmpl": "Komplementierer", - "grammarCopyADVadv": "Adverbial", - "grammarCopyMOODjus": "Jussiv", - "grammarCopyGENDERcom": "Gemeinsam", - "grammarCopyREFLEXrflx": "Reflexiv", - "grammarCopyPARTTYPEpar": "Partitiv", - "grammarCopySPCspc": "Spezifisch", - "grammarCopyTENSEpqp": "Plusquamperfekt", - "grammarCopyREFLEXref": "Reflexiv", - "grammarCopyPUNCTTYPEnshrt": "Kurz", - "grammarCopyNUMBERdual": "Dual", - "grammarCopyNUMFORMlng": "Lang", - "grammarCopyVOICEmid": "Mittelfeld", - "grammarCopyINTRELintRel": "Interrogativ, Relativ", - "grammarCopyINTint": "Interrogativ", - "grammarCopyVOICEcaus": "Kausativ", - "grammarCopyUnknown": "Unbekannt", - "grammarCopyEVIDENTevident": "Beweisbarkeit", - "grammarCopyNUMFORMnumberPsor": "Besitzernummer", - "grammarCopyASPECThab": "Gewohnheitsmäßig", - "grammarCopyCASEabl": "Ablativ", - "grammarCopyCASEall": "Allativ", - "grammarCopyCASEess": "Essiv", - "grammarCopyCASEtra": "Translativ", - "grammarCopyCASEequ": "Äquativ", - "grammarCopyCASEdis": "Distributiv", - "grammarCopyCASEabs": "Absolut", - "grammarCopyCASEerg": "Ergativ", - "grammarCopyCASEcau": "Kausal", - "grammarCopyCASEben": "Benefaktiv", - "grammarCopyCASEtem": "Temporär", - "grammarCopyCONJTYPEcoord": "Koordinierend", - "grammarCopyDEFINITEcons": "Konstruktionszustand", - "grammarCopyDEGREEabs": "Absoluter Grad", - "grammarCopyEVIDENTfh": "Faktische Evidentialität", - "grammarCopyEVIDENTnfh": "Nicht-faktische Evidentialität", - "grammarCopyMOODopt": "Optativ", - "grammarCopyMOODadm": "Admirativ", - "grammarCopyMOODdes": "Desiderativ", - "grammarCopyMOODnec": "Notwendig", - "grammarCopyMOODpot": "Potenzial", - "grammarCopyMOODprp": "Propositive", - "grammarCopyMOODqot": "Quotativ", - "grammarCopyNUMFORMword": "Wortform", - "grammarCopyNUMFORMroman": "Römische Zahl", - "grammarCopyNUMFORMletter": "Buchstabenform", - "grammarCopyNUMTYPEmult": "Multiplikativ", - "grammarCopyNUMTYPEfrac": "Bruchzahl", - "grammarCopyNUMTYPEsets": "Menge", - "grammarCopyNUMTYPErange": "Bereich", - "grammarCopyNUMTYPEdist": "Distributiv", - "grammarCopyNUMBERtri": "Versuch", - "grammarCopyNUMBERpauc": "Paukal", - "grammarCopyNUMBERgrpa": "Größerer Paukal", - "grammarCopyNUMBERgrpl": "Größerer Plural", - "grammarCopyNUMBERinv": "Inverse", - "grammarCopyPERSON0": "Null", - "grammarCopyPERSON4": "Vierte", - "grammarCopyPOLITEform": "Formell", - "grammarCopyPOLITEelev": "Hochgestochen", - "grammarCopyPOLITEhumb": "Demütig", - "grammarCopyPRONTYPEemp": "Betont", - "grammarCopyPRONTYPEexc": "Ausruf", - "grammarCopyPRONTYPErcp": "Reziprok", - "grammarCopyPRONTYPEintRelPronType": "Interrogativ-Relativ", - "grammarCopyTENSEaor": "Aorist", - "grammarCopyTENSEeps": "Epistemisch", - "grammarCopyTENSEprosp": "Prospektiv", - "grammarCopyVERBFORMpart": "Partizip", - "grammarCopyVERBFORMconv": "Converb", - "grammarCopyVERBFORMvnoun": "Verbalnomen", - "grammarCopyVOICEantip": "Antipassiv", - "grammarCopyVOICEcauVoice": "Kausativ", - "grammarCopyVOICedir": "Direkt", - "grammarCopyVOICEinvVoice": "Inverse", - "grammarCopyVOICErcpVoice": "Reziprok", - "grammarCopyPOS": "Wortart", - "grammarCopyGENDER": "Geschlecht", - "grammarCopyPERSON": "Person", - "grammarCopyMOOD": "Modus", - "grammarCopyPUNCTTYPE": "Zeichensetzungstyp", - "grammarCopyASPECT": "Aspekt", - "grammarCopyCASE": "Fall", - "grammarCopyVOICE": "Stimme", - "grammarCopyNOUNTYPE": "Substantivart", - "grammarCopyVERBTYPE": "Verbart", - "grammarCopyADVTYPE": "Adverbart", - "grammarCopyNUMFORM": "Numeralform", - "grammarCopyNUMTYPE": "Numeralart", - "grammarCopyNUMBER": "Nummer", - "grammarCopyDEFINITE": "Bestimmtheit", - "grammarCopyDEGREE": "Grad", - "grammarCopyEVIDENT": "Evidentialität", - "grammarCopyFOREIGN": "Fremd", - "grammarCopyPOLARITY": "Polarität", - "grammarCopyPOLITE": "Höflichkeit", - "grammarCopyPREPCASE": "Präpositionalfall", - "grammarCopyPRONTYPE": "Pronomenart", - "grammarCopyPUNCTSIDE": "Zeichensetzung", - "grammarCopyREFLEX": "Reflexiv", - "grammarCopyTENSE": "Zeitform", - "grammarCopyVERBFORM": "Verbform", - "grammarCopyCONJTYPE": "Konjunktionstyp", - "grammarCopySPC": "Spezifizität", - "grammarCopyPARTTYPE": "Partitivtyp", - "grammarCopyINTREL": "Interrogativ-Relativ", - "grammarCopyUNKNOWN": "Unbekannt", - "grammarCopyNUMBERPSOR": "Besitzers Nummer", - "grammarCopyPOSS": "Possessiv", - "grammarCopyASPECTimp": "Unvollendeter Aspekt", - "grammarCopyCASEvoc": "Vokativ", - "grammarCopyCASEcom": "Kommativ", - "grammarCopyCASEpar": "Partitiv", - "grammarCopyCASEadv": "Adverbial", - "grammarCopyCASEref": "Referenziell", - "grammarCopyCASErel": "Relativ", - "grammarCopyCASEsub": "Subessiv", - "grammarCopyCASEsup": "Superessiv", - "grammarCopyCASEaccdat": "Akkusativ-Dativ", - "grammarCopyCASEpre": "Präpositional", - "grammarCopyCONJTYPEsub": "Subordinierend", - "grammarCopyCONJTYPEcmp": "Komparativ", - "grammarCopyDEFINITEind": "Indefinit", - "grammarCopyMOODint": "Fragesatz", - "grammarCopyNOUNTYPEcomm": "Gemeines Substantiv", - "grammarCopyNUMBERPSORsing": "Possessors Singular", - "grammarCopyNUMBERPSORplur": "Possessors Plural", - "grammarCopyNUMBERPSORdual": "Possessors Dual", - "grammarCopyPOLARITYpos": "Positive Polarität", - "grammarCopyPOSSyes": "Possessiv", - "grammarCopyPREPCASEnpr": "Nicht-präpositional", - "grammarCopyPRONTYPEprs": "Personal", - "grammarCopyPRONTYPEint": "Interrogativ", - "grammarCopyPRONTYPEtot": "Total", - "grammarCopyPRONTYPEneg": "Negativ", - "grammarCopyPRONTYPEart": "Artikel", - "grammarCopyPRONTYPEind": "Indefinit", - "grammarCopyPRONTYPEintrel": "Interrogativ-Relativ", - "grammarCopyPUNCTSIDEfin": "Endzeichen", - "grammarCopyPUNCTTYPEperi": "Punkt", - "grammarCopyREFLEXyes": "Reflexiv", - "grammarCopyTENSEimp": "Unvollkommen", - "grammarCopyVERBFORMsup": "Supin", - "grammarCopyVERBFORMadn": "Adjektivisch", - "grammarCopyVERBFORMlng": "Lang", - "grammarCopyVERBFORMshrt": "Kurz", - "grammarCopyVERBTYPEcaus": "Kausatives Verb", - "grammarCopyVOICEcau": "Kausativ", - "grammarCopyVOICEdir": "Direkt", - "grammarCopyVOICEinv": "Inverse", - "grammarCopyVOICErcp": "Reziprok", - "other": "Andere", - "levelShort": "LVL {level}", - "clickBestOption": "Wählen Sie die besten Optionen, um Ihre Nachricht zu übersetzen!", - "completeActivitiesToUnlock": "Absolvieren Sie mindestens eine Aktivität, um die Übersetzung freizuschalten!", - "noCapacityLimit": "Keine Kapazitätsbegrenzung", - "downloadGroupText": "Gruppentext herunterladen", - "notificationsOn": "Benachrichtigungen aktiviert", - "notificationsOff": "Benachrichtigungen deaktiviert", - "createChatAndInviteUsers": "Chat erstellen und Benutzer einladen", - "updatedNewSpaceDescription": "Kurse ermöglichen es, Ihre Chats zu konsolidieren und private oder öffentliche Gemeinschaften aufzubauen.", - "joinWithCode": "Mit Code beitreten", - "enterCodeToJoin": "Code eingeben, um beizutreten", - "updateNow": "Jetzt aktualisieren", - "updateLater": "Später aktualisieren", - "constructUseWaDesc": "Ohne Hilfe verwendet", - "constructUseGaDesc": "Grammatikunterstützung", - "constructUseTaDesc": "Übersetzungshilfe", - "constructUseUnkDesc": "Unbekannt", - "constructUseCorITDesc": "Korrekt in Übersetzung", - "constructUseIgnITDesc": "Ignoriert in Übersetzung", - "constructUseIncITDesc": "Falsch in Übersetzung", - "constructUseIgnIGCDesc": "Ignoriert in Grammatikprüfung", - "constructUseCorIGCDesc": "Korrekt in Grammatikprüfung", - "constructUseIncIGCDesc": "Falsch in Grammatikprüfung", - "constructUseCorPADesc": "Korrekt in Wortbedeutungsaktivität", - "constructUseIgnPADesc": "Ignoriert in Wortbedeutungsaktivität", - "constructUseIncPADesc": "Falsch in Wortbedeutungsaktivität", - "constructUseCorWLDesc": "Korrekt in Worthöraktivität", - "constructUseIncWLDesc": "Falsch in Worthöraktivität", - "constructUseIngWLDesc": "Ignoriert in Worthöraktivität", - "constructUseCorHWLDesc": "Korrekt in verstecktem Wortspiel", - "constructUseIncHWLDesc": "Falsch in verstecktem Wortspiel", - "constructUseIgnHWLDesc": "Ignoriert in verstecktem Wortspiel", - "constructUseCorLDesc": "Korrekt in Lemma-Aktivität", - "constructUseIncLDesc": "Falsch in Lemma-Aktivität", - "constructUseIgnLDesc": "Ignoriert in Lemma-Aktivität", - "constructUseCorMDesc": "Korrekt in Grammatikaktivität", - "constructUseIncMDesc": "Falsch in Grammatikaktivität", - "constructUseIgnMDesc": "In der Grammatikaktivität ignoriert", - "constructUseEmojiDesc": "In der Emoji-Aktivität korrekt", - "constructUseCollected": "Im Chat gesammelt", - "constructUseNanDesc": "Nicht anwendbar", - "xpIntoLevel": "{currentXP} / {maxXP} XP", - "enableTTSToolName": "Text-zu-Sprache aktiviert", - "enableTTSToolDescription": "Erlauben Sie der App, Text-zu-Sprache-Ausgaben für Textteile in Ihrer Zielsprache zu generieren.", - "yourUsername": "Ihr Benutzername", - "yourEmail": "Ihre E-Mail", - "iWantToLearn": "Ich möchte lernen", - "pleaseEnterEmail": "Bitte gib eine gültige E-Mail-Adresse ein.", - "myBaseLanguage": "Meine Basissprache", - "meaningSectionHeader": "Bedeutung:", - "formSectionHeader": "In Chats verwendete Formen:", - "writingExercisesTooltip": "Schreiben", - "listeningExercisesTooltip": "Hören", - "readingExercisesTooltip": "Lesen", - "meaningNotFound": "Bedeutung konnte nicht gefunden werden.", - "chooseBaseForm": "Wähle die Grundform", - "notTheCodeError": "Entschuldigung, das ist nicht der Code!", - "totalXP": "Gesamt-XP", - "numLemmas": "Gesamtzahl der Lemmas", - "numLemmasUsedCorrectly": "Anzahl der Lemmas, die mindestens einmal korrekt verwendet wurden", - "numLemmasUsedIncorrectly": "Anzahl der Lemmas, die 0-mal korrekt verwendet wurden", - "numLemmasSmallXP": "Anzahl der Lemmas mit 0 - 30 XP", - "numLemmasMediumXP": "Anzahl der Lemmas mit 31 - 200 XP", - "numLemmasLargeXP": "Anzahl der Lemmas mit > 200 XP", - "numGrammarConcepts": "Anzahl der Grammatikbegriffe", - "listGrammarConcepts": "Grammatikbegriffe", - "listGrammarConceptsUsedCorrectly": "Grammatikbegriffe, die in Originalnachrichten mindestens 80 % der Zeit korrekt verwendet wurden", - "listGrammarConceptsUsedIncorrectly": "Grammatikbegriffe, die in Originalnachrichten weniger als 80 % der Zeit korrekt verwendet wurden", - "listGrammarConceptsUseCorrectlySystemGenerated": "Grammatikbegriffe, die aus systemgenerierten Vorschlägen mindestens 80 % der Zeit korrekt ausgewählt wurden", - "listGrammarConceptsUseIncorrectlySystemGenerated": "Grammatikbegriffe, die aus systemgenerierten Vorschlägen weniger als 80 % der Zeit korrekt ausgewählt wurden", - "listGrammarConceptsSmallXP": "Grammatikbegriffe mit 0-50 XP", - "listGrammarConceptsMediumXP": "Grammatikbegriffe mit 51-200 XP", - "listGrammarConceptsLargeXP": "Grammatikbegriffe mit 201-500 XP", - "listGrammarConceptsHugeXP": "Grammatikbegriffe mit > 500 XP", - "numMessagesSent": "Anzahl der gesendeten Nachrichten", - "numWordsTyped": "Anzahl der in Originalnachrichten getippten Wörter", - "numCorrectChoices": "Anzahl der korrekt gewählten Wörter aus systemgenerierten Vorschlägen", - "numIncorrectChoices": "Anzahl der falsch gewählten Wörter aus systemgenerierten Vorschlägen", - "commaSeparatedFile": "CSV", - "excelFile": "Excel", - "fileType": "Dateityp", - "download": "Herunterladen", - "analyticsNotAvailable": "Benutzeranalysen nicht verfügbar", - "downloading": "Wird heruntergeladen...", - "failedFetchUserAnalytics": "Fehler beim Herunterladen der Benutzeranalysen", - "downloadComplete": "Download abgeschlossen!", - "whatIsTheMorphTag": "Was ist das {morphologicalFeature} von '{wordForm}'?", - "dataAvailable": "Datenverfügbarkeit", - "available": "Verfügbar", - "pangeaBotIsFallible": "Pangea Bot macht auch Fehler!", - "whatIsMeaning": "Was bedeutet '{lemma}'?", - "pickAnEmoji": "Welches ist dein Lieblings-Emoji für '{lemma}'?", - "chooseLemmaMeaningInstructionsBody": "Ordne Bedeutungen den Wörtern in der Nachricht zu!", - "doubleClickToEdit": "Doppelklicken zum Bearbeiten.", - "activityPlannerTitle": "Aktivitätsplaner", - "topicLabel": "Thema", - "topicPlaceholder": "Wählen Sie ein Thema...", - "modeLabel": "Aktivitätstyp", - "modePlaceholder": "Wählen Sie einen Modus...", - "learningObjectiveLabel": "Lernziel", - "learningObjectivePlaceholder": "Wählen Sie ein Lernziel...", - "languageOfInstructionsLabel": "Sprache der Anweisungen", - "targetLanguageLabel": "Zielsprache", - "cefrLevelLabel": "CEFR-Niveau", - "generateActivitiesButton": "Aktivität generieren", - "launchActivityButton": "Aktivität starten", - "image": "Bild", - "video": "Video", - "nan": "Nicht zutreffend", - "activityPlannerOverviewInstructionsBody": "Wählen Sie ein Thema, einen Modus, ein Lernziel und generieren Sie eine Aktivität für den Chat!", - "activityTitle": "Titel der Aktivität", - "addVocabulary": "Vokabular hinzufügen", - "instructions": "Anweisungen", - "numberOfLearners": "Anzahl der Lernenden", - "mustBeInteger": "Muss eine ganze Zahl sein, z.B. 1, 2, 3, ...", - "constructUsePvmDesc": "In Sprachmitteilung produziert", - "leaveSpaceDescription": "Wenn Sie den Kurs verlassen, verlassen Sie alle Chats darin. Andere Nutzer sehen, dass Sie den Kurs verlassen haben.", - "constructUseCorMmDesc": "Korrekte Nachrichtenbedeutung", - "constructUseIncMmDesc": "Falsche Nachrichtenbedeutung", - "constructUseIgnMmDesc": "Ignorierte Nachrichtenbedeutung", - "clickForMeaningActivity": "Klicken Sie hier für eine Bedeutungsherausforderung", - "meaning": "Bedeutung", - "chatWith": "Gruppe mit {displayname}", - "clickOnEmailLink": "Bitte klicken Sie auf den Link in der E-Mail und fahren Sie fort.\n\nÜberprüfen Sie Ihren Spam-Ordner, falls die E-Mail nicht angekommen ist.", - "dontForgetPassword": "Vergessen Sie nicht Ihr Passwort!", - "enableAutocorrectToolName": "Gerätekorrektur aktivieren", - "enableAutocorrectDescription": "Wenn Ihr Gerät die Sprache unterstützt, die Sie lernen, können Sie die Gerätekorrektur aktivieren, um häufige Fehler beim Tippen zu korrigieren.", - "ttsDisbledTitle": "Text-zu-Sprache deaktiviert", - "ttsDisabledBody": "Sie können die Text-zu-Sprache-Funktion in Ihren Lerneinstellungen aktivieren", - "noSpaceDescriptionYet": "Noch keine Kursbeschreibung erstellt.", - "tooLargeToSend": "Diese Nachricht ist zu groß zum Senden", - "exitWithoutSaving": "Möchten Sie wirklich ohne Speichern verlassen?", - "enableAutocorrectPopupTitle": "Fügen Sie Ihre Zielsprachentastatur hinzu, indem Sie zu gehen:", - "enableAutocorrectPopupSteps": " • Einstellungen\n • Allgemein\n • Tastatur\n • Tastaturen\n • Neue Tastatur hinzufügen", - "enableAutocorrectPopupDescription": "Sobald die Sprache ausgewählt ist, können Sie auf das kleine Globus-Symbol unten links auf Ihrer Tastatur klicken, um die neu installierte Tastatur zu aktivieren.", - "downloadGboardTitle": "Laden Sie Gboard aus dem Google Play Store herunter, um Autokorrektur und andere Tastaturfunktionen zu aktivieren:", - "downloadGboardSteps": " • Gboard herunterladen\n • App öffnen\n • Sprachen\n • Tastatur hinzufügen\n • Sprache auswählen\n • Tastaturtyp auswählen\n • Fertig", - "downloadGboardDescription": "Sobald die Sprache ausgewählt ist, können Sie auf das kleine Globus-Symbol unten links auf Ihrer Tastatur klicken, um die neu installierte Tastatur zu aktivieren.", - "enableAutocorrectWarning": "Warnung! Erfordert das Hinzufügen Ihrer Zielsprachentastatur", - "displayName": "Anzeigename", - "leaveRoomDescription": "Sie sind dabei, diesen Chat zu verlassen. Andere Benutzer werden sehen, dass Sie den Chat verlassen haben.", - "confirmUserId": "Bitte bestätigen Sie Ihren Pangea-Chat-Benutzernamen, um Ihr Konto zu löschen.", - "startingToday": "Ab heute", - "oneWeekFreeTrial": "Eine Woche kostenlos testen", - "paidSubscriptionStarts": "Beginnt am {startDate}", - "cancelInSubscriptionSettings": "• Jederzeit in den Abonnement-Einstellungen kündigen", - "cancelToAvoidCharges": "• Vor {trialEnds} kündigen, um Gebühren zu vermeiden", - "downloadGboard": "Gboard herunterladen", - "autocorrectNotAvailable": "Leider wird diese Plattform derzeit nicht für diese Funktion unterstützt. Bleiben Sie dran für weitere Entwicklungen!", - "pleaseUpdateApp": "Bitte aktualisieren Sie die App, um fortzufahren.", - "chooseEmojiInstructionsBody": "Ordnen Sie Emojis den Wörtern zu, die sie am besten repräsentieren. Keine Sorge! Es gibt keine Punktabzüge bei Meinungsverschiedenheiten. 😅", - "analyticsVocabListBody": "Das ist Ihr gesamter Wortschatz! Während Sie XP für jedes Wort verdienen, entwickeln sie sich vom Keimling zur vollen Blüte. Klicken Sie auf ein Wort, um mehr Details zu sehen.", - "morphAnalyticsListBody": "Dies sind alle Grammatik-Konzepte in der Sprache, die Sie lernen! Sie werden sie freischalten, wenn Sie ihnen beim Chatten begegnen. Klicken Sie für Details.", - "knockSpaceSuccess": "Sie haben die Anfrage gestellt, an diesem Kurs teilzunehmen! Ein Administrator wird auf Ihre Anfrage antworten, sobald er sie erhält 😄", - "chooseWordAudioInstructionsBody": "Hören Sie sich die vollständige Nachricht an. Ordnen Sie dann die Audios den Wörtern zu.", - "chooseMorphsInstructionsBody": "Klicken Sie auf die Puzzleteile für Grammatikfragen!", - "pleaseEnterInt": "Bitte geben Sie eine Zahl ein", - "home": "Startseite", - "join": "Beitreten", - "readingAssistanceOverviewBody": "Klicken Sie auf die Buttons unten für Minispiele zum Zuordnen von Emojis, Audios, Wortbedeutungen und Grammatik-Konzepten. Oder klicken Sie auf ein Wort für Details.", - "levelSummaryPopupTitle": "Level {level} Zusammenfassung", - "resetInstructionTooltipsTitle": "Anleitungstooltips zurücksetzen", - "resetInstructionTooltipsDesc": "Klicke, um Anleitungstooltips wie für einen völlig neuen Benutzer anzuzeigen.", - "selectForGrammar": "Wähle ein Grammatik-Icon für Aktivitäten und Details.", - "randomize": "Zufällig", - "clear": "Löschen", - "makeYourOwnActivity": "Erstelle deine eigene Aktivität", - "featuredActivities": "Vorgestellt", - "save": "Speichern", - "startChat": "Chat starten", - "translationProblem": "Übersetzungsproblem", - "askToJoin": "Frage zum Beitritt", - "emptyChatWarningTitle": "Chat ist leer", - "emptyChatWarningDesc": "Du hast niemanden zu deinem Chat eingeladen. Gehe zu den Chat-Einstellungen, um deine Kontakte oder den Bot einzuladen. Du kannst das auch später tun.", - "areYouLikeMe": "Bist du wie ich?", - "tryAgainLater": "Zu viele Versuche unternommen. Bitte versuche es in 5 Minuten erneut.", - "enterSpaceCode": "Gib den Kurscode ein", - "shareSpaceLink": "Link teilen", - "byUsingPangeaChat": "Durch die Nutzung von Pangea Chat stimme ich den ", - "details": "Details", - "languageLevelPreA1Desc": "Ich habe die Sprache noch nie gelernt oder benutzt.", - "languageLevelA1Desc": "Ich kann einige vertraute alltägliche Ausdrücke und sehr grundlegende Sätze verstehen und verwenden.", - "languageLevelA2Desc": "Ich kann Sätze und häufig verwendete Ausdrücke verstehen, die sich auf Bereiche unmittelbarer Relevanz beziehen.", - "languageLevelB1Desc": "Ich kann mit den meisten vertrauten Situationen umgehen und einfache zusammenhängende Texte zu vertrauten Themen produzieren.", - "languageLevelB2Desc": "Ich kann die Hauptideen komplexer Texte verstehen und mit einem gewissen Maß an Flüssigkeit und Spontaneität interagieren.", - "languageLevelC1Desc": "Ich kann Ideen fließend und spontan ausdrücken, ohne große Mühe, und eine Vielzahl anspruchsvoller Texte verstehen.", - "languageLevelC2Desc": "Ich kann fast alles, was ich höre oder lese, verstehen und mich fließend und präzise ausdrücken.", - "newVocab": "Neues Vokabular", - "newGrammar": "Neue Grammatik-Konzepte", - "choosePracticeMode": "Klicken Sie auf eine der Schaltflächen oben, um eine Übungsaktivität zu starten", - "ban": "Ban", - "unban": "Entbannen", - "kick": "Kicken", - "lemma": "Lemma", - "grammarFeature": "Grammatikmerkmal", - "grammarTag": "Grammatik-Tag", - "forms": "Formen", - "exampleMessages": "Beispielnachrichten", - "timesUsedIndependently": "Anzahl der unabhängigen Verwendungen", - "timesUsedWithAssistance": "Anzahl der Verwendungen mit Unterstützung", - "shareInviteCode": "Einladungs-Code teilen: {code}", - "leaderboard": "Bestenliste", - "skipForNow": "Jetzt überspringen", - "permissions": "Berechtigungen", - "spaceChildPermission": "Wer kann neue Chats zu diesem Kurs hinzufügen", - "addEnvironmentOverride": "Umgebungsüberschreibung hinzufügen", - "defaultOption": "Standard", - "deleteChatDesc": "Möchten Sie diesen Chat wirklich löschen? Er wird für alle Teilnehmer gelöscht und alle Nachrichten im Chat sind nicht mehr für Übungen oder Lernanalysen verfügbar.", - "deleteSpaceDesc": "Der Kurs und alle ausgewählten Chats werden für alle Teilnehmer gelöscht und alle Nachrichten im Chat sind nicht mehr für Übungen oder Lernanalysen verfügbar. Diese Aktion kann nicht rückgängig gemacht werden.", - "launch": "Starten", - "searchChats": "Chats suchen", - "maxFifty": "Maximal 50", - "configureSpace": "Kurs konfigurieren", - "pinMessages": "Nachrichten anheften", - "setJoinRules": "Beitrittsregeln festlegen", - "changeGeneralSettings": "Allgemeine Einstellungen ändern", - "inviteOtherUsersToRoom": "Andere Benutzer einladen", - "changeTheNameOfTheSpace": "Den Namen des Kurses ändern", - "changeTheDescription": "Ändere die Beschreibung", - "changeThePermissions": "Ändere die Berechtigungen", - "introductions": "Vorstellungen", - "announcements": "Ankündigungen", - "activities": "Aktivitäten", - "access": "Zugang", - "activitySuggestionTimeoutMessage": "Wir arbeiten hart daran, mehr Aktivitäten für Sie zu generieren. Bitte schauen Sie in einer Minute wieder vorbei", - "howSpaceCanBeFound": "Wie dieser Kurs gefunden werden kann", - "private": "Privat", - "cannotBeFoundInSearch": "In der Suche nicht auffindbar", - "public": "Öffentlich", - "visibleToCommunity": "Sichtbar für die breitere Pangea-Chat-Community über \"Einen Kurs finden\"", - "howSpaceCanBeJoined": "Wie dieser Kurs beigetreten werden kann", - "canBeFoundVia": "Kann gefunden werden über:", - "canBeFoundViaInvitation": "• Einladung", - "canBeFoundViaCodeOrLink": "• Code oder Link", - "canBeFoundViaKnock": "• Anfrage zum Beitritt und Admin-Genehmigung", - "youHaveLeveledUp": "Du hast ein Level aufgestiegen!", - "sendActivities": "Aktivitäten senden", - "groupChat": "Gruppenchats", - "directMessage": "Direktnachricht", - "newDirectMessage": "Neue Direktnachricht", - "speakingExercisesTooltip": "Sprechen", - "noChatsFoundHereYet": "Hier wurden noch keine Chats gefunden", - "duration": "Dauer", - "transcriptionFailed": "Transkription des Audios fehlgeschlagen", - "aUserIsKnocking": "Ein Benutzer bittet, deinem Kurs beizutreten", - "usersAreKnocking": "{users} Benutzer bitten, deinem Kurs beizutreten", - "failedToFetchTranscription": "Transkription konnte nicht abgerufen werden", - "deleteEmptySpaceDesc": "Der Kurs wird für alle Teilnehmer gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.", - "regenerate": "Neu generieren", - "mySavedActivities": "Meine gespeicherten Aktivitäten", - "noSavedActivities": "Keine gespeicherten Aktivitäten", - "saveActivity": "Aktivität speichern", - "failedToPlayVideo": "Video konnte nicht abgespielt werden", - "done": "Fertig", - "inThisSpace": "In diesem Kurs", - "myContacts": "Meine Kontakte", - "inviteAllInSpace": "Alle in diesem Kurs einladen", - "spaceParticipantsHaveBeenInvitedToTheChat": "Alle Kursteilnehmer wurden zum Chat eingeladen", - "numKnocking": "{count} klopft", - "numInvited": "{count} eingeladen", - "saved": "Gespeichert", - "reset": "Zurücksetzen", - "errorGenerateActivityMessage": "Aktivität konnte nicht generiert werden", - "errorRegenerateActivityMessage": "Aktivität konnte nicht neu generiert werden", - "errorLaunchActivityMessage": "Aktivität konnte nicht gestartet werden", - "errorFetchingActivitiesMessage": "Fehler beim Abrufen der Aktivitäten", - "errorFetchingDefinition": "Fehler beim Abrufen der Definition", - "errorProcessAnalytics": "Fehler bei der Verarbeitung der Analysen", - "errorDownloading": "Download fehlgeschlagen", - "errorFetchingLevelSummary": "Fehler beim Abrufen der Level-Zusammenfassung", - "errorLoadingSpaceChildren": "Fehler beim Laden der Chats innerhalb dieses Kurses", - "unexpectedError": "Unerwarteter Fehler.", - "pleaseReload": "Bitte neu laden und erneut versuchen.", - "translationError": "Übersetzungsfehler", - "errorFetchingTranslation": "Fehler beim Abrufen der Übersetzung", - "errorFetchingActivity": "Fehler beim Abrufen der Aktivität", - "check": "Prüfen", - "unableToFindRoom": "Kein Chat oder Kurs mit diesem Code gefunden. Bitte versuchen Sie es erneut.", - "numCompletedActivities": "Anzahl der abgeschlossenen Aktivitäten", - "viewingAnalytics": "Anzeige von {visible}/{users} Analysen", - "request": "Anfrage", - "requestAll": "Alle anfordern", - "confirmMessageUnpin": "Möchten Sie diese Nachricht wirklich lösen?", - "createActivityPlan": "Einen neuen Aktivitätsplan erstellen", - "saveAndLaunch": "Speichern und starten", - "launchToSpace": "Starten Sie den Kurs", - "numberOfActivities": "Anzahl der Aktivitätssitzungen", - "maximumActivityParticipants": "Jede Aktivität kann maximal {count} Teilnehmer haben.", - "pending": "Ausstehend", - "inactive": "Inaktiv", - "confirmRole": "Rolle bestätigen", - "openRoleLabel": "OFFEN", - "joinedTheActivity": "👋 {username} ist als {role} beigetreten", - "finishedTheActivity": "🎯 {username} hat diese Aktivität beendet", - "archiveToAnalytics": "Zu meinen abgeschlossenen Aktivitäten hinzufügen", - "activitySummaryError": "Aktivitätszusammenfassungen nicht verfügbar", - "requestSummaries": "Zusammenfassungen anfordern", - "generatingNewActivities": "Sie sind der erste Benutzer dieses Sprachpaares! Bitte geben Sie uns eine Minute, wir bereiten Aktivitäten nur für Sie vor.", - "requestAccessTitle": "Zugriff auf Analysen anfordern?", - "requestAccessDesc": "Möchten Sie Zugriff auf die Teilnehmeranalyse beantragen?\n\nWenn die Teilnehmer zustimmen, können Administratoren dieses Kurses ihre:\n • Gesamtvokabular\n • Gesammtgrammatik-Konzepte\n • Anzahl der abgeschlossenen Aktivitätssitzungen\n • die verwendeten Grammatik-Konzepte, richtig und falsch\n\nSie werden nicht in der Lage sein, ihre:\n • Nachrichten in Chats außerhalb des Kurses\n • Vokabelliste", - "requestAccess": "Zugriff anfordern ({count})", - "analyticsInactiveTitle": "Anfragen an inaktive Benutzer konnten nicht gesendet werden", - "analyticsInactiveDesc": "Inaktive Benutzer, die sich seit der Einführung dieses Features nicht angemeldet haben, sehen Ihre Anfrage nicht.\n\nDer Button „Anfrage“ erscheint, sobald sie zurückkehren. Sie können die Anfrage später erneut senden, indem Sie auf den Button „Anfrage“ unter ihrem Namen klicken, wenn dieser verfügbar ist.", - "accessRequestedTitle": "Anfrage für Analytics-Zugriff", - "accessRequestedDesc": "Anfordernde(r) Admin(s): {admin} \n\nAdmins von “{space}” bitten darum, Ihre Lernanalysen einzusehen.\n\nWenn Sie zustimmen, können sie Folgendes einsehen:\n • gesamten Wortschatz\n • gesamte Grammatik Konzepte\n • insgesamt abgeschlossene Aktivitätssitzungen\n • die spezifischen Grammatik Konzepte, die korrekt und inkorrekt verwendet wurden\n\nSie werden nicht in der Lage sein, Folgendes einzusehen:\n • Nachrichten in Chats außerhalb des Kurses\n • Wortschatzliste", - "adminRequestedAccess": "Administratoren haben um Zugriff auf Ihre Analysen gebeten.", - "lastUpdated": "Aktualisiert\n{time}", - "activityFinishedMessage": "Alles erledigt!", - "endForAll": "Für alle beenden", - "newCourse": "Neuer Kurs", - "numModules": "{num} Module", - "coursePlan": "Kursplan", - "editCourseLater": "Sie können den Titel, die Beschreibungen und das Kursbild später bearbeiten.", - "createCourse": "Kurs erstellen", - "stats": "Statistiken", - "createGroupChat": "Gruppenchats erstellen", - "editCourse": "Kurs bearbeiten", - "inviteDesc": "Per Benutzername, Code oder Link", - "editCourseDesc": "Hier können Sie den Kurstitel, die Beschreibung usw. bearbeiten.", - "permissionsDesc": "Berechtigungen festlegen, z. B. wer Einladungen senden, Nachrichten schicken, Chats erstellen kann usw.", - "accessDesc": "Sie können Ihren Kurs für die Welt öffnen! Oder machen Sie Ihren Kurs privat und sicher.", - "createGroupChatDesc": "Während Aktivitätssitzungen starten und enden, bleiben Gruppenchats für die routinemäßige Kommunikation offen.", - "deleteDesc": "Nur Administratoren können einen Kurs löschen. Dies ist eine zerstörerische Aktion, die alle Benutzer entfernt und alle ausgewählten Chats im Kurs löscht. Bitte vorsichtig vorgehen.", - "noCourseFound": "Oh, dieser Kurs braucht einen Plan!\n\nKurspläne sind eine Abfolge von Themen und Gesprächsaktivitäten.", - "additionalParticipants": "+ {num} weitere", - "directMessages": "Direktnachrichten", - "whatNow": "Was jetzt?", - "chooseNextActivity": "Wählen Sie Ihre nächste Aktivität!", - "letsGo": "Los geht's", - "chooseRole": "Wähle eine Rolle!", - "chooseRoleToParticipate": "Wähle eine Rolle zur Teilnahme!", - "waitingToFillRole": "Warte darauf, {num} Rollen zu besetzen...", - "pingParticipants": "Benachrichtige Kursteilnehmer", - "playWithBot": "Mit Pangea Bot spielen", - "inviteFriends": "Freunde einladen", - "waitNotDone": "Warte, ich bin noch nicht fertig!", - "waitingForOthersToFinish": "Warte, bis die anderen fertig sind...", - "generatingSummary": "Chat wird analysiert und Ergebnisse werden generiert", - "findCourse": "Kurs finden", - "pingParticipantsNotification": "{user} sucht nach Nutzern, die an der Sitzung in {room} teilnehmen möchten", - "course": "Kurs", - "courses": "Kurse", - "courseName": "Kursname", - "createNewCourse": "Neuer Kurs", - "goToCourse": "Zum Kurs: {course}", - "activityComplete": "Diese Aktivität wurde abgeschlossen. Die Zusammenfassung der Aktivität sollte unten verfügbar sein.", - "startNewSession": "Neue Sitzung starten", - "joinOpenSession": "Offene Sitzung beitreten", - "less": "weniger", - "activityNotFound": "Aktivität nicht gefunden", - "levelUp": "Aufstieg", - "myActivities": "Meine Aktivitäten", - "openToJoin": "Offen zum Beitreten", - "results": "Ergebnisse", - "activityDone": "Aktivität abgeschlossen!", - "promoCodeInfo": "Promo-Codes können auf der nächsten Seite eingegeben werden", - "editsComingSoon": "Die Möglichkeit, Städte und Aktivitäten zu bearbeiten, kommt bald.", - "editing": "Bearbeiten", - "activityNeedsOneMember": "Oh je! Für diese Aktivität wird noch eine Person benötigt.", - "activityNeedsMembers": "Oh nein! Diese Aktivität benötigt {num} weitere Personen.", - "inviteFriendsToCourse": "Freunde zu meinem Kurs einladen", - "subscribeToUnlockActivitySummaries": "Abonnieren, um Aktivitätszusammenfassungen freizuschalten", - "subscribeToUnlockDefinitions": "Abonnieren, um Definitionen freizuschalten", - "subscribeToUnlockTranscriptions": "Abonnieren, um Transkriptionen freizuschalten", - "pingSent": "🔔 Kurs-Ping gesendet! 🔔", - "courseTitle": "Kurstitel", - "courseDesc": "Kursbeschreibung", - "courseSavedSuccessfully": "Kurs erfolgreich gespeichert", - "addCoursePlan": "Einen Kursplan hinzufügen", - "activityStatsButtonInstruction": "Klicken Sie hier, um Ihre Aktivitätsstatistiken anzuzeigen und die Aktivität bei Abschluss zu schließen", - "loginToAccount": "In mein Konto einloggen", - "appDescription": "Lerne eine Sprache\nwährend du deinen Freunden schreibst.", - "languages": "Sprachen", - "chooseLanguage": "Wählen Sie eine Zielsprache.", - "planTrip": "Plane deine Reise", - "howAreYouTraveling": "Wie reist du?", - "unlockPrivateTrip": "Einen privaten Trip freischalten", - "joinPublicTrip": "Einem öffentlichen Trip beitreten", - "startOwnTrip": "Eigenen Trip starten", - "tripPlanDesc": "Trips sind Kurse. Jeder hat 8-10 sequenzierte Themen mit einer Vielzahl von aufgabenbasierten Sprachlernaktivitäten.", - "unlockPrivateTripTitle": "Privaten Trip freischalten", - "browsePublicTrips": "Öffentliche Trips durchsuchen", - "startOwnTripTitle": "Eigenen Trip starten", - "courseCode": "Was ist das geheime Passwort?", - "courseCodeHint": "Reisecode oder Link", - "unlockMyTrip": "Meinen Trip freischalten", - "signupOption": "Wie möchten Sie sich anmelden?", - "withApple": "Mit Apple", - "withGoogle": "Mit Google", - "withEmail": "Mit E-Mail", - "createAccount": "Konto erstellen", - "loginWithEmail": "Mit E-Mail anmelden", - "usernameOrEmail": "Benutzername oder E-Mail", - "email": "E-Mail", - "forgotPassword": "Passwort vergessen?", - "endActivity": "Aktivität beenden", - "allLanguages": "Alle Sprachen", - "chatListTooltip": "Hier finden Sie Ihre Direktnachrichten! Klicken Sie auf das Avatar eines Nutzers und “Unterhaltung starten”, um eine DM zu senden.", - "directMessageBotTitle": "Direktnachricht Pangea Bot", - "feedbackTitle": "Aktivitätsfeedback", - "feedbackHint": "Ihr Feedback", - "feedbackButton": "Feedback absenden", - "directMessageBotDesc": "Mit Menschen zu sprechen macht mehr Spaß, aber... KI ist immer bereit!", - "inviteYourFriends": "Laden Sie Ihre Freunde ein", - "playWithAI": "Jetzt mit KI spielen", - "courseStartDesc": "Pangea Bot ist jederzeit einsatzbereit!\n\n...aber Lernen macht mit Freunden mehr Spaß!", - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, - "@writeAMessageLangCodes": { - "type": "String", - "placeholders": { - "l1": { - "type": "String" - }, - "l2": { - "type": "String" - } - } - }, - "@requests": { - "type": "String", - "placeholders": {} - }, - "@holdForInfo": { - "type": "String", - "placeholders": {} - }, - "@greenFeedback": { - "type": "String", - "placeholders": {} - }, - "@yellowFeedback": { - "type": "String", - "placeholders": {} - }, - "@redFeedback": { - "type": "String", - "placeholders": {} - }, - "@itInstructionsTitle": { - "type": "String", - "placeholders": {} - }, - "@itInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@gaTooltip": { - "type": "String", - "placeholders": {} - }, - "@taTooltip": { - "type": "String", - "placeholders": {} - }, - "@unTooltip": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorSliderHeader": { - "type": "String", - "placeholders": {} - }, - "@interactiveGrammarSliderHeader": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorNotAllowed": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorAllowed": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorRequired": { - "type": "String", - "placeholders": {} - }, - "@notYetSet": { - "type": "String", - "placeholders": {} - }, - "@waTooltip": { - "type": "String", - "placeholders": {} - }, - "@languageSettings": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslator": { - "type": "String", - "placeholders": {} - }, - "@noIdenticalLanguages": { - "type": "String", - "placeholders": {} - }, - "@searchBy": { - "type": "String", - "placeholders": {} - }, - "@joinWithClassCode": { - "type": "String", - "placeholders": {} - }, - "@languageLevelPreA1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA2": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB2": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC2": { - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheClass": { - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheChat": { - "type": "String", - "placeholders": {} - }, - "@sorryNoResults": { - "type": "String", - "placeholders": {} - }, - "@ignoreInThisText": { - "type": "String", - "placeholders": {} - }, - "@needsItMessage": { - "type": "String", - "placeholders": { - "targetLanguage": { - "type": "String" - } - } - }, - "@countryInformation": { - "type": "String", - "placeholders": {} - }, - "@targetLanguage": { - "type": "String", - "placeholders": {} - }, - "@sourceLanguage": { - "type": "String", - "placeholders": {} - }, - "@updateLanguage": { - "type": "String", - "placeholders": {} - }, - "@whatLanguageYouWantToLearn": { - "type": "String", - "placeholders": {} - }, - "@whatIsYourBaseLanguage": { - "type": "String", - "placeholders": {} - }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, - "@publicProfileTitle": { - "type": "String", - "placeholders": {} - }, - "@publicProfileDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIT": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIGC": { - "type": "String", - "placeholders": {} - }, - "@errorDisableLanguageAssistance": { - "type": "String", - "placeholders": {} - }, - "@errorDisableITUserDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIGCUserDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableLanguageAssistanceUserDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableITClassDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIGCClassDesc": { - "type": "String", - "placeholders": {} - }, - "@error405Title": { - "type": "String", - "placeholders": {} - }, - "@error405Desc": { - "type": "String", - "placeholders": {} - }, - "@termsAndConditions": { - "type": "String", - "placeholders": {} - }, - "@andCertifyIAmAtLeast13YearsOfAge": { - "type": "String", - "placeholders": {} - }, - "@error502504Title": { - "type": "String", - "placeholders": {} - }, - "@error502504Desc": { - "type": "String", - "placeholders": {} - }, - "@error404Title": { - "type": "String", - "placeholders": {} - }, - "@error404Desc": { - "type": "String", - "placeholders": {} - }, - "@errorPleaseRefresh": { - "type": "String", - "placeholders": {} - }, - "@connectedToStaging": { - "type": "String", - "placeholders": {} - }, - "@learningSettings": { - "type": "String", - "placeholders": {} - }, - "@participants": { - "type": "String", - "placeholders": {} - }, - "@clickMessageTitle": { - "type": "String", - "placeholders": {} - }, - "@clickMessageBody": { - "type": "String", - "placeholders": {} - }, - "@allDone": { - "type": "String", - "placeholders": {} - }, - "@vocab": { - "type": "String", - "placeholders": {} - }, - "@low": { - "type": "String", - "placeholders": {} - }, - "@medium": { - "type": "String", - "placeholders": {} - }, - "@high": { - "type": "String", - "placeholders": {} - }, - "@subscribe": { - "type": "String", - "placeholders": {} - }, - "@getAccess": { - "type": "String", - "placeholders": {} - }, - "@subscriptionDesc": { - "type": "String", - "placeholders": {} - }, - "@subscriptionManagement": { - "type": "String", - "placeholders": {} - }, - "@currentSubscription": { - "type": "String", - "placeholders": {} - }, - "@cancelSubscription": { - "type": "String", - "placeholders": {} - }, - "@selectYourPlan": { - "type": "String", - "placeholders": {} - }, - "@subsciptionPlatformTooltip": { - "type": "String", - "placeholders": {} - }, - "@subscriptionManagementUnavailable": { - "type": "String", - "placeholders": {} - }, - "@paymentMethod": { - "type": "String", - "placeholders": {} - }, - "@paymentHistory": { - "type": "String", - "placeholders": {} - }, - "@emptyChatDownloadWarning": { - "type": "String", - "placeholders": {} - }, - "@update": { - "type": "String", - "placeholders": {} - }, - "@toggleImmersionMode": { - "type": "String", - "placeholders": {} - }, - "@toggleImmersionModeDesc": { - "type": "String", - "placeholders": {} - }, - "@itToggleDescription": { - "type": "String", - "placeholders": {} - }, - "@igcToggleDescription": { - "type": "String", - "placeholders": {} - }, - "@originalMessage": { - "type": "String", - "placeholders": {} - }, - "@sentMessage": { - "type": "String", - "placeholders": {} - }, - "@useType": { - "type": "String", - "placeholders": {} - }, - "@notAvailable": { - "type": "String", - "placeholders": {} - }, - "@taAndGaTooltip": { - "type": "String", - "placeholders": {} - }, - "@definitionsToolName": { - "type": "String", - "placeholders": {} - }, - "@messageTranslationsToolName": { - "type": "String", - "placeholders": {} - }, - "@definitionsToolDescription": { - "type": "String", - "placeholders": {} - }, - "@translationsToolDescrption": { - "type": "String", - "placeholders": {} - }, - "@welcomeBack": { - "type": "String", - "placeholders": {} - }, - "@downloadTxtFile": { - "type": "String", - "placeholders": {} - }, - "@downloadCSVFile": { - "type": "String", - "placeholders": {} - }, - "@promotionalSubscriptionDesc": { - "type": "String", - "placeholders": {} - }, - "@originalSubscriptionPlatform": { - "type": "String", - "placeholders": { - "purchasePlatform": { - "type": "String" - } - } - }, - "@oneWeekTrial": { - "type": "String", - "placeholders": {} - }, - "@downloadXLSXFile": { - "type": "String", - "placeholders": {} - }, - "@unkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@afCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@axCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@alCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@asCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@adCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@aoCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@aiCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@agCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@arCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@amCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@awCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@acCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@auCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@atCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@azCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bhCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@byCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@beCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@btCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@boCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@baCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@brCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ioCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@biCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@khCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@caCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@clCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cxCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ccCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@coCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ckCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@crCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ciCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cuCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@czCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@djCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@doCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tlCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ecCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@egCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@svCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@erCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@eeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@szCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@etCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@foCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fiCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@frCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@geCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@deCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ghCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@giCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@grCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@glCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@guCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gtCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ggCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@htCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@huCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@isCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@inCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@idCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@irCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@iqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ieCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@imCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ilCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@itCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@joCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@keCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kiCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@xkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@laCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@liCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ltCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@luCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@moCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@myCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mlCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mtCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mhCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@muCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ytCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mxCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@meCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@maCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@naCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@npCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nlCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ncCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@niCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@neCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ngCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nuCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@noCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@omCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@psCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@peCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@phCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@plCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@prCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@qaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@reCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@roCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ruCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@blCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@shCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@knCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@smCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@stCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@saCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@snCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@scCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@slCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sxCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@skCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@siCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@soCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@krCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ssCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@srCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@seCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@chCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@syCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@twCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@thCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@toCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ttCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@trCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@viCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ugCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@aeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@usCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vuCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@veCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ehCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pay": { - "type": "String", - "placeholders": {} - }, - "@invitedToSpace": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - }, - "space": { - "type": "String" - } - } - }, - "@youreInvited": { - "type": "String", - "placeholders": {} - }, - "@invitedToChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - }, - "name": { - "type": "String" - } - } - }, - "@monthlySubscription": { - "type": "String", - "placeholders": {} - }, - "@yearlySubscription": { - "type": "String", - "placeholders": {} - }, - "@defaultSubscription": { - "type": "String", - "placeholders": {} - }, - "@freeTrial": { - "type": "String", - "placeholders": {} - }, - "@total": { - "type": "String", - "placeholders": {} - }, - "@noDataFound": { - "type": "String", - "placeholders": {} - }, - "@blurMeansTranslateTitle": { - "type": "String", - "placeholders": {} - }, - "@blurMeansTranslateBody": { - "type": "String", - "placeholders": {} - }, - "@bestCorrectionFeedback": { - "type": "String", - "placeholders": {} - }, - "@distractorFeedback": { - "type": "String", - "placeholders": {} - }, - "@bestAnswerFeedback": { - "type": "String", - "placeholders": {} - }, - "@definitionDefaultPrompt": { - "type": "String", - "placeholders": {} - }, - "@practiceDefaultPrompt": { - "type": "String", - "placeholders": {} - }, - "@correctionDefaultPrompt": { - "type": "String", - "placeholders": {} - }, - "@acceptSelection": { - "type": "String", - "placeholders": {} - }, - "@why": { - "type": "String", - "placeholders": {} - }, - "@definition": { - "type": "String", - "placeholders": {} - }, - "@exampleSentence": { - "type": "String", - "placeholders": {} - }, - "@reportToTeacher": { - "type": "String", - "placeholders": {} - }, - "@reportMessageTitle": { - "type": "String", - "placeholders": { - "reportingUserId": { - "type": "String" - }, - "reportedUserId": { - "type": "String" - }, - "roomName": { - "type": "String" - } - } - }, - "@reportMessageBody": { - "type": "String", - "placeholders": { - "reportedMessage": { - "type": "String" - }, - "reason": { - "type": "String" - } - } - }, - "@noTeachersFound": { - "type": "String", - "placeholders": {} - }, - "@trialExpiration": { - "type": "String", - "placeholders": { - "expiration": { - "type": "String" - } - } - }, - "@freeTrialDesc": { - "type": "String", - "placeholders": {} - }, - "@activateTrial": { - "type": "String", - "placeholders": {} - }, - "@successfullySubscribed": { - "type": "String", - "placeholders": {} - }, - "@clickToManageSubscription": { - "type": "String", - "placeholders": {} - }, - "@signUp": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAtLeastChars": { - "type": "String", - "placeholders": { - "min": { - "type": "String" - } - } - }, - "@noEmailWarning": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterValidEmail": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAUsername": { - "type": "String", - "placeholders": {} - }, - "@define": { - "type": "String", - "placeholders": {} - }, - "@listen": { - "type": "String", - "placeholders": {} - }, - "@trialPeriodExpired": { - "type": "String", - "placeholders": {} - }, - "@translations": { - "type": "String", - "placeholders": {} - }, - "@messageAudio": { - "type": "String", - "placeholders": {} - }, - "@definitions": { - "type": "String", - "placeholders": {} - }, - "@subscribedToUnlockTools": { - "type": "String", - "placeholders": {} - }, - "@translationTooltip": { - "type": "String", - "placeholders": {} - }, - "@speechToTextTooltip": { - "type": "String", - "placeholders": {} - }, - "@kickBotWarning": { - "type": "String", - "placeholders": {} - }, - "@refresh": { - "type": "String", - "placeholders": {} - }, - "@messageAnalytics": { - "type": "String", - "placeholders": {} - }, - "@words": { - "type": "String", - "placeholders": {} - }, - "@score": { - "type": "String", - "placeholders": {} - }, - "@accuracy": { - "type": "String", - "placeholders": {} - }, - "@points": { - "type": "String", - "placeholders": {} - }, - "@noPaymentInfo": { - "type": "String", - "placeholders": {} - }, - "@updatePhoneOS": { - "type": "String", - "placeholders": {} - }, - "@wordsPerMinute": { - "type": "String", - "placeholders": {} - }, - "@tooltipInstructionsTitle": { - "type": "String", - "placeholders": {} - }, - "@tooltipInstructionsMobileBody": { - "type": "String", - "placeholders": {} - }, - "@tooltipInstructionsBrowserBody": { - "type": "String", - "placeholders": {} - }, - "@chatCapacity": { - "type": "String", - "placeholders": {} - }, - "@roomFull": { - "type": "String", - "placeholders": {} - }, - "@chatCapacityHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@chatCapacitySetTooLow": { - "type": "int", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@chatCapacityExplanation": { - "type": "String", - "placeholders": {} - }, - "@tooManyRequest": { - "type": "String", - "placeholders": {} - }, - "@enterNumber": { - "type": "String", - "placeholders": {} - }, - "@buildTranslation": { - "type": "String", - "placeholders": {} - }, - "@practice": { - "type": "String", - "placeholders": {} - }, - "@noLanguagesSet": { - "type": "String", - "placeholders": {} - }, - "@speechToTextBody": { - "type": "String", - "placeholders": {} - }, - "@versionNotFound": { - "type": "String", - "placeholders": {} - }, - "@fetchingVersion": { - "type": "String", - "placeholders": {} - }, - "@versionFetchError": { - "type": "String", - "placeholders": {} - }, - "@versionText": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - }, - "buildNumber": { - "type": "String" - } - } - }, - "@l1TranslationBody": { - "type": "String", - "placeholders": {} - }, - "@deleteSubscriptionWarningTitle": { - "type": "String", - "placeholders": {} - }, - "@deleteSubscriptionWarningBody": { - "type": "String", - "placeholders": {} - }, - "@manageSubscription": { - "type": "String", - "placeholders": {} - }, - "@error520Title": { - "type": "String", - "placeholders": {} - }, - "@error520Desc": { - "type": "String", - "placeholders": {} - }, - "@wordsUsed": { - "type": "String", - "placeholders": {} - }, - "@level": { - "type": "String", - "placeholders": {} - }, - "@morphsUsed": { - "type": "String", - "placeholders": {} - }, - "@translationChoicesBody": { - "type": "String", - "placeholders": {} - }, - "@grammar": { - "type": "String", - "placeholders": {} - }, - "@contactHasBeenInvitedToTheChat": { - "type": "String", - "placeholders": {} - }, - "@inviteChat": { - "type": "String", - "placeholders": {} - }, - "@chatName": { - "type": "String", - "placeholders": {} - }, - "@reportContentIssueTitle": { - "type": "String", - "placeholders": {} - }, - "@feedback": { - "type": "String", - "placeholders": {} - }, - "@reportContentIssueDescription": { - "type": "String", - "placeholders": {} - }, - "@clickTheWordAgainToDeselect": { - "type": "String", - "placeholders": {} - }, - "@l2SupportNa": { - "type": "String", - "placeholders": {} - }, - "@l2SupportAlpha": { - "type": "String", - "placeholders": {} - }, - "@l2SupportBeta": { - "type": "String", - "placeholders": {} - }, - "@l2SupportFull": { - "type": "String", - "placeholders": {} - }, - "@missingVoiceTitle": { - "type": "String", - "placeholders": {} - }, - "@voiceNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@openVoiceSettings": { - "type": "String", - "placeholders": {} - }, - "@playAudio": { - "type": "String", - "placeholders": {} - }, - "@stop": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSsconj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSnum": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSverb": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSaffix": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSadj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOScconj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpunct": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSadv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSaux": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSspace": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSsym": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSdet": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpron": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSadp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpropn": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSnoun": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSintj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSx": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERfem": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON2": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODimp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEqest": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTperf": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEaccnom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEobl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEact": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEbrck": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERsing": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERmasc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPEmod": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEadverbial": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEperi": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMdigit": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEnot_proper": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEcard": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEprop": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEdash": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEsemi": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEcomm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODcnd": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEacc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPARTTYPEpart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEpast": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEsup": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEcolo": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON3": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERplur": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEnpr": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEinterrogative": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEinfm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEtim": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITYneg": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEtot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEadnomial": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTprog": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODsub": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMcomplementive": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEnom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEfut": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEdat": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEpres": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERneut": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPErel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMfinalEnding": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEdem": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASEpre": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMfin": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEpos": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEquot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMger": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEpass": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEgen": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEprs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEdef": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEord": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEins": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMinf": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMaux": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMlong": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEloc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODind": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEcmp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASErelativeCase": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEexcl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON1": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDEini": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERperson": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyFOREIGNyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEvoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPEverbType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSSpass": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASEprepCase": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEnumType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEnounType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXreflex": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEpronType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDEpunctSide": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMverbForm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERgender": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODmood": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTaspect": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEpunctType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEtense": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEdegree": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEpolite": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEadvType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMnumber": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEconjType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITYpolarity": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEcase": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEdefinite": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMnumForm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEadn": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOCvoc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCMPLcmpl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVadv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODjus": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERcom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXrflx": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPARTTYPEpar": { - "type": "String", - "placeholders": {} - }, - "@grammarCopySPCspc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEpqp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXref": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEnshrt": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERdual": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMlng": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEmid": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyINTRELintRel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyINTint": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEcaus": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyUnknown": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENTevident": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMnumberPsor": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECThab": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEabl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEall": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEess": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEtra": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEequ": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEdis": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEabs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEerg": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEcau": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEben": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEtem": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEcoord": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEcons": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEabs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENTfh": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENTnfh": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODopt": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODadm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODdes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODnec": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODpot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODprp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODqot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMword": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMroman": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMletter": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEmult": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEfrac": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEsets": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPErange": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEdist": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERtri": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERpauc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERgrpa": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERgrpl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERinv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON0": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON4": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEform": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEelev": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEhumb": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEemp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEexc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPErcp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEintRelPronType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEaor": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEeps": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEprosp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMpart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMconv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMvnoun": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEantip": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEcauVoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICedir": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEinvVoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICErcpVoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOS": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDER": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOOD": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECT": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORM": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBER": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENT": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyFOREIGN": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITY": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEX": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORM": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopySPC": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPARTTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyINTREL": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyUNKNOWN": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSOR": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSS": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTimp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEvoc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEcom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEpar": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEadv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEref": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASErel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEsub": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEsup": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEaccdat": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEpre": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEsub": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEcmp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEind": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODint": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEcomm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSORsing": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSORplur": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSORdual": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITYpos": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSSyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASEnpr": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEprs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEint": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEtot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEneg": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEind": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEintrel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDEfin": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEperi": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEimp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMsup": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMadn": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMlng": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMshrt": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPEcaus": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEcau": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEdir": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEinv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICErcp": { - "type": "String", - "placeholders": {} - }, - "@other": { - "type": "String", - "placeholders": {} - }, - "@levelShort": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@clickBestOption": { - "type": "String", - "placeholders": {} - }, - "@completeActivitiesToUnlock": { - "type": "String", - "placeholders": {} - }, - "@noCapacityLimit": { - "type": "String", - "placeholders": {} - }, - "@downloadGroupText": { - "type": "String", - "placeholders": {} - }, - "@notificationsOn": { - "type": "String", - "placeholders": {} - }, - "@notificationsOff": { - "type": "String", - "placeholders": {} - }, - "@createChatAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@updatedNewSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@joinWithCode": { - "type": "String", - "placeholders": {} - }, - "@enterCodeToJoin": { - "type": "String", - "placeholders": {} - }, - "@updateNow": { - "type": "String", - "placeholders": {} - }, - "@updateLater": { - "type": "String", - "placeholders": {} - }, - "@constructUseWaDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseGaDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseTaDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseUnkDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorITDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnITDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncITDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnIGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorIGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncIGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorPADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnPADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncPADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIngWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorHWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncHWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnHWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseEmojiDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCollected": { - "type": "String", - "placeholders": {} - }, - "@constructUseNanDesc": { - "type": "String", - "placeholders": {} - }, - "@xpIntoLevel": { - "type": "String", - "placeholders": { - "currentXP": { - "type": "int" - }, - "maxXP": { - "type": "int" - } - } - }, - "@enableTTSToolName": { - "type": "String", - "placeholders": {} - }, - "@enableTTSToolDescription": { - "type": "String", - "placeholders": {} - }, - "@yourUsername": { - "type": "String", - "placeholders": {} - }, - "@yourEmail": { - "type": "String", - "placeholders": {} - }, - "@iWantToLearn": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterEmail": { - "type": "String", - "placeholders": {} - }, - "@myBaseLanguage": { - "type": "String", - "placeholders": {} - }, - "@meaningSectionHeader": { - "type": "String", - "placeholders": {} - }, - "@formSectionHeader": { - "type": "String", - "placeholders": {} - }, - "@writingExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@listeningExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@readingExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@meaningNotFound": { - "type": "String", - "placeholders": {} - }, - "@chooseBaseForm": { - "type": "String", - "placeholders": {} - }, - "@notTheCodeError": { - "type": "String", - "placeholders": {} - }, - "@totalXP": { - "type": "String", - "placeholders": {} - }, - "@numLemmas": { - "type": "String", - "placeholders": {} - }, - "@numLemmasUsedCorrectly": { - "type": "String", - "placeholders": {} - }, - "@numLemmasUsedIncorrectly": { - "type": "String", - "placeholders": {} - }, - "@numLemmasSmallXP": { - "type": "String", - "placeholders": {} - }, - "@numLemmasMediumXP": { - "type": "String", - "placeholders": {} - }, - "@numLemmasLargeXP": { - "type": "String", - "placeholders": {} - }, - "@numGrammarConcepts": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConcepts": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUsedCorrectly": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUsedIncorrectly": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUseCorrectlySystemGenerated": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUseIncorrectlySystemGenerated": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsSmallXP": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsMediumXP": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsLargeXP": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsHugeXP": { - "type": "String", - "placeholders": {} - }, - "@numMessagesSent": { - "type": "String", - "placeholders": {} - }, - "@numWordsTyped": { - "type": "String", - "placeholders": {} - }, - "@numCorrectChoices": { - "type": "String", - "placeholders": {} - }, - "@numIncorrectChoices": { - "type": "String", - "placeholders": {} - }, - "@commaSeparatedFile": { - "type": "String", - "placeholders": {} - }, - "@excelFile": { - "type": "String", - "placeholders": {} - }, - "@fileType": { - "type": "String", - "placeholders": {} - }, - "@download": { - "type": "String", - "placeholders": {} - }, - "@analyticsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@downloading": { - "type": "String", - "placeholders": {} - }, - "@failedFetchUserAnalytics": { - "type": "String", - "placeholders": {} - }, - "@downloadComplete": { - "type": "String", - "placeholders": {} - }, - "@whatIsTheMorphTag": { - "type": "String", - "placeholders": { - "morphologicalFeature": { - "type": "String" - }, - "wordForm": { - "type": "String" - } - } - }, - "@dataAvailable": { - "type": "String", - "placeholders": {} - }, - "@available": { - "type": "String", - "placeholders": {} - }, - "@pangeaBotIsFallible": { - "type": "String", - "placeholders": {} - }, - "@whatIsMeaning": { - "type": "String", - "placeholders": { - "lemma": { - "type": "String" - } - } - }, - "@pickAnEmoji": { - "type": "String", - "placeholders": { - "lemma": { - "type": "String" - } - } - }, - "@chooseLemmaMeaningInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@doubleClickToEdit": { - "type": "String", - "placeholders": {} - }, - "@activityPlannerTitle": { - "type": "String", - "placeholders": {} - }, - "@topicLabel": { - "type": "String", - "placeholders": {} - }, - "@topicPlaceholder": { - "type": "String", - "placeholders": {} - }, - "@modeLabel": { - "type": "String", - "placeholders": {} - }, - "@modePlaceholder": { - "type": "String", - "placeholders": {} - }, - "@learningObjectiveLabel": { - "type": "String", - "placeholders": {} - }, - "@learningObjectivePlaceholder": { - "type": "String", - "placeholders": {} - }, - "@languageOfInstructionsLabel": { - "type": "String", - "placeholders": {} - }, - "@targetLanguageLabel": { - "type": "String", - "placeholders": {} - }, - "@cefrLevelLabel": { - "type": "String", - "placeholders": {} - }, - "@generateActivitiesButton": { - "type": "String", - "placeholders": {} - }, - "@launchActivityButton": { - "type": "String", - "placeholders": {} - }, - "@image": { - "type": "String", - "placeholders": {} - }, - "@video": { - "type": "String", - "placeholders": {} - }, - "@nan": { - "type": "String", - "placeholders": {} - }, - "@activityPlannerOverviewInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@activityTitle": { - "type": "String", - "placeholders": {} - }, - "@addVocabulary": { - "type": "String", - "placeholders": {} - }, - "@instructions": { - "type": "String", - "placeholders": {} - }, - "@numberOfLearners": { - "type": "String", - "placeholders": {} - }, - "@mustBeInteger": { - "type": "String", - "placeholders": {} - }, - "@constructUsePvmDesc": { - "type": "String", - "placeholders": {} - }, - "@leaveSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorMmDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncMmDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnMmDesc": { - "type": "String", - "placeholders": {} - }, - "@clickForMeaningActivity": { - "type": "String", - "placeholders": {} - }, - "@meaning": { - "type": "String", - "placeholders": {} - }, - "@chatWith": { - "type": "String", - "placeholders": { - "displayname": { - "type": "String" - } - } - }, - "@clickOnEmailLink": { - "type": "String", - "placeholders": {} - }, - "@dontForgetPassword": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectToolName": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectDescription": { - "type": "String", - "placeholders": {} - }, - "@ttsDisbledTitle": { - "type": "String", - "placeholders": {} - }, - "@ttsDisabledBody": { - "type": "String", - "placeholders": {} - }, - "@noSpaceDescriptionYet": { - "type": "String", - "placeholders": {} - }, - "@tooLargeToSend": { - "type": "String", - "placeholders": {} - }, - "@exitWithoutSaving": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectPopupTitle": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectPopupSteps": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectPopupDescription": { - "type": "String", - "placeholders": {} - }, - "@downloadGboardTitle": { - "type": "String", - "placeholders": {} - }, - "@downloadGboardSteps": { - "type": "String", - "placeholders": {} - }, - "@downloadGboardDescription": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectWarning": { - "type": "String", - "placeholders": {} - }, - "@displayName": { - "type": "String", - "placeholders": {} - }, - "@leaveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@confirmUserId": { - "type": "String", - "placeholders": {} - }, - "@startingToday": { - "type": "String", - "placeholders": {} - }, - "@oneWeekFreeTrial": { - "type": "String", - "placeholders": {} - }, - "@paidSubscriptionStarts": { - "type": "String", - "placeholders": { - "startDate": { - "type": "String" - } - } - }, - "@cancelInSubscriptionSettings": { - "type": "String", - "placeholders": {} - }, - "@cancelToAvoidCharges": { - "type": "String", - "placeholders": { - "trialEnds": { - "type": "String" - } - } - }, - "@downloadGboard": { - "type": "String", - "placeholders": {} - }, - "@autocorrectNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@pleaseUpdateApp": { - "type": "String", - "placeholders": {} - }, - "@chooseEmojiInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@analyticsVocabListBody": { - "type": "String", - "placeholders": {} - }, - "@morphAnalyticsListBody": { - "type": "String", - "placeholders": {} - }, - "@knockSpaceSuccess": { - "type": "String", - "placeholders": {} - }, - "@chooseWordAudioInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@chooseMorphsInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterInt": { - "type": "String", - "placeholders": {} - }, - "@home": { - "type": "String", - "placeholders": {} - }, - "@join": { - "type": "String", - "placeholders": {} - }, - "@readingAssistanceOverviewBody": { - "type": "String", - "placeholders": {} - }, - "@levelSummaryPopupTitle": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@resetInstructionTooltipsTitle": { - "type": "String", - "placeholders": {} - }, - "@resetInstructionTooltipsDesc": { - "type": "String", - "placeholders": {} - }, - "@selectForGrammar": { - "type": "String", - "placeholders": {} - }, - "@randomize": { - "type": "String", - "placeholders": {} - }, - "@clear": { - "type": "String", - "placeholders": {} - }, - "@makeYourOwnActivity": { - "type": "String", - "placeholders": {} - }, - "@featuredActivities": { - "type": "String", - "placeholders": {} - }, - "@save": { - "type": "String", - "placeholders": {} - }, - "@startChat": { - "type": "String", - "placeholders": {} - }, - "@translationProblem": { - "type": "String", - "placeholders": {} - }, - "@askToJoin": { - "type": "String", - "placeholders": {} - }, - "@emptyChatWarningTitle": { - "type": "String", - "placeholders": {} - }, - "@emptyChatWarningDesc": { - "type": "String", - "placeholders": {} - }, - "@areYouLikeMe": { - "type": "String", - "placeholders": {} - }, - "@tryAgainLater": { - "type": "String", - "placeholders": {} - }, - "@enterSpaceCode": { - "type": "String", - "placeholders": {} - }, - "@shareSpaceLink": { - "type": "String", - "placeholders": {} - }, - "@byUsingPangeaChat": { - "type": "String", - "placeholders": {} - }, - "@details": { - "type": "String", - "placeholders": {} - }, - "@languageLevelPreA1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA2Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB2Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC2Desc": { - "type": "String", - "placeholders": {} - }, - "@newVocab": { - "type": "String", - "placeholders": {} - }, - "@newGrammar": { - "type": "String", - "placeholders": {} - }, - "@choosePracticeMode": { - "type": "String", - "placeholders": {} - }, - "@ban": { - "type": "String", - "placeholders": {} - }, - "@unban": { - "type": "String", - "placeholders": {} - }, - "@kick": { - "type": "String", - "placeholders": {} - }, - "@lemma": { - "type": "String", - "placeholders": {} - }, - "@grammarFeature": { - "type": "String", - "placeholders": {} - }, - "@grammarTag": { - "type": "String", - "placeholders": {} - }, - "@forms": { - "type": "String", - "placeholders": {} - }, - "@exampleMessages": { - "type": "String", - "placeholders": {} - }, - "@timesUsedIndependently": { - "type": "String", - "placeholders": {} - }, - "@timesUsedWithAssistance": { - "type": "String", - "placeholders": {} - }, - "@shareInviteCode": { - "type": "String", - "placeholders": { - "code": { - "type": "String" - } - } - }, - "@leaderboard": { - "type": "String", - "placeholders": {} - }, - "@skipForNow": { - "type": "String", - "placeholders": {} - }, - "@permissions": { - "type": "String", - "placeholders": {} - }, - "@spaceChildPermission": { - "type": "String", - "placeholders": {} - }, - "@addEnvironmentOverride": { - "type": "String", - "placeholders": {} - }, - "@defaultOption": { - "type": "String", - "placeholders": {} - }, - "@deleteChatDesc": { - "type": "String", - "placeholders": {} - }, - "@deleteSpaceDesc": { - "type": "String", - "placeholders": {} - }, - "@launch": { - "type": "String", - "placeholders": {} - }, - "@searchChats": { - "type": "String", - "placeholders": {} - }, - "@maxFifty": { - "type": "String", - "placeholders": {} - }, - "@configureSpace": { - "type": "String", - "placeholders": {} - }, - "@pinMessages": { - "type": "String", - "placeholders": {} - }, - "@setJoinRules": { - "type": "String", - "placeholders": {} - }, - "@changeGeneralSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsersToRoom": { - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheSpace": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescription": { - "type": "String", - "placeholders": {} - }, - "@changeThePermissions": { - "type": "String", - "placeholders": {} - }, - "@introductions": { - "type": "String", - "placeholders": {} - }, - "@announcements": { - "type": "String", - "placeholders": {} - }, - "@activities": { - "type": "String", - "placeholders": {} - }, - "@access": { - "type": "String", - "placeholders": {} - }, - "@activitySuggestionTimeoutMessage": { - "type": "String", - "placeholders": {} - }, - "@howSpaceCanBeFound": { - "type": "String", - "placeholders": {} - }, - "@private": { - "type": "String", - "placeholders": {} - }, - "@cannotBeFoundInSearch": { - "type": "String", - "placeholders": {} - }, - "@public": { - "type": "String", - "placeholders": {} - }, - "@visibleToCommunity": { - "type": "String", - "placeholders": {} - }, - "@howSpaceCanBeJoined": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundVia": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundViaInvitation": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundViaCodeOrLink": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundViaKnock": { - "type": "String", - "placeholders": {} - }, - "@youHaveLeveledUp": { - "type": "String", - "placeholders": {} - }, - "@sendActivities": { - "type": "String", - "placeholders": {} - }, - "@groupChat": { - "type": "String", - "placeholders": {} - }, - "@directMessage": { - "type": "String", - "placeholders": {} - }, - "@newDirectMessage": { - "type": "String", - "placeholders": {} - }, - "@speakingExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@noChatsFoundHereYet": { - "type": "String", - "placeholders": {} - }, - "@duration": { - "type": "String", - "placeholders": {} - }, - "@transcriptionFailed": { - "type": "String", - "placeholders": {} - }, - "@aUserIsKnocking": { - "type": "String", - "placeholders": {} - }, - "@usersAreKnocking": { - "type": "int", - "placeholders": { - "users": { - "type": "int" - } - } - }, - "@failedToFetchTranscription": { - "type": "String", - "placeholders": {} - }, - "@deleteEmptySpaceDesc": { - "type": "String", - "placeholders": {} - }, - "@regenerate": { - "type": "String", - "placeholders": {} - }, - "@mySavedActivities": { - "type": "String", - "placeholders": {} - }, - "@noSavedActivities": { - "type": "String", - "placeholders": {} - }, - "@saveActivity": { - "type": "String", - "placeholders": {} - }, - "@failedToPlayVideo": { - "type": "String", - "placeholders": {} - }, - "@done": { - "type": "String", - "placeholders": {} - }, - "@inThisSpace": { - "type": "String", - "placeholders": {} - }, - "@myContacts": { - "type": "String", - "placeholders": {} - }, - "@inviteAllInSpace": { - "type": "String", - "placeholders": {} - }, - "@spaceParticipantsHaveBeenInvitedToTheChat": { - "type": "String", - "placeholders": {} - }, - "@numKnocking": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@numInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@saved": { - "type": "String", - "placeholders": {} - }, - "@reset": { - "type": "String", - "placeholders": {} - }, - "@errorGenerateActivityMessage": { - "type": "String", - "placeholders": {} - }, - "@errorRegenerateActivityMessage": { - "type": "String", - "placeholders": {} - }, - "@errorLaunchActivityMessage": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingActivitiesMessage": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingDefinition": { - "type": "String", - "placeholders": {} - }, - "@errorProcessAnalytics": { - "type": "String", - "placeholders": {} - }, - "@errorDownloading": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingLevelSummary": { - "type": "String", - "placeholders": {} - }, - "@errorLoadingSpaceChildren": { - "type": "String", - "placeholders": {} - }, - "@unexpectedError": { - "type": "String", - "placeholders": {} - }, - "@pleaseReload": { - "type": "String", - "placeholders": {} - }, - "@translationError": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingTranslation": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingActivity": { - "type": "String", - "placeholders": {} - }, - "@check": { - "type": "String", - "placeholders": {} - }, - "@unableToFindRoom": { - "type": "String", - "placeholders": {} - }, - "@numCompletedActivities": { - "type": "String", - "placeholders": {} - }, - "@viewingAnalytics": { - "type": "String", - "placeholders": { - "visible": { - "type": "int" - }, - "users": { - "type": "int" - } - } - }, - "@request": { - "type": "String", - "placeholders": {} - }, - "@requestAll": { - "type": "String", - "placeholders": {} - }, - "@confirmMessageUnpin": { - "type": "String", - "placeholders": {} - }, - "@createActivityPlan": { - "type": "String", - "placeholders": {} - }, - "@saveAndLaunch": { - "type": "String", - "placeholders": {} - }, - "@launchToSpace": { - "type": "String", - "placeholders": {} - }, - "@numberOfActivities": { - "type": "String", - "placeholders": {} - }, - "@maximumActivityParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@pending": { - "type": "String", - "placeholders": {} - }, - "@inactive": { - "type": "String", - "placeholders": {} - }, - "@confirmRole": { - "type": "String", - "placeholders": {} - }, - "@openRoleLabel": { - "type": "String", - "placeholders": {} - }, - "@joinedTheActivity": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "role": { - "type": "String" - } - } - }, - "@finishedTheActivity": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@archiveToAnalytics": { - "type": "String", - "placeholders": {} - }, - "@activitySummaryError": { - "type": "String", - "placeholders": {} - }, - "@requestSummaries": { - "type": "String", - "placeholders": {} - }, - "@generatingNewActivities": { - "type": "String", - "placeholders": {} - }, - "@requestAccessTitle": { - "type": "String", - "placeholders": {} - }, - "@requestAccessDesc": { - "type": "String", - "placeholders": {} - }, - "@requestAccess": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@analyticsInactiveTitle": { - "type": "String", - "placeholders": {} - }, - "@analyticsInactiveDesc": { - "type": "String", - "placeholders": {} - }, - "@accessRequestedTitle": { - "type": "String", - "placeholders": {} - }, - "@accessRequestedDesc": { - "type": "String", - "placeholders": { - "admin": { - "type": "String" - }, - "space": { - "type": "String" - } - } - }, - "@adminRequestedAccess": { - "type": "String", - "placeholders": {} - }, - "@lastUpdated": { - "type": "String", - "placeholders": { - "time": { - "type": "String" - } - } - }, - "@activityFinishedMessage": { - "type": "String", - "placeholders": {} - }, - "@endForAll": { - "type": "String", - "placeholders": {} - }, - "@newCourse": { - "type": "String", - "placeholders": {} - }, - "@numModules": { - "type": "int", - "placeholders": { - "num": { - "type": "int" - } - } - }, - "@coursePlan": { - "type": "String", - "placeholders": {} - }, - "@editCourseLater": { - "type": "String", - "placeholders": {} - }, - "@createCourse": { - "type": "String", - "placeholders": {} - }, - "@stats": { - "type": "String", - "placeholders": {} - }, - "@createGroupChat": { - "type": "String", - "placeholders": {} - }, - "@editCourse": { - "type": "String", - "placeholders": {} - }, - "@inviteDesc": { - "type": "String", - "placeholders": {} - }, - "@editCourseDesc": { - "type": "String", - "placeholders": {} - }, - "@permissionsDesc": { - "type": "String", - "placeholders": {} - }, - "@accessDesc": { - "type": "String", - "placeholders": {} - }, - "@createGroupChatDesc": { - "type": "String", - "placeholders": {} - }, - "@deleteDesc": { - "type": "String", - "placeholders": {} - }, - "@noCourseFound": { - "type": "String", - "placeholders": {} - }, - "@additionalParticipants": { - "type": "int", - "placeholders": { - "num": { - "type": "int" - } - } - }, - "@directMessages": { - "type": "String", - "placeholders": {} - }, - "@whatNow": { - "type": "String", - "placeholders": {} - }, - "@chooseNextActivity": { - "type": "String", - "placeholders": {} - }, - "@letsGo": { - "type": "String", - "placeholders": {} - }, - "@chooseRole": { - "type": "String", - "placeholders": {} - }, - "@chooseRoleToParticipate": { - "type": "String", - "placeholders": {} - }, - "@waitingToFillRole": { - "type": "int", - "placeholders": { - "num": { - "type": "int" - } - } - }, - "@pingParticipants": { - "type": "String", - "placeholders": {} - }, - "@playWithBot": { - "type": "String", - "placeholders": {} - }, - "@inviteFriends": { - "type": "String", - "placeholders": {} - }, - "@waitNotDone": { - "type": "String", - "placeholders": {} - }, - "@waitingForOthersToFinish": { - "type": "String", - "placeholders": {} - }, - "@generatingSummary": { - "type": "String", - "placeholders": {} - }, - "@findCourse": { - "type": "String", - "placeholders": {} - }, - "@pingParticipantsNotification": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - }, - "room": { - "type": "String" - } - } - }, - "@course": { - "type": "String", - "placeholders": {} - }, - "@courses": { - "type": "String", - "placeholders": {} - }, - "@courseName": { - "type": "String", - "placeholders": {} - }, - "@createNewCourse": { - "type": "String", - "placeholders": {} - }, - "@goToCourse": { - "type": "String", - "placeholders": { - "course": {} - } - }, - "@activityComplete": { - "type": "String", - "placeholders": {} - }, - "@startNewSession": { - "type": "String", - "placeholders": {} - }, - "@joinOpenSession": { - "type": "String", - "placeholders": {} - }, - "@less": { - "type": "String", - "placeholders": {} - }, - "@activityNotFound": { - "type": "String", - "placeholders": {} - }, - "@levelUp": { - "type": "String", - "placeholders": {} - }, - "@myActivities": { - "type": "String", - "placeholders": {} - }, - "@openToJoin": { - "type": "String", - "placeholders": {} - }, - "@results": { - "type": "String", - "placeholders": {} - }, - "@activityDone": { - "type": "String", - "placeholders": {} - }, - "@promoCodeInfo": { - "type": "String", - "placeholders": {} - }, - "@editsComingSoon": { - "type": "String", - "placeholders": {} - }, - "@editing": { - "type": "String", - "placeholders": {} - }, - "@activityNeedsOneMember": { - "type": "String", - "placeholders": {} - }, - "@activityNeedsMembers": { - "type": "String", - "placeholders": { - "num": { - "type": "int" - } - } - }, - "@inviteFriendsToCourse": { - "type": "String", - "placeholders": {} - }, - "@subscribeToUnlockActivitySummaries": { - "type": "String", - "placeholders": {} - }, - "@subscribeToUnlockDefinitions": { - "type": "String", - "placeholders": {} - }, - "@subscribeToUnlockTranscriptions": { - "type": "String", - "placeholders": {} - }, - "@pingSent": { - "type": "String", - "placeholders": {} - }, - "@courseTitle": { - "type": "String", - "placeholders": {} - }, - "@courseDesc": { - "type": "String", - "placeholders": {} - }, - "@courseSavedSuccessfully": { - "type": "String", - "placeholders": {} - }, - "@addCoursePlan": { - "type": "String", - "placeholders": {} - }, - "@activityStatsButtonInstruction": { - "type": "String", - "placeholders": {} - }, - "@loginToAccount": { - "type": "String", - "placeholders": {} - }, - "@appDescription": { - "type": "String", - "placeholders": {} - }, - "@languages": { - "type": "String", - "placeholders": {} - }, - "@chooseLanguage": { - "type": "String", - "placeholders": {} - }, - "@planTrip": { - "type": "String", - "placeholders": {} - }, - "@howAreYouTraveling": { - "type": "String", - "placeholders": {} - }, - "@unlockPrivateTrip": { - "type": "String", - "placeholders": {} - }, - "@joinPublicTrip": { - "type": "String", - "placeholders": {} - }, - "@startOwnTrip": { - "type": "String", - "placeholders": {} - }, - "@tripPlanDesc": { - "type": "String", - "placeholders": {} - }, - "@unlockPrivateTripTitle": { - "type": "String", - "placeholders": {} - }, - "@browsePublicTrips": { - "type": "String", - "placeholders": {} - }, - "@startOwnTripTitle": { - "type": "String", - "placeholders": {} - }, - "@courseCode": { - "type": "String", - "placeholders": {} - }, - "@courseCodeHint": { - "type": "String", - "placeholders": {} - }, - "@unlockMyTrip": { - "type": "String", - "placeholders": {} - }, - "@signupOption": { - "type": "String", - "placeholders": {} - }, - "@withApple": { - "type": "String", - "placeholders": {} - }, - "@withGoogle": { - "type": "String", - "placeholders": {} - }, - "@withEmail": { - "type": "String", - "placeholders": {} - }, - "@createAccount": { - "type": "String", - "placeholders": {} - }, - "@loginWithEmail": { - "type": "String", - "placeholders": {} - }, - "@usernameOrEmail": { - "type": "String", - "placeholders": {} - }, - "@email": { - "type": "String", - "placeholders": {} - }, - "@forgotPassword": { - "type": "String", - "placeholders": {} - }, - "@endActivity": { - "type": "String", - "placeholders": {} - }, - "@allLanguages": { - "type": "String", - "placeholders": {} - }, - "@chatListTooltip": { - "type": "String", - "placeholders": {} - }, - "@directMessageBotTitle": { - "type": "String", - "placeholders": {} - }, - "@feedbackTitle": { - "type": "String", - "placeholders": {} - }, - "@feedbackHint": { - "type": "String", - "placeholders": {} - }, - "@feedbackButton": { - "type": "String", - "placeholders": {} - }, - "@directMessageBotDesc": { - "type": "String", - "placeholders": {} - }, - "@inviteYourFriends": { - "type": "String", - "placeholders": {} - }, - "@playWithAI": { - "type": "String", - "placeholders": {} - }, - "@courseStartDesc": { - "type": "String", - "placeholders": {} - }, - "feedbackRespDesc": "Kehren Sie morgen zurück, um Updates zur Aktivität zu erhalten.", - "activityDropdownDesc": "Wenn Sie mit dieser Aktivität fertig sind, klicken Sie unten", - "languageMismatchTitle": "Sprachinkonsistenz", - "languageMismatchDesc": "Ihre Zielsprache stimmt nicht mit der Sprache dieser Aktivität überein. Möchten Sie Ihre Zielsprache aktualisieren?", - "reportWordIssueTooltip": "Meldung eines Wortinformationsproblems", - "tokenInfoFeedbackDialogTitle": "Feedback zu Wortinformationen", - "noPublicCoursesFound": "Keine öffentlichen Kurse gefunden. Möchten Sie einen erstellen?", - "noCourseTemplatesFound": "Wir konnten keine Kurse für Ihre Zielsprache finden. Sie können in der Zwischenzeit mit Pangea Bot chatten und später nach weiteren Kursen suchen.", - "botActivityJoinFailMessage": "Pangea Bot benötigt etwas Zeit, um zu antworten. Bitte versuchen Sie es später erneut oder laden Sie einen Freund ein.", - "unsubscribedResponseError": "Diese Funktion erfordert ein Abonnement", - "leaveDesc": "Diesen Raum und alle Chats darin verlassen", - "selectAll": "Alle auswählen", - "deselectAll": "Alle abwählen", - "@feedbackRespDesc": { - "type": "String", - "placeholders": {} - }, - "@activityDropdownDesc": { - "type": "String", - "placeholders": {} - }, - "@languageMismatchTitle": { - "type": "String", - "placeholders": {} - }, - "@languageMismatchDesc": { - "type": "String", - "placeholders": {} - }, - "@reportWordIssueTooltip": { - "type": "String", - "placeholders": {} - }, - "@tokenInfoFeedbackDialogTitle": { - "type": "String", - "placeholders": {} - }, - "@noPublicCoursesFound": { - "type": "String", - "placeholders": {} - }, - "@noCourseTemplatesFound": { - "type": "String", - "placeholders": {} - }, - "@botActivityJoinFailMessage": { - "type": "String", - "placeholders": {} - }, - "@unsubscribedResponseError": { - "type": "String", - "placeholders": {} - }, - "@leaveDesc": { - "type": "String", - "placeholders": {} - }, - "@selectAll": { - "type": "String", - "placeholders": {} - }, - "@deselectAll": { - "type": "String", - "placeholders": {} - }, - "startOwn": "Eigene starten", - "joinCourseDesc": "Jeder Kurs besteht aus 8-10 sequenziellen Themen mit einer Vielzahl von aufgabenbasierten Sprachlernaktivitäten.", - "newMessageInPangeaChat": "🗨️ Neue Nachricht im Pangea-Chat", - "shareCourse": "Kurs teilen", - "addCourse": "Einen Kurs hinzufügen", - "joinPublicCourse": "Öffentlichen Kurs beitreten", - "vocabLevelsDesc": "Hier kommen die Vokabeln hin, sobald du sie aufgestuft hast!", - "highlightVocabTooltip": "Hebe die Zielvokabeln unten hervor, indem du sie im Chat sendest oder mit ihnen übst.", - "@startOwn": { - "type": "String", - "placeholders": {} - }, - "@joinCourseDesc": { - "type": "String", - "placeholders": {} - }, - "@newMessageInPangeaChat": { - "type": "String", - "placeholders": {} - }, - "@shareCourse": { - "type": "String", - "placeholders": {} - }, - "@addCourse": { - "type": "String", - "placeholders": {} - }, - "@joinPublicCourse": { - "type": "String", - "placeholders": {} - }, - "@vocabLevelsDesc": { - "type": "String", - "placeholders": {} - }, - "emptyChatSearch": "Keine DMs oder Chats gefunden. Stellen Sie sicher, dass Ihre Suche korrekt geschrieben ist.", - "activityAnalyticsTooltipBody": "Dies sind Ihre gespeicherten Aktivitäten zur Überprüfung und Übung.", - "numSavedActivities": "Anzahl der gespeicherten Aktivitäten", - "saveActivityTitle": "Aktivität speichern", - "saveActivityDesc": "Gut gemacht! Speichern Sie diese Aktivität für eine spätere Überprüfung und Übung.", - "levelInfoTooltip": "Hier können Sie alle Punkte sehen, die Sie verdient haben und wie!", - "alreadyInCourseWithID": "Sie sind bereits in einem Kurs mit diesem Plan. Möchten Sie einen Kurs mit demselben Plan erstellen oder zum bestehenden Kurs gehen?", - "goToExistingCourse": "Zum bestehenden Kurs gehen", - "emojiView": "Emoji-Ansicht", - "feedbackDialogDesc": "Ich mache auch Fehler! Gibt es etwas, das mir helfen kann, mich zu verbessern?", - "contactHasBeenInvitedToTheCourse": "Kontakt wurde zum Kurs eingeladen", - "activityStatsButtonTooltip": "Aktivitätsinformationen", - "allow": "Erlauben", - "deny": "Ablehnen", - "enabledRenewal": "Abonnementverlängerung aktivieren", - "subscriptionEndsOn": "Abonnement endet am", - "subscriptionRenewsOn": "Abonnement erneuert sich am", - "waitForSubscriptionChanges": "Änderungen an Ihrem Abonnement können einen Moment dauern, um in der App angezeigt zu werden.", - "subscribeReadingAssistance": "Abonnieren, um Nachrichtentools freizuschalten", - "aceDisplayName": "Achinese", - "achDisplayName": "Acoli", - "afDisplayName": "Afrikaans", - "akDisplayName": "Akan", - "alzDisplayName": "Alur", - "amDisplayName": "Amharisch", - "arDisplayName": "Arabisch", - "asDisplayName": "Assamesisch", - "awaDisplayName": "Awadhi", - "ayDisplayName": "Aymara", - "azDisplayName": "Aserbaidschanisch", - "baDisplayName": "Baskirisch", - "banDisplayName": "Balinésisch", - "bbcDisplayName": "Batak Toba", - "beDisplayName": "Weißrussisch", - "bemDisplayName": "Bemba", - "bewDisplayName": "Betawi", - "bgDisplayName": "Bulgarisch", - "bhoDisplayName": "Bhojpuri", - "bikDisplayName": "Bikol", - "bmDisplayName": "Bambara", - "bnDisplayName": "Bengalisch", - "bnBDDisplayName": "Bengalisch (Bangladesch)", - "bnINDisplayName": "Bengalisch (Indien)", - "brDisplayName": "Bretonisch", - "bsDisplayName": "Bosnisch", - "btsDisplayName": "Batak Simalungun", - "btxDisplayName": "Batak Karo", - "buaDisplayName": "Burjatisch", - "caDisplayName": "Katalanisch", - "cebDisplayName": "Cebuano", - "cggDisplayName": "Chiga", - "chmDisplayName": "Mari", - "ckbDisplayName": "Zentral-Kurdisch", - "cnhDisplayName": "Hakha Chin", - "coDisplayName": "Korsisch", - "crhDisplayName": "Krim-Türkisch", - "crsDisplayName": "Seselwa-Kreolisch-Französisch", - "csDisplayName": "Tschechisch", - "cvDisplayName": "Tschuwasch", - "cyDisplayName": "Walisisch", - "daDisplayName": "Dänisch", - "deDisplayName": "Deutsch", - "dinDisplayName": "Dinka", - "doiDisplayName": "Dogri", - "dovDisplayName": "Dombe", - "dzDisplayName": "Dzongkha", - "eeDisplayName": "Ewe", - "enDisplayName": "Englisch", - "enAUDisplayName": "Englisch (Australien)", - "enGBDisplayName": "Englisch (Vereinigtes Königreich)", - "enINDisplayName": "Englisch (Indien)", - "enUSDisplayName": "Englisch (USA)", - "eoDisplayName": "Esperanto", - "esDisplayName": "Spanisch", - "esESDisplayName": "Spanisch (Spanien)", - "esMXDisplayName": "Spanisch (Mexiko)", - "euDisplayName": "Baskisch", - "faDisplayName": "Persisch", - "ffDisplayName": "Fulah", - "fiDisplayName": "Finnisch", - "filDisplayName": "Filipino", - "fjDisplayName": "Fidschian", - "foDisplayName": "Färöisch", - "frDisplayName": "Französisch", - "frCADisplayName": "Französisch (Kanada)", - "frFRDisplayName": "Französisch (Frankreich)", - "fyDisplayName": "Westfriesisch", - "gaDisplayName": "Irisch", - "gaaDisplayName": "Ga", - "gdDisplayName": "Schottisch-Gälisch", - "glDisplayName": "Galizisch", - "gnDisplayName": "Guaraní", - "gomDisplayName": "Goan Konkani", - "guDisplayName": "Gujarati", - "haDisplayName": "Hausa", - "hawDisplayName": "Hawaiian", - "heDisplayName": "Hebräisch", - "hiDisplayName": "Hindi", - "hilDisplayName": "Hiligaynon", - "hmnDisplayName": "Hmong", - "hneDisplayName": "Chhattisgarhi", - "hrDisplayName": "Kroatisch", - "hrxDisplayName": "Hunsrik", - "htDisplayName": "Haitianisch Kreolisch", - "huDisplayName": "Ungarisch", - "hyDisplayName": "Armenisch", - "idDisplayName": "Indonesisch", - "igDisplayName": "Igbo", - "iloDisplayName": "Iloko", - "isDisplayName": "Isländisch", - "itDisplayName": "Italienisch", - "jaDisplayName": "Japanisch", - "jvDisplayName": "Javanesisch", - "kaDisplayName": "Georgisch", - "kkDisplayName": "Kasachisch", - "kmDisplayName": "Khmer", - "knDisplayName": "Kannada", - "koDisplayName": "Koreanisch", - "kokDisplayName": "Konkani", - "kriDisplayName": "Krio", - "ksDisplayName": "Kaschmirisch", - "ktuDisplayName": "Kituba (Demokratische Republik Kongo)", - "kuDisplayName": "Kurdisch", - "kyDisplayName": "Kirgisisch", - "laDisplayName": "Latein", - "lbDisplayName": "Luxemburgisch", - "lgDisplayName": "Ganda", - "liDisplayName": "Limburgisch", - "lijDisplayName": "Ligurisch", - "lmoDisplayName": "Lombardisch", - "lnDisplayName": "Lingala", - "loDisplayName": "Lao", - "ltDisplayName": "Litauisch", - "ltgDisplayName": "Latgalisch", - "luoDisplayName": "Luo (Kenia und Tansania)", - "lusDisplayName": "Mizo", - "lvDisplayName": "Lettisch", - "maiDisplayName": "Maithili", - "makDisplayName": "Makasar", - "mgDisplayName": "Malagasy", - "miDisplayName": "Māori", - "minDisplayName": "Minangkabau", - "mkDisplayName": "Mazedonisch", - "mlDisplayName": "Malayalam", - "mnDisplayName": "Mongolisch", - "mniDisplayName": "Manipuri", - "mrDisplayName": "Marathi", - "msDisplayName": "Malaiisch", - "msArabDisplayName": "Malaiisch (Arabisch)", - "msMYDisplayName": "Malaiisch (Malaysia)", - "mtDisplayName": "Maltesisch", - "mwrDisplayName": "Marwari", - "myDisplayName": "Burmesisch", - "nanDisplayName": "Min Nan", - "nbDisplayName": "Norwegisch (Bokmål)", - "neDisplayName": "Nepali", - "newDisplayName": "Newari", - "nlDisplayName": "Niederländisch", - "nlBEDisplayName": "Flämisch", - "noDisplayName": "Norwegisch", - "nrDisplayName": "Süd-Ndebele", - "nsoDisplayName": "Nord-Sotho", - "nusDisplayName": "Nuer", - "nyDisplayName": "Nyanja", - "ocDisplayName": "Okzitanisch", - "omDisplayName": "Oromo", - "orDisplayName": "Odia", - "paDisplayName": "Punjabi", - "paArabDisplayName": "Punjabi (Shahmukhi)", - "paINDisplayName": "Punjabi (Gurmukhi)", - "pagDisplayName": "Pangasinan", - "pamDisplayName": "Pampanga", - "papDisplayName": "Papiamento", - "plDisplayName": "Polnisch", - "psDisplayName": "Paschtu", - "ptDisplayName": "Portugiesisch", - "ptBRDisplayName": "Portugiesisch (Brasilien)", - "ptPTDisplayName": "Portugiesisch (Portugal)", - "quDisplayName": "Quechua", - "rajDisplayName": "Rajasthani", - "rnDisplayName": "Rundi", - "roDisplayName": "Rumänisch", - "roMDDisplayName": "Moldawisch", - "romDisplayName": "Romanes", - "ruDisplayName": "Russisch", - "rwDisplayName": "Kinyarwanda", - "saDisplayName": "Sanskrit", - "satDisplayName": "Santali", - "scnDisplayName": "Sizilianisch", - "sdDisplayName": "Sindhi", - "sgDisplayName": "Sango", - "shnDisplayName": "Shan", - "siDisplayName": "Singhalesisch", - "skDisplayName": "Slowakisch", - "slDisplayName": "Slowenisch", - "smDisplayName": "Samoanisch", - "snDisplayName": "Shona", - "soDisplayName": "Somalisch", - "sqDisplayName": "Albanisch", - "srDisplayName": "Serbisch", - "srMEDisplayName": "Montenegrinisch", - "ssDisplayName": "Swati", - "stDisplayName": "Süd-Sotho", - "suDisplayName": "Sundanesisch", - "svDisplayName": "Schwedisch", - "swDisplayName": "Swahili", - "szlDisplayName": "Schlesisch", - "taDisplayName": "Tamil", - "teDisplayName": "Telugu", - "tetDisplayName": "Tetum", - "tgDisplayName": "Tadschikisch", - "thDisplayName": "Thailändisch", - "tiDisplayName": "Tigrinya", - "tkDisplayName": "Turkmenisch", - "tlDisplayName": "Tagalog", - "tnDisplayName": "Tswana", - "trDisplayName": "Türkisch", - "tsDisplayName": "Tsonga", - "ttDisplayName": "Tatarisch", - "ugDisplayName": "Uigur", - "ukDisplayName": "Ukrainisch", - "urDisplayName": "Urdu", - "urINDisplayName": "Urdu (Indien)", - "urPKDisplayName": "Urdu (Pakistan)", - "uzDisplayName": "Usbekisch", - "viDisplayName": "Vietnamesisch", - "wuuDisplayName": "Wu", - "xhDisplayName": "Xhosa", - "yiDisplayName": "Jiddisch", - "yoDisplayName": "Yoruba", - "yuaDisplayName": "Yucateco", - "yueDisplayName": "Kantonesisch", - "yueCNDisplayName": "Kantonesisch (China)", - "yueHKDisplayName": "Kantonesisch (Hongkong)", - "zhDisplayName": "Chinesisch", - "zhCNDisplayName": "Chinesisch (Vereinfacht)", - "zhTWDisplayName": "Chinesisch (Traditionell)", - "zuDisplayName": "Zulu", - "@emptyChatSearch": { - "type": "String", - "placeholders": {} - }, - "@activityAnalyticsTooltipBody": { - "type": "String", - "placeholders": {} - }, - "@numSavedActivities": { - "type": "String", - "placeholders": {} - }, - "@saveActivityTitle": { - "type": "String", - "placeholders": {} - }, - "@saveActivityDesc": { - "type": "String", - "placeholders": {} - }, - "@levelInfoTooltip": { - "type": "String", - "placeholders": {} - }, - "@alreadyInCourseWithID": { - "type": "String", - "placeholders": {} - }, - "@goToExistingCourse": { - "type": "String", - "placeholders": {} - }, - "@emojiView": { - "type": "String", - "placeholders": {} - }, - "@feedbackDialogDesc": { - "type": "String", - "placeholders": {} - }, - "@contactHasBeenInvitedToTheCourse": { - "type": "String", - "placeholders": {} - }, - "@activityStatsButtonTooltip": { - "type": "String", - "placeholders": {} - }, - "@allow": { - "type": "String", - "placeholders": {} - }, - "@deny": { - "type": "String", - "placeholders": {} - }, - "@enabledRenewal": { - "type": "String", - "placeholders": {} - }, - "@subscriptionEndsOn": { - "type": "String", - "placeholders": {} - }, - "@subscriptionRenewsOn": { - "type": "String", - "placeholders": {} - }, - "@waitForSubscriptionChanges": { - "type": "String", - "placeholders": {} - }, - "@subscribeReadingAssistance": { - "type": "String", - "placeholders": {} - }, - "@aceDisplayName": { - "type": "String", - "placeholders": {} - }, - "@achDisplayName": { - "type": "String", - "placeholders": {} - }, - "@afDisplayName": { - "type": "String", - "placeholders": {} - }, - "@akDisplayName": { - "type": "String", - "placeholders": {} - }, - "@alzDisplayName": { - "type": "String", - "placeholders": {} - }, - "@amDisplayName": { - "type": "String", - "placeholders": {} - }, - "@arDisplayName": { - "type": "String", - "placeholders": {} - }, - "@asDisplayName": { - "type": "String", - "placeholders": {} - }, - "@awaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ayDisplayName": { - "type": "String", - "placeholders": {} - }, - "@azDisplayName": { - "type": "String", - "placeholders": {} - }, - "@baDisplayName": { - "type": "String", - "placeholders": {} - }, - "@banDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bbcDisplayName": { - "type": "String", - "placeholders": {} - }, - "@beDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bemDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bewDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bhoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bikDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bmDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnBDDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@brDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@btsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@btxDisplayName": { - "type": "String", - "placeholders": {} - }, - "@buaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@caDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cebDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cggDisplayName": { - "type": "String", - "placeholders": {} - }, - "@chmDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ckbDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cnhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@coDisplayName": { - "type": "String", - "placeholders": {} - }, - "@crhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@crsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@csDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cvDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@daDisplayName": { - "type": "String", - "placeholders": {} - }, - "@deDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dinDisplayName": { - "type": "String", - "placeholders": {} - }, - "@doiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dovDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dzDisplayName": { - "type": "String", - "placeholders": {} - }, - "@eeDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enAUDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enGBDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enUSDisplayName": { - "type": "String", - "placeholders": {} - }, - "@eoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esESDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esMXDisplayName": { - "type": "String", - "placeholders": {} - }, - "@euDisplayName": { - "type": "String", - "placeholders": {} - }, - "@faDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ffDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@filDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fjDisplayName": { - "type": "String", - "placeholders": {} - }, - "@foDisplayName": { - "type": "String", - "placeholders": {} - }, - "@frDisplayName": { - "type": "String", - "placeholders": {} - }, - "@frCADisplayName": { - "type": "String", - "placeholders": {} - }, - "@frFRDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gaaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gdDisplayName": { - "type": "String", - "placeholders": {} - }, - "@glDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gomDisplayName": { - "type": "String", - "placeholders": {} - }, - "@guDisplayName": { - "type": "String", - "placeholders": {} - }, - "@haDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hawDisplayName": { - "type": "String", - "placeholders": {} - }, - "@heDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hilDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hmnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hneDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hrxDisplayName": { - "type": "String", - "placeholders": {} - }, - "@htDisplayName": { - "type": "String", - "placeholders": {} - }, - "@huDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@idDisplayName": { - "type": "String", - "placeholders": {} - }, - "@igDisplayName": { - "type": "String", - "placeholders": {} - }, - "@iloDisplayName": { - "type": "String", - "placeholders": {} - }, - "@isDisplayName": { - "type": "String", - "placeholders": {} - }, - "@itDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jvDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kmDisplayName": { - "type": "String", - "placeholders": {} - }, - "@knDisplayName": { - "type": "String", - "placeholders": {} - }, - "@koDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kokDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kriDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ksDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ktuDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kuDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@laDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lbDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@liDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lijDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lmoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@loDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ltDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ltgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@luoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lusDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lvDisplayName": { - "type": "String", - "placeholders": {} - }, - "@maiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@makDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@miDisplayName": { - "type": "String", - "placeholders": {} - }, - "@minDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mniDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msArabDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msMYDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mtDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mwrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@myDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nanDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nbDisplayName": { - "type": "String", - "placeholders": {} - }, - "@neDisplayName": { - "type": "String", - "placeholders": {} - }, - "@newDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nlBEDisplayName": { - "type": "String", - "placeholders": {} - }, - "@noDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nsoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nusDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ocDisplayName": { - "type": "String", - "placeholders": {} - }, - "@omDisplayName": { - "type": "String", - "placeholders": {} - }, - "@orDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paArabDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pagDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pamDisplayName": { - "type": "String", - "placeholders": {} - }, - "@papDisplayName": { - "type": "String", - "placeholders": {} - }, - "@plDisplayName": { - "type": "String", - "placeholders": {} - }, - "@psDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptBRDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptPTDisplayName": { - "type": "String", - "placeholders": {} - }, - "@quDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rajDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@roDisplayName": { - "type": "String", - "placeholders": {} - }, - "@roMDDisplayName": { - "type": "String", - "placeholders": {} - }, - "@romDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ruDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rwDisplayName": { - "type": "String", - "placeholders": {} - }, - "@saDisplayName": { - "type": "String", - "placeholders": {} - }, - "@satDisplayName": { - "type": "String", - "placeholders": {} - }, - "@scnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sdDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@shnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@siDisplayName": { - "type": "String", - "placeholders": {} - }, - "@skDisplayName": { - "type": "String", - "placeholders": {} - }, - "@slDisplayName": { - "type": "String", - "placeholders": {} - }, - "@smDisplayName": { - "type": "String", - "placeholders": {} - }, - "@snDisplayName": { - "type": "String", - "placeholders": {} - }, - "@soDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sqDisplayName": { - "type": "String", - "placeholders": {} - }, - "@srDisplayName": { - "type": "String", - "placeholders": {} - }, - "@srMEDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ssDisplayName": { - "type": "String", - "placeholders": {} - }, - "@stDisplayName": { - "type": "String", - "placeholders": {} - }, - "@suDisplayName": { - "type": "String", - "placeholders": {} - }, - "@svDisplayName": { - "type": "String", - "placeholders": {} - }, - "@swDisplayName": { - "type": "String", - "placeholders": {} - }, - "@szlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@taDisplayName": { - "type": "String", - "placeholders": {} - }, - "@teDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tetDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@thDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@trDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ttDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ugDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ukDisplayName": { - "type": "String", - "placeholders": {} - }, - "@urDisplayName": { - "type": "String", - "placeholders": {} - }, - "@urINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@urPKDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uzDisplayName": { - "type": "String", - "placeholders": {} - }, - "@viDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wuuDisplayName": { - "type": "String", - "placeholders": {} - }, - "@xhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yuaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yueDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yueCNDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yueHKDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zhCNDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zhTWDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zuDisplayName": { - "type": "String", - "placeholders": {} - }, - "teacherModeTitle": "Lehrermodus", - "teacherModeDesc": "Umschalten, um alle Themen und Aktivitäten freizuschalten. Nur für Kursadministratoren.", - "@teacherModeTitle": { - "type": "String", - "placeholders": {} - }, - "@teacherModeDesc": { - "type": "String", - "placeholders": {} - }, - "failedToLoadFeedback": "Fehler beim Laden des Feedbacks.", - "unreadPlus": "99+", - "noSavedActivitiesYet": "Aktivitäten erscheinen hier, sobald sie abgeschlossen und gespeichert sind.", - "@failedToLoadFeedback": { - "type": "String", - "placeholders": {} - }, - "@unreadPlus": { - "type": "String", - "placeholders": {} - }, - "@noSavedActivitiesYet": { - "type": "String", - "placeholders": {} - }, - "changeCourse": "Kurs ändern", - "changeCourseDesc": "Hier können Sie den Kursplan dieses Kurses ändern.", - "@changeCourse": { - "type": "String", - "placeholders": {} - }, - "@changeCourseDesc": { - "type": "String", - "placeholders": {} - }, - "practiceActivityCompleted": "Übungsaktivität abgeschlossen", - "@practiceActivityCompleted": { - "type": "String", - "placeholders": {} - }, - "introChatTitle": "Erstelle Einführungs-Chat", - "introChatDesc": "Jeder im Raum kann posten.", - "announcementsChatTitle": "Ankündigungen-Chat", - "announcementsChatDesc": "Nur der Raum-Administrator kann posten.", - "@introChatTitle": { - "type": "String", - "placeholders": {} - }, - "@introChatDesc": { - "type": "String", - "placeholders": {} - }, - "@announcementsChatTitle": { - "type": "String", - "placeholders": {} - }, - "@announcementsChatDesc": { - "type": "String", - "placeholders": {} - }, - "notStartedActivitiesTitle": "Offene Sitzungen ({num})", - "inProgressActivitiesTitle": "Gerade jetzt ({num})", - "completedActivitiesTitle": "Fertig ({num})", - "pickDifferentActivity": "Wählen Sie eine andere Aktivität", - "inOngoingActivity": "Sie haben eine laufende Aktivität!", - "@notStartedActivitiesTitle": { - "type": "String", - "placeholders": { - "num": { - "type": "int" - } - } - }, - "@inProgressActivitiesTitle": { - "type": "String", - "placeholders": { - "num": { - "type": "int" - } - } - }, - "@completedActivitiesTitle": { - "type": "String", - "placeholders": { - "num": { - "type": "int" - } - } - }, - "@pickDifferentActivity": { - "type": "String", - "placeholders": {} - }, - "messageLanguageMismatchMessage": "Ihre Zielsprache stimmt nicht mit dieser Nachricht überein. Möchten Sie Ihre Zielsprache aktualisieren?", - "@messageLanguageMismatchMessage": { - "type": "String", - "placeholders": {} - }, - "courseParticipantTooltip": "Das sind alle in diesem Kurs. Klicken Sie auf das Avatar eines Benutzers und \"Gespräch starten\", um eine DM zu senden.", - "@courseParticipantTooltip": { - "type": "String", - "placeholders": {} - }, - "blockLemmaConfirmation": "Dieses Vokabelwort wird dauerhaft aus Ihren Analysen entfernt", - "woman": "Frau", - "man": "Mann", - "otherGender": "Sonstiges", - "unselectedGender": "Wählen Sie eine Geschlechtsoption", - "gender": "Geschlecht", - "chatParticipantTooltip": "Das sind alle in diesem Chat. Klicken Sie auf das Avatar eines Benutzers und \"Gespräch starten\", um eine DM zu senden.", - "@blockLemmaConfirmation": { - "type": "String", - "placeholders": {} - }, - "@woman": { - "type": "String", - "placeholders": {} - }, - "@man": { - "type": "String", - "placeholders": {} - }, - "@otherGender": { - "type": "String", - "placeholders": {} - }, - "@unselectedGender": { - "type": "String", - "placeholders": {} - }, - "@gender": { - "type": "String", - "placeholders": {} - }, - "@chatParticipantTooltip": { - "type": "String", - "placeholders": {} - }, - "@inOngoingActivity": { - "type": "String", - "placeholders": {} - }, - "modeDisabled": "Lernwerkzeuge sind für Nachrichten, die nicht in deiner Zielsprache sind, deaktiviert.", - "vocabEmoji": "Vokabel-Emoji", - "@modeDisabled": { - "type": "String", - "placeholders": {} - }, - "@vocabEmoji": { - "type": "String", - "placeholders": {} - }, - "requestRegeneration": "Anforderung der Regeneration", - "optionalRegenerateReason": "(Optional) Grund", - "@requestRegeneration": { - "type": "String", - "placeholders": {} - }, - "@optionalRegenerateReason": { - "type": "String", - "placeholders": {} - }, - "emojiSelectedSnackbar": "Sie haben das Emoji für {lemma} festgelegt! Wir werden dieses Emoji verwenden, um das Wort in zukünftigen Übungsaktivitäten darzustellen.", - "@emojiSelectedSnackbar": { - "type": "String", - "placeholders": { - "lemma": { - "type": "String" - } - } - }, - "ssoDialogTitle": "Warten auf den Abschluss der Anmeldung", - "ssoDialogDesc": "Wir haben einen neuen Tab geöffnet, damit Sie sich sicher anmelden können.", - "ssoDialogHelpText": "🤔 Wenn Sie den neuen Tab nicht gesehen haben, überprüfen Sie bitte Ihren Popup-Blocker.", - "@ssoDialogTitle": { - "type": "String", - "placeholders": {} - }, - "@ssoDialogDesc": { - "type": "String", - "placeholders": {} - }, - "@ssoDialogHelpText": { - "type": "String", - "placeholders": {} - }, - "disableLanguageToolsTitle": "Spracheinstellungen deaktivieren", - "disableLanguageToolsDesc": "Möchten Sie die automatische Sprachunterstützung deaktivieren?", - "@disableLanguageToolsTitle": { - "type": "String", - "placeholders": {} - }, - "@disableLanguageToolsDesc": { - "type": "String", - "placeholders": {} - }, - "recordingPermissionDenied": "Zugriff verweigert. Aktivieren Sie die Aufnahmeberechtigungen, um Sprachnachrichten aufzunehmen.", - "genericWebRecordingError": "Etwas ist schiefgelaufen. Wir empfehlen, den Chrome-Browser zu verwenden, wenn Sie Nachrichten aufnehmen.", - "@recordingPermissionDenied": { - "type": "String", - "placeholders": {} - }, - "@genericWebRecordingError": { - "type": "String", - "placeholders": {} - }, - "screenSizeWarning": "Für das beste Erlebnis mit dieser Anwendung erweitern Sie bitte Ihre Bildschirmgröße.", - "@screenSizeWarning": { - "type": "String", - "placeholders": {} - }, - "activitiesToUnlockTopicTitle": "Aktivitäten zum Freischalten des nächsten Themas", - "activitiesToUnlockTopicDesc": "Legen Sie die Anzahl der Aktivitäten fest, um das nächste Kursthema freizuschalten", - "@activitiesToUnlockTopicTitle": { - "type": "String", - "placeholders": {} - }, - "@activitiesToUnlockTopicDesc": { - "type": "String", - "placeholders": {} - }, - "constructUseCorLMDesc": "Korrekte Vokabeldefinitionsübung", - "constructUseIncLMDesc": "Falsche Vokabeldefinitionsübung", - "constructUseCorLADesc": "Korrekte Vokabelaudioübung", - "constructUseIncLADesc": "Falsche Vokabelaudioübung", - "constructUseBonus": "Bonus während der Vokabelübung", - "practiceVocab": "Vokabeln üben", - "selectMeaning": "Wähle die Bedeutung", - "selectAudio": "Wähle das passende Audio", - "congratulations": "Herzlichen Glückwunsch!", - "anotherRound": "Eine weitere Runde", - "noActivityRequest": "Keine aktuellen Aktivitätsanfragen.", - "mustHave10Words": "Sie müssen mindestens 10 Vokabeln haben, um sie zu üben. Versuchen Sie, mit einem Freund oder dem Pangea Bot zu sprechen, um mehr zu entdecken!", - "botSettings": "Bot-Einstellungen", - "activitySettingsOverrideWarning": "Sprache und Sprachniveau werden durch den Aktivitätsplan bestimmt", - "voice": "Stimme", - "@constructUseCorLMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncLMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorLADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncLADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseBonus": { - "type": "String", - "placeholders": {} - }, - "@practiceVocab": { - "type": "String", - "placeholders": {} - }, - "@selectMeaning": { - "type": "String", - "placeholders": {} - }, - "@selectAudio": { - "type": "String", - "placeholders": {} - }, - "@congratulations": { - "type": "String", - "placeholders": {} - }, - "@anotherRound": { - "type": "String", - "placeholders": {} - }, - "@noActivityRequest": { - "type": "String", - "placeholders": {} - }, - "@mustHave10Words": { - "type": "String", - "placeholders": {} - }, - "@botSettings": { - "type": "String", - "placeholders": {} - }, - "@activitySettingsOverrideWarning": { - "type": "String", - "placeholders": {} - }, - "@voice": { - "type": "String", - "placeholders": {} - }, - "youLeftTheChat": "🚪 Du hast den Chat verlassen", - "@youLeftTheChat": { - "type": "String", - "placeholders": {} - }, - "downloadInitiated": "Download gestartet", - "webDownloadPermissionMessage": "Wenn Ihr Browser Downloads blockiert, aktivieren Sie bitte Downloads für diese Seite.", - "@downloadInitiated": { - "type": "String", - "placeholders": {} - }, - "@webDownloadPermissionMessage": { - "type": "String", - "placeholders": {} - }, - "exitPractice": "Ihr Fortschritt in der Übungssitzung wird nicht gespeichert.", - "practiceGrammar": "Grammatik üben", - "notEnoughToPractice": "Senden Sie mehr Nachrichten, um die Übung freizuschalten", - "constructUseCorGCDesc": "Übung der korrekten Grammatikkategorie", - "constructUseIncGCDesc": "Übung der inkorrekten Grammatikkategorie", - "@exitPractice": { - "type": "String", - "placeholders": {} - }, - "@practiceGrammar": { - "type": "String", - "placeholders": {} - }, - "@notEnoughToPractice": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncGCDesc": { - "type": "String", - "placeholders": {} - }, - "constructUseCorGEDesc": "Korrekte Grammatikfehlerübung", - "constructUseIncGEDesc": "Falsche Grammatikfehlerübung", - "fillInBlank": "Füllen Sie die Lücke mit der richtigen Wahl aus", - "learn": "Lernen", - "languageUpdated": "Zielsprache aktualisiert!", - "@constructUseCorGEDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncGEDesc": { - "type": "String", - "placeholders": {} - }, - "@fillInBlank": { - "type": "String", - "placeholders": {} - }, - "@learn": { - "type": "String", - "placeholders": {} - }, - "@languageUpdated": { - "type": "String", - "placeholders": {} - }, - "voiceDropdownTitle": "Pangea Bot Stimme", - "@voiceDropdownTitle": { - "type": "String", - "placeholders": {} - }, - "knockDesc": "Ihre Anfrage wurde an den Kursadministrator gesendet! Sie werden eingelassen, wenn sie zustimmen.", - "@knockDesc": { - "type": "String", - "placeholders": {} - }, - "joinSpaceOnboardingDesc": "Haben Sie einen Einladungscode oder einen Link zu einem öffentlichen Kurs?", - "welcomeUser": "Willkommen {user}", - "@joinSpaceOnboardingDesc": { - "type": "String", - "placeholders": {} - }, - "@welcomeUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "publicInviteDescChat": "Suchen Sie nach Benutzern, um sie zu diesem Chat einzuladen.", - "publicInviteDescSpace": "Suchen Sie nach Benutzern, um sie zu diesem Raum einzuladen.", - "@publicInviteDescChat": { - "type": "String", - "placeholders": {} - }, - "@publicInviteDescSpace": { - "type": "String", - "placeholders": {} - }, - "enableNotificationsTitle": "Pangea Chat ist eine Messaging-App, daher sind Benachrichtigungen wichtig!", - "enableNotificationsDesc": "Benachrichtigungen erlauben", - "@enableNotificationsTitle": { - "type": "String", - "placeholders": {} - }, - "@enableNotificationsDesc": { - "type": "String", - "placeholders": {} - }, - "useActivityImageAsChatBackground": "Aktivitätsbild als Chat-Hintergrund verwenden", - "@useActivityImageAsChatBackground": { - "type": "String", - "placeholders": {} - }, - "chatWithSupport": "Chat mit dem Support", - "@chatWithSupport": { - "type": "String", - "placeholders": {} - }, - "newCourseAccess": "Standardmäßig sind Kurse öffentlich durchsuchbar und erfordern die Genehmigung eines Administrators, um beizutreten. Sie können diese Einstellungen jederzeit bearbeiten.", - "@newCourseAccess": { - "type": "String", - "placeholders": {} - }, - "onboardingLanguagesTitle": "Welche Sprache lernst du?", - "searchLanguagesHint": "Zielsprachen suchen", - "@onboardingLanguagesTitle": { - "type": "String", - "placeholders": {} - }, - "@searchLanguagesHint": { - "type": "String", - "placeholders": {} - }, - "supportSubtitle": "Fragen? Wir sind hier, um zu helfen!", - "@supportSubtitle": { - "type": "String", - "placeholders": {} - }, - "courseLoadingError": "Etwas ist schiefgelaufen, und wir arbeiten hart daran, es zu beheben. Überprüfen Sie es später erneut.", - "@courseLoadingError": { - "type": "String", - "placeholders": {} - }, - "autoIGCToolName": "Schreibassistenz aktivieren", - "autoIGCToolDescription": "Automatisch Pangea Chat-Tools ausführen, um gesendete Nachrichten in die Zielsprache zu korrigieren.", - "@autoIGCToolName": { - "type": "String", - "placeholders": {} - }, - "@autoIGCToolDescription": { - "type": "String", - "placeholders": {} - }, - "emptyAudioError": "Die Aufnahme ist fehlgeschlagen. Bitte überprüfen Sie Ihre Audio-Berechtigungen und versuchen Sie es erneut.", - "@emptyAudioError": { - "type": "String", - "placeholders": {} - }, - "aboutMeHint": "Über mich", - "@aboutMeHint": { - "type": "String", - "placeholders": {} - }, - "grammarCopyPOSidiom": "Idiom", - "grammarCopyPOSphrasalv": "Phrasal Verb", - "grammarCopyPOScompn": "Zusammengesetztes Wort", - "@grammarCopyPOSidiom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSphrasalv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOScompn": { - "type": "String", - "placeholders": {} - }, - "perfectPractice": "Perfekte Übung!", - "greatPractice": "Großartige Übung!", - "usedNoHints": "Gut gemacht, keine Hinweise zu verwenden!", - "youveCompletedPractice": "Du hast die Übung abgeschlossen, mach weiter so, um besser zu werden!", - "@perfectPractice": { - "type": "String", - "placeholders": {} - }, - "@greatPractice": { - "type": "String", - "placeholders": {} - }, - "@usedNoHints": { - "type": "String", - "placeholders": {} - }, - "@youveCompletedPractice": { - "type": "String", - "placeholders": {} - }, - "changeEmail": "E-Mail ändern", - "withTheseAddressesDescription": "Mit diesen E-Mail-Adressen können Sie sich anmelden, Ihr Passwort wiederherstellen und Abonnements verwalten.", - "noAddressDescription": "Sie haben noch keine E-Mail-Adressen hinzugefügt.", - "@changeEmail": { - "type": "String", - "placeholders": {} - }, - "@withTheseAddressesDescription": { - "type": "String", - "placeholders": {} - }, - "@noAddressDescription": { - "type": "String", - "placeholders": {} - }, - "spanTypeGrammar": "Grammatik", - "spanTypeWordChoice": "Wortwahl", - "spanTypeSpelling": "Rechtschreibung", - "spanTypePunctuation": "Interpunktion", - "spanTypeStyle": "Stil", - "spanTypeFluency": "Flüssigkeit", - "spanTypeAccents": "Akzente", - "spanTypeCapitalization": "Großschreibung", - "spanTypeCorrection": "Korrektur", - "spanFeedbackTitle": "Korrekturproblem melden", - "@spanTypeGrammar": { - "type": "String", - "placeholders": {} - }, - "@spanTypeWordChoice": { - "type": "String", - "placeholders": {} - }, - "@spanTypeSpelling": { - "type": "String", - "placeholders": {} - }, - "@spanTypePunctuation": { - "type": "String", - "placeholders": {} - }, - "@spanTypeStyle": { - "type": "String", - "placeholders": {} - }, - "@spanTypeFluency": { - "type": "String", - "placeholders": {} - }, - "@spanTypeAccents": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCapitalization": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCorrection": { - "type": "String", - "placeholders": {} - }, - "@spanFeedbackTitle": { - "type": "String", - "placeholders": {} - } -} \ No newline at end of file + "@requests": { + "type": "String", + "placeholders": {} + }, + "@holdForInfo": { + "type": "String", + "placeholders": {} + }, + "@greenFeedback": { + "type": "String", + "placeholders": {} + }, + "@yellowFeedback": { + "type": "String", + "placeholders": {} + }, + "@redFeedback": { + "type": "String", + "placeholders": {} + }, + "@itInstructionsTitle": { + "type": "String", + "placeholders": {} + }, + "@itInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@gaTooltip": { + "type": "String", + "placeholders": {} + }, + "@taTooltip": { + "type": "String", + "placeholders": {} + }, + "@unTooltip": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorSliderHeader": { + "type": "String", + "placeholders": {} + }, + "@interactiveGrammarSliderHeader": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorNotAllowed": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorAllowed": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorRequired": { + "type": "String", + "placeholders": {} + }, + "@notYetSet": { + "type": "String", + "placeholders": {} + }, + "@waTooltip": { + "type": "String", + "placeholders": {} + }, + "@languageSettings": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslator": { + "type": "String", + "placeholders": {} + }, + "@noIdenticalLanguages": { + "type": "String", + "placeholders": {} + }, + "@searchBy": { + "type": "String", + "placeholders": {} + }, + "@joinWithClassCode": { + "type": "String", + "placeholders": {} + }, + "@languageLevelPreA1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA2": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB2": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC2": { + "type": "String", + "placeholders": {} + }, + "@changeTheNameOfTheClass": { + "type": "String", + "placeholders": {} + }, + "@changeTheNameOfTheChat": { + "type": "String", + "placeholders": {} + }, + "@sorryNoResults": { + "type": "String", + "placeholders": {} + }, + "@ignoreInThisText": { + "type": "String", + "placeholders": {} + }, + "@needsItMessage": { + "type": "String", + "placeholders": { + "targetLanguage": { + "type": "String" + } + } + }, + "@countryInformation": { + "type": "String", + "placeholders": {} + }, + "@targetLanguage": { + "type": "String", + "placeholders": {} + }, + "@sourceLanguage": { + "type": "String", + "placeholders": {} + }, + "@updateLanguage": { + "type": "String", + "placeholders": {} + }, + "@whatLanguageYouWantToLearn": { + "type": "String", + "placeholders": {} + }, + "@whatIsYourBaseLanguage": { + "type": "String", + "placeholders": {} + }, + "@publicProfileTitle": { + "type": "String", + "placeholders": {} + }, + "@publicProfileDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIT": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIGC": { + "type": "String", + "placeholders": {} + }, + "@errorDisableLanguageAssistance": { + "type": "String", + "placeholders": {} + }, + "@errorDisableITUserDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIGCUserDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableLanguageAssistanceUserDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableITClassDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIGCClassDesc": { + "type": "String", + "placeholders": {} + }, + "@error405Title": { + "type": "String", + "placeholders": {} + }, + "@error405Desc": { + "type": "String", + "placeholders": {} + }, + "@termsAndConditions": { + "type": "String", + "placeholders": {} + }, + "@andCertifyIAmAtLeast13YearsOfAge": { + "type": "String", + "placeholders": {} + }, + "@error502504Title": { + "type": "String", + "placeholders": {} + }, + "@error502504Desc": { + "type": "String", + "placeholders": {} + }, + "@error404Title": { + "type": "String", + "placeholders": {} + }, + "@error404Desc": { + "type": "String", + "placeholders": {} + }, + "@errorPleaseRefresh": { + "type": "String", + "placeholders": {} + }, + "@connectedToStaging": { + "type": "String", + "placeholders": {} + }, + "@learningSettings": { + "type": "String", + "placeholders": {} + }, + "@participants": { + "type": "String", + "placeholders": {} + }, + "@clickMessageTitle": { + "type": "String", + "placeholders": {} + }, + "@clickMessageBody": { + "type": "String", + "placeholders": {} + }, + "@allDone": { + "type": "String", + "placeholders": {} + }, + "@vocab": { + "type": "String", + "placeholders": {} + }, + "@low": { + "type": "String", + "placeholders": {} + }, + "@medium": { + "type": "String", + "placeholders": {} + }, + "@high": { + "type": "String", + "placeholders": {} + }, + "@subscribe": { + "type": "String", + "placeholders": {} + }, + "@getAccess": { + "type": "String", + "placeholders": {} + }, + "@subscriptionDesc": { + "type": "String", + "placeholders": {} + }, + "@subscriptionManagement": { + "type": "String", + "placeholders": {} + }, + "@currentSubscription": { + "type": "String", + "placeholders": {} + }, + "@cancelSubscription": { + "type": "String", + "placeholders": {} + }, + "@selectYourPlan": { + "type": "String", + "placeholders": {} + }, + "@subsciptionPlatformTooltip": { + "type": "String", + "placeholders": {} + }, + "@subscriptionManagementUnavailable": { + "type": "String", + "placeholders": {} + }, + "@paymentMethod": { + "type": "String", + "placeholders": {} + }, + "@paymentHistory": { + "type": "String", + "placeholders": {} + }, + "@emptyChatDownloadWarning": { + "type": "String", + "placeholders": {} + }, + "@update": { + "type": "String", + "placeholders": {} + }, + "@toggleImmersionMode": { + "type": "String", + "placeholders": {} + }, + "@toggleImmersionModeDesc": { + "type": "String", + "placeholders": {} + }, + "@itToggleDescription": { + "type": "String", + "placeholders": {} + }, + "@igcToggleDescription": { + "type": "String", + "placeholders": {} + }, + "@originalMessage": { + "type": "String", + "placeholders": {} + }, + "@sentMessage": { + "type": "String", + "placeholders": {} + }, + "@useType": { + "type": "String", + "placeholders": {} + }, + "@notAvailable": { + "type": "String", + "placeholders": {} + }, + "@taAndGaTooltip": { + "type": "String", + "placeholders": {} + }, + "@definitionsToolName": { + "type": "String", + "placeholders": {} + }, + "@messageTranslationsToolName": { + "type": "String", + "placeholders": {} + }, + "@definitionsToolDescription": { + "type": "String", + "placeholders": {} + }, + "@translationsToolDescrption": { + "type": "String", + "placeholders": {} + }, + "@welcomeBack": { + "type": "String", + "placeholders": {} + }, + "@downloadTxtFile": { + "type": "String", + "placeholders": {} + }, + "@downloadCSVFile": { + "type": "String", + "placeholders": {} + }, + "@promotionalSubscriptionDesc": { + "type": "String", + "placeholders": {} + }, + "@originalSubscriptionPlatform": { + "type": "String", + "placeholders": { + "purchasePlatform": { + "type": "String" + } + } + }, + "@oneWeekTrial": { + "type": "String", + "placeholders": {} + }, + "@downloadXLSXFile": { + "type": "String", + "placeholders": {} + }, + "@unkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@afCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@axCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@alCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@asCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@adCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@aoCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@aiCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@agCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@arCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@amCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@awCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@acCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@auCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@atCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@azCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bhCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@byCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@beCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@btCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@boCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@baCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@brCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ioCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@biCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@khCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@caCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@clCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cxCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ccCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@coCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ckCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@crCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ciCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cuCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@czCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@djCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@doCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tlCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ecCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@egCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@svCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@erCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@eeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@szCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@etCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@foCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fiCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@frCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@geCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@deCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ghCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@giCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@grCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@glCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@guCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gtCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ggCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@htCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@huCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@isCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@inCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@idCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@irCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@iqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ieCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@imCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ilCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@itCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@joCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@keCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kiCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@xkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@laCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@liCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ltCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@luCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@moCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@myCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mlCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mtCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mhCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@muCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ytCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mxCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@meCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@maCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@naCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@npCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nlCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ncCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@niCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@neCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ngCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nuCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@noCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@omCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@psCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@peCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@phCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@plCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@prCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@qaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@reCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@roCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ruCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@blCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@shCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@knCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@smCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@stCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@saCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@snCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@scCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@slCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sxCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@skCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@siCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@soCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@krCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ssCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@srCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@seCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@chCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@syCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@twCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@thCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@toCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ttCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@trCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@viCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ugCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@aeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@usCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vuCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@veCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ehCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pay": { + "type": "String", + "placeholders": {} + }, + "@invitedToSpace": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + }, + "space": { + "type": "String" + } + } + }, + "@youreInvited": { + "type": "String", + "placeholders": {} + }, + "@invitedToChat": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + }, + "name": { + "type": "String" + } + } + }, + "@monthlySubscription": { + "type": "String", + "placeholders": {} + }, + "@yearlySubscription": { + "type": "String", + "placeholders": {} + }, + "@defaultSubscription": { + "type": "String", + "placeholders": {} + }, + "@freeTrial": { + "type": "String", + "placeholders": {} + }, + "@total": { + "type": "String", + "placeholders": {} + }, + "@noDataFound": { + "type": "String", + "placeholders": {} + }, + "@blurMeansTranslateTitle": { + "type": "String", + "placeholders": {} + }, + "@blurMeansTranslateBody": { + "type": "String", + "placeholders": {} + }, + "@bestCorrectionFeedback": { + "type": "String", + "placeholders": {} + }, + "@distractorFeedback": { + "type": "String", + "placeholders": {} + }, + "@bestAnswerFeedback": { + "type": "String", + "placeholders": {} + }, + "@definitionDefaultPrompt": { + "type": "String", + "placeholders": {} + }, + "@practiceDefaultPrompt": { + "type": "String", + "placeholders": {} + }, + "@correctionDefaultPrompt": { + "type": "String", + "placeholders": {} + }, + "@acceptSelection": { + "type": "String", + "placeholders": {} + }, + "@why": { + "type": "String", + "placeholders": {} + }, + "@definition": { + "type": "String", + "placeholders": {} + }, + "@exampleSentence": { + "type": "String", + "placeholders": {} + }, + "@reportToTeacher": { + "type": "String", + "placeholders": {} + }, + "@reportMessageTitle": { + "type": "String", + "placeholders": { + "reportingUserId": { + "type": "String" + }, + "reportedUserId": { + "type": "String" + }, + "roomName": { + "type": "String" + } + } + }, + "@reportMessageBody": { + "type": "String", + "placeholders": { + "reportedMessage": { + "type": "String" + }, + "reason": { + "type": "String" + } + } + }, + "@noTeachersFound": { + "type": "String", + "placeholders": {} + }, + "@trialExpiration": { + "type": "String", + "placeholders": { + "expiration": { + "type": "String" + } + } + }, + "@freeTrialDesc": { + "type": "String", + "placeholders": {} + }, + "@activateTrial": { + "type": "String", + "placeholders": {} + }, + "@successfullySubscribed": { + "type": "String", + "placeholders": {} + }, + "@clickToManageSubscription": { + "type": "String", + "placeholders": {} + }, + "@signUp": { + "type": "String", + "placeholders": {} + }, + "@pleaseChooseAtLeastChars": { + "type": "String", + "placeholders": { + "min": { + "type": "String" + } + } + }, + "@noEmailWarning": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterValidEmail": { + "type": "String", + "placeholders": {} + }, + "@pleaseChooseAUsername": { + "type": "String", + "placeholders": {} + }, + "@define": { + "type": "String", + "placeholders": {} + }, + "@listen": { + "type": "String", + "placeholders": {} + }, + "@trialPeriodExpired": { + "type": "String", + "placeholders": {} + }, + "@translations": { + "type": "String", + "placeholders": {} + }, + "@messageAudio": { + "type": "String", + "placeholders": {} + }, + "@definitions": { + "type": "String", + "placeholders": {} + }, + "@subscribedToUnlockTools": { + "type": "String", + "placeholders": {} + }, + "@translationTooltip": { + "type": "String", + "placeholders": {} + }, + "@speechToTextTooltip": { + "type": "String", + "placeholders": {} + }, + "@kickBotWarning": { + "type": "String", + "placeholders": {} + }, + "@refresh": { + "type": "String", + "placeholders": {} + }, + "@messageAnalytics": { + "type": "String", + "placeholders": {} + }, + "@words": { + "type": "String", + "placeholders": {} + }, + "@score": { + "type": "String", + "placeholders": {} + }, + "@accuracy": { + "type": "String", + "placeholders": {} + }, + "@points": { + "type": "String", + "placeholders": {} + }, + "@noPaymentInfo": { + "type": "String", + "placeholders": {} + }, + "@updatePhoneOS": { + "type": "String", + "placeholders": {} + }, + "@wordsPerMinute": { + "type": "String", + "placeholders": {} + }, + "@tooltipInstructionsTitle": { + "type": "String", + "placeholders": {} + }, + "@tooltipInstructionsMobileBody": { + "type": "String", + "placeholders": {} + }, + "@tooltipInstructionsBrowserBody": { + "type": "String", + "placeholders": {} + }, + "@chatCapacity": { + "type": "String", + "placeholders": {} + }, + "@roomFull": { + "type": "String", + "placeholders": {} + }, + "@chatCapacityHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "@chatCapacitySetTooLow": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@chatCapacityExplanation": { + "type": "String", + "placeholders": {} + }, + "@tooManyRequest": { + "type": "String", + "placeholders": {} + }, + "@enterNumber": { + "type": "String", + "placeholders": {} + }, + "@buildTranslation": { + "type": "String", + "placeholders": {} + }, + "@practice": { + "type": "String", + "placeholders": {} + }, + "@noLanguagesSet": { + "type": "String", + "placeholders": {} + }, + "@speechToTextBody": { + "type": "String", + "placeholders": {} + }, + "@versionNotFound": { + "type": "String", + "placeholders": {} + }, + "@fetchingVersion": { + "type": "String", + "placeholders": {} + }, + "@versionFetchError": { + "type": "String", + "placeholders": {} + }, + "@versionText": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + }, + "buildNumber": { + "type": "String" + } + } + }, + "@l1TranslationBody": { + "type": "String", + "placeholders": {} + }, + "@deleteSubscriptionWarningTitle": { + "type": "String", + "placeholders": {} + }, + "@deleteSubscriptionWarningBody": { + "type": "String", + "placeholders": {} + }, + "@manageSubscription": { + "type": "String", + "placeholders": {} + }, + "@error520Title": { + "type": "String", + "placeholders": {} + }, + "@error520Desc": { + "type": "String", + "placeholders": {} + }, + "@wordsUsed": { + "type": "String", + "placeholders": {} + }, + "@level": { + "type": "String", + "placeholders": {} + }, + "@morphsUsed": { + "type": "String", + "placeholders": {} + }, + "@translationChoicesBody": { + "type": "String", + "placeholders": {} + }, + "@grammar": { + "type": "String", + "placeholders": {} + }, + "@contactHasBeenInvitedToTheChat": { + "type": "String", + "placeholders": {} + }, + "@inviteChat": { + "type": "String", + "placeholders": {} + }, + "@chatName": { + "type": "String", + "placeholders": {} + }, + "@reportContentIssueTitle": { + "type": "String", + "placeholders": {} + }, + "@feedback": { + "type": "String", + "placeholders": {} + }, + "@reportContentIssueDescription": { + "type": "String", + "placeholders": {} + }, + "@clickTheWordAgainToDeselect": { + "type": "String", + "placeholders": {} + }, + "@l2SupportNa": { + "type": "String", + "placeholders": {} + }, + "@l2SupportAlpha": { + "type": "String", + "placeholders": {} + }, + "@l2SupportBeta": { + "type": "String", + "placeholders": {} + }, + "@l2SupportFull": { + "type": "String", + "placeholders": {} + }, + "@missingVoiceTitle": { + "type": "String", + "placeholders": {} + }, + "@voiceNotAvailable": { + "type": "String", + "placeholders": {} + }, + "@openVoiceSettings": { + "type": "String", + "placeholders": {} + }, + "@playAudio": { + "type": "String", + "placeholders": {} + }, + "@stop": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSsconj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSnum": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSverb": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSaffix": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSadj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOScconj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpunct": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSadv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSaux": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSspace": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSsym": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSdet": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpron": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSadp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpropn": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSnoun": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSintj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSx": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERfem": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON2": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODimp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEqest": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTperf": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEaccnom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEobl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEact": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEbrck": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERsing": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERmasc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPEmod": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEadverbial": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEperi": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMdigit": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEnot_proper": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEcard": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEprop": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEdash": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEsemi": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEcomm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODcnd": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEacc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPARTTYPEpart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEpast": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEsup": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEcolo": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON3": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERplur": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEnpr": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEinterrogative": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEinfm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEtim": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITYneg": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEtot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEadnomial": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTprog": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODsub": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMcomplementive": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEnom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEfut": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEdat": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEpres": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERneut": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPErel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMfinalEnding": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEdem": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASEpre": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMfin": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEpos": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEquot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMger": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEpass": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEgen": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEprs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEdef": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEord": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEins": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMinf": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMaux": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMlong": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEloc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODind": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEcmp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASErelativeCase": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEexcl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON1": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDEini": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERperson": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyFOREIGNyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEvoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPEverbType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSSpass": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASEprepCase": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEnumType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEnounType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXreflex": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEpronType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDEpunctSide": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMverbForm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERgender": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODmood": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTaspect": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEpunctType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEtense": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEdegree": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEpolite": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEadvType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMnumber": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEconjType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITYpolarity": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEcase": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEdefinite": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMnumForm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEadn": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOCvoc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCMPLcmpl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVadv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODjus": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERcom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXrflx": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPARTTYPEpar": { + "type": "String", + "placeholders": {} + }, + "@grammarCopySPCspc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEpqp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXref": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEnshrt": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERdual": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMlng": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEmid": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyINTRELintRel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyINTint": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEcaus": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyUnknown": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENTevident": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMnumberPsor": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECThab": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEabl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEall": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEess": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEtra": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEequ": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEdis": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEabs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEerg": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEcau": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEben": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEtem": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEcoord": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEcons": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEabs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENTfh": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENTnfh": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODopt": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODadm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODdes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODnec": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODpot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODprp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODqot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMword": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMroman": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMletter": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEmult": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEfrac": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEsets": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPErange": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEdist": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERtri": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERpauc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERgrpa": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERgrpl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERinv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON0": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON4": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEform": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEelev": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEhumb": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEemp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEexc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPErcp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEintRelPronType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEaor": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEeps": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEprosp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMpart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMconv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMvnoun": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEantip": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEcauVoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICedir": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEinvVoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICErcpVoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOS": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDER": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOOD": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECT": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORM": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBER": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENT": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyFOREIGN": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITY": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEX": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORM": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopySPC": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPARTTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyINTREL": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyUNKNOWN": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSOR": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSS": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTimp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEvoc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEcom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEpar": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEadv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEref": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASErel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEsub": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEsup": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEaccdat": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEpre": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEsub": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEcmp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEind": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODint": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEcomm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSORsing": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSORplur": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSORdual": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITYpos": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSSyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASEnpr": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEprs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEint": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEtot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEneg": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEind": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEintrel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDEfin": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEperi": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEimp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMsup": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMadn": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMlng": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMshrt": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPEcaus": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEcau": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEdir": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEinv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICErcp": { + "type": "String", + "placeholders": {} + }, + "@other": { + "type": "String", + "placeholders": {} + }, + "@levelShort": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "@clickBestOption": { + "type": "String", + "placeholders": {} + }, + "@completeActivitiesToUnlock": { + "type": "String", + "placeholders": {} + }, + "@noCapacityLimit": { + "type": "String", + "placeholders": {} + }, + "@downloadGroupText": { + "type": "String", + "placeholders": {} + }, + "@notificationsOn": { + "type": "String", + "placeholders": {} + }, + "@notificationsOff": { + "type": "String", + "placeholders": {} + }, + "@createChatAndInviteUsers": { + "type": "String", + "placeholders": {} + }, + "@updatedNewSpaceDescription": { + "type": "String", + "placeholders": {} + }, + "@joinWithCode": { + "type": "String", + "placeholders": {} + }, + "@enterCodeToJoin": { + "type": "String", + "placeholders": {} + }, + "@updateNow": { + "type": "String", + "placeholders": {} + }, + "@updateLater": { + "type": "String", + "placeholders": {} + }, + "@constructUseWaDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseGaDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseTaDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseUnkDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorITDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnITDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncITDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnIGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorIGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncIGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorPADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnPADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncPADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIngWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorHWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncHWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnHWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseEmojiDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCollected": { + "type": "String", + "placeholders": {} + }, + "@constructUseNanDesc": { + "type": "String", + "placeholders": {} + }, + "@xpIntoLevel": { + "type": "String", + "placeholders": { + "currentXP": { + "type": "int" + }, + "maxXP": { + "type": "int" + } + } + }, + "@enableTTSToolName": { + "type": "String", + "placeholders": {} + }, + "@enableTTSToolDescription": { + "type": "String", + "placeholders": {} + }, + "@yourUsername": { + "type": "String", + "placeholders": {} + }, + "@yourEmail": { + "type": "String", + "placeholders": {} + }, + "@iWantToLearn": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterEmail": { + "type": "String", + "placeholders": {} + }, + "@myBaseLanguage": { + "type": "String", + "placeholders": {} + }, + "@meaningSectionHeader": { + "type": "String", + "placeholders": {} + }, + "@formSectionHeader": { + "type": "String", + "placeholders": {} + }, + "@writingExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@listeningExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@readingExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@meaningNotFound": { + "type": "String", + "placeholders": {} + }, + "@chooseBaseForm": { + "type": "String", + "placeholders": {} + }, + "@notTheCodeError": { + "type": "String", + "placeholders": {} + }, + "@totalXP": { + "type": "String", + "placeholders": {} + }, + "@numLemmas": { + "type": "String", + "placeholders": {} + }, + "@numLemmasUsedCorrectly": { + "type": "String", + "placeholders": {} + }, + "@numLemmasUsedIncorrectly": { + "type": "String", + "placeholders": {} + }, + "@numLemmasSmallXP": { + "type": "String", + "placeholders": {} + }, + "@numLemmasMediumXP": { + "type": "String", + "placeholders": {} + }, + "@numLemmasLargeXP": { + "type": "String", + "placeholders": {} + }, + "@numGrammarConcepts": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConcepts": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUsedCorrectly": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUsedIncorrectly": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUseCorrectlySystemGenerated": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUseIncorrectlySystemGenerated": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsSmallXP": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsMediumXP": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsLargeXP": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsHugeXP": { + "type": "String", + "placeholders": {} + }, + "@numMessagesSent": { + "type": "String", + "placeholders": {} + }, + "@numWordsTyped": { + "type": "String", + "placeholders": {} + }, + "@numCorrectChoices": { + "type": "String", + "placeholders": {} + }, + "@numIncorrectChoices": { + "type": "String", + "placeholders": {} + }, + "@commaSeparatedFile": { + "type": "String", + "placeholders": {} + }, + "@excelFile": { + "type": "String", + "placeholders": {} + }, + "@fileType": { + "type": "String", + "placeholders": {} + }, + "@download": { + "type": "String", + "placeholders": {} + }, + "@analyticsNotAvailable": { + "type": "String", + "placeholders": {} + }, + "@downloading": { + "type": "String", + "placeholders": {} + }, + "@failedFetchUserAnalytics": { + "type": "String", + "placeholders": {} + }, + "@downloadComplete": { + "type": "String", + "placeholders": {} + }, + "@whatIsTheMorphTag": { + "type": "String", + "placeholders": { + "morphologicalFeature": { + "type": "String" + }, + "wordForm": { + "type": "String" + } + } + }, + "@dataAvailable": { + "type": "String", + "placeholders": {} + }, + "@available": { + "type": "String", + "placeholders": {} + }, + "@pangeaBotIsFallible": { + "type": "String", + "placeholders": {} + }, + "@whatIsMeaning": { + "type": "String", + "placeholders": { + "lemma": { + "type": "String" + } + } + }, + "@pickAnEmoji": { + "type": "String", + "placeholders": { + "lemma": { + "type": "String" + } + } + }, + "@chooseLemmaMeaningInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@doubleClickToEdit": { + "type": "String", + "placeholders": {} + }, + "@activityPlannerTitle": { + "type": "String", + "placeholders": {} + }, + "@topicLabel": { + "type": "String", + "placeholders": {} + }, + "@topicPlaceholder": { + "type": "String", + "placeholders": {} + }, + "@modeLabel": { + "type": "String", + "placeholders": {} + }, + "@modePlaceholder": { + "type": "String", + "placeholders": {} + }, + "@learningObjectiveLabel": { + "type": "String", + "placeholders": {} + }, + "@learningObjectivePlaceholder": { + "type": "String", + "placeholders": {} + }, + "@languageOfInstructionsLabel": { + "type": "String", + "placeholders": {} + }, + "@targetLanguageLabel": { + "type": "String", + "placeholders": {} + }, + "@cefrLevelLabel": { + "type": "String", + "placeholders": {} + }, + "@generateActivitiesButton": { + "type": "String", + "placeholders": {} + }, + "@launchActivityButton": { + "type": "String", + "placeholders": {} + }, + "@image": { + "type": "String", + "placeholders": {} + }, + "@video": { + "type": "String", + "placeholders": {} + }, + "@nan": { + "type": "String", + "placeholders": {} + }, + "@activityPlannerOverviewInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@activityTitle": { + "type": "String", + "placeholders": {} + }, + "@addVocabulary": { + "type": "String", + "placeholders": {} + }, + "@instructions": { + "type": "String", + "placeholders": {} + }, + "@numberOfLearners": { + "type": "String", + "placeholders": {} + }, + "@mustBeInteger": { + "type": "String", + "placeholders": {} + }, + "@constructUsePvmDesc": { + "type": "String", + "placeholders": {} + }, + "@leaveSpaceDescription": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorMmDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncMmDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnMmDesc": { + "type": "String", + "placeholders": {} + }, + "@clickForMeaningActivity": { + "type": "String", + "placeholders": {} + }, + "@meaning": { + "type": "String", + "placeholders": {} + }, + "@chatWith": { + "type": "String", + "placeholders": { + "displayname": { + "type": "String" + } + } + }, + "@clickOnEmailLink": { + "type": "String", + "placeholders": {} + }, + "@dontForgetPassword": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectToolName": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectDescription": { + "type": "String", + "placeholders": {} + }, + "@ttsDisbledTitle": { + "type": "String", + "placeholders": {} + }, + "@ttsDisabledBody": { + "type": "String", + "placeholders": {} + }, + "@noSpaceDescriptionYet": { + "type": "String", + "placeholders": {} + }, + "@tooLargeToSend": { + "type": "String", + "placeholders": {} + }, + "@exitWithoutSaving": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectPopupTitle": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectPopupSteps": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectPopupDescription": { + "type": "String", + "placeholders": {} + }, + "@downloadGboardTitle": { + "type": "String", + "placeholders": {} + }, + "@downloadGboardSteps": { + "type": "String", + "placeholders": {} + }, + "@downloadGboardDescription": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectWarning": { + "type": "String", + "placeholders": {} + }, + "@displayName": { + "type": "String", + "placeholders": {} + }, + "@leaveRoomDescription": { + "type": "String", + "placeholders": {} + }, + "@confirmUserId": { + "type": "String", + "placeholders": {} + }, + "@startingToday": { + "type": "String", + "placeholders": {} + }, + "@oneWeekFreeTrial": { + "type": "String", + "placeholders": {} + }, + "@paidSubscriptionStarts": { + "type": "String", + "placeholders": { + "startDate": { + "type": "String" + } + } + }, + "@cancelInSubscriptionSettings": { + "type": "String", + "placeholders": {} + }, + "@cancelToAvoidCharges": { + "type": "String", + "placeholders": { + "trialEnds": { + "type": "String" + } + } + }, + "@downloadGboard": { + "type": "String", + "placeholders": {} + }, + "@autocorrectNotAvailable": { + "type": "String", + "placeholders": {} + }, + "@pleaseUpdateApp": { + "type": "String", + "placeholders": {} + }, + "@chooseEmojiInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@analyticsVocabListBody": { + "type": "String", + "placeholders": {} + }, + "@morphAnalyticsListBody": { + "type": "String", + "placeholders": {} + }, + "@knockSpaceSuccess": { + "type": "String", + "placeholders": {} + }, + "@chooseWordAudioInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@chooseMorphsInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterInt": { + "type": "String", + "placeholders": {} + }, + "@home": { + "type": "String", + "placeholders": {} + }, + "@join": { + "type": "String", + "placeholders": {} + }, + "@readingAssistanceOverviewBody": { + "type": "String", + "placeholders": {} + }, + "@levelSummaryPopupTitle": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "@resetInstructionTooltipsTitle": { + "type": "String", + "placeholders": {} + }, + "@resetInstructionTooltipsDesc": { + "type": "String", + "placeholders": {} + }, + "@selectForGrammar": { + "type": "String", + "placeholders": {} + }, + "@randomize": { + "type": "String", + "placeholders": {} + }, + "@clear": { + "type": "String", + "placeholders": {} + }, + "@makeYourOwnActivity": { + "type": "String", + "placeholders": {} + }, + "@featuredActivities": { + "type": "String", + "placeholders": {} + }, + "@save": { + "type": "String", + "placeholders": {} + }, + "@startChat": { + "type": "String", + "placeholders": {} + }, + "@translationProblem": { + "type": "String", + "placeholders": {} + }, + "@askToJoin": { + "type": "String", + "placeholders": {} + }, + "@emptyChatWarningTitle": { + "type": "String", + "placeholders": {} + }, + "@emptyChatWarningDesc": { + "type": "String", + "placeholders": {} + }, + "@areYouLikeMe": { + "type": "String", + "placeholders": {} + }, + "@tryAgainLater": { + "type": "String", + "placeholders": {} + }, + "@enterSpaceCode": { + "type": "String", + "placeholders": {} + }, + "@shareSpaceLink": { + "type": "String", + "placeholders": {} + }, + "@byUsingPangeaChat": { + "type": "String", + "placeholders": {} + }, + "@details": { + "type": "String", + "placeholders": {} + }, + "@languageLevelPreA1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA2Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB2Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC2Desc": { + "type": "String", + "placeholders": {} + }, + "@newVocab": { + "type": "String", + "placeholders": {} + }, + "@newGrammar": { + "type": "String", + "placeholders": {} + }, + "@choosePracticeMode": { + "type": "String", + "placeholders": {} + }, + "@ban": { + "type": "String", + "placeholders": {} + }, + "@unban": { + "type": "String", + "placeholders": {} + }, + "@kick": { + "type": "String", + "placeholders": {} + }, + "@lemma": { + "type": "String", + "placeholders": {} + }, + "@grammarFeature": { + "type": "String", + "placeholders": {} + }, + "@grammarTag": { + "type": "String", + "placeholders": {} + }, + "@forms": { + "type": "String", + "placeholders": {} + }, + "@exampleMessages": { + "type": "String", + "placeholders": {} + }, + "@timesUsedIndependently": { + "type": "String", + "placeholders": {} + }, + "@timesUsedWithAssistance": { + "type": "String", + "placeholders": {} + }, + "@shareInviteCode": { + "type": "String", + "placeholders": { + "code": { + "type": "String" + } + } + }, + "@leaderboard": { + "type": "String", + "placeholders": {} + }, + "@skipForNow": { + "type": "String", + "placeholders": {} + }, + "@permissions": { + "type": "String", + "placeholders": {} + }, + "@spaceChildPermission": { + "type": "String", + "placeholders": {} + }, + "@addEnvironmentOverride": { + "type": "String", + "placeholders": {} + }, + "@defaultOption": { + "type": "String", + "placeholders": {} + }, + "@deleteChatDesc": { + "type": "String", + "placeholders": {} + }, + "@deleteSpaceDesc": { + "type": "String", + "placeholders": {} + }, + "@launch": { + "type": "String", + "placeholders": {} + }, + "@searchChats": { + "type": "String", + "placeholders": {} + }, + "@maxFifty": { + "type": "String", + "placeholders": {} + }, + "@configureSpace": { + "type": "String", + "placeholders": {} + }, + "@pinMessages": { + "type": "String", + "placeholders": {} + }, + "@setJoinRules": { + "type": "String", + "placeholders": {} + }, + "@changeGeneralSettings": { + "type": "String", + "placeholders": {} + }, + "@inviteOtherUsersToRoom": { + "type": "String", + "placeholders": {} + }, + "@changeTheNameOfTheSpace": { + "type": "String", + "placeholders": {} + }, + "@changeTheDescription": { + "type": "String", + "placeholders": {} + }, + "@changeThePermissions": { + "type": "String", + "placeholders": {} + }, + "@introductions": { + "type": "String", + "placeholders": {} + }, + "@announcements": { + "type": "String", + "placeholders": {} + }, + "@activities": { + "type": "String", + "placeholders": {} + }, + "@access": { + "type": "String", + "placeholders": {} + }, + "@activitySuggestionTimeoutMessage": { + "type": "String", + "placeholders": {} + }, + "@howSpaceCanBeFound": { + "type": "String", + "placeholders": {} + }, + "@private": { + "type": "String", + "placeholders": {} + }, + "@cannotBeFoundInSearch": { + "type": "String", + "placeholders": {} + }, + "@public": { + "type": "String", + "placeholders": {} + }, + "@visibleToCommunity": { + "type": "String", + "placeholders": {} + }, + "@howSpaceCanBeJoined": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundVia": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundViaInvitation": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundViaCodeOrLink": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundViaKnock": { + "type": "String", + "placeholders": {} + }, + "@youHaveLeveledUp": { + "type": "String", + "placeholders": {} + }, + "@sendActivities": { + "type": "String", + "placeholders": {} + }, + "@groupChat": { + "type": "String", + "placeholders": {} + }, + "@directMessage": { + "type": "String", + "placeholders": {} + }, + "@newDirectMessage": { + "type": "String", + "placeholders": {} + }, + "@speakingExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@noChatsFoundHereYet": { + "type": "String", + "placeholders": {} + }, + "@duration": { + "type": "String", + "placeholders": {} + }, + "@transcriptionFailed": { + "type": "String", + "placeholders": {} + }, + "@aUserIsKnocking": { + "type": "String", + "placeholders": {} + }, + "@usersAreKnocking": { + "type": "int", + "placeholders": { + "users": { + "type": "int" + } + } + }, + "@failedToFetchTranscription": { + "type": "String", + "placeholders": {} + }, + "@deleteEmptySpaceDesc": { + "type": "String", + "placeholders": {} + }, + "@regenerate": { + "type": "String", + "placeholders": {} + }, + "@mySavedActivities": { + "type": "String", + "placeholders": {} + }, + "@noSavedActivities": { + "type": "String", + "placeholders": {} + }, + "@saveActivity": { + "type": "String", + "placeholders": {} + }, + "@failedToPlayVideo": { + "type": "String", + "placeholders": {} + }, + "@done": { + "type": "String", + "placeholders": {} + }, + "@inThisSpace": { + "type": "String", + "placeholders": {} + }, + "@myContacts": { + "type": "String", + "placeholders": {} + }, + "@inviteAllInSpace": { + "type": "String", + "placeholders": {} + }, + "@spaceParticipantsHaveBeenInvitedToTheChat": { + "type": "String", + "placeholders": {} + }, + "@numKnocking": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@numInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@saved": { + "type": "String", + "placeholders": {} + }, + "@reset": { + "type": "String", + "placeholders": {} + }, + "@errorGenerateActivityMessage": { + "type": "String", + "placeholders": {} + }, + "@errorRegenerateActivityMessage": { + "type": "String", + "placeholders": {} + }, + "@errorLaunchActivityMessage": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingActivitiesMessage": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingDefinition": { + "type": "String", + "placeholders": {} + }, + "@errorProcessAnalytics": { + "type": "String", + "placeholders": {} + }, + "@errorDownloading": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingLevelSummary": { + "type": "String", + "placeholders": {} + }, + "@errorLoadingSpaceChildren": { + "type": "String", + "placeholders": {} + }, + "@unexpectedError": { + "type": "String", + "placeholders": {} + }, + "@pleaseReload": { + "type": "String", + "placeholders": {} + }, + "@translationError": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingTranslation": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingActivity": { + "type": "String", + "placeholders": {} + }, + "@check": { + "type": "String", + "placeholders": {} + }, + "@unableToFindRoom": { + "type": "String", + "placeholders": {} + }, + "@numCompletedActivities": { + "type": "String", + "placeholders": {} + }, + "@viewingAnalytics": { + "type": "String", + "placeholders": { + "visible": { + "type": "int" + }, + "users": { + "type": "int" + } + } + }, + "@request": { + "type": "String", + "placeholders": {} + }, + "@requestAll": { + "type": "String", + "placeholders": {} + }, + "@confirmMessageUnpin": { + "type": "String", + "placeholders": {} + }, + "@createActivityPlan": { + "type": "String", + "placeholders": {} + }, + "@saveAndLaunch": { + "type": "String", + "placeholders": {} + }, + "@launchToSpace": { + "type": "String", + "placeholders": {} + }, + "@numberOfActivities": { + "type": "String", + "placeholders": {} + }, + "@maximumActivityParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@pending": { + "type": "String", + "placeholders": {} + }, + "@inactive": { + "type": "String", + "placeholders": {} + }, + "@confirmRole": { + "type": "String", + "placeholders": {} + }, + "@openRoleLabel": { + "type": "String", + "placeholders": {} + }, + "@joinedTheActivity": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "role": { + "type": "String" + } + } + }, + "@finishedTheActivity": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "@archiveToAnalytics": { + "type": "String", + "placeholders": {} + }, + "@activitySummaryError": { + "type": "String", + "placeholders": {} + }, + "@requestSummaries": { + "type": "String", + "placeholders": {} + }, + "@generatingNewActivities": { + "type": "String", + "placeholders": {} + }, + "@requestAccessTitle": { + "type": "String", + "placeholders": {} + }, + "@requestAccessDesc": { + "type": "String", + "placeholders": {} + }, + "@requestAccess": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@analyticsInactiveTitle": { + "type": "String", + "placeholders": {} + }, + "@analyticsInactiveDesc": { + "type": "String", + "placeholders": {} + }, + "@accessRequestedTitle": { + "type": "String", + "placeholders": {} + }, + "@accessRequestedDesc": { + "type": "String", + "placeholders": { + "admin": { + "type": "String" + }, + "space": { + "type": "String" + } + } + }, + "@adminRequestedAccess": { + "type": "String", + "placeholders": {} + }, + "@lastUpdated": { + "type": "String", + "placeholders": { + "time": { + "type": "String" + } + } + }, + "@activityFinishedMessage": { + "type": "String", + "placeholders": {} + }, + "@endForAll": { + "type": "String", + "placeholders": {} + }, + "@newCourse": { + "type": "String", + "placeholders": {} + }, + "@numModules": { + "type": "int", + "placeholders": { + "num": { + "type": "int" + } + } + }, + "@coursePlan": { + "type": "String", + "placeholders": {} + }, + "@editCourseLater": { + "type": "String", + "placeholders": {} + }, + "@createCourse": { + "type": "String", + "placeholders": {} + }, + "@stats": { + "type": "String", + "placeholders": {} + }, + "@createGroupChat": { + "type": "String", + "placeholders": {} + }, + "@editCourse": { + "type": "String", + "placeholders": {} + }, + "@inviteDesc": { + "type": "String", + "placeholders": {} + }, + "@editCourseDesc": { + "type": "String", + "placeholders": {} + }, + "@permissionsDesc": { + "type": "String", + "placeholders": {} + }, + "@accessDesc": { + "type": "String", + "placeholders": {} + }, + "@createGroupChatDesc": { + "type": "String", + "placeholders": {} + }, + "@deleteDesc": { + "type": "String", + "placeholders": {} + }, + "@noCourseFound": { + "type": "String", + "placeholders": {} + }, + "@additionalParticipants": { + "type": "int", + "placeholders": { + "num": { + "type": "int" + } + } + }, + "@directMessages": { + "type": "String", + "placeholders": {} + }, + "@whatNow": { + "type": "String", + "placeholders": {} + }, + "@chooseNextActivity": { + "type": "String", + "placeholders": {} + }, + "@letsGo": { + "type": "String", + "placeholders": {} + }, + "@chooseRole": { + "type": "String", + "placeholders": {} + }, + "@chooseRoleToParticipate": { + "type": "String", + "placeholders": {} + }, + "@waitingToFillRole": { + "type": "int", + "placeholders": { + "num": { + "type": "int" + } + } + }, + "@pingParticipants": { + "type": "String", + "placeholders": {} + }, + "@playWithBot": { + "type": "String", + "placeholders": {} + }, + "@inviteFriends": { + "type": "String", + "placeholders": {} + }, + "@waitNotDone": { + "type": "String", + "placeholders": {} + }, + "@waitingForOthersToFinish": { + "type": "String", + "placeholders": {} + }, + "@generatingSummary": { + "type": "String", + "placeholders": {} + }, + "@findCourse": { + "type": "String", + "placeholders": {} + }, + "@pingParticipantsNotification": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + }, + "room": { + "type": "String" + } + } + }, + "@course": { + "type": "String", + "placeholders": {} + }, + "@courses": { + "type": "String", + "placeholders": {} + }, + "@courseName": { + "type": "String", + "placeholders": {} + }, + "@createNewCourse": { + "type": "String", + "placeholders": {} + }, + "@goToCourse": { + "type": "String", + "placeholders": { + "course": {} + } + }, + "@activityComplete": { + "type": "String", + "placeholders": {} + }, + "@startNewSession": { + "type": "String", + "placeholders": {} + }, + "@joinOpenSession": { + "type": "String", + "placeholders": {} + }, + "@less": { + "type": "String", + "placeholders": {} + }, + "@activityNotFound": { + "type": "String", + "placeholders": {} + }, + "@levelUp": { + "type": "String", + "placeholders": {} + }, + "@myActivities": { + "type": "String", + "placeholders": {} + }, + "@openToJoin": { + "type": "String", + "placeholders": {} + }, + "@results": { + "type": "String", + "placeholders": {} + }, + "@activityDone": { + "type": "String", + "placeholders": {} + }, + "@promoCodeInfo": { + "type": "String", + "placeholders": {} + }, + "@editsComingSoon": { + "type": "String", + "placeholders": {} + }, + "@editing": { + "type": "String", + "placeholders": {} + }, + "@activityNeedsOneMember": { + "type": "String", + "placeholders": {} + }, + "@activityNeedsMembers": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } + } + }, + "@inviteFriendsToCourse": { + "type": "String", + "placeholders": {} + }, + "@subscribeToUnlockActivitySummaries": { + "type": "String", + "placeholders": {} + }, + "@subscribeToUnlockDefinitions": { + "type": "String", + "placeholders": {} + }, + "@subscribeToUnlockTranscriptions": { + "type": "String", + "placeholders": {} + }, + "@pingSent": { + "type": "String", + "placeholders": {} + }, + "@courseTitle": { + "type": "String", + "placeholders": {} + }, + "@courseDesc": { + "type": "String", + "placeholders": {} + }, + "@courseSavedSuccessfully": { + "type": "String", + "placeholders": {} + }, + "@addCoursePlan": { + "type": "String", + "placeholders": {} + }, + "@activityStatsButtonInstruction": { + "type": "String", + "placeholders": {} + }, + "@loginToAccount": { + "type": "String", + "placeholders": {} + }, + "@appDescription": { + "type": "String", + "placeholders": {} + }, + "@languages": { + "type": "String", + "placeholders": {} + }, + "@chooseLanguage": { + "type": "String", + "placeholders": {} + }, + "@planTrip": { + "type": "String", + "placeholders": {} + }, + "@howAreYouTraveling": { + "type": "String", + "placeholders": {} + }, + "@unlockPrivateTrip": { + "type": "String", + "placeholders": {} + }, + "@joinPublicTrip": { + "type": "String", + "placeholders": {} + }, + "@startOwnTrip": { + "type": "String", + "placeholders": {} + }, + "@tripPlanDesc": { + "type": "String", + "placeholders": {} + }, + "@unlockPrivateTripTitle": { + "type": "String", + "placeholders": {} + }, + "@browsePublicTrips": { + "type": "String", + "placeholders": {} + }, + "@startOwnTripTitle": { + "type": "String", + "placeholders": {} + }, + "@courseCode": { + "type": "String", + "placeholders": {} + }, + "@courseCodeHint": { + "type": "String", + "placeholders": {} + }, + "@unlockMyTrip": { + "type": "String", + "placeholders": {} + }, + "@signupOption": { + "type": "String", + "placeholders": {} + }, + "@withApple": { + "type": "String", + "placeholders": {} + }, + "@withGoogle": { + "type": "String", + "placeholders": {} + }, + "@withEmail": { + "type": "String", + "placeholders": {} + }, + "@createAccount": { + "type": "String", + "placeholders": {} + }, + "@loginWithEmail": { + "type": "String", + "placeholders": {} + }, + "@usernameOrEmail": { + "type": "String", + "placeholders": {} + }, + "@email": { + "type": "String", + "placeholders": {} + }, + "@forgotPassword": { + "type": "String", + "placeholders": {} + }, + "@endActivity": { + "type": "String", + "placeholders": {} + }, + "@allLanguages": { + "type": "String", + "placeholders": {} + }, + "@chatListTooltip": { + "type": "String", + "placeholders": {} + }, + "@directMessageBotTitle": { + "type": "String", + "placeholders": {} + }, + "@feedbackTitle": { + "type": "String", + "placeholders": {} + }, + "@feedbackHint": { + "type": "String", + "placeholders": {} + }, + "@feedbackButton": { + "type": "String", + "placeholders": {} + }, + "@directMessageBotDesc": { + "type": "String", + "placeholders": {} + }, + "@inviteYourFriends": { + "type": "String", + "placeholders": {} + }, + "@playWithAI": { + "type": "String", + "placeholders": {} + }, + "@courseStartDesc": { + "type": "String", + "placeholders": {} + }, + "feedbackRespDesc": "Kehren Sie morgen zurück, um Updates zur Aktivität zu erhalten.", + "activityDropdownDesc": "Wenn Sie mit dieser Aktivität fertig sind, klicken Sie unten", + "languageMismatchTitle": "Sprachinkonsistenz", + "languageMismatchDesc": "Ihre Zielsprache stimmt nicht mit der Sprache dieser Aktivität überein. Möchten Sie Ihre Zielsprache aktualisieren?", + "reportWordIssueTooltip": "Meldung eines Wortinformationsproblems", + "tokenInfoFeedbackDialogTitle": "Feedback zu Wortinformationen", + "noPublicCoursesFound": "Keine öffentlichen Kurse gefunden. Möchten Sie einen erstellen?", + "noCourseTemplatesFound": "Wir konnten keine Kurse für Ihre Zielsprache finden. Sie können in der Zwischenzeit mit Pangea Bot chatten und später nach weiteren Kursen suchen.", + "botActivityJoinFailMessage": "Pangea Bot benötigt etwas Zeit, um zu antworten. Bitte versuchen Sie es später erneut oder laden Sie einen Freund ein.", + "unsubscribedResponseError": "Diese Funktion erfordert ein Abonnement", + "leaveDesc": "Diesen Raum und alle Chats darin verlassen", + "selectAll": "Alle auswählen", + "deselectAll": "Alle abwählen", + "@feedbackRespDesc": { + "type": "String", + "placeholders": {} + }, + "@activityDropdownDesc": { + "type": "String", + "placeholders": {} + }, + "@languageMismatchTitle": { + "type": "String", + "placeholders": {} + }, + "@languageMismatchDesc": { + "type": "String", + "placeholders": {} + }, + "@reportWordIssueTooltip": { + "type": "String", + "placeholders": {} + }, + "@tokenInfoFeedbackDialogTitle": { + "type": "String", + "placeholders": {} + }, + "@noPublicCoursesFound": { + "type": "String", + "placeholders": {} + }, + "@noCourseTemplatesFound": { + "type": "String", + "placeholders": {} + }, + "@botActivityJoinFailMessage": { + "type": "String", + "placeholders": {} + }, + "@unsubscribedResponseError": { + "type": "String", + "placeholders": {} + }, + "@leaveDesc": { + "type": "String", + "placeholders": {} + }, + "@selectAll": { + "type": "String", + "placeholders": {} + }, + "@deselectAll": { + "type": "String", + "placeholders": {} + }, + "startOwn": "Eigene starten", + "joinCourseDesc": "Jeder Kurs besteht aus 8-10 sequenziellen Themen mit einer Vielzahl von aufgabenbasierten Sprachlernaktivitäten.", + "newMessageInPangeaChat": "🗨️ Neue Nachricht im Pangea-Chat", + "shareCourse": "Kurs teilen", + "addCourse": "Einen Kurs hinzufügen", + "joinPublicCourse": "Öffentlichen Kurs beitreten", + "vocabLevelsDesc": "Hier kommen die Vokabeln hin, sobald du sie aufgestuft hast!", + "highlightVocabTooltip": "Hebe die Zielvokabeln unten hervor, indem du sie im Chat sendest oder mit ihnen übst.", + "@startOwn": { + "type": "String", + "placeholders": {} + }, + "@joinCourseDesc": { + "type": "String", + "placeholders": {} + }, + "@newMessageInPangeaChat": { + "type": "String", + "placeholders": {} + }, + "@shareCourse": { + "type": "String", + "placeholders": {} + }, + "@addCourse": { + "type": "String", + "placeholders": {} + }, + "@joinPublicCourse": { + "type": "String", + "placeholders": {} + }, + "@vocabLevelsDesc": { + "type": "String", + "placeholders": {} + }, + "emptyChatSearch": "Keine DMs oder Chats gefunden. Stellen Sie sicher, dass Ihre Suche korrekt geschrieben ist.", + "activityAnalyticsTooltipBody": "Dies sind Ihre gespeicherten Aktivitäten zur Überprüfung und Übung.", + "numSavedActivities": "Anzahl der gespeicherten Aktivitäten", + "saveActivityTitle": "Aktivität speichern", + "saveActivityDesc": "Gut gemacht! Speichern Sie diese Aktivität für eine spätere Überprüfung und Übung.", + "levelInfoTooltip": "Hier können Sie alle Punkte sehen, die Sie verdient haben und wie!", + "alreadyInCourseWithID": "Sie sind bereits in einem Kurs mit diesem Plan. Möchten Sie einen Kurs mit demselben Plan erstellen oder zum bestehenden Kurs gehen?", + "goToExistingCourse": "Zum bestehenden Kurs gehen", + "emojiView": "Emoji-Ansicht", + "feedbackDialogDesc": "Ich mache auch Fehler! Gibt es etwas, das mir helfen kann, mich zu verbessern?", + "contactHasBeenInvitedToTheCourse": "Kontakt wurde zum Kurs eingeladen", + "activityStatsButtonTooltip": "Aktivitätsinformationen", + "allow": "Erlauben", + "deny": "Ablehnen", + "enabledRenewal": "Abonnementverlängerung aktivieren", + "subscriptionEndsOn": "Abonnement endet am", + "subscriptionRenewsOn": "Abonnement erneuert sich am", + "waitForSubscriptionChanges": "Änderungen an Ihrem Abonnement können einen Moment dauern, um in der App angezeigt zu werden.", + "subscribeReadingAssistance": "Abonnieren, um Nachrichtentools freizuschalten", + "aceDisplayName": "Achinese", + "achDisplayName": "Acoli", + "afDisplayName": "Afrikaans", + "akDisplayName": "Akan", + "alzDisplayName": "Alur", + "amDisplayName": "Amharisch", + "arDisplayName": "Arabisch", + "asDisplayName": "Assamesisch", + "awaDisplayName": "Awadhi", + "ayDisplayName": "Aymara", + "azDisplayName": "Aserbaidschanisch", + "baDisplayName": "Baskirisch", + "banDisplayName": "Balinésisch", + "bbcDisplayName": "Batak Toba", + "beDisplayName": "Weißrussisch", + "bemDisplayName": "Bemba", + "bewDisplayName": "Betawi", + "bgDisplayName": "Bulgarisch", + "bhoDisplayName": "Bhojpuri", + "bikDisplayName": "Bikol", + "bmDisplayName": "Bambara", + "bnDisplayName": "Bengalisch", + "bnBDDisplayName": "Bengalisch (Bangladesch)", + "bnINDisplayName": "Bengalisch (Indien)", + "brDisplayName": "Bretonisch", + "bsDisplayName": "Bosnisch", + "btsDisplayName": "Batak Simalungun", + "btxDisplayName": "Batak Karo", + "buaDisplayName": "Burjatisch", + "caDisplayName": "Katalanisch", + "cebDisplayName": "Cebuano", + "cggDisplayName": "Chiga", + "chmDisplayName": "Mari", + "ckbDisplayName": "Zentral-Kurdisch", + "cnhDisplayName": "Hakha Chin", + "coDisplayName": "Korsisch", + "crhDisplayName": "Krim-Türkisch", + "crsDisplayName": "Seselwa-Kreolisch-Französisch", + "csDisplayName": "Tschechisch", + "cvDisplayName": "Tschuwasch", + "cyDisplayName": "Walisisch", + "daDisplayName": "Dänisch", + "deDisplayName": "Deutsch", + "dinDisplayName": "Dinka", + "doiDisplayName": "Dogri", + "dovDisplayName": "Dombe", + "dzDisplayName": "Dzongkha", + "eeDisplayName": "Ewe", + "enDisplayName": "Englisch", + "enAUDisplayName": "Englisch (Australien)", + "enGBDisplayName": "Englisch (Vereinigtes Königreich)", + "enINDisplayName": "Englisch (Indien)", + "enUSDisplayName": "Englisch (USA)", + "eoDisplayName": "Esperanto", + "esDisplayName": "Spanisch", + "esESDisplayName": "Spanisch (Spanien)", + "esMXDisplayName": "Spanisch (Mexiko)", + "euDisplayName": "Baskisch", + "faDisplayName": "Persisch", + "ffDisplayName": "Fulah", + "fiDisplayName": "Finnisch", + "filDisplayName": "Filipino", + "fjDisplayName": "Fidschian", + "foDisplayName": "Färöisch", + "frDisplayName": "Französisch", + "frCADisplayName": "Französisch (Kanada)", + "frFRDisplayName": "Französisch (Frankreich)", + "fyDisplayName": "Westfriesisch", + "gaDisplayName": "Irisch", + "gaaDisplayName": "Ga", + "gdDisplayName": "Schottisch-Gälisch", + "glDisplayName": "Galizisch", + "gnDisplayName": "Guaraní", + "gomDisplayName": "Goan Konkani", + "guDisplayName": "Gujarati", + "haDisplayName": "Hausa", + "hawDisplayName": "Hawaiian", + "heDisplayName": "Hebräisch", + "hiDisplayName": "Hindi", + "hilDisplayName": "Hiligaynon", + "hmnDisplayName": "Hmong", + "hneDisplayName": "Chhattisgarhi", + "hrDisplayName": "Kroatisch", + "hrxDisplayName": "Hunsrik", + "htDisplayName": "Haitianisch Kreolisch", + "huDisplayName": "Ungarisch", + "hyDisplayName": "Armenisch", + "idDisplayName": "Indonesisch", + "igDisplayName": "Igbo", + "iloDisplayName": "Iloko", + "isDisplayName": "Isländisch", + "itDisplayName": "Italienisch", + "jaDisplayName": "Japanisch", + "jvDisplayName": "Javanesisch", + "kaDisplayName": "Georgisch", + "kkDisplayName": "Kasachisch", + "kmDisplayName": "Khmer", + "knDisplayName": "Kannada", + "koDisplayName": "Koreanisch", + "kokDisplayName": "Konkani", + "kriDisplayName": "Krio", + "ksDisplayName": "Kaschmirisch", + "ktuDisplayName": "Kituba (Demokratische Republik Kongo)", + "kuDisplayName": "Kurdisch", + "kyDisplayName": "Kirgisisch", + "laDisplayName": "Latein", + "lbDisplayName": "Luxemburgisch", + "lgDisplayName": "Ganda", + "liDisplayName": "Limburgisch", + "lijDisplayName": "Ligurisch", + "lmoDisplayName": "Lombardisch", + "lnDisplayName": "Lingala", + "loDisplayName": "Lao", + "ltDisplayName": "Litauisch", + "ltgDisplayName": "Latgalisch", + "luoDisplayName": "Luo (Kenia und Tansania)", + "lusDisplayName": "Mizo", + "lvDisplayName": "Lettisch", + "maiDisplayName": "Maithili", + "makDisplayName": "Makasar", + "mgDisplayName": "Malagasy", + "miDisplayName": "Māori", + "minDisplayName": "Minangkabau", + "mkDisplayName": "Mazedonisch", + "mlDisplayName": "Malayalam", + "mnDisplayName": "Mongolisch", + "mniDisplayName": "Manipuri", + "mrDisplayName": "Marathi", + "msDisplayName": "Malaiisch", + "msArabDisplayName": "Malaiisch (Arabisch)", + "msMYDisplayName": "Malaiisch (Malaysia)", + "mtDisplayName": "Maltesisch", + "mwrDisplayName": "Marwari", + "myDisplayName": "Burmesisch", + "nanDisplayName": "Min Nan", + "nbDisplayName": "Norwegisch (Bokmål)", + "neDisplayName": "Nepali", + "newDisplayName": "Newari", + "nlDisplayName": "Niederländisch", + "nlBEDisplayName": "Flämisch", + "noDisplayName": "Norwegisch", + "nrDisplayName": "Süd-Ndebele", + "nsoDisplayName": "Nord-Sotho", + "nusDisplayName": "Nuer", + "nyDisplayName": "Nyanja", + "ocDisplayName": "Okzitanisch", + "omDisplayName": "Oromo", + "orDisplayName": "Odia", + "paDisplayName": "Punjabi", + "paArabDisplayName": "Punjabi (Shahmukhi)", + "paINDisplayName": "Punjabi (Gurmukhi)", + "pagDisplayName": "Pangasinan", + "pamDisplayName": "Pampanga", + "papDisplayName": "Papiamento", + "plDisplayName": "Polnisch", + "psDisplayName": "Paschtu", + "ptDisplayName": "Portugiesisch", + "ptBRDisplayName": "Portugiesisch (Brasilien)", + "ptPTDisplayName": "Portugiesisch (Portugal)", + "quDisplayName": "Quechua", + "rajDisplayName": "Rajasthani", + "rnDisplayName": "Rundi", + "roDisplayName": "Rumänisch", + "roMDDisplayName": "Moldawisch", + "romDisplayName": "Romanes", + "ruDisplayName": "Russisch", + "rwDisplayName": "Kinyarwanda", + "saDisplayName": "Sanskrit", + "satDisplayName": "Santali", + "scnDisplayName": "Sizilianisch", + "sdDisplayName": "Sindhi", + "sgDisplayName": "Sango", + "shnDisplayName": "Shan", + "siDisplayName": "Singhalesisch", + "skDisplayName": "Slowakisch", + "slDisplayName": "Slowenisch", + "smDisplayName": "Samoanisch", + "snDisplayName": "Shona", + "soDisplayName": "Somalisch", + "sqDisplayName": "Albanisch", + "srDisplayName": "Serbisch", + "srMEDisplayName": "Montenegrinisch", + "ssDisplayName": "Swati", + "stDisplayName": "Süd-Sotho", + "suDisplayName": "Sundanesisch", + "svDisplayName": "Schwedisch", + "swDisplayName": "Swahili", + "szlDisplayName": "Schlesisch", + "taDisplayName": "Tamil", + "teDisplayName": "Telugu", + "tetDisplayName": "Tetum", + "tgDisplayName": "Tadschikisch", + "thDisplayName": "Thailändisch", + "tiDisplayName": "Tigrinya", + "tkDisplayName": "Turkmenisch", + "tlDisplayName": "Tagalog", + "tnDisplayName": "Tswana", + "trDisplayName": "Türkisch", + "tsDisplayName": "Tsonga", + "ttDisplayName": "Tatarisch", + "ugDisplayName": "Uigur", + "ukDisplayName": "Ukrainisch", + "urDisplayName": "Urdu", + "urINDisplayName": "Urdu (Indien)", + "urPKDisplayName": "Urdu (Pakistan)", + "uzDisplayName": "Usbekisch", + "viDisplayName": "Vietnamesisch", + "wuuDisplayName": "Wu", + "xhDisplayName": "Xhosa", + "yiDisplayName": "Jiddisch", + "yoDisplayName": "Yoruba", + "yuaDisplayName": "Yucateco", + "yueDisplayName": "Kantonesisch", + "yueCNDisplayName": "Kantonesisch (China)", + "yueHKDisplayName": "Kantonesisch (Hongkong)", + "zhDisplayName": "Chinesisch", + "zhCNDisplayName": "Chinesisch (Vereinfacht)", + "zhTWDisplayName": "Chinesisch (Traditionell)", + "zuDisplayName": "Zulu", + "@emptyChatSearch": { + "type": "String", + "placeholders": {} + }, + "@activityAnalyticsTooltipBody": { + "type": "String", + "placeholders": {} + }, + "@numSavedActivities": { + "type": "String", + "placeholders": {} + }, + "@saveActivityTitle": { + "type": "String", + "placeholders": {} + }, + "@saveActivityDesc": { + "type": "String", + "placeholders": {} + }, + "@levelInfoTooltip": { + "type": "String", + "placeholders": {} + }, + "@alreadyInCourseWithID": { + "type": "String", + "placeholders": {} + }, + "@goToExistingCourse": { + "type": "String", + "placeholders": {} + }, + "@emojiView": { + "type": "String", + "placeholders": {} + }, + "@feedbackDialogDesc": { + "type": "String", + "placeholders": {} + }, + "@contactHasBeenInvitedToTheCourse": { + "type": "String", + "placeholders": {} + }, + "@activityStatsButtonTooltip": { + "type": "String", + "placeholders": {} + }, + "@allow": { + "type": "String", + "placeholders": {} + }, + "@deny": { + "type": "String", + "placeholders": {} + }, + "@enabledRenewal": { + "type": "String", + "placeholders": {} + }, + "@subscriptionEndsOn": { + "type": "String", + "placeholders": {} + }, + "@subscriptionRenewsOn": { + "type": "String", + "placeholders": {} + }, + "@waitForSubscriptionChanges": { + "type": "String", + "placeholders": {} + }, + "@subscribeReadingAssistance": { + "type": "String", + "placeholders": {} + }, + "@aceDisplayName": { + "type": "String", + "placeholders": {} + }, + "@achDisplayName": { + "type": "String", + "placeholders": {} + }, + "@afDisplayName": { + "type": "String", + "placeholders": {} + }, + "@akDisplayName": { + "type": "String", + "placeholders": {} + }, + "@alzDisplayName": { + "type": "String", + "placeholders": {} + }, + "@amDisplayName": { + "type": "String", + "placeholders": {} + }, + "@arDisplayName": { + "type": "String", + "placeholders": {} + }, + "@asDisplayName": { + "type": "String", + "placeholders": {} + }, + "@awaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ayDisplayName": { + "type": "String", + "placeholders": {} + }, + "@azDisplayName": { + "type": "String", + "placeholders": {} + }, + "@baDisplayName": { + "type": "String", + "placeholders": {} + }, + "@banDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bbcDisplayName": { + "type": "String", + "placeholders": {} + }, + "@beDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bemDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bewDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bhoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bikDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bmDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnBDDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@brDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@btsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@btxDisplayName": { + "type": "String", + "placeholders": {} + }, + "@buaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@caDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cebDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cggDisplayName": { + "type": "String", + "placeholders": {} + }, + "@chmDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ckbDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cnhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@coDisplayName": { + "type": "String", + "placeholders": {} + }, + "@crhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@crsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@csDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cvDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@daDisplayName": { + "type": "String", + "placeholders": {} + }, + "@deDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dinDisplayName": { + "type": "String", + "placeholders": {} + }, + "@doiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dovDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dzDisplayName": { + "type": "String", + "placeholders": {} + }, + "@eeDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enAUDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enGBDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enUSDisplayName": { + "type": "String", + "placeholders": {} + }, + "@eoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esESDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esMXDisplayName": { + "type": "String", + "placeholders": {} + }, + "@euDisplayName": { + "type": "String", + "placeholders": {} + }, + "@faDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ffDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@filDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fjDisplayName": { + "type": "String", + "placeholders": {} + }, + "@foDisplayName": { + "type": "String", + "placeholders": {} + }, + "@frDisplayName": { + "type": "String", + "placeholders": {} + }, + "@frCADisplayName": { + "type": "String", + "placeholders": {} + }, + "@frFRDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gaaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gdDisplayName": { + "type": "String", + "placeholders": {} + }, + "@glDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gomDisplayName": { + "type": "String", + "placeholders": {} + }, + "@guDisplayName": { + "type": "String", + "placeholders": {} + }, + "@haDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hawDisplayName": { + "type": "String", + "placeholders": {} + }, + "@heDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hilDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hmnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hneDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hrxDisplayName": { + "type": "String", + "placeholders": {} + }, + "@htDisplayName": { + "type": "String", + "placeholders": {} + }, + "@huDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@idDisplayName": { + "type": "String", + "placeholders": {} + }, + "@igDisplayName": { + "type": "String", + "placeholders": {} + }, + "@iloDisplayName": { + "type": "String", + "placeholders": {} + }, + "@isDisplayName": { + "type": "String", + "placeholders": {} + }, + "@itDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jvDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kmDisplayName": { + "type": "String", + "placeholders": {} + }, + "@knDisplayName": { + "type": "String", + "placeholders": {} + }, + "@koDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kokDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kriDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ksDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ktuDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kuDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@laDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lbDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@liDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lijDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lmoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@loDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ltDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ltgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@luoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lusDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lvDisplayName": { + "type": "String", + "placeholders": {} + }, + "@maiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@makDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@miDisplayName": { + "type": "String", + "placeholders": {} + }, + "@minDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mniDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msArabDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msMYDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mtDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mwrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@myDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nanDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nbDisplayName": { + "type": "String", + "placeholders": {} + }, + "@neDisplayName": { + "type": "String", + "placeholders": {} + }, + "@newDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nlBEDisplayName": { + "type": "String", + "placeholders": {} + }, + "@noDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nsoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nusDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ocDisplayName": { + "type": "String", + "placeholders": {} + }, + "@omDisplayName": { + "type": "String", + "placeholders": {} + }, + "@orDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paArabDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pagDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pamDisplayName": { + "type": "String", + "placeholders": {} + }, + "@papDisplayName": { + "type": "String", + "placeholders": {} + }, + "@plDisplayName": { + "type": "String", + "placeholders": {} + }, + "@psDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptBRDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptPTDisplayName": { + "type": "String", + "placeholders": {} + }, + "@quDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rajDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@roDisplayName": { + "type": "String", + "placeholders": {} + }, + "@roMDDisplayName": { + "type": "String", + "placeholders": {} + }, + "@romDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ruDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rwDisplayName": { + "type": "String", + "placeholders": {} + }, + "@saDisplayName": { + "type": "String", + "placeholders": {} + }, + "@satDisplayName": { + "type": "String", + "placeholders": {} + }, + "@scnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sdDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@shnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@siDisplayName": { + "type": "String", + "placeholders": {} + }, + "@skDisplayName": { + "type": "String", + "placeholders": {} + }, + "@slDisplayName": { + "type": "String", + "placeholders": {} + }, + "@smDisplayName": { + "type": "String", + "placeholders": {} + }, + "@snDisplayName": { + "type": "String", + "placeholders": {} + }, + "@soDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sqDisplayName": { + "type": "String", + "placeholders": {} + }, + "@srDisplayName": { + "type": "String", + "placeholders": {} + }, + "@srMEDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ssDisplayName": { + "type": "String", + "placeholders": {} + }, + "@stDisplayName": { + "type": "String", + "placeholders": {} + }, + "@suDisplayName": { + "type": "String", + "placeholders": {} + }, + "@svDisplayName": { + "type": "String", + "placeholders": {} + }, + "@swDisplayName": { + "type": "String", + "placeholders": {} + }, + "@szlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@taDisplayName": { + "type": "String", + "placeholders": {} + }, + "@teDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tetDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@thDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@trDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ttDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ugDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ukDisplayName": { + "type": "String", + "placeholders": {} + }, + "@urDisplayName": { + "type": "String", + "placeholders": {} + }, + "@urINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@urPKDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uzDisplayName": { + "type": "String", + "placeholders": {} + }, + "@viDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wuuDisplayName": { + "type": "String", + "placeholders": {} + }, + "@xhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yuaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yueDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yueCNDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yueHKDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zhCNDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zhTWDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zuDisplayName": { + "type": "String", + "placeholders": {} + }, + "teacherModeTitle": "Lehrermodus", + "teacherModeDesc": "Umschalten, um alle Themen und Aktivitäten freizuschalten. Nur für Kursadministratoren.", + "@teacherModeTitle": { + "type": "String", + "placeholders": {} + }, + "@teacherModeDesc": { + "type": "String", + "placeholders": {} + }, + "failedToLoadFeedback": "Fehler beim Laden des Feedbacks.", + "unreadPlus": "99+", + "noSavedActivitiesYet": "Aktivitäten erscheinen hier, sobald sie abgeschlossen und gespeichert sind.", + "@failedToLoadFeedback": { + "type": "String", + "placeholders": {} + }, + "@unreadPlus": { + "type": "String", + "placeholders": {} + }, + "@noSavedActivitiesYet": { + "type": "String", + "placeholders": {} + }, + "changeCourse": "Kurs ändern", + "changeCourseDesc": "Hier können Sie den Kursplan dieses Kurses ändern.", + "@changeCourse": { + "type": "String", + "placeholders": {} + }, + "@changeCourseDesc": { + "type": "String", + "placeholders": {} + }, + "practiceActivityCompleted": "Übungsaktivität abgeschlossen", + "@practiceActivityCompleted": { + "type": "String", + "placeholders": {} + }, + "introChatTitle": "Erstelle Einführungs-Chat", + "introChatDesc": "Jeder im Raum kann posten.", + "announcementsChatTitle": "Ankündigungen-Chat", + "announcementsChatDesc": "Nur der Raum-Administrator kann posten.", + "@introChatTitle": { + "type": "String", + "placeholders": {} + }, + "@introChatDesc": { + "type": "String", + "placeholders": {} + }, + "@announcementsChatTitle": { + "type": "String", + "placeholders": {} + }, + "@announcementsChatDesc": { + "type": "String", + "placeholders": {} + }, + "notStartedActivitiesTitle": "Offene Sitzungen ({num})", + "inProgressActivitiesTitle": "Gerade jetzt ({num})", + "completedActivitiesTitle": "Fertig ({num})", + "pickDifferentActivity": "Wählen Sie eine andere Aktivität", + "inOngoingActivity": "Sie haben eine laufende Aktivität!", + "@notStartedActivitiesTitle": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } + } + }, + "@inProgressActivitiesTitle": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } + } + }, + "@completedActivitiesTitle": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } + } + }, + "@pickDifferentActivity": { + "type": "String", + "placeholders": {} + }, + "messageLanguageMismatchMessage": "Ihre Zielsprache stimmt nicht mit dieser Nachricht überein. Möchten Sie Ihre Zielsprache aktualisieren?", + "@messageLanguageMismatchMessage": { + "type": "String", + "placeholders": {} + }, + "courseParticipantTooltip": "Das sind alle in diesem Kurs. Klicken Sie auf das Avatar eines Benutzers und \"Gespräch starten\", um eine DM zu senden.", + "@courseParticipantTooltip": { + "type": "String", + "placeholders": {} + }, + "blockLemmaConfirmation": "Dieses Vokabelwort wird dauerhaft aus Ihren Analysen entfernt", + "woman": "Frau", + "man": "Mann", + "otherGender": "Sonstiges", + "unselectedGender": "Wählen Sie eine Geschlechtsoption", + "gender": "Geschlecht", + "chatParticipantTooltip": "Das sind alle in diesem Chat. Klicken Sie auf das Avatar eines Benutzers und \"Gespräch starten\", um eine DM zu senden.", + "@blockLemmaConfirmation": { + "type": "String", + "placeholders": {} + }, + "@woman": { + "type": "String", + "placeholders": {} + }, + "@man": { + "type": "String", + "placeholders": {} + }, + "@otherGender": { + "type": "String", + "placeholders": {} + }, + "@unselectedGender": { + "type": "String", + "placeholders": {} + }, + "@gender": { + "type": "String", + "placeholders": {} + }, + "@chatParticipantTooltip": { + "type": "String", + "placeholders": {} + }, + "@inOngoingActivity": { + "type": "String", + "placeholders": {} + }, + "modeDisabled": "Lernwerkzeuge sind für Nachrichten, die nicht in deiner Zielsprache sind, deaktiviert.", + "vocabEmoji": "Vokabel-Emoji", + "@modeDisabled": { + "type": "String", + "placeholders": {} + }, + "@vocabEmoji": { + "type": "String", + "placeholders": {} + }, + "requestRegeneration": "Anforderung der Regeneration", + "optionalRegenerateReason": "(Optional) Grund", + "@requestRegeneration": { + "type": "String", + "placeholders": {} + }, + "@optionalRegenerateReason": { + "type": "String", + "placeholders": {} + }, + "emojiSelectedSnackbar": "Sie haben das Emoji für {lemma} festgelegt! Wir werden dieses Emoji verwenden, um das Wort in zukünftigen Übungsaktivitäten darzustellen.", + "@emojiSelectedSnackbar": { + "type": "String", + "placeholders": { + "lemma": { + "type": "String" + } + } + }, + "ssoDialogTitle": "Warten auf den Abschluss der Anmeldung", + "ssoDialogDesc": "Wir haben einen neuen Tab geöffnet, damit Sie sich sicher anmelden können.", + "ssoDialogHelpText": "🤔 Wenn Sie den neuen Tab nicht gesehen haben, überprüfen Sie bitte Ihren Popup-Blocker.", + "@ssoDialogTitle": { + "type": "String", + "placeholders": {} + }, + "@ssoDialogDesc": { + "type": "String", + "placeholders": {} + }, + "@ssoDialogHelpText": { + "type": "String", + "placeholders": {} + }, + "disableLanguageToolsTitle": "Spracheinstellungen deaktivieren", + "disableLanguageToolsDesc": "Möchten Sie die automatische Sprachunterstützung deaktivieren?", + "@disableLanguageToolsTitle": { + "type": "String", + "placeholders": {} + }, + "@disableLanguageToolsDesc": { + "type": "String", + "placeholders": {} + }, + "recordingPermissionDenied": "Zugriff verweigert. Aktivieren Sie die Aufnahmeberechtigungen, um Sprachnachrichten aufzunehmen.", + "genericWebRecordingError": "Etwas ist schiefgelaufen. Wir empfehlen, den Chrome-Browser zu verwenden, wenn Sie Nachrichten aufnehmen.", + "@recordingPermissionDenied": { + "type": "String", + "placeholders": {} + }, + "@genericWebRecordingError": { + "type": "String", + "placeholders": {} + }, + "screenSizeWarning": "Für das beste Erlebnis mit dieser Anwendung erweitern Sie bitte Ihre Bildschirmgröße.", + "@screenSizeWarning": { + "type": "String", + "placeholders": {} + }, + "activitiesToUnlockTopicTitle": "Aktivitäten zum Freischalten des nächsten Themas", + "activitiesToUnlockTopicDesc": "Legen Sie die Anzahl der Aktivitäten fest, um das nächste Kursthema freizuschalten", + "@activitiesToUnlockTopicTitle": { + "type": "String", + "placeholders": {} + }, + "@activitiesToUnlockTopicDesc": { + "type": "String", + "placeholders": {} + }, + "constructUseCorLMDesc": "Korrekte Vokabeldefinitionsübung", + "constructUseIncLMDesc": "Falsche Vokabeldefinitionsübung", + "constructUseCorLADesc": "Korrekte Vokabelaudioübung", + "constructUseIncLADesc": "Falsche Vokabelaudioübung", + "constructUseBonus": "Bonus während der Vokabelübung", + "practiceVocab": "Vokabeln üben", + "selectMeaning": "Wähle die Bedeutung", + "selectAudio": "Wähle das passende Audio", + "congratulations": "Herzlichen Glückwunsch!", + "anotherRound": "Eine weitere Runde", + "noActivityRequest": "Keine aktuellen Aktivitätsanfragen.", + "quit": "Beenden", + "congratulationsYouveCompletedPractice": "Herzlichen Glückwunsch! Sie haben die Übungssitzung abgeschlossen.", + "mustHave10Words": "Sie müssen mindestens 10 Vokabeln haben, um sie zu üben. Versuchen Sie, mit einem Freund oder dem Pangea Bot zu sprechen, um mehr zu entdecken!", + "botSettings": "Bot-Einstellungen", + "activitySettingsOverrideWarning": "Sprache und Sprachniveau werden durch den Aktivitätsplan bestimmt", + "voice": "Stimme", + "@constructUseCorLMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncLMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorLADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncLADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseBonus": { + "type": "String", + "placeholders": {} + }, + "@practiceVocab": { + "type": "String", + "placeholders": {} + }, + "@selectMeaning": { + "type": "String", + "placeholders": {} + }, + "@selectAudio": { + "type": "String", + "placeholders": {} + }, + "@congratulations": { + "type": "String", + "placeholders": {} + }, + "@anotherRound": { + "type": "String", + "placeholders": {} + }, + "@noActivityRequest": { + "type": "String", + "placeholders": {} + }, + "@quit": { + "type": "String", + "placeholders": {} + }, + "@congratulationsYouveCompletedPractice": { + "type": "String", + "placeholders": {} + }, + "@mustHave10Words": { + "type": "String", + "placeholders": {} + }, + "@botSettings": { + "type": "String", + "placeholders": {} + }, + "@activitySettingsOverrideWarning": { + "type": "String", + "placeholders": {} + }, + "@voice": { + "type": "String", + "placeholders": {} + }, + "youLeftTheChat": "🚪 Du hast den Chat verlassen", + "@youLeftTheChat": { + "type": "String", + "placeholders": {} + }, + "downloadInitiated": "Download gestartet", + "webDownloadPermissionMessage": "Wenn Ihr Browser Downloads blockiert, aktivieren Sie bitte Downloads für diese Seite.", + "@downloadInitiated": { + "type": "String", + "placeholders": {} + }, + "@webDownloadPermissionMessage": { + "type": "String", + "placeholders": {} + }, + "exitPractice": "Ihr Fortschritt in der Übungssitzung wird nicht gespeichert.", + "practiceGrammar": "Grammatik üben", + "notEnoughToPractice": "Senden Sie mehr Nachrichten, um die Übung freizuschalten", + "constructUseCorGCDesc": "Übung der korrekten Grammatikkategorie", + "constructUseIncGCDesc": "Übung der inkorrekten Grammatikkategorie", + "@exitPractice": { + "type": "String", + "placeholders": {} + }, + "@practiceGrammar": { + "type": "String", + "placeholders": {} + }, + "@notEnoughToPractice": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGCDesc": { + "type": "String", + "placeholders": {} + }, + "constructUseCorGEDesc": "Korrekte Grammatikfehlerübung", + "constructUseIncGEDesc": "Falsche Grammatikfehlerübung", + "fillInBlank": "Füllen Sie die Lücke mit der richtigen Wahl aus", + "learn": "Lernen", + "languageUpdated": "Zielsprache aktualisiert!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot Stimme", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} + }, + "knockDesc": "Ihre Anfrage wurde an den Kursadministrator gesendet! Sie werden eingelassen, wenn sie zustimmen.", + "@knockDesc": { + "type": "String", + "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Haben Sie einen Einladungscode oder einen Link zu einem öffentlichen Kurs?", + "welcomeUser": "Willkommen {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } + }, + "publicInviteDescChat": "Suchen Sie nach Benutzern, um sie zu diesem Chat einzuladen.", + "publicInviteDescSpace": "Suchen Sie nach Benutzern, um sie zu diesem Raum einzuladen.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} + }, + "enableNotificationsTitle": "Pangea Chat ist eine Messaging-App, daher sind Benachrichtigungen wichtig!", + "enableNotificationsDesc": "Benachrichtigungen erlauben", + "@enableNotificationsTitle": { + "type": "String", + "placeholders": {} + }, + "@enableNotificationsDesc": { + "type": "String", + "placeholders": {} + }, + "useActivityImageAsChatBackground": "Aktivitätsbild als Chat-Hintergrund verwenden", + "@useActivityImageAsChatBackground": { + "type": "String", + "placeholders": {} + }, + "chatWithSupport": "Chat mit dem Support", + "@chatWithSupport": { + "type": "String", + "placeholders": {} + }, + "newCourseAccess": "Standardmäßig sind Kurse öffentlich durchsuchbar und erfordern die Genehmigung eines Administrators, um beizutreten. Sie können diese Einstellungen jederzeit bearbeiten.", + "@newCourseAccess": { + "type": "String", + "placeholders": {} + }, + "onboardingLanguagesTitle": "Welche Sprache lernst du?", + "searchLanguagesHint": "Zielsprachen suchen", + "@onboardingLanguagesTitle": { + "type": "String", + "placeholders": {} + }, + "@searchLanguagesHint": { + "type": "String", + "placeholders": {} + }, + "supportSubtitle": "Fragen? Wir sind hier, um zu helfen!", + "@supportSubtitle": { + "type": "String", + "placeholders": {} + }, + "courseLoadingError": "Etwas ist schiefgelaufen, und wir arbeiten hart daran, es zu beheben. Überprüfen Sie es später erneut.", + "@courseLoadingError": { + "type": "String", + "placeholders": {} + }, + "autoIGCToolName": "Schreibassistenz aktivieren", + "autoIGCToolDescription": "Automatisch Pangea Chat-Tools ausführen, um gesendete Nachrichten in die Zielsprache zu korrigieren.", + "@autoIGCToolName": { + "type": "String", + "placeholders": {} + }, + "@autoIGCToolDescription": { + "type": "String", + "placeholders": {} + }, + "emptyAudioError": "Die Aufnahme ist fehlgeschlagen. Bitte überprüfen Sie Ihre Audio-Berechtigungen und versuchen Sie es erneut.", + "@emptyAudioError": { + "type": "String", + "placeholders": {} + }, + "grammarCopyPOSidiom": "Idiom", + "grammarCopyPOSphrasalv": "Phrasal Verb", + "grammarCopyPOScompn": "Zusammengesetztes Wort", + "@grammarCopyPOSidiom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSphrasalv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOScompn": { + "type": "String", + "placeholders": {} + } +} diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index fe8f0e20e..a60bbc725 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -1,12126 +1,11992 @@ { - "@showPassword": { - "type": "String", - "placeholders": {} - }, - "hugContent": "{senderName} σε αγκαλιάζει", - "@hugContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@darkTheme": { - "type": "String", - "placeholders": {} - }, - "@passphraseOrKey": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourPassword": { - "type": "String", - "placeholders": {} - }, - "@theyMatch": { - "type": "String", - "placeholders": {} - }, - "@connect": { - "type": "String", - "placeholders": {} - }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, - "@allRooms": { - "type": "String", - "placeholders": {} - }, - "@obtainingLocation": { - "type": "String", - "placeholders": {} - }, - "commandHint_cuddle": "Στείλτε μια αγκαλιά", - "@commandHint_cuddle": {}, - "@chats": { - "type": "String", - "placeholders": {} - }, - "@widgetVideo": { - "type": "String", - "placeholders": {} - }, - "@dismiss": { - "type": "String", - "placeholders": {} - }, - "@unknownDevice": { - "type": "String", - "placeholders": {} - }, - "@emoteShortcode": { - "type": "String", - "placeholders": {} - }, - "@noEncryptionForPublicRooms": { - "type": "String", - "placeholders": {} - }, - "admin": "Διαχειριστής", - "@admin": { - "type": "String", - "placeholders": {} - }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, - "@directChats": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevel": { - "type": "String", - "placeholders": {} - }, - "@inviteContactToGroup": { - "type": "String", - "placeholders": { - "groupName": { - "type": "String" - } - } - }, - "@addAccount": { - "type": "String", - "placeholders": {} - }, - "@close": { - "type": "String", - "placeholders": {} - }, - "@configureChat": { - "type": "String", - "placeholders": {} - }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, - "@reply": { - "type": "String", - "placeholders": {} - }, - "@currentlyActive": { - "type": "String", - "placeholders": {} - }, - "@removeYourAvatar": { - "type": "String", - "placeholders": {} - }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, - "@device": { - "type": "String", - "placeholders": {} - }, - "blockDevice": "Συσκευή μπλοκ", - "@blockDevice": { - "type": "String", - "placeholders": {} - }, - "@commandHint_html": { - "type": "String", - "description": "Usage hint for the command /html" - }, - "@widgetJitsi": { - "type": "String", - "placeholders": {} - }, - "@youAreNoLongerParticipatingInThisChat": { - "type": "String", - "placeholders": {} - }, - "@encryption": { - "type": "String", - "placeholders": {} - }, - "@messageType": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorLong": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, - "@toggleMuted": { - "type": "String", - "placeholders": {} - }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, - "@kicked": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@title": { - "description": "Title for the application", - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "@changedTheChatAvatar": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@verifySuccess": { - "type": "String", - "placeholders": {} - }, - "@sendFile": { - "type": "String", - "placeholders": {} - }, - "@newVerificationRequest": { - "type": "String", - "placeholders": {} - }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, - "@callingAccount": { - "type": "String", - "placeholders": {} - }, - "@requestPermission": { - "type": "String", - "placeholders": {} - }, - "@sentAPicture": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@invited": { - "type": "String", - "placeholders": {} - }, - "@changedTheDisplaynameTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "displayname": { - "type": "String" - } - } - }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@nextAccount": { - "type": "String", - "placeholders": {} - }, - "@commandHint_create": { - "type": "String", - "description": "Usage hint for the command /create" - }, - "@singlesignon": { - "type": "String", - "placeholders": {} - }, - "@warning": { - "type": "String", - "placeholders": {} - }, - "@password": { - "type": "String", - "placeholders": {} - }, - "@allSpaces": { - "type": "String", - "placeholders": {} - }, - "supposedMxid": "Αυτό θα πρέπει να είναι {mxid}", - "@supposedMxid": { - "type": "String", - "placeholders": { - "mxid": { - "type": "String" - } - } - }, - "@editDisplayname": { - "type": "String", - "placeholders": {} - }, - "@user": { - "type": "String", - "placeholders": {} - }, - "@roomVersion": { - "type": "String", - "placeholders": {} - }, - "@sentAFile": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@videoCall": { - "type": "String", - "placeholders": {} - }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "banFromChat": "Απαγόρευση από τη συνομιλία", - "@banFromChat": { - "type": "String", - "placeholders": {} - }, - "@noMatrixServer": { - "type": "String", - "placeholders": { - "server1": { - "type": "String" - }, - "server2": { - "type": "String" - } - } - }, - "@userAndOthersAreTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "count": { - "type": "int" - } - } - }, - "@youInvitedBy": { - "placeholders": { - "user": { - "type": "String" + "hugContent": "{senderName} σε αγκαλιάζει", + "@hugContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "commandHint_cuddle": "Στείλε μια αγκαλιά", + "@commandHint_cuddle": {}, + "admin": "Διαχειριστής", + "@admin": { + "type": "String", + "placeholders": {} + }, + "blockDevice": "Αποκλεισμός Συσκευής", + "@blockDevice": { + "type": "String", + "placeholders": {} + }, + "supposedMxid": "Αυτό θα πρέπει να είναι {mxid}", + "@supposedMxid": { + "type": "String", + "placeholders": { + "mxid": { + "type": "String" + } + } + }, + "banFromChat": "Αποκλεισμός από τη συνομιλία", + "@banFromChat": { + "type": "String", + "placeholders": {} + }, + "askSSSSSign": "Για να μπορέσεις να υπογράψεις το άλλο άτομο, πληκτρολόγησε τη συνθηματική φράση ασφαλούς αποθήκευσης ή το κλειδί ανάκτησης.", + "@askSSSSSign": { + "type": "String", + "placeholders": {} + }, + "remove": "Αφαίρεση", + "@remove": { + "type": "String", + "placeholders": {} + }, + "areGuestsAllowedToJoin": "Επιτρέπεται στους επισκέπτες χρήστες να συμμετάσχουν", + "@areGuestsAllowedToJoin": { + "type": "String", + "placeholders": {} + }, + "blocked": "Αποκλείστηκε", + "@blocked": { + "type": "String", + "placeholders": {} + }, + "sendOnEnter": "Αποστολή με enter", + "@sendOnEnter": {}, + "answeredTheCall": "{senderName} απάντησε στην κλήση", + "@answeredTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "alias": "ψευδώνυμο", + "@alias": { + "type": "String", + "placeholders": {} + }, + "all": "Όλα", + "@all": { + "type": "String", + "placeholders": {} + }, + "badServerLoginTypesException": "Ο οικιακός διακομιστής υποστηρίζει τους τύπους σύνδεσης:\n{serverVersions}\nΑλλά αυτή η εφαρμογή υποστηρίζει μόνο:\n{supportedVersions}", + "@badServerLoginTypesException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "cantOpenUri": "Δεν μπορεί να ανοίξει το URI {uri}", + "@cantOpenUri": { + "type": "String", + "placeholders": { + "uri": { + "type": "String" + } + } + }, + "importFromZipFile": "Εισαγωγή από αρχείο .zip", + "@importFromZipFile": {}, + "autoplayImages": "Αυτόματη αναπαραγωγή κινούμενων αυτοκόλλητων και emotes", + "@autoplayImages": { + "type": "String", + "placeholder": {} + }, + "repeatPassword": "Επανάληψη κωδικού πρόσβασης", + "@repeatPassword": {}, + "acceptedTheInvitation": "👍 {username} αποδέχτηκε την πρόσκληση", + "@acceptedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "banned": "Αποκλείστηκε", + "@banned": { + "type": "String", + "placeholders": {} + }, + "exportEmotePack": "Εξαγωγή πακέτου Emote ως .zip", + "@exportEmotePack": {}, + "account": "Λογαριασμός", + "@account": { + "type": "String", + "placeholders": {} + }, + "areYouSure": "Σίγουρα;", + "@areYouSure": { + "type": "String", + "placeholders": {} + }, + "allChats": "Όλες οι συνομιλίες", + "@allChats": { + "type": "String", + "placeholders": {} + }, + "badServerVersionsException": "Ο οικιακός διακομιστής υποστηρίζει τις εκδόσεις Spec:\n{serverVersions}\nΑλλά αυτή η εφαρμογή υποστηρίζει μόνο {supportedVersions}", + "@badServerVersionsException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "addToSpace": "Προσθήκη στο χώρο", + "@addToSpace": {}, + "about": "Σχετικά", + "@about": { + "type": "String", + "placeholders": {} + }, + "activatedEndToEndEncryption": "🔐 {username} ενεργοποίησε την κρυπτογράφηση από άκρο σε άκρο", + "@activatedEndToEndEncryption": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "googlyEyesContent": "{senderName} σού στέλνει αστεία μάτια", + "@googlyEyesContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "addChatDescription": "Πρόσθεσε μια περιγραφή συνομιλίας...", + "@addChatDescription": {}, + "cancel": "Ακύρωση", + "@cancel": { + "type": "String", + "placeholders": {} + }, + "appLock": "Κλείδωμα εφαρμογής", + "@appLock": { + "type": "String", + "placeholders": {} + }, + "sendTypingNotifications": "Αποστολή ειδοποιήσεων πληκτρολόγησης", + "@sendTypingNotifications": {}, + "importEmojis": "Εισαγωγή Emojis", + "@importEmojis": {}, + "confirmMatrixId": "Παρακαλούμε επιβεβαίωσε το Matrix ID σου για να διαγράψεις τον λογαριασμό σου.", + "@confirmMatrixId": {}, + "notAnImage": "Δεν είναι αρχείο εικόνας.", + "@notAnImage": {}, + "areYouSureYouWantToLogout": "Σίγουρα θες να αποσυνδεθείς;", + "@areYouSureYouWantToLogout": { + "type": "String", + "placeholders": {} + }, + "bannedUser": "{username} απέκλεισε {targetName}", + "@bannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "cuddleContent": "{senderName} σέ αγκαλιάζει", + "@cuddleContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "askVerificationRequest": "Αποδοχή αυτού του αιτήματος επαλήθευσης από {username};", + "@askVerificationRequest": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "addEmail": "Προσθήκη email", + "@addEmail": { + "type": "String", + "placeholders": {} + }, + "commandHint_hug": "Στείλτε μια αγκαλιά", + "@commandHint_hug": {}, + "replace": "Αντικατάσταση", + "@replace": {}, + "archive": "Αρχείο", + "@archive": { + "type": "String", + "placeholders": {} + }, + "accept": "Αποδοχή", + "@accept": { + "type": "String", + "placeholders": {} + }, + "commandHint_googly": "Στείλε αστεία μάτια", + "@commandHint_googly": {}, + "botMessages": "Μηνύματα bot", + "@botMessages": { + "type": "String", + "placeholders": {} + }, + "importNow": "Εισαγωγή τώρα", + "@importNow": {}, + "anyoneCanJoin": "Οποιοσδήποτε μπορεί να συμμετάσχει", + "@anyoneCanJoin": { + "type": "String", + "placeholders": {} + }, + "alwaysUse24HourFormat": "Ψευδής", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "setCustomPermissionLevel": "Όρισε προσαρμοσμένο επίπεδο άδειας", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Επέλεξε έναν προκαθορισμένο ρόλο παρακάτω ή εισήγαγε ένα προσαρμοσμένο επίπεδο άδειας μεταξύ 0 και 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Αγνόηση χρήστη", + "@ignoreUser": {}, + "normalUser": "Κανονικός χρήστης", + "@normalUser": {}, + "aboutHomeserver": "Σχετικά με {homeserver}", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "commandHint_roomupgrade": "Αναβάθμιση αυτού του δωματίου στην δεδομένη έκδοση δωματίου", + "@commandHint_roomupgrade": {}, + "appLockDescription": "Κλείδωμα εφαρμογής όταν δεν χρησιμοποιείται με κωδικό PIN", + "@appLockDescription": {}, + "swipeRightToLeftToReply": "Σύρσιμο δεξιά προς αριστερά για απάντηση", + "@swipeRightToLeftToReply": {}, + "countChatsAndCountParticipants": "{chats} συνομιλίες και {participants} συμμετέχοντες", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noMoreChatsFound": "Δεν βρέθηκαν άλλες συνομιλίες...", + "@noMoreChatsFound": {}, + "noChatsFoundHere": "Δεν υπάρχουν συνομιλίες ακόμα. Ξεκίνα μια νέα συνομιλία με κάποιον χρησιμοποιώντας το κουμπί παρακάτω. ⤵️", + "@noChatsFoundHere": {}, + "unread": "Μη αναγνωσμένα", + "@unread": {}, + "space": "Χώρος", + "@space": {}, + "spaces": "Χώροι", + "@spaces": {}, + "changeDeviceName": "Αλλαγή ονόματος συσκευής", + "@changeDeviceName": { + "type": "String", + "placeholders": {} + }, + "changedTheChatAvatar": "{username} άλλαξε το άβαταρ συνομιλίας", + "@changedTheChatAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheChatDescriptionTo": "{username} άλλαξε την περιγραφή συνομιλίας σε: '{description}'", + "@changedTheChatDescriptionTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "description": { + "type": "String" + } + } + }, + "changedTheChatNameTo": "{username} άλλαξε το όνομα συνομιλίας σε: '{chatname}'", + "@changedTheChatNameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "chatname": { + "type": "String" + } + } + }, + "changedTheChatPermissions": "{username} άλλαξε τα δικαιώματα συνομιλίας", + "@changedTheChatPermissions": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheDisplaynameTo": "{username} άλλαξε το εμφανιζόμενο όνομά του σε: '{displayname}'", + "@changedTheDisplaynameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "displayname": { + "type": "String" + } + } + }, + "changedTheGuestAccessRules": "{username} άλλαξε τους κανόνες πρόσβασης των επισκεπτών", + "@changedTheGuestAccessRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheGuestAccessRulesTo": "{username} άλλαξε τους κανόνες πρόσβασης των επισκεπτών σε: {rules}", + "@changedTheGuestAccessRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, + "changedTheHistoryVisibility": "{username} άλλαξε την ορατότητα ιστορικού", + "@changedTheHistoryVisibility": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheHistoryVisibilityTo": "{username} άλλαξε την ορατότητα ιστορικού σε: {rules}", + "@changedTheHistoryVisibilityTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, + "changedTheJoinRules": "{username} άλλαξε τους κανόνες συμμετοχής", + "@changedTheJoinRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheJoinRulesTo": "{username} άλλαξε τους κανόνες συμμετοχής σε: {joinRules}", + "@changedTheJoinRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "joinRules": { + "type": "String" + } + } + }, + "changedTheProfileAvatar": "Ο χρήστης {username} άλλαξε την εικόνα προφίλ του", + "@changedTheProfileAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changePassword": "Αλλαγή κωδικού πρόσβασης", + "@changePassword": { + "type": "String", + "placeholders": {} + }, + "changeTheme": "Άλλαξε το στυλ σου", + "@changeTheme": { + "type": "String", + "placeholders": {} + }, + "changeYourAvatar": "Αλλαγή εικόνας προφιλ", + "@changeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "joinedChats": "Συνδεδεμένες συνομιλίες", + "@joinedChats": {}, + "chatBackup": "Αντίγραφο ασφαλείας συνομιλίας", + "@chatBackup": { + "type": "String", + "placeholders": {} + }, + "chatDetails": "Λεπτομέρειες συνομιλίας", + "@chatDetails": { + "type": "String", + "placeholders": {} + }, + "chatHasBeenAddedToThisSpace": "Η συνομιλία προστέθηκε στο δωμάτιο", + "@chatHasBeenAddedToThisSpace": {}, + "chats": "Συνομιλίες", + "@chats": { + "type": "String", + "placeholders": {} + }, + "chooseAStrongPassword": "Εισάγετε ένα δυνατό κωδικό πρόσβασης", + "@chooseAStrongPassword": { + "type": "String", + "placeholders": {} + }, + "close": "Κλείσιμο", + "@close": { + "type": "String", + "placeholders": {} + }, + "commandHint_ban": "Αποκλεισμός χρήστη από το δωμάτιο", + "@commandHint_ban": { + "type": "String", + "description": "Usage hint for the command /ban" + }, + "commandHint_clearcache": "Εκκαθάριση προσωρινής μνήμης", + "@commandHint_clearcache": { + "type": "String", + "description": "Usage hint for the command /clearcache" + }, + "commandHint_invite": "Πρόσκληση αυτού του χρήστη στο δωμάτιο", + "@commandHint_invite": { + "type": "String", + "description": "Usage hint for the command /invite" + }, + "@showPassword": { + "type": "String", + "placeholders": {} + }, + "@darkTheme": { + "type": "String", + "placeholders": {} + }, + "@passphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterYourPassword": { + "type": "String", + "placeholders": {} + }, + "@theyMatch": { + "type": "String", + "placeholders": {} + }, + "@connect": { + "type": "String", + "placeholders": {} + }, + "@jumpToLastReadMessage": { + "type": "String", + "placeholders": {} + }, + "@allRooms": { + "type": "String", + "placeholders": {} + }, + "@obtainingLocation": { + "type": "String", + "placeholders": {} + }, + "@widgetVideo": { + "type": "String", + "placeholders": {} + }, + "@dismiss": { + "type": "String", + "placeholders": {} + }, + "@unknownDevice": { + "type": "String", + "placeholders": {} + }, + "@emoteShortcode": { + "type": "String", + "placeholders": {} + }, + "@noEncryptionForPublicRooms": { + "type": "String", + "placeholders": {} + }, + "@reportErrorDescription": { + "type": "String", + "placeholders": {} + }, + "@directChats": { + "type": "String", + "placeholders": {} + }, + "@setPermissionsLevel": { + "type": "String", + "placeholders": {} + }, + "@inviteContactToGroup": { + "type": "String", + "placeholders": { + "groupName": { + "type": "String" + } } }, - "type": "String" - }, - "@userIsTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@openAppToReadMessages": { - "type": "String", - "placeholders": {} - }, - "@sentAVideo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, - "@inviteContact": { - "type": "String", - "placeholders": {} - }, - "askSSSSSign": "Για να μπορέσετε να υπογράψετε το άλλο άτομο, πληκτρολογήστε τη συνθηματική φράση ασφαλούς αποθήκευσης ή το κλειδί ανάκτησης.", - "@askSSSSSign": { - "type": "String", - "placeholders": {} - }, - "@widgetEtherpad": { - "type": "String", - "placeholders": {} - }, - "@waitingPartnerAcceptRequest": { - "type": "String", - "placeholders": {} - }, - "remove": "Αφαιρέστε το", - "@remove": { - "type": "String", - "placeholders": {} - }, - "@writeAMessage": { - "type": "String", - "placeholders": {} - }, - "@changeTheme": { - "type": "String", - "placeholders": {} - }, - "@id": { - "type": "String", - "placeholders": {} - }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, - "@changedTheChatDescriptionTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "description": { - "type": "String" - } - } - }, - "@countParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@separateChatTypes": { - "type": "String", - "placeholders": {} - }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, - "areGuestsAllowedToJoin": "Επιτρέπεται στους φιλοξενούμενους χρήστες να συμμετάσχουν", - "@areGuestsAllowedToJoin": { - "type": "String", - "placeholders": {} - }, - "blocked": "Αποκλεισμένο", - "@blocked": { - "type": "String", - "placeholders": {} - }, - "@youKickedAndBanned": { - "placeholders": { - "user": { - "type": "String" + "@addAccount": { + "type": "String", + "placeholders": {} + }, + "@configureChat": { + "type": "String", + "placeholders": {} + }, + "@reply": { + "type": "String", + "placeholders": {} + }, + "@currentlyActive": { + "type": "String", + "placeholders": {} + }, + "@removeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "@unsupportedAndroidVersion": { + "type": "String", + "placeholders": {} + }, + "@device": { + "type": "String", + "placeholders": {} + }, + "@commandHint_html": { + "type": "String", + "description": "Usage hint for the command /html" + }, + "@widgetJitsi": { + "type": "String", + "placeholders": {} + }, + "@youAreNoLongerParticipatingInThisChat": { + "type": "String", + "placeholders": {} + }, + "@encryption": { + "type": "String", + "placeholders": {} + }, + "@messageType": { + "type": "String", + "placeholders": {} + }, + "@indexedDbErrorLong": { + "type": "String", + "placeholders": {} + }, + "@oneClientLoggedOut": { + "type": "String", + "placeholders": {} + }, + "@toggleMuted": { + "type": "String", + "placeholders": {} + }, + "@unsupportedAndroidVersionLong": { + "type": "String", + "placeholders": {} + }, + "@kicked": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } } }, - "type": "String" - }, - "@dateWithoutYear": { - "type": "String", - "placeholders": { - "month": { - "type": "String" - }, - "day": { - "type": "String" - } - } - }, - "@removeDevice": { - "type": "String", - "placeholders": {} - }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, - "@userAndUserAreTyping": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "username2": { - "type": "String" - } - } - }, - "@pleaseClickOnLink": { - "type": "String", - "placeholders": {} - }, - "@saveFile": { - "type": "String", - "placeholders": {} - }, - "sendOnEnter": "Αποστολή με enter", - "@sendOnEnter": {}, - "@pickImage": { - "type": "String", - "placeholders": {} - }, - "answeredTheCall": "{senderName} απάντησε στην κλήση", - "@answeredTheCall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, - "@couldNotDecryptMessage": { - "type": "String", - "placeholders": { - "error": { - "type": "String" - } - } - }, - "@invitedUsersOnly": { - "type": "String", - "placeholders": {} - }, - "@link": { - "type": "String", - "placeholders": {} - }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, - "@newSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, - "@next": { - "type": "String", - "placeholders": {} - }, - "@pleaseFollowInstructionsOnWeb": { - "type": "String", - "placeholders": {} - }, - "@changedTheGuestAccessRules": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@dateWithYear": { - "type": "String", - "placeholders": { - "year": { - "type": "String" - }, - "month": { - "type": "String" - }, - "day": { - "type": "String" - } - } - }, - "@editRoomAliases": { - "type": "String", - "placeholders": {} - }, - "@enterSpace": { - "type": "String", - "placeholders": {} - }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, - "@fileName": { - "type": "String", - "placeholders": {} - }, - "@unavailable": { - "type": "String", - "placeholders": {} - }, - "@previousAccount": { - "type": "String", - "placeholders": {} - }, - "@publicRooms": { - "type": "String", - "placeholders": {} - }, - "@fromTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@sendMessages": { - "type": "String", - "placeholders": {} - }, - "@incorrectPassphraseOrKey": { - "type": "String", - "placeholders": {} - }, - "@emoteWarnNeedToPick": { - "type": "String", - "placeholders": {} - }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@create": { - "type": "String", - "placeholders": {} - }, - "@toggleFavorite": { - "type": "String", - "placeholders": {} - }, - "@no": { - "type": "String", - "placeholders": {} - }, - "alias": "ψευδώνυμο", - "@alias": { - "type": "String", - "placeholders": {} - }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, - "@inoffensive": { - "type": "String", - "placeholders": {} - }, - "@unpin": { - "type": "String", - "placeholders": {} - }, - "@addToBundle": { - "type": "String", - "placeholders": {} - }, - "@reportMessage": { - "type": "String", - "placeholders": {} - }, - "@spaceIsPublic": { - "type": "String", - "placeholders": {} - }, - "@addWidget": { - "type": "String", - "placeholders": {} - }, - "all": "Όλα", - "@all": { - "type": "String", - "placeholders": {} - }, - "@removeAllOtherDevices": { - "type": "String", - "placeholders": {} - }, - "@unblockDevice": { - "type": "String", - "placeholders": {} - }, - "@countFiles": { - "placeholders": { - "count": { - "type": "int" + "@title": { + "description": "Title for the application", + "type": "String", + "placeholders": {} + }, + "@changeTheNameOfTheGroup": { + "type": "String", + "placeholders": {} + }, + "@verifySuccess": { + "type": "String", + "placeholders": {} + }, + "@sendFile": { + "type": "String", + "placeholders": {} + }, + "@newVerificationRequest": { + "type": "String", + "placeholders": {} + }, + "@startFirstChat": { + "type": "String", + "placeholders": {} + }, + "@callingAccount": { + "type": "String", + "placeholders": {} + }, + "@requestPermission": { + "type": "String", + "placeholders": {} + }, + "@sentAPicture": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } }, - "type": "String" - }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} - }, - "@enableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@inviteText": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "link": { - "type": "String" - } - } - }, - "@shareLocation": { - "type": "String", - "placeholders": {} - }, - "@reason": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasgroup": { - "type": "String", - "placeholders": {} - }, - "@errorObtainingLocation": { - "type": "String", - "placeholders": { - "error": { - "type": "String" - } - } - }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@passwordRecovery": { - "type": "String", - "placeholders": {} - }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, - "@replaceRoomWithNewerVersion": { - "type": "String", - "placeholders": {} - }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, - "@voiceMessage": { - "type": "String", - "placeholders": {} - }, - "badServerLoginTypesException": "Ο homeserver υποστηρίζει τους τύπους σύνδεσης:\n{serverVersions}\nΑλλά αυτή η εφαρμογή υποστηρίζει μόνο:\n{supportedVersions}", - "@badServerLoginTypesException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "@wipeChatBackup": { - "type": "String", - "placeholders": {} - }, - "cantOpenUri": "Δεν μπορεί να ανοίξει το URI {uri}", - "@cantOpenUri": { - "type": "String", - "placeholders": { - "uri": { - "type": "String" - } - } - }, - "@sender": { - "type": "String", - "placeholders": {} - }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedEvents": { - "type": "String", - "placeholders": {} - }, - "@online": { - "type": "String", - "placeholders": {} - }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, - "@ignoredUsers": { - "type": "String", - "placeholders": {} - }, - "@lastActiveAgo": { - "type": "String", - "placeholders": { - "localizedTimeShort": { - "type": "String" - } - } - }, - "@changedTheGuestAccessRulesTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "rules": { - "type": "String" - } - } - }, - "@weSentYouAnEmail": { - "type": "String", - "placeholders": {} - }, - "@offensive": { - "type": "String", - "placeholders": {} - }, - "@needPantalaimonWarning": { - "type": "String", - "placeholders": {} - }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, - "@edit": { - "type": "String", - "placeholders": {} - }, - "@loadMore": { - "type": "String", - "placeholders": {} - }, - "@noEmotesFound": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWait": { - "type": "String", - "placeholders": {} - }, - "@transferFromAnotherDevice": { - "type": "String", - "placeholders": {} - }, - "@passwordHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@pushRules": { - "type": "String", - "placeholders": {} - }, - "@goToTheNewRoom": { - "type": "String", - "placeholders": {} - }, - "@commandHint_clearcache": { - "type": "String", - "description": "Usage hint for the command /clearcache" - }, - "@loadingPleaseWait": { - "type": "String", - "placeholders": {} - }, - "@copy": { - "type": "String", - "placeholders": {} - }, - "@saveKeyManuallyDescription": { - "type": "String", - "placeholders": {} - }, - "@none": { - "type": "String", - "placeholders": {} - }, - "@editBundlesForAccount": { - "type": "String", - "placeholders": {} - }, - "@enableEncryption": { - "type": "String", - "placeholders": {} - }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, - "@unreadChats": { - "type": "String", - "placeholders": { - "unreadCount": { - "type": "int" - } - } - }, - "@rejectedTheInvitation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, - "@userLeftTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@spaceName": { - "type": "String", - "placeholders": {} - }, - "importFromZipFile": "Εισαγωγή από αρχείο .zip", - "@importFromZipFile": {}, - "@toggleUnread": { - "type": "String", - "placeholders": {} - }, - "@or": { - "type": "String", - "placeholders": {} - }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, - "@sendOriginal": { - "type": "String", - "placeholders": {} - }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, - "@whoIsAllowedToJoinThisGroup": { - "type": "String", - "placeholders": {} - }, - "@emptyChat": { - "type": "String", - "placeholders": {} - }, - "@seenByUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, - "@chatBackup": { - "type": "String", - "placeholders": {} - }, - "@redactedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@submit": { - "type": "String", - "placeholders": {} - }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, - "@unmuteChat": { - "type": "String", - "placeholders": {} - }, - "@createdTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@redactedAnEvent": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "autoplayImages": "Αυτόματη αναπαραγωγή κινούμενων αυτοκόλλητων και emotes", - "@autoplayImages": { - "type": "String", - "placeholder": {} - }, - "@compareEmojiMatch": { - "type": "String", - "placeholders": {} - }, - "@participant": { - "type": "String", - "placeholders": {} - }, - "@logInTo": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@yes": { - "type": "String", - "placeholders": {} - }, - "@containsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, - "@username": { - "type": "String", - "placeholders": {} - }, - "@changedTheRoomAliases": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "@homeserver": { - "type": "String", - "placeholders": {} - }, - "@help": { - "type": "String", - "placeholders": {} - }, - "@chatDetails": { - "type": "String", - "placeholders": {} - }, - "@people": { - "type": "String", - "placeholders": {} - }, - "@changedTheHistoryVisibilityTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "rules": { - "type": "String" - } - } - }, - "@leftTheChat": { - "type": "String", - "placeholders": {} - }, - "@verified": { - "type": "String", - "placeholders": {} - }, - "repeatPassword": "Επανάληψη κωδικού πρόσβασης", - "@repeatPassword": {}, - "@setStatus": { - "type": "String", - "placeholders": {} - }, - "@groupWith": { - "type": "String", - "placeholders": { - "displayname": { - "type": "String" - } - } - }, - "@callingPermissions": { - "type": "String", - "placeholders": {} - }, - "@delete": { - "type": "String", - "placeholders": {} - }, - "@newMessageInFluffyChat": { - "type": "String", - "placeholders": {} - }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@start": { - "type": "String", - "placeholders": {} - }, - "@downloadFile": { - "type": "String", - "placeholders": {} - }, - "@deviceId": { - "type": "String", - "placeholders": {} - }, - "@register": { - "type": "String", - "placeholders": {} - }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, - "@identity": { - "type": "String", - "placeholders": {} - }, - "@numChats": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "@changedTheJoinRulesTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "joinRules": { - "type": "String" - } - } - }, - "@ignore": { - "type": "String", - "placeholders": {} - }, - "@recording": { - "type": "String", - "placeholders": {} - }, - "@changedTheChatPermissions": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@moderator": { - "type": "String", - "placeholders": {} - }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, - "acceptedTheInvitation": "👍 {username} αποδέχτηκε την πρόσκληση", - "@acceptedTheInvitation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@waitingPartnerEmoji": { - "type": "String", - "placeholders": {} - }, - "@channelCorruptedDecryptError": { - "type": "String", - "placeholders": {} - }, - "@tryToSendAgain": { - "type": "String", - "placeholders": {} - }, - "@guestsCanJoin": { - "type": "String", - "placeholders": {} - }, - "@ok": { - "type": "String", - "placeholders": {} - }, - "@copyToClipboard": { - "type": "String", - "placeholders": {} - }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, - "@locationPermissionDeniedNotice": { - "type": "String", - "placeholders": {} - }, - "@send": { - "type": "String", - "placeholders": {} - }, - "@hasWithdrawnTheInvitationFor": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@visibleForAllParticipants": { - "type": "String", - "placeholders": {} - }, - "@noRoomsFound": { - "type": "String", - "placeholders": {} - }, - "banned": "Απαγορευμένο", - "@banned": { - "type": "String", - "placeholders": {} - }, - "@sendAsText": { - "type": "String" - }, - "@inviteForMe": { - "type": "String", - "placeholders": {} - }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "exportEmotePack": "Εξαγωγή πακέτου Emote ως .zip", - "@exportEmotePack": {}, - "@changedTheChatNameTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "chatname": { - "type": "String" - } - } - }, - "@sendSticker": { - "type": "String", - "placeholders": {} - }, - "account": "Λογαριασμός", - "@account": { - "type": "String", - "placeholders": {} - }, - "@switchToAccount": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "@commandInvalid": { - "type": "String" - }, - "@setAsCanonicalAlias": { - "type": "String", - "placeholders": {} - }, - "@whyDoYouWantToReportThis": { - "type": "String", - "placeholders": {} - }, - "@locationDisabledNotice": { - "type": "String", - "placeholders": {} - }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, - "@removedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@changedTheRoomInvitationLink": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@newChat": { - "type": "String", - "placeholders": {} - }, - "@notifications": { - "type": "String", - "placeholders": {} - }, - "@commandHint_plain": { - "type": "String", - "description": "Usage hint for the command /plain" - }, - "@emoteSettings": { - "type": "String", - "placeholders": {} - }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, - "@openCamera": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, - "@guestsAreForbidden": { - "type": "String", - "placeholders": {} - }, - "@mention": { - "type": "String", - "placeholders": {} - }, - "@openInMaps": { - "type": "String", - "placeholders": {} - }, - "@withTheseAddressesRecoveryDescription": { - "type": "String", - "placeholders": {} - }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, - "@emoteExists": { - "type": "String", - "placeholders": {} - }, - "@redactedByBecause": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "reason": { - "type": "String" - } - } - }, - "@isTyping": { - "type": "String", - "placeholders": {} - }, - "@youHaveWithdrawnTheInvitationFor": { - "placeholders": { - "user": { - "type": "String" + "@invited": { + "type": "String", + "placeholders": {} + }, + "@setColorTheme": { + "type": "String", + "placeholders": {} + }, + "@nextAccount": { + "type": "String", + "placeholders": {} + }, + "@commandHint_create": { + "type": "String", + "description": "Usage hint for the command /create" + }, + "@singlesignon": { + "type": "String", + "placeholders": {} + }, + "@warning": { + "type": "String", + "placeholders": {} + }, + "@password": { + "type": "String", + "placeholders": {} + }, + "@allSpaces": { + "type": "String", + "placeholders": {} + }, + "@editDisplayname": { + "type": "String", + "placeholders": {} + }, + "@user": { + "type": "String", + "placeholders": {} + }, + "@roomVersion": { + "type": "String", + "placeholders": {} + }, + "@sentAFile": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } }, - "type": "String" - }, - "@chat": { - "type": "String", - "placeholders": {} - }, - "@group": { - "type": "String", - "placeholders": {} - }, - "@leave": { - "type": "String", - "placeholders": {} - }, - "@skip": { - "type": "String", - "placeholders": {} - }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, - "@roomHasBeenUpgraded": { - "type": "String", - "placeholders": {} - }, - "@enterRoom": { - "type": "String", - "placeholders": {} - }, - "@enableEmotesGlobally": { - "type": "String", - "placeholders": {} - }, - "areYouSure": "Είσαι σίγουρος;", - "@areYouSure": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAPasscode": { - "type": "String", - "placeholders": {} - }, - "@noPasswordRecoveryDescription": { - "type": "String", - "placeholders": {} - }, - "@changedTheProfileAvatar": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "allChats": "Όλες οι συνομιλίες", - "@allChats": { - "type": "String", - "placeholders": {} - }, - "@reportUser": { - "type": "String", - "placeholders": {} - }, - "@sharedTheLocation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@commandHint_send": { - "type": "String", - "description": "Usage hint for the command /send" - }, - "@onlineKeyBackupEnabled": { - "type": "String", - "placeholders": {} - }, - "@unbannedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@confirmEventUnpin": { - "type": "String", - "placeholders": {} - }, - "badServerVersionsException": "Ο homeserver υποστηρίζει τις εκδόσεις Spec:\n{serverVersions}\nΑλλά αυτή η εφαρμογή υποστηρίζει μόνο τις {supportedVersions}", - "@badServerVersionsException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "@youInvitedUser": { - "placeholders": { - "user": { - "type": "String" + "@videoCall": { + "type": "String", + "placeholders": {} + }, + "@youAcceptedTheInvitation": { + "type": "String", + "placeholders": {} + }, + "@noMatrixServer": { + "type": "String", + "placeholders": { + "server1": { + "type": "String" + }, + "server2": { + "type": "String" + } } }, - "type": "String" - }, - "@kickedAndBanned": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@noConnectionToTheServer": { - "type": "String", - "placeholders": {} - }, - "@fileHasBeenSavedAt": { - "type": "String", - "placeholders": { - "path": { - "type": "String" - } - } - }, - "@license": { - "type": "String", - "placeholders": {} - }, - "addToSpace": "Προσθήκη στο χώρο", - "@addToSpace": {}, - "@unbanFromChat": { - "type": "String", - "placeholders": {} - }, - "@commandMissing": { - "type": "String", - "placeholders": { - "command": { - "type": "String" + "@userAndOthersAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "count": { + "type": "int" + } } }, - "description": "State that {command} is not a valid /command." - }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@rejoin": { - "type": "String", - "placeholders": {} - }, - "@recoveryKey": { - "type": "String", - "placeholders": {} - }, - "@redactMessage": { - "type": "String", - "placeholders": {} - }, - "@forward": { - "type": "String", - "placeholders": {} - }, - "@commandHint_discardsession": { - "type": "String", - "description": "Usage hint for the command /discardsession" - }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, - "about": "Σχετικά με το", - "@about": { - "type": "String", - "placeholders": {} - }, - "@chooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@hideUnknownEvents": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@yourPublicKey": { - "type": "String", - "placeholders": {} - }, - "@tooManyRequestsWarning": { - "type": "String", - "placeholders": {} - }, - "@invitedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" + "@youInvitedBy": { + "placeholders": { + "user": { + "type": "String" + } }, - "targetName": { - "type": "String" - } - } - }, - "@kickFromChat": { - "type": "String", - "placeholders": {} - }, - "@commandHint_myroomnick": { - "type": "String", - "description": "Usage hint for the command /myroomnick" - }, - "@offline": { - "type": "String", - "placeholders": {} - }, - "@noPermission": { - "type": "String", - "placeholders": {} - }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, - "activatedEndToEndEncryption": "🔐 {username} ενεργοποίησε κρυπτογράφηση από άκρη σε άκρη", - "@activatedEndToEndEncryption": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@report": { - "type": "String", - "placeholders": {} - }, - "@status": { - "type": "String", - "placeholders": {} - }, - "@compareNumbersMatch": { - "type": "String", - "placeholders": {} - }, - "@groupIsPublic": { - "type": "String", - "placeholders": {} - }, - "@verifyStart": { - "type": "String", - "placeholders": {} - }, - "@memberChanges": { - "type": "String", - "placeholders": {} - }, - "@joinRoom": { - "type": "String", - "placeholders": {} - }, - "@unverified": { - "type": "String", - "placeholders": {} - }, - "@fluffychat": { - "type": "String", - "placeholders": {} - }, - "@howOffensiveIsThisContent": { - "type": "String", - "placeholders": {} - }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@screenSharingTitle": { - "type": "String", - "placeholders": {} - }, - "@widgetCustom": { - "type": "String", - "placeholders": {} - }, - "@sentCallInformations": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "googlyEyesContent": "{senderName} σας στέλνει googly eyes", - "@googlyEyesContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@youBannedUser": { - "placeholders": { - "user": { - "type": "String" + "type": "String" + }, + "@userIsTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } }, - "type": "String" - }, - "@theyDontMatch": { - "type": "String", - "placeholders": {} - }, - "@youHaveBeenBannedFromThisChat": { - "type": "String", - "placeholders": {} - }, - "@displaynameHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "addChatDescription": "Προσθέστε μια περιγραφή συνομιλίας...", - "@addChatDescription": {}, - "@sentAnAudio": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@editRoomAvatar": { - "type": "String", - "placeholders": {} - }, - "@encrypted": { - "type": "String", - "placeholders": {} - }, - "@commandHint_leave": { - "type": "String", - "description": "Usage hint for the command /leave" - }, - "@commandHint_myroomavatar": { - "type": "String", - "description": "Usage hint for the command /myroomavatar" - }, - "cancel": "Ακύρωση", - "@cancel": { - "type": "String", - "placeholders": {} - }, - "@hasKnocked": { - "placeholders": { - "user": { - "type": "String" + "@openAppToReadMessages": { + "type": "String", + "placeholders": {} + }, + "@sentAVideo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } }, - "type": "String" - }, - "@publish": { - "type": "String", - "placeholders": {} - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} - }, - "@clearArchive": { - "type": "String", - "placeholders": {} - }, - "appLock": "Κλείδωμα εφαρμογών", - "@appLock": { - "type": "String", - "placeholders": {} - }, - "@commandHint_react": { - "type": "String", - "description": "Usage hint for the command /react" - }, - "@changedTheHistoryVisibility": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@commandHint_me": { - "type": "String", - "description": "Usage hint for the command /me" - }, - "@pleaseEnterYourUsername": { - "type": "String", - "placeholders": {} - }, - "@messageInfo": { - "type": "String", - "placeholders": {} - }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, - "@encryptionNotEnabled": { - "type": "String", - "placeholders": {} - }, - "@wrongPinEntered": { - "type": "String", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "sendTypingNotifications": "Αποστολή ειδοποιήσεων δακτυλογράφησης", - "@sendTypingNotifications": {}, - "@lightTheme": { - "type": "String", - "placeholders": {} - }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, - "@verifyTitle": { - "type": "String", - "placeholders": {} - }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, - "@enterAnEmailAddress": { - "type": "String", - "placeholders": {} - }, - "@voiceCall": { - "type": "String", - "placeholders": {} - }, - "@commandHint_kick": { - "type": "String", - "description": "Usage hint for the command /kick" - }, - "@copiedToClipboard": { - "type": "String", - "placeholders": {} - }, - "@createNewSpace": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unban": { - "type": "String", - "description": "Usage hint for the command /unban" - }, - "@unknownEncryptionAlgorithm": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ban": { - "type": "String", - "description": "Usage hint for the command /ban" - }, - "importEmojis": "Εισαγωγή Emojis", - "@importEmojis": {}, - "@confirm": { - "type": "String", - "placeholders": {} - }, - "@wasDirectChatDisplayName": { - "type": "String", - "placeholders": { - "oldDisplayName": { - "type": "String" - } - } - }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, - "@defaultPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@removeFromBundle": { - "type": "String", - "placeholders": {} - }, - "@numUsersTyping": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@fontSize": { - "type": "String", - "placeholders": {} - }, - "@whoCanPerformWhichAction": { - "type": "String", - "placeholders": {} - }, - "confirmMatrixId": "Παρακαλούμε επιβεβαιώστε το Matrix ID σας για να διαγράψετε τον λογαριασμό σας.", - "@confirmMatrixId": {}, - "@learnMore": { - "type": "String", - "placeholders": {} - }, - "@iHaveClickedOnLink": { - "type": "String", - "placeholders": {} - }, - "@you": { - "type": "String", - "placeholders": {} - }, - "notAnImage": "Δεν είναι αρχείο εικόνας.", - "@notAnImage": {}, - "@users": { - "type": "String", - "placeholders": {} - }, - "@openGallery": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@search": { - "type": "String", - "placeholders": {} - }, - "@newGroup": { - "type": "String", - "placeholders": {} - }, - "@bundleName": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, - "@dateAndTimeOfDay": { - "type": "String", - "placeholders": { - "date": { - "type": "String" - }, - "timeOfDay": { - "type": "String" - } - } - }, - "@commandHint_op": { - "type": "String", - "description": "Usage hint for the command /op" - }, - "@commandHint_join": { - "type": "String", - "description": "Usage hint for the command /join" - }, - "@sourceCode": { - "type": "String", - "placeholders": {} - }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, - "@commandHint_invite": { - "type": "String", - "description": "Usage hint for the command /invite" - }, - "@userSentUnknownEvent": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "type": { - "type": "String" - } - } - }, - "@scanQrCode": { - "type": "String", - "placeholders": {} - }, - "@logout": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, - "@contactHasBeenInvitedToTheGroup": { - "type": "String", - "placeholders": {} - }, - "@youKicked": { - "placeholders": { - "user": { - "type": "String" + "@banUserDescription": { + "type": "String", + "placeholders": {} + }, + "@inviteContact": { + "type": "String", + "placeholders": {} + }, + "@widgetEtherpad": { + "type": "String", + "placeholders": {} + }, + "@waitingPartnerAcceptRequest": { + "type": "String", + "placeholders": {} + }, + "@writeAMessage": { + "type": "String", + "placeholders": {} + }, + "@id": { + "type": "String", + "placeholders": {} + }, + "@removeDevicesDescription": { + "type": "String", + "placeholders": {} + }, + "@countParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } } }, - "type": "String" - }, - "areYouSureYouWantToLogout": "Σίγουρα θέλετε να αποσυνδεθείτε;", - "@areYouSureYouWantToLogout": { - "type": "String", - "placeholders": {} - }, - "@changedTheJoinRules": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, - "@groups": { - "type": "String", - "placeholders": {} - }, - "@reactedWith": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" + "@separateChatTypes": { + "type": "String", + "placeholders": {} + }, + "@tryAgain": { + "type": "String", + "placeholders": {} + }, + "@youKickedAndBanned": { + "placeholders": { + "user": { + "type": "String" + } }, - "reaction": { - "type": "String" - } - } - }, - "bannedUser": "{username} banned {targetName}", - "@bannedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, - "@videoWithSize": { - "type": "String", - "placeholders": { - "size": { - "type": "String" - } - } - }, - "@oopsSomethingWentWrong": { - "type": "String", - "placeholders": {} - }, - "@loadCountMoreParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasdm": { - "type": "String", - "placeholders": {} - }, - "@recoveryKeyLost": { - "type": "String", - "placeholders": {} - }, - "cuddleContent": "{senderName} σε αγκαλιάζει", - "@cuddleContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "askVerificationRequest": "Αποδοχή αυτού του αιτήματος επαλήθευσης από {username};", - "@askVerificationRequest": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@containsUserName": { - "type": "String", - "placeholders": {} - }, - "@messages": { - "type": "String", - "placeholders": {} - }, - "@login": { - "type": "String", - "placeholders": {} - }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, - "@waitingPartnerNumbers": { - "type": "String", - "placeholders": {} - }, - "@noGoogleServicesWarning": { - "type": "String", - "placeholders": {} - }, - "@everythingReady": { - "type": "String", - "placeholders": {} - }, - "addEmail": "Προσθήκη email", - "@addEmail": { - "type": "String", - "placeholders": {} - }, - "@emoteKeyboardNoRecents": { - "type": "String", - "placeholders": {} - }, - "@setCustomEmotes": { - "type": "String", - "placeholders": {} - }, - "@startedACall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@emoteInvalid": { - "type": "String", - "placeholders": {} - }, - "@systemTheme": { - "type": "String", - "placeholders": {} - }, - "@notificationsEnabledForThisAccount": { - "type": "String", - "placeholders": {} - }, - "@deleteMessage": { - "type": "String", - "placeholders": {} - }, - "@visibilityOfTheChatHistory": { - "type": "String", - "placeholders": {} - }, - "@settings": { - "type": "String", - "placeholders": {} - }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, - "@changeTheHomeserver": { - "type": "String", - "placeholders": {} - }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, - "@wallpaper": { - "type": "String", - "placeholders": {} - }, - "@openVideoCamera": { - "type": "String", - "placeholders": {} - }, - "@play": { - "type": "String", - "placeholders": { - "fileName": { - "type": "String" - } - } - }, - "@chatBackupDescription": { - "type": "String", - "placeholders": {} - }, - "@changeDeviceName": { - "type": "String", - "placeholders": {} - }, - "@passwordForgotten": { - "type": "String", - "placeholders": {} - }, - "@statusExampleMessage": { - "type": "String", - "placeholders": {} - }, - "@security": { - "type": "String", - "placeholders": {} - }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, - "@sendAudio": { - "type": "String", - "placeholders": {} - }, - "@widgetName": { - "type": "String", - "placeholders": {} - }, - "@sentASticker": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@errorAddingWidget": { - "type": "String", - "placeholders": {} - }, - "@commandHint_dm": { - "type": "String", - "description": "Usage hint for the command /dm" - }, - "commandHint_hug": "Στείλτε μια αγκαλιά", - "@commandHint_hug": {}, - "replace": "Αντικαταστήστε το", - "@replace": {}, - "@reject": { - "type": "String", - "placeholders": {} - }, - "@extremeOffensive": { - "type": "String", - "placeholders": {} - }, - "@editBlockedServers": { - "type": "String", - "placeholders": {} - }, - "@oopsPushError": { - "type": "String", - "placeholders": {} - }, - "@youUnbannedUser": { - "placeholders": { - "user": { - "type": "String" + "type": "String" + }, + "@dateWithoutYear": { + "type": "String", + "placeholders": { + "month": { + "type": "String" + }, + "day": { + "type": "String" + } } }, - "type": "String" - }, - "@deactivateAccountWarning": { - "type": "String", - "placeholders": {} - }, - "archive": "Αρχείο", - "@archive": { - "type": "String", - "placeholders": {} - }, - "@joinedTheChat": { - "type": "String", - "placeholders": { - "username": { - "type": "String" + "@removeDevice": { + "type": "String", + "placeholders": {} + }, + "@unbanUserDescription": { + "type": "String", + "placeholders": {} + }, + "@userAndUserAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "username2": { + "type": "String" + } } - } - }, - "@visibleForEveryone": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnter4Digits": { - "type": "String", - "placeholders": {} - }, - "@newSpace": { - "type": "String", - "placeholders": {} - }, - "@changePassword": { - "type": "String", - "placeholders": {} - }, - "@devices": { - "type": "String", - "placeholders": {} - }, - "accept": "Αποδοχή", - "@accept": { - "type": "String", - "placeholders": {} - }, - "@unknownEvent": { - "type": "String", - "placeholders": { - "type": { - "type": "String" + }, + "@pleaseClickOnLink": { + "type": "String", + "placeholders": {} + }, + "@saveFile": { + "type": "String", + "placeholders": {} + }, + "@pickImage": { + "type": "String", + "placeholders": {} + }, + "@youRejectedTheInvitation": { + "type": "String", + "placeholders": {} + }, + "@otherCallingPermissions": { + "type": "String", + "placeholders": {} + }, + "@messagesStyle": { + "type": "String", + "placeholders": {} + }, + "@couldNotDecryptMessage": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } } - } - }, - "@emojis": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourPin": { - "type": "String", - "placeholders": {} - }, - "@pleaseChoose": { - "type": "String", - "placeholders": {} - }, - "@share": { - "type": "String", - "placeholders": {} - }, - "commandHint_googly": "Στείλτε μερικά μάτια", - "@commandHint_googly": {}, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, - "@privacy": { - "type": "String", - "placeholders": {} - }, - "@changeYourAvatar": { - "type": "String", - "placeholders": {} - }, - "@sendImage": { - "type": "String", - "placeholders": {} - }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@time": { - "type": "String", - "placeholders": {} - }, - "@enterYourHomeserver": { - "type": "String", - "placeholders": {} - }, - "botMessages": "Μηνύματα bot", - "@botMessages": { - "type": "String", - "placeholders": {} - }, - "@contentHasBeenReported": { - "type": "String", - "placeholders": {} - }, - "@custom": { - "type": "String", - "placeholders": {} - }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, - "@fromJoining": { - "type": "String", - "placeholders": {} - }, - "@verify": { - "type": "String", - "placeholders": {} - }, - "@sendVideo": { - "type": "String", - "placeholders": {} - }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, - "@openChat": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, - "@sendAMessage": { - "type": "String", - "placeholders": {} - }, - "@pin": { - "type": "String", - "placeholders": {} - }, - "importNow": "Εισαγωγή τώρα", - "@importNow": {}, - "@deleteAccount": { - "type": "String", - "placeholders": {} - }, - "@setInvitationLink": { - "type": "String", - "placeholders": {} - }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, - "@muteChat": { - "type": "String", - "placeholders": {} - }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, - "anyoneCanJoin": "Οποιοσδήποτε μπορεί να συμμετάσχει", - "@anyoneCanJoin": { - "type": "String", - "placeholders": {} - }, - "@emotePacks": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorTitle": { - "type": "String", - "placeholders": {} - }, - "@endedTheCall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" + }, + "@invitedUsersOnly": { + "type": "String", + "placeholders": {} + }, + "@link": { + "type": "String", + "placeholders": {} + }, + "@widgetUrlError": { + "type": "String", + "placeholders": {} + }, + "@emailOrUsername": { + "type": "String", + "placeholders": {} + }, + "@newSpaceDescription": { + "type": "String", + "placeholders": {} + }, + "@chatDescription": { + "type": "String", + "placeholders": {} + }, + "@callingAccountDetails": { + "type": "String", + "placeholders": {} + }, + "@next": { + "type": "String", + "placeholders": {} + }, + "@pleaseFollowInstructionsOnWeb": { + "type": "String", + "placeholders": {} + }, + "@dateWithYear": { + "type": "String", + "placeholders": { + "year": { + "type": "String" + }, + "month": { + "type": "String" + }, + "day": { + "type": "String" + } } - } - }, - "alwaysUse24HourFormat": "πραγματικά Χρησιμοποιήστε 24ωρη μορφή", - "setCustomPermissionLevel": "Ορίστε προσαρμοσμένο επίπεδο άδειας", - "setPermissionsLevelDescription": "Παρακαλώ επιλέξτε έναν προκαθορισμένο ρόλο παρακάτω ή εισάγετε ένα προσαρμοσμένο επίπεδο άδειας μεταξύ 0 και 100.", - "ignoreUser": "Αγνόηση χρήστη", - "normalUser": "Κανονικός χρήστης", - "aboutHomeserver": "Σχετικά με {homeserver}", - "commandHint_roomupgrade": "Αναβαθμίστε αυτό το δωμάτιο στην εκδοχή δωματίου που δίνεται", - "appLockDescription": "Κλειδώστε την εφαρμογή όταν δεν τη χρησιμοποιείτε με κωδικό PIN", - "swipeRightToLeftToReply": "Σύρετε δεξιά προς τα αριστερά για απάντηση", - "countChatsAndCountParticipants": "{chats} συνομιλίες και {participants} συμμετέχοντες", - "noMoreChatsFound": "Δεν βρέθηκαν άλλες συνομιλίες...", - "noChatsFoundHere": "Δεν βρέθηκαν συνομιλίες εδώ ακόμα. Ξεκινήστε μια νέα συνομιλία με κάποιον χρησιμοποιώντας το κουμπί παρακάτω. ⤵️", - "joinedChats": "Ενταγμένες συνομιλίες", - "unread": "Ανεγνωσμένα", - "space": "Χώρος", - "spaces": "Χώροι", - "changeDeviceName": "Αλλάξτε το όνομα της συσκευής", - "changedTheChatAvatar": "{username} άλλαξε το avatar της συνομιλίας", - "changedTheChatDescriptionTo": "{username} άλλαξε την περιγραφή της συνομιλίας σε: '{description}'", - "changedTheChatNameTo": "{username} άλλαξε το όνομα της συνομιλίας σε: '{chatname}'", - "changedTheChatPermissions": "{username} άλλαξε τα δικαιώματα συνομιλίας", - "changedTheDisplaynameTo": "{username} άλλαξε το όνομά του σε: '{displayname}'", - "changedTheGuestAccessRules": "{username} άλλαξε τους κανόνες πρόσβασης επισκεπτών", - "changedTheGuestAccessRulesTo": "{username} άλλαξε τους κανόνες πρόσβασης επισκεπτών σε: {rules}", - "changedTheHistoryVisibility": "{username} άλλαξε την ορατότητα ιστορικού", - "changedTheHistoryVisibilityTo": "{username} άλλαξε την ορατότητα ιστορικού σε: {rules}", - "changedTheJoinRules": "{username} άλλαξε τους κανόνες συμμετοχής", - "changedTheJoinRulesTo": "{username} άλλαξε τους κανόνες συμμετοχής σε: {joinRules}", - "changedTheProfileAvatar": "{username} άλλαξε το προφίλ εικόνας του", - "changedTheRoomAliases": "{username} άλλαξε τα ψευδώνυμα του δωματίου", - "changedTheRoomInvitationLink": "{username} άλλαξε τον σύνδεσμο πρόσκλησης", - "changePassword": "Αλλαγή κωδικού πρόσβασης", - "changeTheHomeserver": "Αλλαγή του διακομιστή σπιτιού", - "changeTheme": "Αλλαγή στυλ", - "changeTheNameOfTheGroup": "Αλλαγή ονόματος ομάδας", - "changeYourAvatar": "Αλλαγή προφίλ εικόνας", - "channelCorruptedDecryptError": "Ο κρυπτογραφημένος κώδικας έχει καταστραφεί", - "chat": "Συνομιλία", - "yourChatBackupHasBeenSetUp": "Η δημιουργία αντιγράφων ασφαλείας της συνομιλίας σας έχει ρυθμιστεί.", - "chatBackup": "Αντίγραφο ασφαλείας συνομιλίας", - "chatBackupDescription": "Τα παλιά σας μηνύματα είναι ασφαλή με ένα κλειδί ανάκτησης. Παρακαλώ βεβαιωθείτε ότι δεν το χάνετε.", - "chatDetails": "Λεπτομέρειες συνομιλίας", - "chatHasBeenAddedToThisSpace": "Η συνομιλία έχει προστεθεί σε αυτόν τον χώρο", - "chats": "Συνομιλίες", - "chooseAStrongPassword": "Επιλέξτε έναν ισχυρό κωδικό πρόσβασης", - "clearArchive": "Διαγραφή αρχείου", - "close": "Κλείσιμο", - "commandHint_markasdm": "Σημειώστε ως άμεσο μήνυμα για το δοσμένο ID του Matrix", - "commandHint_markasgroup": "Σημειώστε ως ομάδα", - "commandHint_ban": "Απαγορεύστε τον δοσμένο χρήστη από αυτό το δωμάτιο", - "commandHint_clearcache": "Εκκαθαρίστε την προσωρινή μνήμη", - "commandHint_create": "Δημιουργήστε μια κενή ομαδική συνομιλία\nΧρησιμοποιήστε --no-encryption για να απενεργοποιήσετε την κρυπτογράφηση", - "commandHint_discardsession": "Απορρίψτε τη συνεδρία", - "commandHint_dm": "Ξεκινήστε μια άμεση συνομιλία\nΧρησιμοποιήστε --no-encryption για να απενεργοποιήσετε την κρυπτογράφηση", - "commandHint_html": "Αποστείλετε κείμενο μορφοποιημένο σε HTML", - "commandHint_invite": "Προσκαλέστε τον δοσμένο χρήστη σε αυτό το δωμάτιο", - "commandHint_join": "Ενταχθείτε στο δοσμένο δωμάτιο", - "commandHint_kick": "Αφαιρέστε τον δοσμένο χρήστη από αυτό το δωμάτιο", - "commandHint_leave": "Αφήστε αυτό το δωμάτιο", - "commandHint_me": "Περιγράψτε τον εαυτό σας", - "commandHint_myroomavatar": "Ορίστε τη φωτογραφία σας για αυτό το δωμάτιο (με mxc-uri)", - "commandHint_myroomnick": "Ορίστε το όνομα εμφάνισης για αυτό το δωμάτιο", - "commandHint_op": "Ορίστε το επίπεδο ισχύος του χρήστη (προεπιλογή: 50)", - "commandHint_plain": "Αποστολή κειμένου χωρίς μορφοποίηση", - "commandHint_react": "Αποστολή απάντησης ως αντίδραση", - "commandHint_send": "Αποστολή κειμένου", - "commandHint_unban": "Άρση αποκλεισμού του χρήστη από αυτό το δωμάτιο", - "commandInvalid": "Μη έγκυρη εντολή", - "commandMissing": "{command} δεν είναι εντολή.", - "compareEmojiMatch": "Παρακαλώ συγκρίνετε τα emojis", - "compareNumbersMatch": "Παρακαλώ συγκρίνετε τους αριθμούς", - "configureChat": "Ρύθμιση συνομιλίας", - "confirm": "Επιβεβαίωση", - "connect": "Σύνδεση", - "contactHasBeenInvitedToTheGroup": "Ο επαφή έχει προσκληθεί στην ομάδα", - "containsDisplayName": "Περιέχει όνομα εμφάνισης", - "containsUserName": "Περιέχει όνομα χρήστη", - "contentHasBeenReported": "Το περιεχόμενο έχει αναφερθεί στους διαχειριστές του διακομιστή", - "copiedToClipboard": "Αντιγράφηκε στο πρόχειρο", - "copy": "Αντιγραφή", - "copyToClipboard": "Αντιγραφή στο πρόχειρο", - "couldNotDecryptMessage": "Αδύνατη η αποκρυπτογράφηση μηνύματος: {error}", - "checkList": "Λίστα ελέγχου", - "countParticipants": "{count} συμμετέχοντες", - "countInvited": "{count} προσκεκλημένοι", - "create": "Δημιουργία", - "createdTheChat": "💬 {username} δημιούργησε τη συνομιλία", - "createGroup": "Δημιουργία ομάδας", - "createNewSpace": "Νέος χώρος", - "currentlyActive": "Ενεργό αυτήν τη στιγμή", - "darkTheme": "Σκοτεινό", - "dateAndTimeOfDay": "{date}, {timeOfDay}", - "dateWithoutYear": "{month}-{day}", - "dateWithYear": "{year}-{month}-{day}", - "deactivateAccountWarning": "Αυτό θα απενεργοποιήσει τον λογαριασμό σας. Δεν μπορεί να αναιρεθεί! Είστε σίγουροι;", - "defaultPermissionLevel": "Προεπιλεγμένο επίπεδο δικαιωμάτων για νέους χρήστες", - "delete": "Διαγραφή", - "deleteAccount": "Διαγραφή λογαριασμού", - "deleteMessage": "Διαγραφή μηνύματος", - "device": "Συσκευή", - "deviceId": "Ταυτότητα συσκευής", - "devices": "Συσκευές", - "directChats": "Άμεσα μηνύματα", - "allRooms": "Όλες οι ομαδικές συνομιλίες", - "displaynameHasBeenChanged": "Το όνομα εμφανιζόμενου έχει αλλάξει", - "downloadFile": "Λήψη αρχείου", - "edit": "Επεξεργασία", - "editBlockedServers": "Επεξεργασία αποκλεισμένων διακομιστών", - "chatPermissions": "Άδειες συνομιλίας", - "editDisplayname": "Επεξεργασία ονόματος εμφανιζόμενου", - "editRoomAliases": "Επεξεργασία ψευδωνύμων δωματίου", - "editRoomAvatar": "Επεξεργασία εικόνας δωματίου", - "emoteExists": "Το emoji ήδη υπάρχει!", - "emoteInvalid": "Μη έγκυρος κωδικός emoji!", - "emoteKeyboardNoRecents": "Πρόσφατα χρησιμοποιημένα emoji θα εμφανίζονται εδώ...", - "emotePacks": "Πακέτα emoji για το δωμάτιο", - "emoteSettings": "Ρυθμίσεις emoji", - "globalChatId": "Παγκόσμιο ID συνομιλίας", - "accessAndVisibility": "Πρόσβαση και ορατότητα", - "accessAndVisibilityDescription": "Ποιος επιτρέπεται να συμμετάσχει σε αυτήν τη συνομιλία και πώς μπορεί να βρεθεί η συνομιλία.", - "calls": "Κλήσεις", - "customEmojisAndStickers": "Προσαρμοσμένα emojis και αυτοκόλλητα", - "customEmojisAndStickersBody": "Προσθέστε ή μοιραστείτε προσαρμοσμένα emojis ή αυτοκόλλητα που μπορούν να χρησιμοποιηθούν σε οποιαδήποτε συνομιλία.", - "emoteShortcode": "Κωδικός σύντομου emoji", - "emoteWarnNeedToPick": "Πρέπει να επιλέξετε έναν κωδικό emoji και μια εικόνα!", - "emptyChat": "Άδειο chat", - "enableEmotesGlobally": "Ενεργοποίηση πακέτου emoji σε όλο το σύστημα", - "enableEncryption": "Ενεργοποίηση κρυπτογράφησης", - "enableEncryptionWarning": "Δεν θα μπορείτε πλέον να απενεργοποιήσετε την κρυπτογράφηση. Είστε σίγουροι;", - "encrypted": "Κρυπτογραφημένο", - "encryption": "Κρυπτογράφηση", - "encryptionNotEnabled": "Η κρυπτογράφηση δεν είναι ενεργοποιημένη", - "endedTheCall": "{senderName} τερμάτισε την κλήση", - "enterAnEmailAddress": "Εισάγετε μια διεύθυνση email", - "homeserver": "Homeserver", - "enterYourHomeserver": "Εισάγετε το homeserver σας", - "errorObtainingLocation": "Σφάλμα κατά την απόκτηση τοποθεσίας: {error}", - "everythingReady": "Όλα έτοιμα!", - "extremeOffensive": "Απολύτως προσβλητικό", - "fileName": "Όνομα αρχείου", - "fluffychat": "FluffyChat", - "fontSize": "Μέγεθος γραμματοσειράς", - "forward": "Προώθηση", - "fromJoining": "Από την ένταξη", - "fromTheInvitation": "Από την πρόσκληση", - "goToTheNewRoom": "Μεταβείτε στο νέο δωμάτιο", - "group": "Ομάδα", - "chatDescription": "Περιγραφή συνομιλίας", - "chatDescriptionHasBeenChanged": "Η περιγραφή της συνομιλίας άλλαξε", - "groupIsPublic": "Η ομάδα είναι δημόσια", - "groups": "Ομάδες", - "groupWith": "Ομάδα με {displayname}", - "guestsAreForbidden": "Οι επισκέπτες απαγορεύονται", - "guestsCanJoin": "Οι επισκέπτες μπορούν να συμμετάσχουν", - "hasWithdrawnTheInvitationFor": "{username} έχει αποσύρει την πρόσκληση για {targetName}", - "help": "Βοήθεια", - "hideRedactedEvents": "Απόκρυψη επεξεργασμένων γεγονότων", - "hideRedactedMessages": "Απόκρυψη επεξεργασμένων μηνυμάτων", - "hideRedactedMessagesBody": "Αν κάποιος επεξεργαστεί ένα μήνυμα, αυτό το μήνυμα δεν θα είναι πλέον ορατό στη συνομιλία.", - "hideInvalidOrUnknownMessageFormats": "Απόκρυψη μη έγκυρων ή άγνωστων μορφών μηνυμάτων", - "howOffensiveIsThisContent": "Πόσο προσβλητικό είναι αυτό το περιεχόμενο;", - "id": "ID", - "identity": "Ταυτότητα", - "block": "Αποκλεισμός", - "blockedUsers": "Αποκλεισμένοι χρήστες", - "blockListDescription": "Μπορείτε να αποκλείσετε χρήστες που σας ενοχλούν. Δεν θα μπορείτε να λαμβάνετε μηνύματα ή προσκλήσεις δωματίου από τους χρήστες στη λίστα αποκλεισμού σας.", - "blockUsername": "Αγνόηση ονόματος χρήστη", - "iHaveClickedOnLink": "Έχω κάνει κλικ στον σύνδεσμο", - "incorrectPassphraseOrKey": "Λάθος φράση πρόσβασης ή κλειδί ανάκτησης", - "inoffensive": "Ακίνδυνο", - "inviteContact": "Πρόσκληση επαφής", - "inviteContactToGroupQuestion": "Θέλετε να προσκαλέσετε τον {contact} στη συνομιλία \"{groupName}\";", - "inviteContactToGroup": "Πρόσκληση επαφής στο {groupName}", - "noChatDescriptionYet": "Δεν έχει δημιουργηθεί ακόμη περιγραφή συνομιλίας.", - "tryAgain": "Δοκιμάστε ξανά", - "invalidServerName": "Μη έγκυρο όνομα διακομιστή", - "invited": "Προσκαλέστηκε", - "redactMessageDescription": "Το μήνυμα θα διαγραφεί για όλους τους συμμετέχοντες σε αυτή τη συνομιλία. Αυτό δεν μπορεί να αναιρεθεί.", - "optionalRedactReason": "(Προαιρετικό) Λόγος διαγραφής αυτού του μηνύματος...", - "invitedUser": "📩 {username} προσκάλεσε {targetName}", - "invitedUsersOnly": "Μόνο για προσκεκλημένους χρήστες", - "inviteForMe": "Πρόσκληση για μένα", - "inviteText": "{username} σε κάλεσε στο FluffyChat.\n1. Επισκεφθείτε το fluffychat.im και εγκαταστήστε την εφαρμογή \n2. Εγγραφείτε ή συνδεθείτε \n3. Ανοίξτε τον σύνδεσμο πρόσκλησης: \n {link}", - "isTyping": "γράφει…", - "joinedTheChat": "👋 {username} μπήκε στη συζήτηση", - "joinRoom": "Ενταχθείτε στο δωμάτιο", - "kicked": "👞 {username} έδιωξε τον/την {targetName}", - "kickedAndBanned": "🙅 {username} έδιωξε και απαγόρευσε τον/την {targetName}", - "kickFromChat": "Απομάκρυνση από τη συζήτηση", - "lastActiveAgo": "Τελευταία δραστηριότητα: {localizedTimeShort}", - "leave": "Αποχώρηση", - "leftTheChat": "Έφυγε από τη συζήτηση", - "license": "Άδεια", - "lightTheme": "Φωτεινό", - "loadCountMoreParticipants": "Φόρτωση {count} επιπλέον συμμετεχόντων", - "dehydrate": "Εξαγωγή συνεδρίας και διαγραφή συσκευής", - "dehydrateWarning": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Βεβαιωθείτε ότι αποθηκεύετε με ασφάλεια το αρχείο αντιγράφου ασφαλείας.", - "dehydrateTor": "Χρήστες TOR: Εξαγωγή συνεδρίας", - "dehydrateTorLong": "Για χρήστες TOR, συνιστάται να εξάγετε τη συνεδρία πριν κλείσετε το παράθυρο.", - "hydrateTor": "Χρήστες TOR: Εισαγωγή εξαγωγής συνεδρίας", - "hydrateTorLong": "Έχετε εξάγει την συνεδρία σας τελευταία φορά στο TOR; Εισάγετέ το γρήγορα και συνεχίστε τη συνομιλία.", - "hydrate": "Ανάκτηση από αρχείο δημιουργίας αντιγράφων ασφαλείας", - "loadingPleaseWait": "Φόρτωση… Παρακαλώ περιμένετε.", - "loadMore": "Φόρτωση περισσότερων…", - "locationDisabledNotice": "Οι υπηρεσίες τοποθεσίας είναι απενεργοποιημένες. Παρακαλώ ενεργοποιήστε τες για να μπορείτε να μοιραστείτε την τοποθεσία σας.", - "locationPermissionDeniedNotice": "Η άδεια τοποθεσίας απορρίφθηκε. Παρακαλώ δώστε την για να μπορείτε να μοιραστείτε την τοποθεσία σας.", - "login": "Σύνδεση", - "logInTo": "Συνδεθείτε στο {homeserver}", - "logout": "Αποσύνδεση", - "memberChanges": "Αλλαγές μελών", - "mention": "Αναφορά", - "messages": "Μηνύματα", - "messagesStyle": "Μηνύματα:", - "moderator": "Διαχειριστής", - "muteChat": "Αθόρυβη συνομιλία", - "needPantalaimonWarning": "Παρακαλώ να γνωρίζετε ότι χρειάζεστε το Pantalaimon για να χρησιμοποιήσετε κρυπτογράφηση άκρο-σε-άκρο προς το παρόν.", - "newChat": "Νέα συνομιλία", - "newMessageInFluffyChat": "🗨️ Νέο μήνυμα στο FluffyChat", - "newVerificationRequest": "Νέο αίτημα επαλήθευσης!", - "next": "Επόμενο", - "no": "Όχι", - "noConnectionToTheServer": "Δεν υπάρχει σύνδεση με τον διακομιστή", - "noEmotesFound": "Δεν βρέθηκαν εμοτέ. 😝", - "noEncryptionForPublicRooms": "Μπορείτε να ενεργοποιήσετε την κρυπτογράφηση μόνο όταν το δωμάτιο δεν είναι πλέον δημόσια προσβάσιμο.", - "noGoogleServicesWarning": "Το Firebase Cloud Messaging δεν φαίνεται να είναι διαθέσιμο στη συσκευή σας. Για να λαμβάνετε ακόμα ειδοποιήσεις push, προτείνουμε την εγκατάσταση του ntfy. Με το ntfy ή έναν άλλο πάροχο Unified Push μπορείτε να λαμβάνετε ειδοποιήσεις push με ασφαλή τρόπο. Μπορείτε να κατεβάσετε το ntfy από το PlayStore ή από το F-Droid.", - "noMatrixServer": "{server1} δεν είναι διακομιστής matrix, να χρησιμοποιήσω το {server2} αντί αυτού;", - "shareInviteLink": "Μοιραστείτε τον σύνδεσμο πρόσκλησης", - "scanQrCode": "Σάρωση κωδικού QR", - "none": "Κανένα", - "noPasswordRecoveryDescription": "Δεν έχετε προσθέσει ακόμα τρόπο ανάκτησης του κωδικού πρόσβασής σας.", - "noPermission": "Χωρίς άδεια", - "noRoomsFound": "Δεν βρέθηκαν δωμάτια…", - "notifications": "Ειδοποιήσεις", - "notificationsEnabledForThisAccount": "Οι ειδοποιήσεις είναι ενεργοποιημένες για αυτόν τον λογαριασμό", - "numUsersTyping": "{count} χρήστες πληκτρολογούν…", - "obtainingLocation": "Λήψη τοποθεσίας…", - "offensive": "Προσβλητικό", - "offline": "Εκτός σύνδεσης", - "ok": "Εντάξει", - "online": "Σε σύνδεση", - "onlineKeyBackupEnabled": "Ενεργοποιήθηκε η online δημιουργία αντιγράφων ασφαλείας κλειδιών", - "oopsPushError": "Ωχ! Δυστυχώς, προέκυψε σφάλμα κατά την εγκατάσταση των ειδοποιήσεων push.", - "oopsSomethingWentWrong": "Ωχ, κάτι πήγε στραβά...", - "openAppToReadMessages": "Ανοίξτε την εφαρμογή για να διαβάσετε μηνύματα", - "openCamera": "Άνοιγμα κάμερας", - "openVideoCamera": "Άνοιγμα κάμερας για βίντεο", - "oneClientLoggedOut": "Ένας από τους πελάτες σας έχει αποσυνδεθεί", - "addAccount": "Προσθήκη λογαριασμού", - "editBundlesForAccount": "Επεξεργασία πακέτων για αυτόν τον λογαριασμό", - "addToBundle": "Προσθήκη στο πακέτο", - "removeFromBundle": "Αφαίρεση από αυτό το πακέτο", - "bundleName": "Όνομα πακέτου", - "enableMultiAccounts": "(BETA) Ενεργοποίηση πολλαπλών λογαριασμών σε αυτήν τη συσκευή", - "openInMaps": "Άνοιγμα στους χάρτες", - "link": "Σύνδεσμος", - "serverRequiresEmail": "Αυτός ο διακομιστής χρειάζεται να επικυρώσει τη διεύθυνση email σας για εγγραφή.", - "or": "Ή", - "participant": "Συμμετέχων", - "passphraseOrKey": "κωδικός πρόσβασης ή κλειδί ανάκτησης", - "password": "Κωδικός πρόσβασης", - "passwordForgotten": "Ξεχάσατε τον κωδικό πρόσβασης", - "passwordHasBeenChanged": "Ο κωδικός πρόσβασης έχει αλλάξει", - "hideMemberChangesInPublicChats": "Απόκρυψη αλλαγών μελών σε δημόσια chats", - "hideMemberChangesInPublicChatsBody": "Μην εμφανίζετε στο χρονολόγιο του chat αν κάποιος ενταχθεί ή φύγει από ένα δημόσιο chat για βελτίωση της αναγνωσιμότητας.", - "overview": "Επισκόπηση", - "notifyMeFor": "Ειδοποίησέ με για", - "passwordRecoverySettings": "Ρυθμίσεις ανάκτησης κωδικού πρόσβασης", - "passwordRecovery": "Ανάκτηση κωδικού πρόσβασης", - "people": "Άνθρωποι", - "pickImage": "Επιλέξτε μια εικόνα", - "pin": "Καρφίτσωμα", - "play": "Αναπαραγωγή {fileName}", - "pleaseChoose": "Παρακαλώ επιλέξτε", - "pleaseChooseAPasscode": "Παρακαλώ επιλέξτε έναν κωδικό πρόσβασης", - "pleaseClickOnLink": "Παρακαλώ κάντε κλικ στον σύνδεσμο στο email και συνεχίστε.", - "pleaseEnter4Digits": "Παρακαλώ εισάγετε 4 ψηφία ή αφήστε κενό για να απενεργοποιήσετε το κλείδωμα εφαρμογής.", - "pleaseEnterRecoveryKey": "Παρακαλώ εισάγετε το κλειδί ανάκτησης σας:", - "pleaseEnterYourPassword": "Παρακαλώ εισάγετε τον κωδικό πρόσβασής σας", - "pleaseEnterYourPin": "Παρακαλώ εισάγετε το PIN σας", - "pleaseEnterYourUsername": "Παρακαλώ εισάγετε το όνομα χρήστη σας", - "pleaseFollowInstructionsOnWeb": "Παρακαλούμε ακολουθήστε τις οδηγίες στην ιστοσελίδα και πατήστε επόμενο.", - "privacy": "Απόρρητο", - "publicRooms": "Δημόσιες αίθουσες", - "pushRules": "Κανόνες ειδοποιήσεων", - "reason": "Αιτία", - "recording": "Εγγραφή", - "redactedBy": "Επεξεργάστηκε από {username}", - "directChat": "Άμεσο μήνυμα", - "redactedByBecause": "Επεξεργάστηκε από {username} επειδή: \"{reason}\"", - "redactedAnEvent": "{username} επεξεργάστηκε ένα γεγονός", - "redactMessage": "Επεξεργασία μηνύματος", - "register": "Εγγραφή", - "reject": "Απόρριψη", - "rejectedTheInvitation": "{username} απέρριψε την πρόσκληση", - "rejoin": "Επανένταξη", - "removeAllOtherDevices": "Αφαίρεση όλων των άλλων συσκευών", - "removedBy": "Αφαιρέθηκε από {username}", - "removeDevice": "Αφαίρεση συσκευής", - "unbanFromChat": "Άρση αποκλεισμού από τη συνομιλία", - "removeYourAvatar": "Αφαίρεση του προφίλ σας", - "replaceRoomWithNewerVersion": "Αντικαταστήστε το δωμάτιο με νεότερη έκδοση", - "reply": "Απάντηση", - "reportMessage": "Αναφορά μηνύματος", - "requestPermission": "Αίτηση άδειας", - "roomHasBeenUpgraded": "Το δωμάτιο έχει αναβαθμιστεί", - "roomVersion": "Έκδοση δωματίου", - "saveFile": "Αποθήκευση αρχείου", - "search": "Αναζήτηση", - "security": "Ασφάλεια", - "recoveryKey": "Κλειδί ανάκτησης", - "recoveryKeyLost": "Έχετε χάσει το κλειδί ανάκτησης;", - "seenByUser": "Εμφανίστηκε από {username}", - "send": "Αποστολή", - "sendAMessage": "Αποστολή μηνύματος", - "sendAsText": "Αποστολή ως κείμενο", - "sendAudio": "Αποστολή ήχου", - "sendFile": "Αποστολή αρχείου", - "sendImage": "Αποστολή εικόνας", - "sendImages": "Αποστολή {count} εικόνας", - "sendMessages": "Αποστολή μηνυμάτων", - "sendOriginal": "Αποστολή πρωτότυπου", - "sendSticker": "Αποστολή αυτοκόλλητου", - "sendVideo": "Αποστολή βίντεο", - "sentAFile": "📁 {username} έστειλε ένα αρχείο", - "sentAnAudio": "🎤 {username} έστειλε έναν ήχο", - "sentAPicture": "🖼️ {username} έστειλε μια εικόνα", - "sentASticker": "😊 {username} έστειλε ένα αυτοκόλλητο", - "sentAVideo": "🎥 {username} έστειλε ένα βίντεο", - "sentCallInformations": "Ο {senderName} έστειλε πληροφορίες κλήσης", - "separateChatTypes": "Διαχωρισμός άμεσων μηνυμάτων και ομάδων", - "setAsCanonicalAlias": "Ορισμός ως κύριο ψευδώνυμο", - "setCustomEmotes": "Ορισμός προσαρμοσμένων εικονιδίων", - "setChatDescription": "Ορισμός περιγραφής συνομιλίας", - "setInvitationLink": "Ορισμός συνδέσμου πρόσκλησης", - "setPermissionsLevel": "Ορισμός επιπέδου δικαιωμάτων", - "setStatus": "Ορισμός κατάστασης", - "settings": "Ρυθμίσεις", - "share": "Κοινοποίηση", - "sharedTheLocation": "{username} κοινοποίησε την τοποθεσία του", - "shareLocation": "Κοινοποίηση τοποθεσίας", - "showPassword": "Εμφάνιση κωδικού πρόσβασης", - "presenceStyle": "Παρουσία:", - "presencesToggle": "Εμφάνιση μηνυμάτων κατάστασης από άλλους χρήστες", - "singlesignon": "Ενιαία σύνδεση", - "skip": "Παραλείπω", - "sourceCode": "Κώδικας πηγής", - "spaceIsPublic": "Ο χώρος είναι δημόσιος", - "spaceName": "Όνομα χώρου", - "startedACall": "{senderName} ξεκίνησε μια κλήση", - "startFirstChat": "Ξεκινήστε την πρώτη σας συνομιλία", - "status": "Κατάσταση", - "statusExampleMessage": "Πώς είστε σήμερα;", - "submit": "Υποβολή", - "synchronizingPleaseWait": "Συγχρονισμός… Παρακαλώ περιμένετε.", - "synchronizingPleaseWaitCounter": "Συγχρονισμός… ({percentage}%)", - "systemTheme": "Σύστημα", - "theyDontMatch": "Δεν ταιριάζουν", - "theyMatch": "Ταιριάζουν", - "title": "FluffyChat", - "toggleFavorite": "Εναλλαγή Αγαπημένου", - "toggleMuted": "Εναλλαγή σίγασης", - "toggleUnread": "Σήμανση ως διαβασμένο/μη διαβασμένο", - "tooManyRequestsWarning": "Πάρα πολλά αιτήματα. Παρακαλώ δοκιμάστε ξανά αργότερα!", - "transferFromAnotherDevice": "Μεταφορά από άλλη συσκευή", - "tryToSendAgain": "Προσπαθήστε ξανά να στείλετε", - "unavailable": "Μη διαθέσιμο", - "unbannedUser": "{username} άρπαξε τον αποκλεισμό του {targetName}", - "unblockDevice": "Ξεκλειδώστε τη συσκευή", - "unknownDevice": "Άγνωστη συσκευή", - "unknownEncryptionAlgorithm": "Άγνωστος αλγόριθμος κρυπτογράφησης", - "unknownEvent": "Άγνωστο γεγονός '{type}'", - "unmuteChat": "Αναίρεση σίγασης συνομιλίας", - "unpin": "Αφαίρεση από την κορυφή", - "unreadChats": "{unreadCount, plural, =1{1 μη αναγνωσμένο μήνυμα} other{{unreadCount} μη αναγνωσμένα μηνύματα}}", - "userAndOthersAreTyping": "{username} και {count} άλλοι πληκτρολογούν…", - "userAndUserAreTyping": "{username} και {username2} πληκτρολογούν…", - "userIsTyping": "{username} πληκτρολογεί…", - "userLeftTheChat": "🚪 {username} έφυγε από τη συνομιλία", - "username": "Όνομα χρήστη", - "userSentUnknownEvent": "{username} έστειλε ένα γεγονός {type}", - "unverified": "Μη επαληθευμένο", - "verified": "Επαληθευμένο", - "verify": "Επαλήθευση", - "verifyStart": "Ξεκινήστε την επαλήθευση", - "verifySuccess": "Επιτυχής επαλήθευση!", - "verifyTitle": "Επαλήθευση άλλου λογαριασμού", - "videoCall": "Βιντεοκλήση", - "visibilityOfTheChatHistory": "Ορατότητα του ιστορικού συνομιλίας", - "visibleForAllParticipants": "Ορατό για όλους τους συμμετέχοντες", - "visibleForEveryone": "Ορατό για όλους", - "voiceMessage": "Φωνητικό μήνυμα", - "waitingPartnerAcceptRequest": "Αναμονή για αποδοχή του αιτήματος από τον συνεργάτη…", - "waitingPartnerEmoji": "Αναμονή για αποδοχή του emoji από τον συνεργάτη…", - "waitingPartnerNumbers": "Αναμονή για αποδοχή των αριθμών από τον συνεργάτη…", - "wallpaper": "Φόντο:", - "warning": "Προειδοποίηση!", - "weSentYouAnEmail": "Σας στείλαμε ένα email", - "whoCanPerformWhichAction": "Ποιος μπορεί να εκτελέσει ποια ενέργεια", - "whoIsAllowedToJoinThisGroup": "Ποιος επιτρέπεται να συμμετάσχει σε αυτήν την ομάδα", - "whyDoYouWantToReportThis": "Γιατί θέλετε να αναφέρετε αυτό;", - "wipeChatBackup": "Διαγράψτε το αντίγραφο ασφαλείας της συνομιλίας σας για να δημιουργήσετε ένα νέο κλειδί ανάκτησης;", - "withTheseAddressesRecoveryDescription": "Με αυτές τις διευθύνσεις μπορείτε να ανακτήσετε τον κωδικό πρόσβασής σας.", - "writeAMessage": "Γράψτε ένα μήνυμα…", - "yes": "Ναι", - "you": "Εσύ", - "youAreNoLongerParticipatingInThisChat": "Δεν συμμετέχετε πλέον σε αυτήν τη συνομιλία", - "youHaveBeenBannedFromThisChat": "Έχετε αποκλειστεί από αυτήν τη συνομιλία", - "yourPublicKey": "Το δημόσιο κλειδί σας", - "messageInfo": "Πληροφορίες μηνύματος", - "time": "Χρόνος", - "messageType": "Τύπος μηνύματος", - "sender": "Αποστολέας", - "openGallery": "Άνοιγμα γκαλερί", - "removeFromSpace": "Αφαίρεση από τον χώρο", - "addToSpaceDescription": "Επιλέξτε έναν χώρο στον οποίο θα προσθέσετε αυτήν τη συνομιλία.", - "start": "Έναρξη", - "pleaseEnterRecoveryKeyDescription": "Για να ξεκλειδώσετε τα παλιά μηνύματά σας, εισάγετε το κλειδί ανάκτησης που δημιουργήθηκε σε προηγούμενη συνεδρία. Το κλειδί ανάκτησης ΔΕΝ είναι ο κωδικός πρόσβασής σας.", - "publish": "Δημοσίευση", - "videoWithSize": "Βίντεο ({size})", - "openChat": "Άνοιγμα συνομιλίας", - "markAsRead": "Σημείωσε ως διαβασμένο", - "reportUser": "Αναφορά χρήστη", - "dismiss": "Απόρριψη", - "reactedWith": "{sender} αντέδρασε με {reaction}", - "pinMessage": "Καρφίτσωσε στο δωμάτιο", - "confirmEventUnpin": "Είστε βέβαιοι ότι θέλετε να αποσυνδέσετε οριστικά το γεγονός;", - "emojis": "Εμοji", - "placeCall": "Κάντε κλήση", - "voiceCall": "Φωνητική κλήση", - "unsupportedAndroidVersion": "Μη υποστηριζόμενη έκδοση Android", - "unsupportedAndroidVersionLong": "Αυτή η λειτουργία απαιτεί νεότερη έκδοση Android. Παρακαλούμε ελέγξτε για ενημερώσεις ή υποστήριξη Lineage OS.", - "videoCallsBetaWarning": "Παρακαλούμε σημειώστε ότι οι βιντεοκλήσεις βρίσκονται επί του παρόντος σε beta. Μπορεί να μην λειτουργούν όπως αναμένεται ή καθόλου σε όλες τις πλατφόρμες.", - "experimentalVideoCalls": "Πειραματικές βιντεοκλήσεις", - "emailOrUsername": "Email ή όνομα χρήστη", - "indexedDbErrorTitle": "Προβλήματα ιδιωτικής λειτουργίας", - "indexedDbErrorLong": "Η αποθήκευση μηνυμάτων δυστυχώς δεν ενεργοποιείται από προεπιλογή σε ιδιωτική λειτουργία.\nΠαρακαλούμε επισκεφθείτε\n - about:config\n - ορίστε το dom.indexedDB.privateBrowsing.enabled σε true\nΑλλιώς, δεν είναι δυνατή η εκτέλεση του FluffyChat.", - "switchToAccount": "Μεταβείτε στον λογαριασμό {number}", - "nextAccount": "Επόμενος λογαριασμός", - "previousAccount": "Προηγούμενος λογαριασμός", - "addWidget": "Προσθήκη widget", - "widgetVideo": "Βίντεο", - "widgetEtherpad": "Κείμενο σημείωσης", - "widgetJitsi": "Jitsi Meet", - "widgetCustom": "Προσαρμοσμένο", - "widgetName": "Όνομα", - "widgetUrlError": "Αυτό δεν είναι έγκυρη διεύθυνση URL.", - "widgetNameError": "Παρακαλώ δώστε ένα όνομα εμφάνισης.", - "errorAddingWidget": "Σφάλμα κατά την προσθήκη του widget.", - "youRejectedTheInvitation": "Απορρίψατε την πρόσκληση", - "youJoinedTheChat": "Έχετε συμμετάσχει στη συνομιλία", - "youAcceptedTheInvitation": "👍 Έχετε αποδεχθεί την πρόσκληση", - "youBannedUser": "Αποκλείσατε τον {user}", - "youHaveWithdrawnTheInvitationFor": "Έχετε αποσύρει την πρόσκληση για τον {user}", - "youInvitedToBy": "📩 Έχετε προσκληθεί μέσω συνδέσμου στο:\n{alias}", - "youInvitedBy": "📩 Έχετε προσκληθεί από τον {user}", - "invitedBy": "📩 Προσκληθείς από τον {user}", - "youInvitedUser": "📩 Προσκαλέσατε τον {user}", - "youKicked": "👞 Απομακρύνατε τον {user}", - "youKickedAndBanned": "🙅‍♀️ Απομακρύνατε και αποκλείσατε τον {user}", - "youUnbannedUser": "Απελευθερώσατε τον {user}", - "hasKnocked": "🚪 {user} χτύπησε", - "usersMustKnock": "Οι χρήστες πρέπει να χτυπήσουν", - "noOneCanJoin": "Κανείς δεν μπορεί να συμμετάσχει", - "userWouldLikeToChangeTheChat": "{user} θα ήθελε να συμμετάσχει στη συζήτηση.", - "noPublicLinkHasBeenCreatedYet": "Δεν έχει δημιουργηθεί ακόμη δημόσιος σύνδεσμος", - "knock": "Χτύπημα", - "users": "Χρήστες", - "unlockOldMessages": "Ξεκλείδωμα παλαιών μηνυμάτων", - "storeInSecureStorageDescription": "Αποθηκεύστε το κλειδί ανάκαμψης στην ασφαλή αποθήκευση αυτής της συσκευής.", - "saveKeyManuallyDescription": "Αποθηκεύστε αυτό το κλειδί χειροκίνητα ενεργοποιώντας το διάλογο κοινής χρήσης συστήματος ή το πρόχειρο.", - "storeInAndroidKeystore": "Αποθήκευση στο Android KeyStore", - "storeInAppleKeyChain": "Αποθήκευση στο Apple KeyChain", - "storeSecurlyOnThisDevice": "Αποθήκευση με ασφάλεια σε αυτήν τη συσκευή", - "countFiles": "{count} αρχεία", - "user": "Χρήστης", - "custom": "Προσαρμοσμένο", - "foregroundServiceRunning": "Αυτή η ειδοποίηση εμφανίζεται όταν τρέχει η υπηρεσία μπροστά.", - "screenSharingTitle": "κοινή χρήση οθόνης", - "screenSharingDetail": "Μοιράζεστε την οθόνη σας στο FuffyChat", - "callingPermissions": "Άδειες κλήσης", - "callingAccount": "Λογαριασμός κλήσης", - "callingAccountDetails": "Επιτρέπει στο FluffyChat να χρησιμοποιεί την εγγενή εφαρμογή τηλεφωνητή Android.", - "appearOnTop": "Εμφάνιση στην κορυφή", - "appearOnTopDetails": "Επιτρέπει στην εφαρμογή να εμφανίζεται στην κορυφή (δεν χρειάζεται αν έχετε ήδη ρυθμίσει το FluffyChat ως λογαριασμό κλήσης)", - "otherCallingPermissions": "Άδειες μικροφώνου, κάμερας και άλλες άδειες του FluffyChat", - "whyIsThisMessageEncrypted": "Γιατί αυτό το μήνυμα είναι μη αναγνώσιμο;", - "noKeyForThisMessage": "Αυτό μπορεί να συμβεί αν το μήνυμα στάλθηκε πριν συνδεθείτε στον λογαριασμό σας σε αυτήν τη συσκευή.\n\nΕίναι επίσης πιθανό ο αποστολέας να έχει μπλοκάρει τη συσκευή σας ή να υπήρξε κάποιο πρόβλημα με τη σύνδεση στο διαδίκτυο.\n\nΜπορείτε να διαβάσετε το μήνυμα σε μια άλλη συνεδρία; Τότε μπορείτε να μεταφέρετε το μήνυμα από αυτήν! Μεταβείτε στις Ρυθμίσεις > Συσκευές και βεβαιωθείτε ότι οι συσκευές σας έχουν επαληθευτεί η μία την άλλη. Όταν ανοίξετε ξανά το δωμάτιο και και οι δύο συνεδρίες είναι στην μπροστινή γραμμή, τα κλειδιά θα μεταδοθούν αυτόματα.\n\nΔεν θέλετε να χάσετε τα κλειδιά κατά το αποσύνδεση ή την αλλαγή συσκευής; Βεβαιωθείτε ότι έχετε ενεργοποιήσει την εφεδρεία συνομιλίας στις ρυθμίσεις.", - "newGroup": "Νέα ομάδα", - "newSpace": "Νέος χώρος", - "enterSpace": "Εισέλθετε στον χώρο", - "enterRoom": "Εισέλθετε στο δωμάτιο", - "allSpaces": "Όλοι οι χώροι", - "numChats": "{number} συνομιλίες", - "hideUnimportantStateEvents": "Απόκρυψη άσχετων γεγονότων κατάστασης", - "hidePresences": "Απόκρυψη λίστας κατάστασης;", - "doNotShowAgain": "Μην το εμφανίζετε ξανά", - "wasDirectChatDisplayName": "Άδειο chat (ήταν {oldDisplayName})", - "newSpaceDescription": "Οι χώροι σας επιτρέπουν να ενοποιήσετε τις συνομιλίες σας και να δημιουργήσετε ιδιωτικές ή δημόσιες κοινότητες.", - "encryptThisChat": "Κρυπτογραφήστε αυτήν τη συνομιλία", - "disableEncryptionWarning": "Για λόγους ασφαλείας, δεν μπορείτε να απενεργοποιήσετε την κρυπτογράφηση σε μια συνομιλία όπου είχε ενεργοποιηθεί προηγουμένως.", - "sorryThatsNotPossible": "Λυπούμαστε... αυτό δεν είναι δυνατό", - "deviceKeys": "Κλειδιά συσκευής:", - "reopenChat": "Άνοιγμα ξανά συνομιλίας", - "noBackupWarning": "Προειδοποίηση! Χωρίς ενεργοποίηση αντιγράφου ασφαλείας συνομιλίας, θα χάσετε την πρόσβαση στα κρυπτογραφημένα μηνύματά σας. Συνιστάται ιδιαίτερα να ενεργοποιήσετε το αντίγραφο ασφαλείας πριν αποσυνδεθείτε.", - "noOtherDevicesFound": "Δεν βρέθηκαν άλλες συσκευές", - "fileIsTooBigForServer": "Αδύνατη η αποστολή! Ο διακομιστής υποστηρίζει μόνο συνημμένα έως {max}.", - "fileHasBeenSavedAt": "Το αρχείο έχει αποθηκευτεί στο {path}", - "jumpToLastReadMessage": "Μετάβαση στο τελευταίο διαβασμένο μήνυμα", - "readUpToHere": "Διάβασε μέχρι εδώ", - "jump": "Μετάβαση", - "openLinkInBrowser": "Άνοιγμα συνδέσμου στον περιηγητή", - "reportErrorDescription": "😞 Ωχ. Κάτι πήγε στραβά. Αν θέλετε, μπορείτε να αναφέρετε αυτό το σφάλμα στους προγραμματιστές.", - "report": "αναφορά", - "signInWithPassword": "Σύνδεση με κωδικό πρόσβασης", - "pleaseTryAgainLaterOrChooseDifferentServer": "Παρακαλώ δοκιμάστε ξανά αργότερα ή επιλέξτε διαφορετικό διακομιστή.", - "signInWith": "Σύνδεση με {provider}", - "profileNotFound": "Ο χρήστης δεν βρέθηκε στον διακομιστή. Ίσως υπάρχει πρόβλημα σύνδεσης ή ο χρήστης δεν υπάρχει.", - "setTheme": "Ορίστε θέμα:", - "setColorTheme": "Ορίστε χρωματικό θέμα:", - "invite": "Πρόσκληση", - "inviteGroupChat": "📨 Πρόσκληση σε ομαδική συνομιλία", - "invitePrivateChat": "📨 Πρόσκληση σε ιδιωτική συνομιλία", - "invalidInput": "Μη έγκυρη εισαγωγή!", - "wrongPinEntered": "Λάθος PIN εισήχθη! Δοκιμάστε ξανά σε {seconds} δευτερόλεπτα...", - "pleaseEnterANumber": "Παρακαλώ εισάγετε έναν αριθμό μεγαλύτερο από το 0", - "archiveRoomDescription": "Η συνομιλία θα μετακινηθεί στο αρχείο. Άλλοι χρήστες θα μπορούν να δουν ότι έχετε φύγει από τη συνομιλία.", - "roomUpgradeDescription": "Η συνομιλία θα αναδημιουργηθεί με τη νέα έκδοση δωματίου. Όλοι οι συμμετέχοντες θα ειδοποιηθούν ότι πρέπει να μεταβούν στη νέα συνομιλία. Μπορείτε να μάθετε περισσότερα για τις εκδόσεις δωματίων στο https://spec.matrix.org/latest/rooms/", - "removeDevicesDescription": "Θα αποσυνδεθείτε από αυτήν τη συσκευή και δεν θα μπορείτε πλέον να λαμβάνετε μηνύματα.", - "banUserDescription": "Ο χρήστης θα αποκλειστεί από τη συνομιλία και δεν θα μπορεί να εισέλθει ξανά μέχρι να αφαιρεθεί ο αποκλεισμός.", - "unbanUserDescription": "Ο χρήστης θα μπορεί να εισέλθει ξανά στη συνομιλία αν προσπαθήσει.", - "kickUserDescription": "Ο χρήστης αποβάλλεται από τη συνομιλία αλλά δεν αποκλείεται. Στις δημόσιες συνομιλίες, ο χρήστης μπορεί να επανενταχθεί οποιαδήποτε στιγμή.", - "makeAdminDescription": "Αφού κάνετε αυτόν τον χρήστη διαχειριστή, ίσως να μην μπορείτε να αναιρέσετε αυτό, καθώς θα έχει τις ίδιες άδειες με εσάς.", - "pushNotificationsNotAvailable": "Οι ειδοποιήσεις push δεν είναι διαθέσιμες", - "learnMore": "Μάθετε περισσότερα", - "yourGlobalUserIdIs": "Το παγκόσμιο αναγνωριστικό χρήστη σας είναι: ", - "noUsersFoundWithQuery": "Δυστυχώς, δεν βρέθηκε χρήστης με την ερώτηση \"{query}\". Παρακαλώ ελέγξτε αν κάνατε τυπογραφικό λάθος.", - "knocking": "Χτυπάει", - "chatCanBeDiscoveredViaSearchOnServer": "Η συνομιλία μπορεί να βρεθεί μέσω της αναζήτησης στο {server}", - "searchChatsRooms": "Αναζήτηση για #συνομιλίες, @χρήστες...", - "nothingFound": "Δεν βρέθηκε τίποτα...", - "groupName": "Όνομα ομάδας", - "createGroupAndInviteUsers": "Δημιουργήστε μια ομάδα και προσκαλέστε χρήστες", - "groupCanBeFoundViaSearch": "Η ομάδα μπορεί να βρεθεί μέσω αναζήτησης", - "wrongRecoveryKey": "Λυπάμαι... αυτό δεν φαίνεται να είναι το σωστό κλειδί ανάκτησης.", - "startConversation": "Ξεκινήστε συνομιλία", - "commandHint_sendraw": "Αποστολή ακατέργαστου json", - "databaseMigrationTitle": "Η βάση δεδομένων είναι βελτιστοποιημένη", - "databaseMigrationBody": "Παρακαλώ περιμένετε. Αυτό μπορεί να διαρκέσει λίγα λεπτά.", - "leaveEmptyToClearStatus": "Αφήστε κενό για να διαγράψετε την κατάσταση σας.", - "select": "Επιλογή", - "searchForUsers": "Αναζήτηση για @χρήστες...", - "pleaseEnterYourCurrentPassword": "Παρακαλώ εισάγετε τον τρέχοντα κωδικό πρόσβασής σας", - "newPassword": "Νέος κωδικός πρόσβασης", - "pleaseChooseAStrongPassword": "Παρακαλώ επιλέξτε έναν ισχυρό κωδικό πρόσβασης", - "passwordsDoNotMatch": "Οι κωδικοί πρόσβασης δεν ταιριάζουν", - "passwordIsWrong": "Ο εισαγόμενος κωδικός πρόσβασης είναι λανθασμένος", - "publicLink": "Δημόσιος σύνδεσμος", - "publicChatAddresses": "Διευθύνσεις δημόσιων συνομιλιών", - "createNewAddress": "Δημιουργία νέας διεύθυνσης", - "joinSpace": "Συμμετοχή στον χώρο", - "publicSpaces": "Δημόσιοι χώροι", - "addChatOrSubSpace": "Προσθήκη συνομιλίας ή υποχώρου", - "subspace": "Υποχώρος", - "decline": "Απόρριψη", - "thisDevice": "Αυτή η συσκευή:", - "initAppError": "Παρουσιάστηκε σφάλμα κατά την αρχικοποίηση της εφαρμογής", - "userRole": "Ρόλος χρήστη", - "minimumPowerLevel": "{level} είναι το ελάχιστο επίπεδο ισχύος.", - "searchIn": "Αναζήτηση στη συνομιλία \"{chat}\"...", - "searchMore": "Αναζήτηση περισσότερο...", - "gallery": "Πινακοθήκη", - "files": "Αρχεία", - "databaseBuildErrorBody": "Αδυναμία δημιουργίας της βάσης δεδομένων SQLite. Η εφαρμογή προσπαθεί να χρησιμοποιήσει την παλαιά βάση δεδομένων προς το παρόν. Παρακαλούμε αναφέρετε αυτό το σφάλμα στους προγραμματιστές στο {url}. Το μήνυμα σφάλματος είναι: {error}", - "sessionLostBody": "Η συνεδρία σας χάθηκε. Παρακαλούμε αναφέρετε αυτό το σφάλμα στους προγραμματιστές στο {url}. Το μήνυμα σφάλματος είναι: {error}", - "restoreSessionBody": "Η εφαρμογή προσπαθεί τώρα να επαναφέρει τη συνεδρία σας από το αντίγραφο ασφαλείας. Παρακαλούμε αναφέρετε αυτό το σφάλμα στους προγραμματιστές στο {url}. Το μήνυμα σφάλματος είναι: {error}", - "forwardMessageTo": "Αποστολή μηνύματος στο {roomName}?", - "sendReadReceipts": "Αποστολή αποδείξεων ανάγνωσης", - "sendTypingNotificationsDescription": "Οι άλλοι συμμετέχοντες σε μια συνομιλία μπορούν να δουν πότε πληκτρολογείτε ένα νέο μήνυμα.", - "sendReadReceiptsDescription": "Οι άλλοι συμμετέχοντες σε μια συνομιλία μπορούν να δουν πότε έχετε διαβάσει ένα μήνυμα.", - "formattedMessages": "Μορφοποιημένα μηνύματα", - "formattedMessagesDescription": "Εμφάνιση πλούσιου περιεχομένου μηνυμάτων όπως έντονο κείμενο χρησιμοποιώντας markdown.", - "verifyOtherUser": "🔐 Επιβεβαίωση άλλου χρήστη", - "verifyOtherUserDescription": "Αν επιβεβαιώσετε έναν άλλο χρήστη, μπορείτε να είστε σίγουροι ότι γνωρίζετε πραγματικά σε ποιον γράφετε. 💪\n\nΌταν ξεκινάτε μια επιβεβαίωση, εσείς και ο άλλος χρήστης θα δείτε ένα αναδυόμενο παράθυρο στην εφαρμογή. Εκεί θα δείτε μια σειρά από emoji ή αριθμούς που πρέπει να συγκρίνετε μεταξύ σας.\n\nΟ καλύτερος τρόπος να το κάνετε αυτό είναι να συναντηθείτε ή να ξεκινήσετε μια βιντεκλήση. 👭", - "verifyOtherDevice": "🔐 Επιβεβαίωση άλλης συσκευής", - "verifyOtherDeviceDescription": "Όταν επιβεβαιώνετε μια άλλη συσκευή, αυτές οι συσκευές μπορούν να ανταλλάξουν κλειδιά, αυξάνοντας τη συνολική ασφάλειά σας. 💪 Όταν ξεκινάτε μια επιβεβαίωση, θα εμφανιστεί ένα αναδυόμενο παράθυρο στην εφαρμογή και στις δύο συσκευές. Εκεί θα δείτε μια σειρά από emoji ή αριθμούς που πρέπει να συγκρίνετε. Είναι καλύτερο να έχετε και τις δύο συσκευές έτοιμες πριν ξεκινήσετε την επιβεβαίωση. 🤳", - "acceptedKeyVerification": "{sender} αποδέχτηκε την επιβεβαίωση κλειδιού", - "canceledKeyVerification": "{sender} ακύρωσε την επιβεβαίωση κλειδιού", - "completedKeyVerification": "{sender} ολοκλήρωσε την επιβεβαίωση κλειδιού", - "isReadyForKeyVerification": "{sender} είναι έτοιμος/η για επιβεβαίωση κλειδιού", - "requestedKeyVerification": "{sender} ζήτησε επιβεβαίωση κλειδιού", - "startedKeyVerification": "{sender} ξεκίνησε την επιβεβαίωση κλειδιού", - "transparent": "Διαφανές", - "incomingMessages": "Εισερχόμενα μηνύματα", - "stickers": "Αυτοκόλλητα", - "discover": "Ανακάλυψη", - "commandHint_ignore": "Αγνόηση του δοθέντος ταυτότητας matrix", - "commandHint_unignore": "Αναίρεση αγνόησης του δοθέντος ταυτότητας matrix", - "unreadChatsInApp": "{appname}: {unread} μη αναγνωσμένα chats", - "noDatabaseEncryption": "Η κρυπτογράφηση βάσης δεδομένων δεν υποστηρίζεται σε αυτήν την πλατφόρμα", - "thereAreCountUsersBlocked": "Αυτήν τη στιγμή υπάρχουν {count} αποκλεισμένοι χρήστες.", - "restricted": "Περιορισμένο", - "knockRestricted": "Περιορισμός χτυπήματος", - "goToSpace": "Μεταβείτε στον χώρο: {space}", - "markAsUnread": "Σήμανση ως μη αναγνωσμένο", - "userLevel": "{level} - Χρήστης", - "moderatorLevel": "{level} - Διαχειριστής", - "adminLevel": "{level} - Διαχειριστής", - "changeGeneralChatSettings": "Αλλαγή γενικών ρυθμίσεων συνομιλίας", - "inviteOtherUsers": "Πρόσκληση άλλων χρηστών σε αυτήν τη συνομιλία", - "changeTheChatPermissions": "Αλλαγή των δικαιωμάτων συνομιλίας", - "changeTheVisibilityOfChatHistory": "Αλλαγή της ορατότητας του ιστορικού συνομιλίας", - "changeTheCanonicalRoomAlias": "Αλλαγή της κύριας δημόσιας διεύθυνσης συνομιλίας", - "sendRoomNotifications": "Αποστολή ειδοποιήσεων @room", - "changeTheDescriptionOfTheGroup": "Αλλαγή της περιγραφής της συνομιλίας", - "chatPermissionsDescription": "Ορίστε ποιο επίπεδο δύναμης είναι απαραίτητο για ορισμένες ενέργειες σε αυτήν τη συνομιλία. Τα επίπεδα δύναμης 0, 50 και 100 συνήθως αντιπροσωπεύουν χρήστες, διαχειριστές και διαχειριστές, αλλά οποιαδήποτε βαθμίδα είναι δυνατή.", - "updateInstalled": "🎉 Εγκαταστάθηκε η ενημέρωση {version}!", - "changelog": "Αλλαγές", - "sendCanceled": "Η αποστολή ακυρώθηκε", - "loginWithMatrixId": "Σύνδεση με Matrix-ID", - "discoverHomeservers": "Ανακαλύψτε τους διακομιστές", - "whatIsAHomeserver": "Τι είναι ένας διακομιστής σπιτιού;", - "homeserverDescription": "Όλα τα δεδομένα σας αποθηκεύονται στον διακομιστή σπιτιού, όπως και ένας πάροχος email. Μπορείτε να επιλέξετε ποιον διακομιστή σπιτιού θέλετε να χρησιμοποιήσετε, ενώ μπορείτε ακόμα να επικοινωνείτε με όλους. Μάθετε περισσότερα στο https://matrix.org.", - "doesNotSeemToBeAValidHomeserver": "Δεν φαίνεται να είναι συμβατός διακομιστής σπιτιού. Λάθος URL;", - "calculatingFileSize": "Υπολογισμός μεγέθους αρχείου...", - "prepareSendingAttachment": "Ετοιμασία αποστολής συνημμένου...", - "sendingAttachment": "Αποστολή συνημμένου...", - "generatingVideoThumbnail": "Δημιουργία μικρογραφίας βίντεο...", - "compressVideo": "Συμπίεση βίντεο...", - "sendingAttachmentCountOfCount": "Αποστολή συνημμένου {index} από {length}...", - "serverLimitReached": "Έφτασε το όριο του διακομιστή! Περιμένετε {seconds} δευτερόλεπτα...", - "oneOfYourDevicesIsNotVerified": "Ένα από τα συσκευές σας δεν είναι επαληθευμένο", - "noticeChatBackupDeviceVerification": "Σημείωση: Όταν συνδέσετε όλες τις συσκευές σας στο αντίγραφο ασφαλείας συνομιλίας, αυτομάτως επαληθεύονται.", - "continueText": "Συνέχεια", - "welcomeText": "Γεια Γεια 👋 Αυτό είναι το FluffyChat. Μπορείτε να συνδεθείτε σε οποιονδήποτε διακομιστή σπιτιού, ο οποίος είναι συμβατός με το https://matrix.org. Και στη συνέχεια να συνομιλήσετε με οποιονδήποτε. Είναι ένα τεράστιο αποκεντρωμένο δίκτυο μηνυμάτων!", - "blur": "Θάμπωμα:", - "opacity": "Αδιαφάνεια:", - "setWallpaper": "Ορισμός φόντου", - "manageAccount": "Διαχείριση λογαριασμού", - "noContactInformationProvided": "Ο διακομιστής δεν παρέχει έγκυρες πληροφορίες επικοινωνίας", - "contactServerAdmin": "Επικοινωνήστε με τον διαχειριστή του διακομιστή", - "contactServerSecurity": "Επικοινωνία με την ασφάλεια διακομιστή", - "supportPage": "Σελίδα υποστήριξης", - "serverInformation": "Πληροφορίες διακομιστή:", - "name": "Όνομα", - "version": "Έκδοση", - "website": "Ιστοσελίδα", - "compress": "Συμπίεση", - "boldText": "Έντονο κείμενο", - "italicText": "Πλάγιο κείμενο", - "strikeThrough": "Διαγραφή", - "pleaseFillOut": "Παρακαλώ συμπληρώστε", - "invalidUrl": "Μη έγκυρο URL", - "addLink": "Προσθήκη συνδέσμου", - "unableToJoinChat": "Αδύνατη η συμμετοχή στη συνομιλία. Ίσως το άλλο μέρος έχει ήδη κλείσει τη συνομιλία.", - "previous": "Προηγούμενο", - "otherPartyNotLoggedIn": "Το άλλο μέρος δεν είναι αυτήν τη στιγμή συνδεδεμένο και επομένως δεν μπορεί να λάβει μηνύματα!", - "appWantsToUseForLogin": "Χρησιμοποιήστε '{server}' για σύνδεση", - "appWantsToUseForLoginDescription": "Επιτρέπετε στην εφαρμογή και στον ιστότοπο να μοιράζονται πληροφορίες σχετικά με εσάς.", - "open": "Άνοιγμα", - "waitingForServer": "Αναμονή για διακομιστή...", - "appIntroduction": "Το FluffyChat σας επιτρέπει να συνομιλείτε με τους φίλους σας σε διαφορετικά μηνύματα. Μάθετε περισσότερα στο https://matrix.org ή απλώς πατήστε *Συνέχεια*.", - "newChatRequest": "📩 Νέα αίτηση συνομιλίας", - "contentNotificationSettings": "Ρυθμίσεις ειδοποιήσεων περιεχομένου", - "generalNotificationSettings": "Γενικές ρυθμίσεις ειδοποιήσεων", - "roomNotificationSettings": "Ρυθμίσεις ειδοποιήσεων δωματίου", - "userSpecificNotificationSettings": "Εξατομικευμένες ρυθμίσεις ειδοποιήσεων χρήστη", - "otherNotificationSettings": "Άλλες ρυθμίσεις ειδοποιήσεων", - "notificationRuleContainsUserName": "Περιέχει όνομα χρήστη", - "notificationRuleContainsUserNameDescription": "Ειδοποιεί τον χρήστη όταν ένα μήνυμα περιέχει το όνομα χρήστη του.", - "notificationRuleMaster": "Απενεργοποίηση όλων των ειδοποιήσεων", - "notificationRuleMasterDescription": "Παρακάμπτει όλους τους άλλους κανόνες και απενεργοποιεί όλες τις ειδοποιήσεις.", - "notificationRuleSuppressNotices": "Καταστολή αυτόματων μηνυμάτων", - "notificationRuleSuppressNoticesDescription": "Καταστέλλει τις ειδοποιήσεις από αυτοματοποιμένους πελάτες όπως τα bots.", - "notificationRuleInviteForMe": "Πρόσκληση για μένα", - "notificationRuleInviteForMeDescription": "Ειδοποιεί τον χρήστη όταν λαμβάνει πρόσκληση σε δωμάτιο.", - "notificationRuleMemberEvent": "Γεγονός μέλους", - "notificationRuleMemberEventDescription": "Καταστέλλει τις ειδοποιήσεις για γεγονότα μέλους.", - "notificationRuleIsUserMention": "Αναφορά χρήστη", - "notificationRuleIsUserMentionDescription": "Ειδοποιεί τον χρήστη όταν αναφέρεται άμεσα σε μήνυμα.", - "notificationRuleContainsDisplayName": "Περιέχει όνομα εμφάνισης", - "notificationRuleContainsDisplayNameDescription": "Ειδοποιεί τον χρήστη όταν ένα μήνυμα περιέχει το όνομά του.", - "notificationRuleIsRoomMention": "Αναφορά Δωματίου", - "notificationRuleIsRoomMentionDescription": "Ειδοποιεί τον χρήστη όταν υπάρχει αναφορά σε δωμάτιο.", - "notificationRuleRoomnotif": "Ειδοποίηση Δωματίου", - "notificationRuleRoomnotifDescription": "Ειδοποιεί τον χρήστη όταν ένα μήνυμα περιέχει '@room'.", - "notificationRuleTombstone": "Ταφόπλακα", - "notificationRuleTombstoneDescription": "Ειδοποιεί τον χρήστη σχετικά με μηνύματα απενεργοποίησης δωματίου.", - "notificationRuleReaction": "Αντίδραση", - "notificationRuleReactionDescription": "Καταστέλλει τις ειδοποιήσεις για αντιδράσεις.", - "notificationRuleRoomServerAcl": "Πρόσβαση Δωματίου στον Διακομιστή", - "notificationRuleRoomServerAclDescription": "Καταστέλλει τις ειδοποιήσεις για λίστες ελέγχου πρόσβασης (ACL) του διακομιστή δωματίου.", - "notificationRuleSuppressEdits": "Καταστολή Επεξεργασιών", - "notificationRuleSuppressEditsDescription": "Καταστέλλει τις ειδοποιήσεις για επεξεργασμένα μηνύματα.", - "notificationRuleCall": "Κλήση", - "notificationRuleCallDescription": "Ειδοποιεί τον χρήστη σχετικά με κλήσεις.", - "notificationRuleEncryptedRoomOneToOne": "Κρυπτογραφημένο Δωμάτιο Μία προς Μία", - "notificationRuleEncryptedRoomOneToOneDescription": "Ειδοποιεί τον χρήστη σχετικά με μηνύματα σε κρυπτογραφημένα δωμάτια ένα προς ένα.", - "notificationRuleRoomOneToOne": "Δωμάτιο Μία προς Μία", - "notificationRuleRoomOneToOneDescription": "Ειδοποιεί τον χρήστη σχετικά με μηνύματα σε δωμάτια ένα προς ένα.", - "notificationRuleMessage": "Μήνυμα", - "notificationRuleMessageDescription": "Ειδοποιεί τον χρήστη για γενικά μηνύματα.", - "notificationRuleEncrypted": "Κρυπτογραφημένο", - "notificationRuleEncryptedDescription": "Ειδοποιεί τον χρήστη για μηνύματα σε κρυπτογραφημένους δωματίους.", - "notificationRuleJitsi": "Jitsi", - "notificationRuleJitsiDescription": "Ειδοποιεί τον χρήστη για γεγονότα widget Jitsi.", - "notificationRuleServerAcl": "Κατάπνιξε γεγονότα Server ACL", - "notificationRuleServerAclDescription": "Καταπνίγει τις ειδοποιήσεις για γεγονότα Server ACL.", - "unknownPushRule": "Άγνωστος κανόνας ώθησης '{rule}'", - "sentVoiceMessage": "🎙️ {duration} - Φωνητικό μήνυμα από {sender}", - "deletePushRuleCanNotBeUndone": "Εάν διαγράψετε αυτήν τη ρύθμιση ειδοποίησης, αυτό δεν μπορεί να αναιρεθεί.", - "more": "Περισσότερα", - "shareKeysWith": "Μοιραστείτε κλειδιά με...", - "shareKeysWithDescription": "Ποια συσκευές πρέπει να είναι αξιόπιστες ώστε να μπορούν να διαβάσουν τα μηνύματά σας σε κρυπτογραφημένες συνομιλίες;", - "allDevices": "Όλες οι συσκευές", - "crossVerifiedDevicesIfEnabled": "Διασταυρωμένες επαληθευμένες συσκευές αν είναι ενεργοποιημένο", - "crossVerifiedDevices": "Διασταυρωμένες επαληθευμένες συσκευές", - "verifiedDevicesOnly": "Μόνο επαληθευμένες συσκευές", - "takeAPhoto": "Βγάλτε μια φωτογραφία", - "recordAVideo": "Καταγράψτε ένα βίντεο", - "optionalMessage": "(Προαιρετικό) μήνυμα...", - "notSupportedOnThisDevice": "Δεν υποστηρίζεται σε αυτήν τη συσκευή", - "enterNewChat": "Ξεκινήστε νέο chat", - "approve": "Έγκριση", - "youHaveKnocked": "Έχετε χτυπήσει", - "pleaseWaitUntilInvited": "Παρακαλώ περιμένετε τώρα, μέχρι κάποιος από το δωμάτιο να σας προσκαλέσει.", - "commandHint_logout": "Αποσυνδεθείτε από τη τρέχουσα συσκευή σας", - "commandHint_logoutall": "Αποσυνδεθείτε από όλες τις ενεργές συσκευές", - "displayNavigationRail": "Εμφάνιση της γραμμής πλοήγησης σε κινητά", - "customReaction": "Προσαρμοσμένη αντίδραση", - "ignore": "Αποκλεισμός", - "ignoredUsers": "Αποκλεισμένοι χρήστες", - "writeAMessageLangCodes": "Πληκτρολογήστε σε {l1} ή {l2}...", - "requests": "Αιτήματα", - "holdForInfo": "Κάντε κλικ και κρατήστε πατημένο για πληροφορίες λέξης.", - "greenFeedback": "Αυτό θα έβαζα!", - "yellowFeedback": "Χμ, μπορείς να το δοκιμάσεις και να δεις αν λειτουργεί! Για να χρησιμοποιήσεις αυτή τη λέξη, απλώς κάνε κλικ ξανά.", - "redFeedback": "Δεν νομίζω ότι είναι σωστό...", - "itInstructionsTitle": "Μπορώ να σε βοηθήσω με τη μετάφραση!", - "itInstructionsBody": "Μπορείς να κάνεις κλικ και να κρατήσεις πατημένο τις επιλογές για πληροφορίες λέξης.", - "gaTooltip": "L2 χρήση με βοήθεια γραμματικής", - "taTooltip": "L2 χρήση με βοήθεια μετάφρασης", - "unTooltip": "Άλλο", - "interactiveTranslatorSliderHeader": "Διαδραστικός Μεταφραστής", - "interactiveGrammarSliderHeader": "Διαδραστικός Έλεγχος Γραμματικής", - "interactiveTranslatorNotAllowed": "Απενεργοποιημένο", - "interactiveTranslatorAllowed": "Επιλογή Μαθητή", - "interactiveTranslatorRequired": "Απαιτείται", - "notYetSet": "Δεν έχει οριστεί ακόμα", - "waTooltip": "Χρήση L2 χωρίς βοήθεια", - "languageSettings": "Ρυθμίσεις γλώσσας", - "interactiveTranslator": "Βοήθεια μετάφρασης", - "noIdenticalLanguages": "Παρακαλώ επιλέξτε διαφορετικές βασικές και στόχους γλώσσες", - "searchBy": "Αναζήτηση με βάση χώρα και γλώσσες", - "joinWithClassCode": "Εγγραφή στο μάθημα", - "languageLevelPreA1": "Νέος Χαμηλός (Προ Α1)", - "languageLevelA1": "Νέος Μέσος (A1)", - "languageLevelA2": "Αρχάριος Υψηλού Επιπέδου (A2)", - "languageLevelB1": "Μεσαίος Μέτριος (B1)", - "languageLevelB2": "Προχωρημένος Χαμηλού Επιπέδου (B2)", - "languageLevelC1": "Προχωρημένος Μέτριος (C1)", - "languageLevelC2": "Ανώτερος (C2)", - "changeTheNameOfTheClass": "Αλλαγή ονόματος", - "changeTheNameOfTheChat": "Αλλαγή ονόματος της συνομιλίας", - "sorryNoResults": "Λυπούμαστε, δεν βρέθηκαν αποτελέσματα.", - "ignoreInThisText": "Αγνόηση", - "needsItMessage": "Περίμενε, αυτό δεν είναι {targetLanguage}! Χρειάζεστε βοήθεια με τη μετάφραση;", - "countryInformation": "Η χώρα μου", - "targetLanguage": "Γλώσσα στόχος", - "sourceLanguage": "Βασική γλώσσα", - "updateLanguage": "Οι γλώσσες μου", - "whatLanguageYouWantToLearn": "Ποια γλώσσα θέλετε να μάθετε;", - "whatIsYourBaseLanguage": "Ποια είναι η βασική σας γλώσσα;", - "saveChanges": "Αποθήκευση αλλαγών", - "publicProfileTitle": "Να επιτρέπεται η εύρεση του προφίλ μου στην αναζήτηση", - "publicProfileDesc": "Με την ενεργοποίηση, επιτρέπετε σε άλλους χρήστες να βρουν το προφίλ σας στη διεθνή γραμμή αναζήτησης και να στείλουν αιτήματα συνομιλίας. Σε αυτό το σημείο, μπορείτε να επιλέξετε να αποδεχτείτε ή να αρνηθείτε το αίτημα.", - "errorDisableIT": "Η βοήθεια μετάφρασης είναι απενεργοποιημένη.", - "errorDisableIGC": "Η βοήθεια γραμματικής είναι απενεργοποιημένη.", - "errorDisableLanguageAssistance": "Η βοήθεια μετάφρασης και γραμματικής είναι απενεργοποιημένες.", - "errorDisableITUserDesc": "Κάντε κλικ εδώ για να ενημερώσετε τις ρυθμίσεις βοήθειας μετάφρασης", - "errorDisableIGCUserDesc": "Κάντε κλικ εδώ για να ενημερώσετε τις ρυθμίσεις βοήθειας γραμματικής", - "errorDisableLanguageAssistanceUserDesc": "Κάντε κλικ εδώ για να ενημερώσετε τις ρυθμίσεις βοήθειας μετάφρασης και γραμματικής", - "errorDisableITClassDesc": "Η βοήθεια μετάφρασης είναι απενεργοποιημένη για το μάθημα στο οποίο βρίσκεται αυτή η συνομιλία.", - "errorDisableIGCClassDesc": "Η βοήθεια γραμματικής είναι απενεργοποιημένη για το μάθημα στο οποίο βρίσκεται αυτή η συνομιλία.", - "error405Title": "Οι γλώσσες δεν έχουν οριστεί", - "error405Desc": "Ορίστε τις γλώσσες σας στο Μενού > Ρυθμίσεις Μάθησης.", - "termsAndConditions": "Όρους και Προϋποθέσεις", - "andCertifyIAmAtLeast13YearsOfAge": " και πιστοποιώ ότι είμαι τουλάχιστον 16 ετών.", - "error502504Title": "Ω, υπάρχουν πολλοί μαθητές online!", - "error502504Desc": "Τα εργαλεία μετάφρασης και γραμματικής ενδέχεται να είναι αργά ή μη διαθέσιμα ενώ οι bots της Pangea ενημερώνονται.", - "error404Title": "Σφάλμα μετάφρασης!", - "error404Desc": "Ο Pangea Bot δεν είναι σίγουρος πώς να μεταφράσει αυτό...", - "errorPleaseRefresh": "Το ερευνούμε! Παρακαλούμε ανανεώστε και δοκιμάστε ξανά.", - "connectedToStaging": "Συνδεδεμένο με το Staging", - "learningSettings": "Ρυθμίσεις Μάθησης", - "participants": "Συμμετέχοντες", - "clickMessageTitle": "Χρειάζεστε βοήθεια;", - "clickMessageBody": "Κάντε κλικ σε ένα μήνυμα για εργαλεία γλώσσας όπως μετάφραση, αναπαραγωγή και άλλα!", - "allDone": "Ολοκληρώθηκε!", - "vocab": "Λεξιλόγιο", - "low": "Έχουμε αποδείξεις ότι ο χρήστης δεν καταλαβαίνει αυτές τις λέξεις.", - "medium": "Αυτές οι λέξεις έχουν χρησιμοποιηθεί. Δεν είναι σαφές αν οι λέξεις κατανοούνται πλήρως ή όχι.", - "high": "Έχουμε αποδείξεις ότι ο χρήστης καταλαβαίνει αυτές τις λέξεις.", - "subscribe": "Εγγραφή", - "getAccess": "Εγγραφείτε τώρα!", - "subscriptionDesc": "Οι μηνύσεις είναι δωρεάν! Εγγραφείτε για να ξεκλειδώσετε διαδραστική μετάφραση, έλεγχο γραμματικής και αναλυτικά στοιχεία μάθησης.", - "subscriptionManagement": "Διαχείριση Συνδρομής", - "currentSubscription": "Τρέχουσα Συνδρομή", - "cancelSubscription": "Ακυρώστε τη συνδρομή σας", - "selectYourPlan": "Επιλέξτε το πλάνο σας", - "subsciptionPlatformTooltip": "Παρακαλώ συνδεθείτε στη αρχική συσκευή σας για να διαχειριστείτε το πλάνο της συνδρομής σας", - "subscriptionManagementUnavailable": "Η διαχείριση συνδρομής δεν είναι διαθέσιμη", - "paymentMethod": "Μέθοδος Πληρωμής", - "paymentHistory": "Ιστορικό Πληρωμών", - "emptyChatDownloadWarning": "Δεν είναι δυνατή η λήψη κενής συνομιλίας", - "update": "Ενημέρωση", - "toggleImmersionMode": "Λειτουργία Εμβάπτισης", - "toggleImmersionModeDesc": "Όταν ενεργοποιείται, όλα τα μηνύματα εμφανίζονται στη γλώσσα στόχο σας. Αυτή η ρύθμιση είναι πιο χρήσιμη σε ανταλλαγές γλωσσών.", - "itToggleDescription": "Αυτό το εργαλείο εκμάθησης γλωσσών θα εντοπίζει λέξεις στη βασική σας γλώσσα και θα σας βοηθά να τις μεταφράσετε στη γλώσσα στόχο. Αν και σπάνιο, το AI μπορεί να κάνει λάθη στη μετάφραση.", - "igcToggleDescription": "Αυτό το εργαλείο εκμάθησης γλωσσών θα εντοπίζει κοινά ορθογραφικά, γραμματικά και σημεία στίξης λάθη στο μήνυμά σας και θα προτείνει διορθώσεις. Αν και σπάνιο, το AI μπορεί να κάνει λάθη στις διορθώσεις.", - "originalMessage": "Αρχικό Μήνυμα", - "sentMessage": "Αποσταλμένο Μήνυμα", - "useType": "Χρησιμοποιήστε Τύπο", - "notAvailable": "Δεν Διαθέσιμη", - "taAndGaTooltip": "Χρήση L2 με βοήθεια μετάφρασης και γραμματικής", - "definitionsToolName": "Ορισμοί Λέξεων", - "messageTranslationsToolName": "Μεταφράσεις Μηνυμάτων", - "definitionsToolDescription": "Όταν ενεργοποιείται, οι λέξεις με υπογράμμιση μπλε μπορούν να πατηθούν για ορισμούς. Πατήστε μηνύματα για πρόσβαση στους ορισμούς.", - "translationsToolDescrption": "Όταν ενεργοποιείται, πατήστε ένα μήνυμα και το εικονίδιο μετάφρασης για να δείτε ένα μήνυμα στη βασική σας γλώσσα.", - "welcomeBack": "Καλώς ήρθατε ξανά! Αν ήσασταν μέρος του πιλοτικού προγράμματος 2023-2024, παρακαλούμε επικοινωνήστε μαζί μας για τη ειδική σας συνδρομή πιλοτικού προγράμματος. Αν είστε εκπαιδευτικός που έχει αγοράσει (ή η εκπαιδευτική σας ιδιότητα έχει αγοράσει) άδειες για την τάξη σας, επικοινωνήστε μαζί μας για τη συνδρομή εκπαιδευτικού.", - "downloadTxtFile": "Λήψη αρχείου κειμένου", - "downloadCSVFile": "Λήψη αρχείου CSV", - "promotionalSubscriptionDesc": "Έχετε επί του παρόντος μια συνδρομή προώθησης διάρκειας ζωής. Στείλτε μήνυμα στο support@pangea.chat για βοήθεια στην αλλαγή της συνδρομής σας.", - "originalSubscriptionPlatform": "Συνδρομή αγοράστηκε μέσω {purchasePlatform}", - "oneWeekTrial": "Δοκιμαστική περίοδος μιας εβδομάδας", - "downloadXLSXFile": "Λήψη αρχείου Excel", - "unkDisplayName": "Άγνωστο", - "wwCountryDisplayName": "Παγκόσμιος", - "afCountryDisplayName": "Αφγανιστάν", - "axCountryDisplayName": "Νήσοι Αλάντες", - "alCountryDisplayName": "Αλβανία", - "dzCountryDisplayName": "Αλγερία", - "asCountryDisplayName": "Αμερικανική Σαμόα", - "adCountryDisplayName": "Ανδόρα", - "aoCountryDisplayName": "Αγκόλα", - "aiCountryDisplayName": "Ανγκουίλα", - "agCountryDisplayName": "Αντίγκουα και Μπαρμπούντα", - "arCountryDisplayName": "Αργεντινή", - "amCountryDisplayName": "Αρμενία", - "awCountryDisplayName": "Αρούμπα", - "acCountryDisplayName": "Νήσος Ασένσιον", - "auCountryDisplayName": "Αυστραλία", - "atCountryDisplayName": "Αυστρία", - "azCountryDisplayName": "Αζερμπαϊτζάν", - "bsCountryDisplayName": "Μπαχάμες", - "bhCountryDisplayName": "Μπαχρέιν", - "bdCountryDisplayName": "Μπανγκλαντές", - "bbCountryDisplayName": "Μπαρμπάντος", - "byCountryDisplayName": "Λευκορωσία", - "beCountryDisplayName": "Βέλγιο", - "bzCountryDisplayName": "Μπελίζ", - "bjCountryDisplayName": "Μπενίν", - "bmCountryDisplayName": "Βερμούδες", - "btCountryDisplayName": "Μπουτάν", - "boCountryDisplayName": "Βολιβία", - "baCountryDisplayName": "Βοσνία και Ερζεγοβίνη", - "bwCountryDisplayName": "Μποτσουάνα", - "brCountryDisplayName": "Βραζιλία", - "ioCountryDisplayName": "Βρετανικό Εδαφικό Τόξο Ινδικού Ωκεανού", - "vgCountryDisplayName": "Βρετανικές Παρθένοι Νήσοι", - "bnCountryDisplayName": "Μπρουνέι", - "bgCountryDisplayName": "Βουλγαρία", - "bfCountryDisplayName": "Μπουρκίνα Φάσο", - "biCountryDisplayName": "Μπουρούντι", - "khCountryDisplayName": "Καμπότζη", - "cmCountryDisplayName": "Καμερούν", - "caCountryDisplayName": "Καναδάς", - "cvCountryDisplayName": "Πράσινο Ακρωτήρι", - "bqCountryDisplayName": "Καραϊβική Ολλανδία", - "kyCountryDisplayName": "Νήσοι Κέιμαν", - "cfCountryDisplayName": "Κεντροαφρικανική Δημοκρατία", - "tdCountryDisplayName": "Τσαντ", - "clCountryDisplayName": "Χιλή", - "cnCountryDisplayName": "Κίνα", - "cxCountryDisplayName": "Νήσος Χριστουγέννων", - "ccCountryDisplayName": "Νήσοι Κόκος [Κίλιγκ]", - "coCountryDisplayName": "Κολομβία", - "kmCountryDisplayName": "Κομόρες", - "cdCountryDisplayName": "Δημοκρατία του Κονγκό", - "cgCountryDisplayName": "Δημοκρατία του Κονγκό", - "ckCountryDisplayName": "Νήσοι Κουκ", - "crCountryDisplayName": "Κόστα Ρίκα", - "ciCountryDisplayName": "Ακτή Ελεφαντοστού", - "hrCountryDisplayName": "Κροατία", - "cuCountryDisplayName": "Κούβα", - "cwCountryDisplayName": "Κουρασάο", - "cyCountryDisplayName": "Κύπρος", - "czCountryDisplayName": "Τσεχική Δημοκρατία", - "dkCountryDisplayName": "Δανία", - "djCountryDisplayName": "Τζιμπουτί", - "dmCountryDisplayName": "Δομινίκα", - "doCountryDisplayName": "Δομινικανή Δημοκρατία", - "tlCountryDisplayName": "Ανατολικό Τιμόρ", - "ecCountryDisplayName": "Ισημερινός", - "egCountryDisplayName": "Αίγυπτος", - "svCountryDisplayName": "Ελ Σαλβαδόρ", - "gqCountryDisplayName": "Ισημερινή Γουινέα", - "erCountryDisplayName": "Ερυθραία", - "eeCountryDisplayName": "Εσθονία", - "szCountryDisplayName": "Σουαζιλάνδη", - "etCountryDisplayName": "Αιθιοπία", - "fkCountryDisplayName": "Νήσοι Φόκλαντ", - "foCountryDisplayName": "Νήσοι Φερόες", - "fjCountryDisplayName": "Φίτζι", - "fiCountryDisplayName": "Φινλανδία", - "frCountryDisplayName": "Γαλλία", - "gfCountryDisplayName": "Γαλλική Γουιάνα", - "pfCountryDisplayName": "Γαλλική Πολυνησία", - "gaCountryDisplayName": "Γκαμπόν", - "gmCountryDisplayName": "Γκάμπια", - "geCountryDisplayName": "Γεωργία", - "deCountryDisplayName": "Γερμανία", - "ghCountryDisplayName": "Γκάνα", - "giCountryDisplayName": "Γιβραλτάρ", - "grCountryDisplayName": "Ελλάδα", - "glCountryDisplayName": "Γροιλανδία", - "gdCountryDisplayName": "Γρενάδα", - "gpCountryDisplayName": "Γουαδελούπη", - "guCountryDisplayName": "Γκουάμ", - "gtCountryDisplayName": "Γουατεμάλα", - "ggCountryDisplayName": "Γκέρνσεϊ", - "gnCountryDisplayName": "Γουινέα Κονάκρι", - "gwCountryDisplayName": "Γουινέα-Μπισάου", - "gyCountryDisplayName": "Γουιάνα", - "htCountryDisplayName": "Αϊτή", - "hmCountryDisplayName": "Νησί Heard και Νησιά McDonald", - "hnCountryDisplayName": "Ονδούρα", - "hkCountryDisplayName": "Χονγκ Κονγκ", - "huCountryDisplayName": "Ουγγαρία", - "isCountryDisplayName": "Ισλανδία", - "inCountryDisplayName": "Ινδία", - "idCountryDisplayName": "Ινδονησία", - "irCountryDisplayName": "Ιράν", - "iqCountryDisplayName": "Ιράκ", - "ieCountryDisplayName": "Ιρλανδία", - "imCountryDisplayName": "Νήσος του Μαν", - "ilCountryDisplayName": "Ισραήλ", - "itCountryDisplayName": "Ιταλία", - "jmCountryDisplayName": "Τζαμάικα", - "jpCountryDisplayName": "Ιαπωνία", - "jeCountryDisplayName": "Τζέρσεϊ", - "joCountryDisplayName": "Ιορδανία", - "kzCountryDisplayName": "Καζακστάν", - "keCountryDisplayName": "Κένυα", - "kiCountryDisplayName": "Κιριμπάτι", - "xkCountryDisplayName": "Κοσσυφοπέδιο", - "kwCountryDisplayName": "Κουβέιτ", - "kgCountryDisplayName": "Κιργιζία", - "laCountryDisplayName": "Λάος", - "lvCountryDisplayName": "Λετονία", - "lbCountryDisplayName": "Λίβανος", - "lsCountryDisplayName": "Λεσόθο", - "lrCountryDisplayName": "Λιβερία", - "lyCountryDisplayName": "Λιβύη", - "liCountryDisplayName": "Λιχτενστάιν", - "ltCountryDisplayName": "Λιθουανία", - "luCountryDisplayName": "Λουξεμβούργο", - "moCountryDisplayName": "Μακάου", - "mkCountryDisplayName": "Βόρεια Μακεδονία", - "mgCountryDisplayName": "Μαδαγασκάρη", - "mwCountryDisplayName": "Μαλάουι", - "myCountryDisplayName": "Μαλαισία", - "mvCountryDisplayName": "Μαλδίβες", - "mlCountryDisplayName": "Μάλι", - "mtCountryDisplayName": "Μάλτα", - "mhCountryDisplayName": "Νήσοι Μάρσαλ", - "mqCountryDisplayName": "Μαρτινίκα", - "mrCountryDisplayName": "Μαυριτανία", - "muCountryDisplayName": "Μαυρίκιος", - "ytCountryDisplayName": "Μαγιότ", - "mxCountryDisplayName": "Μεξικό", - "fmCountryDisplayName": "Μικρονησία", - "mdCountryDisplayName": "Μολδαβία", - "mcCountryDisplayName": "Μονακό", - "mnCountryDisplayName": "Μογγολία", - "meCountryDisplayName": "Μαυροβούνιο", - "msCountryDisplayName": "Μοντσερράτ", - "maCountryDisplayName": "Μαρόκο", - "mzCountryDisplayName": "Μοζαμβίκη", - "mmCountryDisplayName": "Μιανμάρ (Βιρμανία)", - "naCountryDisplayName": "Ναμίμπια", - "nrCountryDisplayName": "Ναουρού", - "npCountryDisplayName": "Νεπάλ", - "nlCountryDisplayName": "Ολλανδία", - "ncCountryDisplayName": "Νέα Καληδονία", - "nzCountryDisplayName": "Νέα Ζηλανδία", - "niCountryDisplayName": "Νικαράγουα", - "neCountryDisplayName": "Νίγηρας", - "ngCountryDisplayName": "Νιγηρία", - "nuCountryDisplayName": "Νιουέ", - "nfCountryDisplayName": "Νησί Νόρφολκ", - "kpCountryDisplayName": "Βόρεια Κορέα", - "mpCountryDisplayName": "Βόρειες Μαριάνες Νήσοι", - "noCountryDisplayName": "Νορβηγία", - "omCountryDisplayName": "Ομάν", - "pkCountryDisplayName": "Πακιστάν", - "pwCountryDisplayName": "Παλάου", - "psCountryDisplayName": "Παλαιστινιακά Εδάφη", - "paCountryDisplayName": "Παναμάς", - "pgCountryDisplayName": "Παπούα Νέα Γουινέα", - "pyCountryDisplayName": "Παραγουάη", - "peCountryDisplayName": "Περού", - "phCountryDisplayName": "Φιλιππίνες", - "plCountryDisplayName": "Πολωνία", - "ptCountryDisplayName": "Πορτογαλία", - "prCountryDisplayName": "Πουέρτο Ρίκο", - "qaCountryDisplayName": "Κατάρ", - "reCountryDisplayName": "Ρεϋνιόν", - "roCountryDisplayName": "Ρουμανία", - "ruCountryDisplayName": "Ρωσία", - "rwCountryDisplayName": "Ρουάντα", - "blCountryDisplayName": "Άγιος Βαρθολομαίος", - "shCountryDisplayName": "Άγιος Ελένη", - "knCountryDisplayName": "Άγιος Κιτς και Νέβις", - "lcCountryDisplayName": "Άγιος Λουκάς", - "mfCountryDisplayName": "Άγιος Μαρτίνος", - "pmCountryDisplayName": "Άγιος Πέτρος και Μικελόν", - "vcCountryDisplayName": "Άγιος Βικέντιος", - "wsCountryDisplayName": "Σαμόα", - "smCountryDisplayName": "Άγιος Μαρίνος", - "stCountryDisplayName": "Σάο Τόμε και Πρίνσιπε", - "saCountryDisplayName": "Σαουδική Αραβία", - "snCountryDisplayName": "Σενεγάλη", - "rsCountryDisplayName": "Σερβία", - "scCountryDisplayName": "Σεϋχέλλες", - "slCountryDisplayName": "Σιέρα Λεόνε", - "sgCountryDisplayName": "Σιγκαπούρη", - "sxCountryDisplayName": "Σίντ Μαρτέν", - "skCountryDisplayName": "Σλοβακία", - "siCountryDisplayName": "Σλοβενία", - "sbCountryDisplayName": "Νήσοι Σολομώντα", - "soCountryDisplayName": "Σομαλία", - "zaCountryDisplayName": "Νότια Αφρική", - "gsCountryDisplayName": "Νότια Γεωργία και Νότιες Νήσοι Σάντουιτς", - "krCountryDisplayName": "Νότια Κορέα", - "ssCountryDisplayName": "Νότιο Σουδάν", - "esCountryDisplayName": "Ισπανία", - "lkCountryDisplayName": "Σρι Λάνκα", - "sdCountryDisplayName": "Σουδάν", - "srCountryDisplayName": "Σουρινάμ", - "sjCountryDisplayName": "Σβάλμπαρντ και Γιαν Μάγιεν", - "seCountryDisplayName": "Σουηδία", - "chCountryDisplayName": "Ελβετία", - "syCountryDisplayName": "Συρία", - "twCountryDisplayName": "Ταϊβάν", - "tjCountryDisplayName": "Τατζικιστάν", - "tzCountryDisplayName": "Τανζανία", - "thCountryDisplayName": "Ταϊλάνδη", - "tgCountryDisplayName": "Τόγκο", - "tkCountryDisplayName": "Τοκελάου", - "toCountryDisplayName": "Τόνγκα", - "ttCountryDisplayName": "Τρινιντάντ / Τόμπακο", - "tnCountryDisplayName": "Τυνησία", - "trCountryDisplayName": "Τουρκία", - "tmCountryDisplayName": "Τουρκμενιστάν", - "tcCountryDisplayName": "Νησιά Τερκς και Κάικος", - "tvCountryDisplayName": "Τουβαλού", - "viCountryDisplayName": "Αμερικανικές Παρθένοι Νήσοι", - "ugCountryDisplayName": "Ουγκάντα", - "uaCountryDisplayName": "Ουκρανία", - "aeCountryDisplayName": "Ηνωμένα Αραβικά Εμιράτα", - "gbCountryDisplayName": "Ηνωμένο Βασίλειο", - "usCountryDisplayName": "Ηνωμένες Πολιτείες", - "uyCountryDisplayName": "Ουρουγουάη", - "uzCountryDisplayName": "Ουζμπεκιστάν", - "vuCountryDisplayName": "Βανουάτου", - "vaCountryDisplayName": "Βατικανό", - "veCountryDisplayName": "Βενεζουέλα", - "vnCountryDisplayName": "Βιετνάμ", - "wfCountryDisplayName": "Wallis και Futuna", - "ehCountryDisplayName": "Δυτική Σαχάρα", - "yeCountryDisplayName": "Υεμένη", - "zmCountryDisplayName": "Ζάμπια", - "zwCountryDisplayName": "Ζιμπάμπουε", - "pay": "Checkout", - "invitedToSpace": "{user} σας έχει προσκαλέσει να συμμετάσχετε σε ένα μάθημα: {space}! Θέλετε να αποδεχθείτε;", - "youreInvited": "📩 Είστε προσκεκλημένοι!", - "invitedToChat": "{user} σας έχει προσκαλέσει να συμμετάσχετε σε μια συνομιλία: {name}! Θέλετε να αποδεχθείτε;", - "monthlySubscription": "Μηνιαία", - "yearlySubscription": "Ετήσια", - "defaultSubscription": "Συνδρομή Pangea Chat", - "freeTrial": "Δωρεάν Δοκιμή", - "total": "Σύνολο: ", - "noDataFound": "Δεν βρέθηκαν δεδομένα", - "blurMeansTranslateTitle": "Γιατί είναι θολό το μήνυμα;", - "blurMeansTranslateBody": "Ενώ είναι ενεργή η λειτουργία Εμβάθυνσης, τα μηνύματα που αποστέλλονται στη βασική σας γλώσσα θα θολώνουν ενώ το Pangea Bot τα μεταφράζει στη γλώσσα στόχο σας. Η λειτουργία Εμβάθυνσης μπορεί να ενεργοποιείται ή να απενεργοποιείται στις ρυθμίσεις μεμονωμένων και μαθημάτων.", - "bestCorrectionFeedback": "Αυτό είναι σωστό!", - "distractorFeedback": "Δεν είναι ακριβώς σωστό.", - "bestAnswerFeedback": "Αυτό είναι σωστό!", - "definitionDefaultPrompt": "Τι σημαίνει αυτή η λέξη;", - "practiceDefaultPrompt": "Ποια είναι η καλύτερη απάντηση;", - "correctionDefaultPrompt": "Ποια είναι η καλύτερη αντικατάσταση;", - "acceptSelection": "Αποδοχή Διόρθωσης", - "why": "Γιατί;", - "definition": "Ορισμός", - "exampleSentence": "Παράδειγμα πρότασης", - "reportToTeacher": "Σε ποιον θέλετε να αναφέρετε αυτό το μήνυμα;", - "reportMessageTitle": "{reportingUserId} ανέφερε ένα μήνυμα από τον {reportedUserId} στη συνομιλία {roomName}", - "reportMessageBody": "Μήνυμα: {reportedMessage}\nΑιτία: {reason}", - "noTeachersFound": "Δεν βρέθηκαν δάσκαλοι για αναφορά", - "trialExpiration": "Η δωρεάν δοκιμή σας λήγει στις {expiration}", - "freeTrialDesc": "Οι νέοι χρήστες λαμβάνουν μια εβδομάδα δωρεάν δοκιμής του Pangea Chat", - "activateTrial": "Δωρεάν 7-ήμερη Δοκιμή", - "successfullySubscribed": "Έχετε εγγραφεί με επιτυχία!", - "clickToManageSubscription": "Κάντε κλικ εδώ για να διαχειριστείτε τη συνδρομή σας.", - "signUp": "Εγγραφή", - "pleaseChooseAtLeastChars": "Παρακαλώ επιλέξτε τουλάχιστον {min} χαρακτήρες.", - "noEmailWarning": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση email. Διαφορετικά δεν θα μπορείτε να επαναφέρετε τον κωδικό πρόσβασής σας. Αν δεν θέλετε, πατήστε ξανά το κουμπί για να συνεχίσετε.", - "pleaseEnterValidEmail": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση email.", - "pleaseChooseAUsername": "Παρακαλώ επιλέξτε ένα όνομα χρήστη", - "define": "Ορίστε", - "listen": "Ακούστε", - "trialPeriodExpired": "Η δοκιμαστική περίοδος σας έχει λήξει", - "translations": "μεταφράσεις", - "messageAudio": "ήχος μηνύματος", - "definitions": "ορισμοί", - "subscribedToUnlockTools": "Εγγραφείτε για να ξεκλειδώσετε διαδραστική μετάφραση και έλεγχο γραμματικής, αναπαραγωγή ήχου, εξατομικευμένες δραστηριότητες πρακτικής και αναλύσεις μάθησης!", - "translationTooltip": "Μετάφραση", - "speechToTextTooltip": "Μεταγραφή", - "kickBotWarning": "Το κλείσιμο του Pangea Bot θα αφαιρέσει το bot συνομιλίας από αυτήν τη συζήτηση.", - "refresh": "Ανανέωση", - "messageAnalytics": "Ανάλυση μηνυμάτων", - "words": "Λέξεις", - "score": "Βαθμολογία", - "accuracy": "Ακρίβεια", - "points": "Βαθμοί", - "noPaymentInfo": "Δεν απαιτείται πληροφορία πληρωμής!", - "updatePhoneOS": "Ίσως χρειαστεί να ενημερώσετε την έκδοση του λειτουργικού συστήματος της συσκευής σας.", - "wordsPerMinute": "Λέξεις ανά λεπτό", - "tooltipInstructionsTitle": "Δεν είστε σίγουροι τι κάνει αυτό;", - "tooltipInstructionsMobileBody": "Πατήστε και κρατήστε πατημένο αντικείμενα για να δείτε τις συμβουλές εργαλείων.", - "tooltipInstructionsBrowserBody": "Τοποθετήστε το δείκτη πάνω από αντικείμενα για να δείτε τις συμβουλές εργαλείων.", - "chatCapacity": "Χωρητικότητα συνομιλίας", - "roomFull": "Αυτή η αίθουσα είναι ήδη γεμάτη.", - "chatCapacityHasBeenChanged": "Η χωρητικότητα της συνομιλίας άλλαξε", - "chatCapacitySetTooLow": "Η χωρητικότητα της συνομιλίας πρέπει να είναι τουλάχιστον {count}.", - "chatCapacityExplanation": "Η χωρητικότητα της συνομιλίας περιορίζει τον αριθμό των μελών που επιτρέπονται σε μια συνομιλία.", - "tooManyRequest": "Πάρα πολλά αιτήματα, παρακαλώ δοκιμάστε ξανά αργότερα.", - "enterNumber": "Παρακαλώ εισάγετε μια ακέραια τιμή.", - "buildTranslation": "Δημιουργήστε τη μετάφρασή σας από τις επιλογές παραπάνω", - "practice": "Εξάσκηση", - "noLanguagesSet": "Δεν έχουν οριστεί γλώσσες", - "speechToTextBody": "Για φωνητικά μηνύματα, μπορείτε να δείτε μια μεταγραφή καθώς και το σκορ Λέξεων ανά Λεπτό του ομιλητή.", - "versionNotFound": "Δεν βρέθηκε έκδοση", - "fetchingVersion": "Λήψη έκδοσης...", - "versionFetchError": "Σφάλμα κατά τη λήψη έκδοσης", - "versionText": "Έκδοση: {version}+{buildNumber}", - "l1TranslationBody": "Τα μηνύματα στη βασική σας γλώσσα δεν θα μεταφραστούν.", - "deleteSubscriptionWarningTitle": "Έχετε ενεργή συνδρομή", - "deleteSubscriptionWarningBody": "Η διαγραφή του λογαριασμού σας δεν θα ακυρώσει αυτόματα τη συνδρομή σας.", - "manageSubscription": "Διαχείριση συνδρομής", - "error520Title": "Παρακαλώ δοκιμάστε ξανά.", - "error520Desc": "Λυπούμαστε, δεν καταλάβαμε το μήνυμά σας...", - "wordsUsed": "Χρησιμοποιημένες λέξεις", - "level": "Επίπεδο", - "morphsUsed": "Χρησιμοποιημένες μορφές", - "translationChoicesBody": "Κάντε κλικ και κρατήστε μια επιλογή για μια υπόδειξη.", - "grammar": "Γραμματική", - "contactHasBeenInvitedToTheChat": "Ο επαφή έχει προσκληθεί στη συνομιλία", - "inviteChat": "📨 Προσκαλέστε στη συνομιλία", - "chatName": "Όνομα συνομιλίας", - "reportContentIssueTitle": "Αναφορά προβλήματος περιεχομένου", - "feedback": "Προαιρετικά σχόλια", - "reportContentIssueDescription": "Ωχ! Η ΤΝ μπορεί να διευκολύνει εξατομικευμένες εκπαιδευτικές εμπειρίες αλλά... επίσης hallucinate. Παρακαλούμε δώστε τυχόν σχόλια και θα προσπαθήσουμε ξανά.", - "clickTheWordAgainToDeselect": "Κάντε κλικ στη επιλεγμένη λέξη για να την αποεπιλέξετε.", - "l2SupportNa": "Μη διαθέσιμο", - "l2SupportAlpha": "Άλφα", - "l2SupportBeta": "Βήτα", - "l2SupportFull": "Πλήρες", - "missingVoiceTitle": "Απουσία φωνής", - "voiceNotAvailable": "Δεν έχετε εγκατεστημένη φωνή για αυτήν τη γλώσσα.", - "openVoiceSettings": "Άνοιγμα ρυθμίσεων φωνής", - "playAudio": "Αναπαραγωγή", - "stop": "Διακοπή", - "grammarCopyPOSsconj": "Υποδεκτική Συζυγία", - "grammarCopyPOSnum": "Αριθμός", - "grammarCopyPOSverb": "Ρήμα", - "grammarCopyPOSaffix": "Επίθημα", - "grammarCopyPOSpart": "Μόριο", - "grammarCopyPOSadj": "Επίθετο", - "grammarCopyPOScconj": "Συντονιστική Συζυγία", - "grammarCopyPOSpunct": "Σημείο Στίξης", - "grammarCopyPOSadv": "Επίρρημα", - "grammarCopyPOSaux": "Βοηθητικό", - "grammarCopyPOSspace": "Κενό", - "grammarCopyPOSsym": "Σύμβολο", - "grammarCopyPOSdet": "Οριστικό", - "grammarCopyPOSpron": "Αντωνυμία", - "grammarCopyPOSadp": "Επιρρηματική Πρόθεση", - "grammarCopyPOSpropn": "Ονόματος Ιδιότητας", - "grammarCopyPOSnoun": "Ουσιαστικό", - "grammarCopyPOSintj": "Επιφώνημα", - "grammarCopyPOSx": "Άλλο", - "grammarCopyGENDERfem": "Θηλυκό", - "grammarCopyPERSON2": "Δεύτερο Πρόσωπο", - "grammarCopyMOODimp": "Υποτακτική", - "grammarCopyPUNCTTYPEqest": "Ερώτηση", - "grammarCopyASPECTperf": "Τελειωμένο", - "grammarCopyCASEaccnom": "Αιτιατική, Ονομαστική", - "grammarCopyCASEobl": "Παρακειμένη", - "grammarCopyVOICEact": "Ενεργητική", - "grammarCopyPUNCTTYPEbrck": "Παρένθεση", - "grammarCopyNOUNTYPEart": "Άρθρο", - "grammarCopyNUMBERsing": "Ενικός", - "grammarCopyGENDERmasc": "Αρσενικό", - "grammarCopyVERBTYPEmod": "Ρήμα Modal", - "grammarCopyADVTYPEadverbial": "Επιρρηματικός", - "grammarCopyTENSEperi": "Περιφραστικός", - "grammarCopyNUMFORMdigit": "Ψηφίο", - "grammarCopyNOUNTYPEnot_proper": "Μη σωστό", - "grammarCopyNUMTYPEcard": "Αριθμητικό", - "grammarCopyNOUNTYPEprop": "Ιδιωτικό", - "grammarCopyPUNCTTYPEdash": "Παύλα", - "grammarCopyPUNCTTYPEyes": "Ναι", - "grammarCopyPUNCTTYPEsemi": "Άνω τελεία", - "grammarCopyPUNCTTYPEcomm": "Κόμμα", - "grammarCopyMOODcnd": "Υποθετικό", - "grammarCopyCASEacc": "Αιτιατική", - "grammarCopyPARTTYPEpart": "Μερικό", - "grammarCopyTENSEpast": "Παρελθόν", - "grammarCopyDEGREEsup": "Υπερθετικός", - "grammarCopyPUNCTTYPEcolo": "Άνω τελεία", - "grammarCopyPERSON3": "Τρίτο πρόσωπο", - "grammarCopyNUMBERplur": "Πληθυντικός", - "grammarCopyPRONTYPEnpr": "Ιδιωτικό ουσιαστικό", - "grammarCopyPRONTYPEinterrogative": "Ερωτηματική", - "grammarCopyPOLITEinfm": "Α informal", - "grammarCopyADVTYPEtim": "Χρόνος", - "grammarCopyPOLARITYneg": "Αρνητικό", - "grammarCopyNUMTYPEtot": "Σύνολο", - "grammarCopyADVTYPEadnomial": "Επιθετικός", - "grammarCopyASPECTprog": "Προοδευτικό", - "grammarCopyMOODsub": "Υποτακτική", - "grammarCopyVERBFORMcomplementive": "Συμπληρωματική", - "grammarCopyCASEnom": "Ονομαστική", - "grammarCopyTENSEfut": "Μέλλοντας", - "grammarCopyCASEdat": "Δοτική", - "grammarCopyTENSEpres": "Ενεστώτας", - "grammarCopyGENDERneut": "Ουδέτερο", - "grammarCopyPRONTYPErel": "Σχετικός", - "grammarCopyVERBFORMfinalEnding": "Τελευταία κατάληξη", - "grammarCopyPRONTYPEdem": "Δεικτικός", - "grammarCopyPREPCASEpre": "Προθετική", - "grammarCopyVERBFORMfin": "Οριστική", - "grammarCopyDEGREEpos": "Θετικό", - "grammarCopyPUNCTTYPEquot": "Παραθέματα", - "grammarCopyVERBFORMger": "Γερουνδικό", - "grammarCopyVOICEpass": "Παθητική", - "grammarCopyCASEgen": "Γενική", - "grammarCopyTENSEprs": "Ενεστώτας", - "grammarCopyDEFINITEdef": "Οριστική", - "grammarCopyNUMTYPEord": "Τακτική", - "grammarCopyCASEins": "Όργανο", - "grammarCopyVERBFORMinf": "Απαρέμφατο", - "grammarCopyVERBFORMaux": "Βοηθητικό", - "grammarCopyNUMFORMlong": "Μακρύ", - "grammarCopyCASEloc": "Τοπική", - "grammarCopyMOODind": "Εγκλιτική", - "grammarCopyDEGREEcmp": "Συγκριτική", - "grammarCopyCASErelativeCase": "Σχετική", - "grammarCopyPUNCTTYPEexcl": "Ευφημιστική", - "grammarCopyPERSON1": "Πρώτο πρόσωπο", - "grammarCopyPUNCTSIDEini": "Αρχικό", - "grammarCopyGENDERperson": "Άτομο", - "grammarCopyFOREIGNyes": "Ξένη", - "grammarCopyVOICEvoice": "Φωνή", - "grammarCopyVERBTYPEverbType": "Ρήμα", - "grammarCopyPOSSpass": "Κτητική", - "grammarCopyPREPCASEprepCase": "Προθετικό", - "grammarCopyNUMTYPEnumType": "Αριθμητικό", - "grammarCopyNOUNTYPEnounType": "Ουσιαστικό", - "grammarCopyREFLEXreflex": "Ανακλαστικό", - "grammarCopyPRONTYPEpronType": "Αντωνυμία", - "grammarCopyPUNCTSIDEpunctSide": "Πλευρά Σημείωσης Στίξης", - "grammarCopyVERBFORMverbForm": "Ρήμα", - "grammarCopyGENDERgender": "Γένος", - "grammarCopyMOODmood": "Τρόπος", - "grammarCopyASPECTaspect": "Πλευρά", - "grammarCopyPUNCTTYPEpunctType": "Στίξη", - "grammarCopyTENSEtense": "Χρόνος", - "grammarCopyDEGREEdegree": "Βαθμός", - "grammarCopyPOLITEpolite": "Ευγένεια", - "grammarCopyADVTYPEadvType": "Επίρρημα", - "grammarCopyNUMFORMnumber": "Αριθμός", - "grammarCopyCONJTYPEconjType": "Σύνδεσμος", - "grammarCopyPOLARITYpolarity": "Πολικότητα", - "grammarCopyCASEcase": "Πτώση", - "grammarCopyDEFINITEdefinite": "Οριστικότητα", - "grammarCopyNUMFORMnumForm": "Αριθμητικό", - "grammarCopyPRONTYPEadn": "Επιθετικό", - "grammarCopyVOCvoc": "Κλητική", - "grammarCopyCMPLcmpl": "Συμπληρωματικός", - "grammarCopyADVadv": "Επιρρηματικός", - "grammarCopyMOODjus": "Επιτακτική", - "grammarCopyGENDERcom": "Κοινό", - "grammarCopyREFLEXrflx": "Ανακλαστικό", - "grammarCopyPARTTYPEpar": "Μερικό", - "grammarCopySPCspc": "Συγκεκριμένο", - "grammarCopyTENSEpqp": "Πλεορισμένο", - "grammarCopyREFLEXref": "Ανακλαστικό", - "grammarCopyPUNCTTYPEnshrt": "Σύντομο", - "grammarCopyNUMBERdual": "Διπλό", - "grammarCopyNUMFORMlng": "Μακρύ", - "grammarCopyVOICEmid": "Μεσαίο", - "grammarCopyINTRELintRel": "Ερωτηματικό, Σχετικό", - "grammarCopyINTint": "Ερωτηματικό", - "grammarCopyVOICEcaus": "Αιτιατικό", - "grammarCopyUnknown": "Άγνωστο", - "grammarCopyEVIDENTevident": "Επικυρωτική", - "grammarCopyNUMFORMnumberPsor": "Αριθμός Κατόχου", - "grammarCopyASPECThab": "Συνήθης", - "grammarCopyCASEabl": "Αποθετική", - "grammarCopyCASEall": "Πολλαπλασιαστική", - "grammarCopyCASEess": "Εξωτική", - "grammarCopyCASEtra": "Μεταβατική", - "grammarCopyCASEequ": "Ισοδύναμη", - "grammarCopyCASEdis": "Διανεμητική", - "grammarCopyCASEabs": "Απόλυτη", - "grammarCopyCASEerg": "Εργατική", - "grammarCopyCASEcau": "Αιτιακή", - "grammarCopyCASEben": "Ωφελική", - "grammarCopyCASEtem": "Χρονική", - "grammarCopyCONJTYPEcoord": "Συντονιστική", - "grammarCopyDEFINITEcons": "Κατασκευαστική Κατάσταση", - "grammarCopyDEGREEabs": "Απόλυτο Βαθμό", - "grammarCopyEVIDENTfh": "Πραγματική Επικυρωτικότητα", - "grammarCopyEVIDENTnfh": "Μη-πραγματική Επικυρωτικότητα", - "grammarCopyMOODopt": "Ευχή", - "grammarCopyMOODadm": "Επιτιμητικό", - "grammarCopyMOODdes": "Επιθυμητικό", - "grammarCopyMOODnec": "Αναγκαστικό", - "grammarCopyMOODpot": "Δυναμικό", - "grammarCopyMOODprp": "Προτατικό", - "grammarCopyMOODqot": "Παραθετικό", - "grammarCopyNUMFORMword": "Μορφή Λέξης", - "grammarCopyNUMFORMroman": "Ρωμαϊκό Αριθμητικό", - "grammarCopyNUMFORMletter": "Μορφή Γράμματος", - "grammarCopyNUMTYPEmult": "Πολλαπλασιαστικό", - "grammarCopyNUMTYPEfrac": "Κλασματικό", - "grammarCopyNUMTYPEsets": "Σετ", - "grammarCopyNUMTYPErange": "Εύρος", - "grammarCopyNUMTYPEdist": "Διανεμητικό", - "grammarCopyNUMBERtri": "Δοκιμαστικό", - "grammarCopyNUMBERpauc": "Ποακού", - "grammarCopyNUMBERgrpa": "Μεγαλύτερο Ποακού", - "grammarCopyNUMBERgrpl": "Μεγαλύτερο Πληθυντικό", - "grammarCopyNUMBERinv": "Αντίστροφο", - "grammarCopyPERSON0": "Μηδέν", - "grammarCopyPERSON4": "Τέταρτος", - "grammarCopyPOLITEform": "Επίσημο", - "grammarCopyPOLITEelev": "Αυξημένο", - "grammarCopyPOLITEhumb": "Ταπεινό", - "grammarCopyPRONTYPEemp": "Διεισδυτικό", - "grammarCopyPRONTYPEexc": "Ερωτηματικό", - "grammarCopyPRONTYPErcp": "Ανταποδοτικό", - "grammarCopyPRONTYPEintRelPronType": "Ερωτηματικό-Σχετικό", - "grammarCopyTENSEaor": "Αόριστος", - "grammarCopyTENSEeps": "Επιδεξιωματικός", - "grammarCopyTENSEprosp": "Προοπτικός", - "grammarCopyVERBFORMpart": "Μέρος", - "grammarCopyVERBFORMconv": "Συνεκδοχή", - "grammarCopyVERBFORMvnoun": "Ρηματικό Ουσιαστικό", - "grammarCopyVOICEantip": "Αντιπαραθετικός", - "grammarCopyVOICEcauVoice": "Αιτιατική", - "grammarCopyVOICedir": "Άμεσος", - "grammarCopyVOICEinvVoice": "Αντίστροφος", - "grammarCopyVOICErcpVoice": "Ανταποδοτικός", - "grammarCopyPOS": "Μέρος του Λόγου", - "grammarCopyGENDER": "Γένος", - "grammarCopyPERSON": "Άτομο", - "grammarCopyMOOD": "Τρόπος", - "grammarCopyPUNCTTYPE": "Τύπος Σημείωσης", - "grammarCopyASPECT": "Πλευρά", - "grammarCopyCASE": "Πτώση", - "grammarCopyVOICE": "Φωνή", - "grammarCopyNOUNTYPE": "Τύπος Ουσιαστικού", - "grammarCopyVERBTYPE": "Τύπος Ρήματος", - "grammarCopyADVTYPE": "Τύπος Επιρρήματος", - "grammarCopyNUMFORM": "Μορφή Αριθμού", - "grammarCopyNUMTYPE": "Τύπος Αριθμού", - "grammarCopyNUMBER": "Αριθμός", - "grammarCopyDEFINITE": "Οριστικότητα", - "grammarCopyDEGREE": "Βαθμός", - "grammarCopyEVIDENT": "Ενδεικτικότητα", - "grammarCopyFOREIGN": "Ξένο", - "grammarCopyPOLARITY": "Πολικότητα", - "grammarCopyPOLITE": "Ευγένεια", - "grammarCopyPREPCASE": "Προθετική Πτώση", - "grammarCopyPRONTYPE": "Τύπος Αντωνυμίας", - "grammarCopyPUNCTSIDE": "Πλευρά Σημείωσης Στίξης", - "grammarCopyREFLEX": "Ανακλαστική", - "grammarCopyTENSE": "Χρόνος", - "grammarCopyVERBFORM": "Μορφή Ρήματος", - "grammarCopyCONJTYPE": "Τύπος Συνδέσμου", - "grammarCopySPC": "Ειδικότητα", - "grammarCopyPARTTYPE": "Τύπος Μερισμού", - "grammarCopyINTREL": "Ερωτηματική-Σχετική", - "grammarCopyUNKNOWN": "Άγνωστο", - "grammarCopyNUMBERPSOR": "Αριθμός Κυριότητας", - "grammarCopyPOSS": "Κτητική", - "grammarCopyASPECTimp": "Ατελής Όψη", - "grammarCopyCASEvoc": "Κλητική", - "grammarCopyCASEcom": "Κοινοτική", - "grammarCopyCASEpar": "Μεριστική", - "grammarCopyCASEadv": "Επιρρηματική", - "grammarCopyCASEref": "Αναφορική", - "grammarCopyCASErel": "Σχετική", - "grammarCopyCASEsub": "Υποθετική", - "grammarCopyCASEsup": "Υπερεστιακή", - "grammarCopyCASEaccdat": "Αιτιατική-Δοτική", - "grammarCopyCASEpre": "Προθετική", - "grammarCopyCONJTYPEsub": "Υποτακτική", - "grammarCopyCONJTYPEcmp": "Συγκριτική", - "grammarCopyDEFINITEind": "Αόριστη", - "grammarCopyMOODint": "Ερωτηματική Έννοια", - "grammarCopyNOUNTYPEcomm": "Κοινό Ουσιαστικό", - "grammarCopyNUMBERPSORsing": "Ενικός του Κατόχου", - "grammarCopyNUMBERPSORplur": "Πληθυντικός του Κατόχου", - "grammarCopyNUMBERPSORdual": "Διπλός του Κατόχου", - "grammarCopyPOLARITYpos": "Θετική Πολικότητα", - "grammarCopyPOSSyes": "Κτητική", - "grammarCopyPREPCASEnpr": "Μη-προθετική", - "grammarCopyPRONTYPEprs": "Προσωπική", - "grammarCopyPRONTYPEint": "Ερωτηματική", - "grammarCopyPRONTYPEtot": "Ολική", - "grammarCopyPRONTYPEneg": "Αρνητική", - "grammarCopyPRONTYPEart": "Άρθρο", - "grammarCopyPRONTYPEind": "Αόριστος", - "grammarCopyPRONTYPEintrel": "Ερωτηματικός-Σχετικός", - "grammarCopyPUNCTSIDEfin": "Τελευταίο Στίξη", - "grammarCopyPUNCTTYPEperi": "Τελεία", - "grammarCopyREFLEXyes": "Ανακλαστικό", - "grammarCopyTENSEimp": "Παρατατικός", - "grammarCopyVERBFORMsup": "Υπερσυντέλικος", - "grammarCopyVERBFORMadn": "Επιθετικός", - "grammarCopyVERBFORMlng": "Μακρύς", - "grammarCopyVERBFORMshrt": "Βραχύς", - "grammarCopyVERBTYPEcaus": "Αιτιατικό Ρήμα", - "grammarCopyVOICEcau": "Αιτιατική", - "grammarCopyVOICEdir": "Άμεση", - "grammarCopyVOICEinv": "Αντίστροφη", - "grammarCopyVOICErcp": "Αμοιβαία", - "other": "Άλλο", - "levelShort": "ΕΠΙΠΕΔΟ {level}", - "clickBestOption": "Επιλέξτε τις καλύτερες επιλογές για να μεταφράσετε το μήνυμά σας!", - "completeActivitiesToUnlock": "Ολοκληρώστε τουλάχιστον μια δραστηριότητα για να ξεκλειδώσετε τη μετάφραση!", - "noCapacityLimit": "Χωρίς όριο χωρητικότητας", - "downloadGroupText": "Λήψη κειμένου ομάδας", - "notificationsOn": "Ειδοποιήσεις ενεργές", - "notificationsOff": "Ειδοποιήσεις απενεργοποιημένες", - "createChatAndInviteUsers": "Δημιουργία συνομιλίας και πρόσκληση χρηστών", - "updatedNewSpaceDescription": "Τα μαθήματα σας επιτρέπουν να ενοποιήσετε τις συνομιλίες σας και να δημιουργήσετε ιδιωτικές ή δημόσιες κοινότητες.", - "joinWithCode": "Συμμετοχή με κωδικό", - "enterCodeToJoin": "Εισάγετε κωδικό για να συμμετάσχετε", - "updateNow": "Ενημέρωση τώρα", - "updateLater": "Αργότερα", - "constructUseWaDesc": "Χρησιμοποιείται χωρίς βοήθεια", - "constructUseGaDesc": "Βοήθεια γραμματικής", - "constructUseTaDesc": "Βοήθεια μετάφρασης", - "constructUseUnkDesc": "Άγνωστο", - "constructUseCorITDesc": "Ορθό στη μετάφραση", - "constructUseIgnITDesc": "Παραβλέπεται στη μετάφραση", - "constructUseIncITDesc": "Λάθος στη μετάφραση", - "constructUseIgnIGCDesc": "Παραβλέπεται στη διόρθωση γραμματικής", - "constructUseCorIGCDesc": "Ορθό στη διόρθωση γραμματικής", - "constructUseIncIGCDesc": "Λάθος στη διόρθωση γραμματικής", - "constructUseCorPADesc": "Ορθό στη δραστηριότητα σημασίας λέξης", - "constructUseIgnPADesc": "Παραβλέπεται στη δραστηριότητα σημασίας λέξης", - "constructUseIncPADesc": "Λάθος στη δραστηριότητα σημασίας λέξης", - "constructUseCorWLDesc": "Ορθό στη δραστηριότητα ακρόασης λέξης", - "constructUseIncWLDesc": "Λάθος στη δραστηριότητα ακρόασης λέξης", - "constructUseIngWLDesc": "Παραβλέπεται στη δραστηριότητα ακρόασης λέξης", - "constructUseCorHWLDesc": "Ορθό στη δραστηριότητα κρυφής λέξης", - "constructUseIncHWLDesc": "Λάθος στην κρυφή δραστηριότητα λέξης", - "constructUseIgnHWLDesc": "Αγνοήθηκε στην κρυφή δραστηριότητα λέξης", - "constructUseCorLDesc": "Σωστό στην δραστηριότητα λήμματος", - "constructUseIncLDesc": "Λάθος στην δραστηριότητα λήμματος", - "constructUseIgnLDesc": "Αγνοήθηκε στην δραστηριότητα λήμματος", - "constructUseCorMDesc": "Σωστό στην δραστηριότητα γραμματικής", - "constructUseIncMDesc": "Λάθος στην δραστηριότητα γραμματικής", - "constructUseIgnMDesc": "Αγνοήθηκε στην δραστηριότητα γραμματικής", - "constructUseEmojiDesc": "Σωστό στην δραστηριότητα emoji", - "constructUseCollected": "Συλλέχθηκε στη συνομιλία", - "constructUseNanDesc": "Μη εφαρμόσιμο", - "xpIntoLevel": "{currentXP} / {maxXP} Πόντοι XP", - "enableTTSToolName": "Ενεργοποιημένη φωνητική ανάγνωση κειμένου", - "enableTTSToolDescription": "Επιτρέψτε στην εφαρμογή να παράγει έξοδο φωνητικής ανάγνωσης για τμήματα κειμένου στη γλώσσα στόχο σας.", - "yourUsername": "Το όνομα χρήστη σας", - "yourEmail": "Το email σας", - "iWantToLearn": "Θέλω να μάθω", - "pleaseEnterEmail": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση email.", - "myBaseLanguage": "Η βασική μου γλώσσα", - "meaningSectionHeader": "Νόημα:", - "formSectionHeader": "Μορφές που χρησιμοποιούνται στα chat:", - "writingExercisesTooltip": "Γράψιμο", - "listeningExercisesTooltip": "Ακρόαση", - "readingExercisesTooltip": "Ανάγνωση", - "meaningNotFound": "Δεν ήταν δυνατή η εύρεση του νοήματος.", - "chooseBaseForm": "Επιλέξτε τη βασική μορφή", - "notTheCodeError": "Λυπούμαστε, αυτό δεν είναι ο κώδικας!", - "totalXP": "Συνολικά XP", - "numLemmas": "Συνολικός αριθμός λημμάτων", - "numLemmasUsedCorrectly": "Αριθμός λημμάτων που χρησιμοποιήθηκαν σωστά τουλάχιστον μία φορά", - "numLemmasUsedIncorrectly": "Αριθμός λημμάτων που χρησιμοποιήθηκαν σωστά 0 φορές", - "numLemmasSmallXP": "Αριθμός λημμάτων με 0 - 30 XP", - "numLemmasMediumXP": "Αριθμός λημμάτων με 31 - 200 XP", - "numLemmasLargeXP": "Αριθμός λημμάτων με > 200 XP", - "numGrammarConcepts": "Αριθμός γραμματικών εννοιών", - "listGrammarConcepts": "Γραμματικές έννοιες", - "listGrammarConceptsUsedCorrectly": "Γραμματικές έννοιες που χρησιμοποιήθηκαν σωστά σε αρχικά μηνύματα τουλάχιστον το 80% του χρόνου", - "listGrammarConceptsUsedIncorrectly": "Γραμματικές έννοιες που χρησιμοποιήθηκαν σωστά σε αρχικά μηνύματα λιγότερο από το 80% του χρόνου", - "listGrammarConceptsUseCorrectlySystemGenerated": "Γραμματικές έννοιες που επιλέχθηκαν σωστά από προτάσεις συστήματος τουλάχιστον το 80% του χρόνου", - "listGrammarConceptsUseIncorrectlySystemGenerated": "Γραμματικές έννοιες που επιλέχθηκαν σωστά από προτάσεις συστήματος λιγότερο από το 80% του χρόνου", - "listGrammarConceptsSmallXP": "Γλωσσικά concepts με 0-50 XP", - "listGrammarConceptsMediumXP": "Γλωσσικά concepts με 51-200 XP", - "listGrammarConceptsLargeXP": "Γλωσσικά concepts 201-500 XP", - "listGrammarConceptsHugeXP": "Γλωσσικά concepts >500 XP", - "numMessagesSent": "Αριθμός αποστελλόμενων μηνυμάτων", - "numWordsTyped": "Αριθμός πληκτρολογημένων λέξεων στα αρχικά μηνύματα", - "numCorrectChoices": "Αριθμός σωστών λέξεων που επιλέχθηκαν από προτάσεις συστήματος", - "numIncorrectChoices": "Αριθμός λανθασμένων λέξεων που επιλέχθηκαν από προτάσεις συστήματος", - "commaSeparatedFile": "CSV", - "excelFile": "Excel", - "fileType": "Τύπος αρχείου", - "download": "Λήψη", - "analyticsNotAvailable": "Ανάλυση χρήστη δεν διατίθεται", - "downloading": "Λήψη σε εξέλιξη...", - "failedFetchUserAnalytics": "Αποτυχία λήψης ανάλυσης χρήστη", - "downloadComplete": "Η λήψη ολοκληρώθηκε!", - "whatIsTheMorphTag": "Τι είναι το {morphologicalFeature} του '{wordForm}'?", - "dataAvailable": "Διαθεσιμότητα δεδομένων", - "available": "Διαθέσιμο", - "pangeaBotIsFallible": "Ο Pangea Bot κάνει και λάθη!", - "whatIsMeaning": "Τι σημαίνει το '{lemma}'?", - "pickAnEmoji": "Ποιο είναι το αγαπημένο σου emoji για το '{lemma}'?", - "chooseLemmaMeaningInstructionsBody": "Ταιριάξτε τις σημασίες με τις λέξεις στο μήνυμα!", - "doubleClickToEdit": "Διπλό κλικ για επεξεργασία.", - "activityPlannerTitle": "Προγραμματιστής Δραστηριοτήτων", - "topicLabel": "Θέμα", - "topicPlaceholder": "Επιλέξτε ένα θέμα...", - "modeLabel": "Τύπος δραστηριότητας", - "modePlaceholder": "Επιλέξτε μια λειτουργία...", - "learningObjectiveLabel": "Στόχος μάθησης", - "learningObjectivePlaceholder": "Επιλέξτε στόχο μάθησης...", - "languageOfInstructionsLabel": "Γλώσσα οδηγιών δραστηριότητας", - "targetLanguageLabel": "Στόχος γλώσσα", - "cefrLevelLabel": "Επίπεδο CEFR", - "generateActivitiesButton": "Δημιουργία Δραστηριότητας", - "launchActivityButton": "Έναρξη Δραστηριότητας", - "image": "Εικόνα", - "video": "Βίντεο", - "nan": "Μη εφαρμόσιμο", - "activityPlannerOverviewInstructionsBody": "Επιλέξτε ένα θέμα, τρόπο, μαθησιακό στόχο και δημιουργήστε μια δραστηριότητα για τη συνομιλία!", - "activityTitle": "Τίτλος Δραστηριότητας", - "addVocabulary": "Προσθήκη λεξιλογίου", - "instructions": "Οδηγίες", - "numberOfLearners": "Αριθμός μαθητών", - "mustBeInteger": "Πρέπει να είναι ακέραιος π.χ. 1, 2, 3, ...", - "constructUsePvmDesc": "Παράγεται σε φωνητικό μήνυμα", - "leaveSpaceDescription": "Αφήνοντας το μάθημα, θα αφήσετε όλες τις συνομιλίες μέσα σε αυτό. Οι άλλοι χρήστες θα δουν ότι έχετε φύγει από το μάθημα.", - "constructUseCorMmDesc": "Ορθό μήνυμα σημασίας", - "constructUseIncMmDesc": "Λάθος μήνυμα σημασίας", - "constructUseIgnMmDesc": "Αγνοημένο μήνυμα σημασίας", - "clickForMeaningActivity": "Κάντε κλικ εδώ για μια Πρόκληση Νοήματος", - "meaning": "Νόημα", - "chatWith": "Ομάδα με {displayname}", - "clickOnEmailLink": "Παρακαλώ κάντε κλικ στον σύνδεσμο στο email και συνεχίστε.\n\nΕλέγξτε τον φάκελο spam αν το email δεν έχει φτάσει.", - "dontForgetPassword": "Μην ξεχάσετε τον κωδικό πρόσβασής σας!", - "enableAutocorrectToolName": "Ενεργοποίηση αυτόματης διόρθωσης συσκευής", - "enableAutocorrectDescription": "Αν η συσκευή σας υποστηρίζει τη γλώσσα που μαθαίνετε, μπορείτε να ενεργοποιήσετε την αυτόματη διόρθωση για να διορθώνει κοινά λάθη καθώς πληκτρολογείτε.", - "ttsDisbledTitle": "Η φωνητική ανάγνωση απενεργοποιήθηκε", - "ttsDisabledBody": "Μπορείτε να ενεργοποιήσετε τη φωνητική ανάγνωση στις ρυθμίσεις εκμάθησης", - "noSpaceDescriptionYet": "Δεν έχει δημιουργηθεί ακόμη περιγραφή μαθήματος.", - "tooLargeToSend": "Αυτό το μήνυμα είναι πολύ μεγάλο για αποστολή", - "exitWithoutSaving": "Είστε βέβαιοι ότι θέλετε να φύγετε χωρίς να αποθηκεύσετε;", - "enableAutocorrectPopupTitle": "Προσθέστε το πληκτρολόγιο της γλώσσας στόχου πηγαίνοντας στα:", - "enableAutocorrectPopupSteps": " • Ρυθμίσεις\n • Γενικά\n • Πληκτρολόγιο\n • Πληκτρολόγια\n • Προσθήκη νέου πληκτρολογίου", - "enableAutocorrectPopupDescription": "Μόλις επιλεγεί η γλώσσα, μπορείτε να κάνετε κλικ στο μικρό εικονίδιο του κόσμου στο κάτω αριστερό μέρος του πληκτρολογίου σας για να ενεργοποιήσετε το νέο εγκατεστημένο πληκτρολόγιο.", - "downloadGboardTitle": "Κατεβάστε το Gboard από το Google Play Store για να ενεργοποιήσετε την αυτόματη διόρθωση και άλλες λειτουργίες πληκτρολογίου:", - "downloadGboardSteps": " • Κατεβάστε το Gboard\n • Ανοίξτε την εφαρμογή\n • Γλώσσες\n • Προσθήκη πληκτρολογίου\n • Επιλογή γλώσσας\n • Επιλογή τύπου πληκτρολογίου\n • Τέλος", - "downloadGboardDescription": "Μόλις επιλεγεί η γλώσσα, μπορείτε να κάνετε κλικ στο μικρό εικονίδιο του κόσμου στο κάτω αριστερό μέρος του πληκτρολογίου σας για να ενεργοποιήσετε το νέο εγκατεστημένο πληκτρολόγιο.", - "enableAutocorrectWarning": "Προειδοποίηση! Απαιτείται η προσθήκη του πληκτρολογίου της γλώσσας στόχου", - "displayName": "Εμφανιζόμενο όνομα", - "leaveRoomDescription": "Πρόκειται να φύγετε από αυτήν τη συνομιλία. Οι άλλοι χρήστες θα δουν ότι έχετε φύγει.", - "confirmUserId": "Παρακαλούμε επιβεβαιώστε το όνομα χρήστη Pangea Chat για να διαγράψετε τον λογαριασμό σας.", - "startingToday": "Από σήμερα", - "oneWeekFreeTrial": "Μια εβδομάδα δωρεάν δοκιμής", - "paidSubscriptionStarts": "Ξεκινάει {startDate}", - "cancelInSubscriptionSettings": "• Ακύρωση οποιαδήποτε στιγμή στις ρυθμίσεις συνδρομής", - "cancelToAvoidCharges": "• Ακύρωση πριν από {trialEnds} για να αποφύγετε χρεώσεις", - "downloadGboard": "Κατεβάστε το Gboard", - "autocorrectNotAvailable": "Δυστυχώς, η πλατφόρμα σας δεν υποστηρίζεται αυτήν τη στιγμή για αυτήν τη λειτουργία. Μείνετε συντονισμένοι για περαιτέρω ανάπτυξη!", - "pleaseUpdateApp": "Παρακαλούμε ενημερώστε την εφαρμογή για να συνεχίσετε.", - "chooseEmojiInstructionsBody": "Ταιριάξτε emojis με τις λέξεις που καλύτερα αντιπροσωπεύουν. Μην ανησυχείτε! Δεν θα χάσετε πόντους αν διαφωνείτε. 😅", - "analyticsVocabListBody": "Αυτή είναι όλη η λεξιλογική σας λίστα! Καθώς κερδίζετε XP για κάθε λέξη, θα μετατρέπονται από σπορά σε πλήρη άνθιση. Κάντε κλικ σε οποιαδήποτε λέξη για περισσότερες λεπτομέρειες.", - "morphAnalyticsListBody": "Αυτές είναι όλες οι γραμματικές έννοιες στη γλώσσα που μαθαίνετε! Θα τις ξεκλειδώσετε καθώς τις συναντάτε κατά τη διάρκεια της συνομιλίας. Κάντε κλικ για λεπτομέρειες.", - "knockSpaceSuccess": "Έχετε ζητήσει να συμμετάσχετε σε αυτό το μάθημα! Ένας διαχειριστής θα απαντήσει στο αίτημά σας μόλις το λάβει 😀", - "chooseWordAudioInstructionsBody": "Ακούστε το πλήρες μήνυμα. Στη συνέχεια, ταιριάξτε τα ηχητικά με τις λέξεις.", - "chooseMorphsInstructionsBody": "Κάντε κλικ στα κομμάτια παζλ για γραμματικές ερωτήσεις!", - "pleaseEnterInt": "Παρακαλούμε εισάγετε έναν αριθμό", - "home": "Αρχική", - "join": "Συμμετοχή", - "readingAssistanceOverviewBody": "Κάντε κλικ στα κουμπιά παρακάτω για mini-games με αντιστοίχιση emoji, ήχων, σημασιών λέξεων και γραμματικών εννοιών. Ή κάντε κλικ σε οποιαδήποτε λέξη για λεπτομέρειες.", - "levelSummaryPopupTitle": "Περίληψη Επιπέδου {level}", - "resetInstructionTooltipsTitle": "Επαναφορά οδηγιών βοήθειας", - "resetInstructionTooltipsDesc": "Κάντε κλικ για να εμφανίσετε ξανά τις οδηγίες βοήθειας, όπως για έναν ολοκαίνουργιο χρήστη.", - "selectForGrammar": "Επιλέξτε ένα εικονίδιο γραμματικής για δραστηριότητες και λεπτομέρειες.", - "randomize": "Τυχαία επιλογή", - "clear": "Καθαρισμός", - "makeYourOwnActivity": "Δημιουργήστε τη δική σας δραστηριότητα", - "featuredActivities": "Προτεινόμενες", - "save": "Αποθήκευση", - "startChat": "Ξεκίνα μια συνομιλία", - "translationProblem": "Πρόβλημα μετάφρασης", - "askToJoin": "Ζήτησε να συμμετάσχεις", - "emptyChatWarningTitle": "Η συνομιλία είναι κενή", - "emptyChatWarningDesc": "Δεν έχεις προσκαλέσει κανέναν στη συνομιλία σου. Πήγαινε στις ρυθμίσεις συνομιλίας για να προσκαλέσεις τις επαφές σου ή το Bot. Μπορείς επίσης να το κάνεις αργότερα.", - "areYouLikeMe": "Είσαι σαν εμένα;", - "tryAgainLater": "Πολλές προσπάθειες. Παρακαλώ δοκίμασε ξανά σε 5 λεπτά.", - "enterSpaceCode": "Εισάγετε τον κωδικό μαθήματος", - "shareSpaceLink": "Μοιράσου τον σύνδεσμο", - "byUsingPangeaChat": "Χρησιμοποιώντας το Pangea Chat, συμφωνώ με το ", - "details": "Λεπτομέρειες", - "languageLevelPreA1Desc": "Ποτέ δεν έμαθα ή χρησιμοποίησα τη γλώσσα.", - "languageLevelA1Desc": "Μπορώ να καταλάβω και να χρησιμοποιήσω μερικές οικείες καθημερινές εκφράσεις και πολύ βασικές φράσεις.", - "languageLevelA2Desc": "Μπορώ να καταλάβω προτάσεις και συχνά χρησιμοποιούμενες εκφράσεις που σχετίζονται με περιοχές άμεσης σημασίας.", - "languageLevelB1Desc": "Μπορώ να αντιμετωπίσω τις περισσότερες οικείες καταστάσεις και μπορώ να παράγω απλό συνδεδεμένο κείμενο σε οικεία θέματα.", - "languageLevelB2Desc": "Μπορώ να καταλάβω τις κύριες ιδέες σύνθετων κειμένων και να αλληλεπιδράσω με βαθμό ευφράδειας και αυθορμητισμού.", - "languageLevelC1Desc": "Μπορώ να εκφράσω ιδέες με ευφράδεια και αυθορμητισμό χωρίς μεγάλη δυσκολία και να καταλάβω ένα ευρύ φάσμα απαιτητικών κειμένων.", - "languageLevelC2Desc": "Μπορώ να καταλάβω σχεδόν τα πάντα που ακούω ή διαβάζω και να εκφράζομαι με ευφράδεια και ακρίβεια.", - "newVocab": "Νέες λέξεις", - "newGrammar": "Νέες γραμματικές έννοιες", - "choosePracticeMode": "Κάνε κλικ σε μία από τις παραπάνω επιλογές για να ξεκινήσεις μια δραστηριότητα εξάσκησης", - "ban": "Απαγόρευση", - "unban": "Άρση απαγόρευσης", - "kick": "Απομάκρυνση", - "lemma": "Λήμμα", - "grammarFeature": "Χαρακτηριστικό γραμματικής", - "grammarTag": "Ετικέτα γραμματικής", - "forms": "Μορφές", - "exampleMessages": "Παραδείγματα μηνυμάτων", - "timesUsedIndependently": "Χρησιμοποιήθηκε αυτόνομα", - "timesUsedWithAssistance": "Χρησιμοποιήθηκε με βοήθεια", - "shareInviteCode": "Μοιραστείτε τον κωδικό πρόσκλησης: {code}", - "leaderboard": "Κατάταξη", - "skipForNow": "Παραλείψτε προς το παρόν", - "permissions": "Άδειες", - "spaceChildPermission": "Ποιος μπορεί να προσθέσει νέες συνομιλίες σε αυτό το μάθημα", - "addEnvironmentOverride": "Προσθήκη παραμέτρου περιβάλλοντος", - "defaultOption": "Προεπιλογή", - "deleteChatDesc": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτήν τη συνομιλία; Θα διαγραφεί για όλους τους συμμετέχοντες και όλα τα μηνύματα μέσα στη συνομιλία δεν θα είναι πλέον διαθέσιμα για πρακτική ή αναλυτικά δεδομένα μάθησης.", - "deleteSpaceDesc": "Το μάθημα και οποιεσδήποτε επιλεγμένες συνομιλίες θα διαγραφούν για όλους τους συμμετέχοντες και όλα τα μηνύματα μέσα στη συνομιλία δεν θα είναι πλέον διαθέσιμα για πρακτική ή αναλυτικά δεδομένα μάθησης. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", - "launch": "Έναρξη", - "searchChats": "Αναζήτηση συνομιλιών", - "maxFifty": "Μέγιστο 50", - "configureSpace": "Διαμόρφωση μαθήματος", - "pinMessages": "Καρφιτσώστε μηνύματα", - "setJoinRules": "Ορίστε κανόνες συμμετοχής", - "changeGeneralSettings": "Αλλάξτε τις γενικές ρυθμίσεις", - "inviteOtherUsersToRoom": "Προσκαλέστε άλλους χρήστες", - "changeTheNameOfTheSpace": "Αλλάξτε το όνομα του μαθήματος", - "changeTheDescription": "Αλλάξτε την περιγραφή", - "changeThePermissions": "Αλλάξτε τα δικαιώματα", - "introductions": "Εισαγωγές", - "announcements": "Ανακοινώσεις", - "activities": "Δραστηριότητες", - "access": "Πρόσβαση", - "activitySuggestionTimeoutMessage": "Δουλεύουμε σκληρά για να δημιουργήσουμε περισσότερες δραστηριότητες για εσάς, παρακαλούμε ελέγξτε ξανά σε ένα λεπτό", - "howSpaceCanBeFound": "Πώς μπορεί να βρεθεί αυτό το μάθημα", - "private": "Ιδιωτικό", - "cannotBeFoundInSearch": "Δεν μπορεί να βρεθεί στην αναζήτηση", - "public": "Δημόσιο", - "visibleToCommunity": "Ορατό στην ευρύτερη κοινότητα Pangea Chat μέσω \"Βρες ένα μάθημα\"", - "howSpaceCanBeJoined": "Πώς μπορεί να ενταχθεί αυτό το μάθημα", - "canBeFoundVia": "Μπορεί να βρεθεί μέσω:", - "canBeFoundViaInvitation": "• πρόσκληση", - "canBeFoundViaCodeOrLink": "• κωδικός ή σύνδεσμος", - "canBeFoundViaKnock": "• αίτημα συμμετοχής και έγκριση διαχειριστή", - "youHaveLeveledUp": "Έχετε ανέβει επίπεδο!", - "sendActivities": "Αποστολή δραστηριοτήτων", - "groupChat": "Ομαδική Συνομιλία", - "directMessage": "Άμεσο μήνυμα", - "newDirectMessage": "Νέο άμεσο μήνυμα", - "speakingExercisesTooltip": "Ομιλία", - "noChatsFoundHereYet": "Δεν βρέθηκαν συνομιλίες εδώ ακόμα", - "duration": "Διάρκεια", - "transcriptionFailed": "Αποτυχία μεταγραφής ήχου", - "aUserIsKnocking": "Ένας χρήστης ζητά να συμμετάσχει στο μάθημά σας", - "usersAreKnocking": "{users} χρήστες ζητούν να συμμετάσχουν στο μάθημά σας", - "failedToFetchTranscription": "Αποτυχία λήψης μεταγραφής", - "deleteEmptySpaceDesc": "Το μάθημα θα διαγραφεί για όλους τους συμμετέχοντες. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", - "regenerate": "Αναδημιουργία", - "mySavedActivities": "Αποθηκευμένες Δραστηριότητες μου", - "noSavedActivities": "Δεν υπάρχουν αποθηκευμένες δραστηριότητες", - "saveActivity": "Αποθήκευση αυτής της δραστηριότητας", - "failedToPlayVideo": "Αποτυχία αναπαραγωγής βίντεο", - "done": "Ολοκληρώθηκε", - "inThisSpace": "Σε αυτό το μάθημα", - "myContacts": "Οι επαφές μου", - "inviteAllInSpace": "Πρόσκληση όλων στο μάθημα", - "spaceParticipantsHaveBeenInvitedToTheChat": "Όλοι οι συμμετέχοντες στο μάθημα έχουν προσκληθεί στη συνομιλία", - "numKnocking": "{count} χτυπήματα", - "numInvited": "{count} προσκεκλημένοι", - "saved": "Αποθηκευμένο", - "reset": "Επαναφορά", - "errorGenerateActivityMessage": "Αποτυχία δημιουργίας δραστηριότητας", - "errorRegenerateActivityMessage": "Αποτυχία αναδημιουργίας δραστηριότητας", - "errorLaunchActivityMessage": "Αποτυχία εκκίνησης δραστηριότητας", - "errorFetchingActivitiesMessage": "Αποτυχία λήψης δραστηριοτήτων", - "errorFetchingDefinition": "Αποτυχία λήψης ορισμού", - "errorProcessAnalytics": "Αποτυχία επεξεργασίας αναλυτικών στοιχείων", - "errorDownloading": "Η λήψη απέτυχε", - "errorFetchingLevelSummary": "Αποτυχία λήψης περίληψης επιπέδου", - "errorLoadingSpaceChildren": "Αποτυχία φόρτωσης συνομιλιών εντός αυτού του μαθήματος", - "unexpectedError": "Απρόβλεπτο σφάλμα.", - "pleaseReload": "Παρακαλώ επαναφορτώστε και δοκιμάστε ξανά.", - "translationError": "Σφάλμα μετάφρασης", - "errorFetchingTranslation": "Αποτυχία λήψης μετάφρασης", - "errorFetchingActivity": "Αποτυχία λήψης δραστηριότητας", - "check": "Έλεγχος", - "unableToFindRoom": "Δεν βρέθηκε συνομιλία ή μάθημα με αυτόν τον κωδικό. Παρακαλώ δοκιμάστε ξανά.", - "numCompletedActivities": "Αριθμός ολοκληρωμένων δραστηριοτήτων", - "viewingAnalytics": "Προβολή {visible}/{users} Αναλύσεων", - "request": "Αίτημα", - "requestAll": "Ζήτηση Όλα", - "confirmMessageUnpin": "Είστε σίγουροι ότι θέλετε να αποσυνδέσετε αυτό το μήνυμα;", - "createActivityPlan": "Δημιουργία νέου σχεδίου δραστηριότητας", - "saveAndLaunch": "Αποθήκευση και Εκκίνηση", - "launchToSpace": "Εκκίνηση στην πορεία", - "numberOfActivities": "Αριθμός συνεδριών δραστηριότητας", - "maximumActivityParticipants": "Κάθε δραστηριότητα μπορεί να έχει το μέγιστο {count} συμμετέχοντα(ους).", - "pending": "Εκκρεμεί", - "inactive": "Ανενεργό", - "confirmRole": "Επιβεβαίωση ρόλου", - "openRoleLabel": "ΑΝΟΙΧΤΟ", - "joinedTheActivity": "👋 {username} συμμετείχε ως {role}", - "finishedTheActivity": "🎯 {username} ολοκλήρωσε αυτήν τη δραστηριότητα", - "archiveToAnalytics": "Προσθήκη στις Ολοκληρωμένες Δραστηριότητες μου", - "activitySummaryError": "Οι περιλήψεις δραστηριοτήτων δεν είναι διαθέσιμες", - "requestSummaries": "Αιτήματα περιλήψεων", - "generatingNewActivities": "Είστε ο πρώτος χρήστης αυτής της ζεύξης γλωσσών! Παρακαλούμε δώστε μας ένα λεπτό, ετοιμάζουμε δραστηριότητες αποκλειστικά για εσάς.", - "requestAccessTitle": "Ζητήστε πρόσβαση σε αναλύσεις;", - "requestAccessDesc": "Θα θέλατε να ζητήσετε πρόσβαση για να δείτε τα αναλυτικά στοιχεία των συμμετεχόντων;\n\nΑν οι συμμετέχοντες συμφωνήσουν, οι διαχειριστές αυτού του μαθήματος θα μπορούν να δουν:\n • συνολικό λεξιλόγιο\n • συνολικές γραμματικές έννοιες\n • συνολικές συνεδρίες δραστηριότητας που ολοκληρώθηκαν\n • τις συγκεκριμένες γραμματικές έννοιες που χρησιμοποιήθηκαν, σωστά και λανθασμένα\n\nΔεν θα μπορούν να δουν:\n • μηνύματα σε συνομιλίες εκτός του μαθήματος\n • λίστα λεξιλογίου", - "requestAccess": "Αίτηση πρόσβασης ({count})", - "analyticsInactiveTitle": "Οι αιτήσεις σε ανενεργούς χρήστες δεν μπορούν να σταλούν", - "analyticsInactiveDesc": "Οι ανενεργοί χρήστες που δεν έχουν συνδεθεί από τότε που εισήχθη αυτή η λειτουργία δεν θα δουν το αίτημά σας.\n\nΤο κουμπί Αίτημα θα εμφανιστεί μόλις επιστρέψουν. Μπορείτε να ξαναστείλετε το αίτημα αργότερα κάνοντας κλικ στο κουμπί Αίτημα κάτω από το όνομά τους όταν είναι διαθέσιμο.", - "accessRequestedTitle": "Αίτημα πρόσβασης στα αναλυτικά στοιχεία", - "accessRequestedDesc": "Ζητώντας admin(s): {admin} \n\nΟι διαχειριστές από το «{space}» ζητούν να δουν τις αναλύσεις μάθησής σας.\n\nΑν συμφωνείτε, θα μπορούν να δουν:\n • το συνολικό λεξιλόγιο\n • τις συνολικές έννοιες γραμματικής\n • τις συνολικές συνεδρίες δραστηριότητας που ολοκληρώθηκαν\n • τις συγκεκριμένες έννοιες γραμματικής που χρησιμοποιήθηκαν, σωστά και λανθασμένα\n\nΔεν θα μπορούν να δουν:\n • μηνύματα σε συνομιλίες εκτός του μαθήματος\n • τη λίστα λεξιλογίου", - "adminRequestedAccess": "Οι διαχειριστές ζήτησαν να δουν τα αναλυτικά σας στοιχεία.", - "lastUpdated": "Ενημερώθηκε\n{time}", - "activityFinishedMessage": "Όλα Ολοκληρώθηκαν!", - "endForAll": "Τέλος για όλους", - "newCourse": "Νέο μάθημα", - "numModules": "{num} ενότητες", - "coursePlan": "Πλάνο Μαθήματος", - "editCourseLater": "Μπορείτε να επεξεργαστείτε τον τίτλο, τις περιγραφές και την εικόνα του μαθήματος αργότερα.", - "createCourse": "Δημιουργία μαθήματος", - "stats": "Στατιστικά", - "createGroupChat": "Δημιουργία ομαδικής συνομιλίας", - "editCourse": "Επεξεργασία μαθήματος", - "inviteDesc": "Με όνομα χρήστη, με κωδικό ή σύνδεσμο", - "editCourseDesc": "Εδώ μπορείτε να επεξεργαστείτε τον τίτλο, την περιγραφή και άλλα στοιχεία του μαθήματος.", - "permissionsDesc": "Ορίστε δικαιώματα, όπως ποιος μπορεί να προσκαλεί χρήστες, να στέλνει μηνύματα, να δημιουργεί συνομιλίες κ.λπ.", - "accessDesc": "Μπορείτε να κάνετε το μάθημά σας ανοιχτό στον κόσμο! Ή, να το κρατήσετε ιδιωτικό και ασφαλές.", - "createGroupChatDesc": "Ενώ οι συνεδρίες δραστηριοτήτων ξεκινούν και τελειώνουν, οι ομαδικές συνομιλίες θα παραμένουν ανοιχτές για τακτική επικοινωνία.", - "deleteDesc": "Μόνο διαχειριστές μπορούν να διαγράψουν ένα μάθημα. Αυτή είναι μια καταστροφική ενέργεια που αφαιρεί όλους τους χρήστες και διαγράφει όλες τις επιλεγμένες συνομιλίες εντός του μαθήματος. Προχωρήστε με προσοχή.", - "noCourseFound": "Ωχ, αυτό το μάθημα χρειάζεται ένα πλάνο!\n\nΤα πλάνα μαθημάτων είναι μια σειρά θεμάτων και δραστηριοτήτων συζήτησης.", - "additionalParticipants": "+ {num} άλλοι", - "directMessages": "Άμεσες Μηνύσεις", - "whatNow": "Τι τώρα;", - "chooseNextActivity": "Επιλέξτε την επόμενη δραστηριότητά σας!", - "letsGo": "Πάμε", - "chooseRole": "Επιλέξτε ρόλο!", - "chooseRoleToParticipate": "Επιλέξτε ρόλο για συμμετοχή!", - "waitingToFillRole": "Αναμονή για συμπλήρωση {num} ρόλων...", - "pingParticipants": "Ειδοποιήστε τους συμμετέχοντες του μαθήματος", - "playWithBot": "Παίξτε με το Pangea Bot", - "inviteFriends": "Προσκαλέστε φίλους", - "waitNotDone": "Περίμενε, δεν έχω τελειώσει!", - "waitingForOthersToFinish": "Αναμονή για τους υπόλοιπους να τελειώσουν...", - "generatingSummary": "Ανάλυση συνομιλίας και δημιουργία αποτελεσμάτων", - "findCourse": "Βρείτε ένα μάθημα", - "pingParticipantsNotification": "{user} ψάχνει χρήστες να συμμετάσχουν στη συνεδρία δραστηριότητας στο {room}", - "course": "Μάθημα", - "courses": "Μαθήματα", - "courseName": "Όνομα μαθήματος", - "createNewCourse": "Νέο μάθημα", - "goToCourse": "Πήγαινε στο μάθημα: {course}", - "activityComplete": "Αυτή η δραστηριότητα έχει ολοκληρωθεί. Η περίληψη της δραστηριότητας θα πρέπει να είναι διαθέσιμη παρακάτω.", - "startNewSession": "Ξεκινήστε νέα συνεδρία", - "joinOpenSession": "Συμμετοχή σε ανοιχτή συνεδρία", - "less": "λιγότερο", - "activityNotFound": "Δεν βρέθηκε δραστηριότητα", - "levelUp": "Επίπεδο πάνω", - "myActivities": "Οι δραστηριότητές μου", - "openToJoin": "Ανοιχτό για συμμετοχή", - "results": "Αποτελέσματα", - "activityDone": "Ολοκληρώθηκε η δραστηριότητα!", - "promoCodeInfo": "Οι κωδικοί προώθησης μπορούν να εισαχθούν στη σελίδα που ακολουθεί", - "editsComingSoon": "Η δυνατότητα επεξεργασίας πόλεων και δραστηριοτήτων έρχεται σύντομα.", - "editing": "Επεξεργασία", - "activityNeedsOneMember": "Ωχ! Αυτή η δραστηριότητα χρειάζεται 1 ακόμη άτομο.", - "activityNeedsMembers": "Ωχ! Αυτή η δραστηριότητα χρειάζεται {num} ακόμη άτομα.", - "inviteFriendsToCourse": "Πρόσκληση φίλων στο μάθημά μου", - "subscribeToUnlockActivitySummaries": "Εγγραφή για ξεκλείδωμα περιλήψεων δραστηριοτήτων", - "subscribeToUnlockDefinitions": "Εγγραφή για ξεκλείδωμα ορισμών", - "subscribeToUnlockTranscriptions": "Εγγραφή για ξεκλείδωμα μεταγραφών", - "pingSent": "🔔 Η ειδοποίηση μαθήματος εστάλη! 🔔", - "courseTitle": "Τίτλος μαθήματος", - "courseDesc": "Περιγραφή μαθήματος", - "courseSavedSuccessfully": "Το μάθημα αποθηκεύτηκε με επιτυχία", - "addCoursePlan": "Προσθήκη σχεδίου μαθήματος", - "activityStatsButtonInstruction": "Κάντε κλικ εδώ για να δείτε τα στατιστικά της δραστηριότητάς σας και να κλείσετε τη δραστηριότητα όταν τελειώσετε", - "loginToAccount": "Συνδεθείτε στον λογαριασμό μου", - "appDescription": "Μάθε μια γλώσσα\nενώ στέλνεις μηνύματα στους φίλους σου.", - "languages": "Γλώσσες", - "chooseLanguage": "Επιλέξτε μια γλώσσα στόχου.", - "planTrip": "Προγραμματίστε το ταξίδι σας", - "howAreYouTraveling": "Πώς ταξιδεύετε;", - "unlockPrivateTrip": "Ξεκλειδώστε ένα ιδιωτικό ταξίδι", - "joinPublicTrip": "Συμμετάσχετε σε ένα δημόσιο ταξίδι", - "startOwnTrip": "Ξεκινήστε το δικό μου", - "tripPlanDesc": "Τα ταξίδια είναι μαθήματα. Κάθε ένα έχει 8-10 διαδοχικά θέματα με μια σειρά δραστηριοτήτων μάθησης γλώσσας με βάση τις εργασίες.", - "unlockPrivateTripTitle": "Ξεκλειδώστε ιδιωτικό ταξίδι", - "browsePublicTrips": "Περιηγηθείτε σε δημόσια ταξίδια", - "startOwnTripTitle": "Ξεκινήστε το δικό μου ταξίδι", - "courseCode": "Ποιος είναι ο μυστικός κωδικός;", - "courseCodeHint": "Κωδικός ή σύνδεσμος ταξιδιού", - "unlockMyTrip": "Ξεκλειδώστε το ταξίδι μου", - "signupOption": "Πώς θέλετε να εγγραφείτε;", - "withApple": "Με την Apple", - "withGoogle": "Με Google", - "withEmail": "Με Email", - "createAccount": "Δημιουργία λογαριασμού", - "loginWithEmail": "Σύνδεση με email", - "usernameOrEmail": "Όνομα χρήστη ή email", - "email": "Email", - "forgotPassword": "Ξεχάσατε τον κωδικό;", - "endActivity": "Τέλος δραστηριότητας", - "allLanguages": "Όλες οι γλώσσες", - "chatListTooltip": "Εδώ θα βρείτε τα άμεσα μηνύματά σας! Κάντε κλικ σε οποιονδήποτε avatar χρήστη και \"ξεκινήστε συνομιλία\" για να στείλετε ένα DM.", - "directMessageBotTitle": "Άμεσο μήνυμα Pangea Bot", - "feedbackTitle": "Ανατροφοδότηση δραστηριότητας", - "feedbackHint": "Η ανατροφοδότησή σας", - "feedbackButton": "Υποβολή ανατροφοδότησης", - "directMessageBotDesc": "Το να μιλάς με ανθρώπους είναι πιο διασκεδαστικό, αλλά... η ΤΝ είναι πάντα έτοιμη!", - "inviteYourFriends": "Προσκαλέστε τους φίλους σας", - "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", - "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", - "@@locale": "el", - "@@last_modified": "2026-02-09 15:32:27.325286", - "@alwaysUse24HourFormat": { - "type": "String", - "placeholders": {} - }, - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@appLockDescription": { - "type": "String", - "placeholders": {} - }, - "@swipeRightToLeftToReply": { - "type": "String", - "placeholders": {} - }, - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" + }, + "@editRoomAliases": { + "type": "String", + "placeholders": {} + }, + "@enterSpace": { + "type": "String", + "placeholders": {} + }, + "@encryptThisChat": { + "type": "String", + "placeholders": {} + }, + "@fileName": { + "type": "String", + "placeholders": {} + }, + "@unavailable": { + "type": "String", + "placeholders": {} + }, + "@previousAccount": { + "type": "String", + "placeholders": {} + }, + "@publicRooms": { + "type": "String", + "placeholders": {} + }, + "@fromTheInvitation": { + "type": "String", + "placeholders": {} + }, + "@sendMessages": { + "type": "String", + "placeholders": {} + }, + "@incorrectPassphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "@emoteWarnNeedToPick": { + "type": "String", + "placeholders": {} + }, + "@reopenChat": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterRecoveryKey": { + "type": "String", + "placeholders": {} + }, + "@create": { + "type": "String", + "placeholders": {} + }, + "@toggleFavorite": { + "type": "String", + "placeholders": {} + }, + "@no": { + "type": "String", + "placeholders": {} + }, + "@widgetNameError": { + "type": "String", + "placeholders": {} + }, + "@inoffensive": { + "type": "String", + "placeholders": {} + }, + "@unpin": { + "type": "String", + "placeholders": {} + }, + "@addToBundle": { + "type": "String", + "placeholders": {} + }, + "@reportMessage": { + "type": "String", + "placeholders": {} + }, + "@spaceIsPublic": { + "type": "String", + "placeholders": {} + }, + "@addWidget": { + "type": "String", + "placeholders": {} + }, + "@removeAllOtherDevices": { + "type": "String", + "placeholders": {} + }, + "@unblockDevice": { + "type": "String", + "placeholders": {} + }, + "@countFiles": { + "placeholders": { + "count": { + "type": "int" + } }, - "participants": { - "type": "int" + "type": "String" + }, + "@noKeyForThisMessage": { + "type": "String", + "placeholders": {} + }, + "@enableEncryptionWarning": { + "type": "String", + "placeholders": {} + }, + "@inviteText": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "link": { + "type": "String" + } } - } - }, - "@noMoreChatsFound": { - "type": "String", - "placeholders": {} - }, - "@noChatsFoundHere": { - "type": "String", - "placeholders": {} - }, - "@joinedChats": { - "type": "String", - "placeholders": {} - }, - "@unread": { - "type": "String", - "placeholders": {} - }, - "@space": { - "type": "String", - "placeholders": {} - }, - "@spaces": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" + }, + "@shareLocation": { + "type": "String", + "placeholders": {} + }, + "@reason": { + "type": "String", + "placeholders": {} + }, + "@commandHint_markasgroup": { + "type": "String", + "placeholders": {} + }, + "@errorObtainingLocation": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } } - } - }, - "@globalChatId": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibility": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibilityDescription": { - "type": "String", - "placeholders": {} - }, - "@calls": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickers": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickersBody": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessages": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessagesBody": { - "type": "String", - "placeholders": {} - }, - "@hideInvalidOrUnknownMessageFormats": { - "type": "String", - "placeholders": {} - }, - "@block": { - "type": "String", - "placeholders": {} - }, - "@blockedUsers": { - "type": "String", - "placeholders": {} - }, - "@blockListDescription": { - "type": "String", - "placeholders": {} - }, - "@blockUsername": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChats": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChatsBody": { - "type": "String", - "placeholders": {} - }, - "@overview": { - "type": "String", - "placeholders": {} - }, - "@notifyMeFor": { - "type": "String", - "placeholders": {} - }, - "@passwordRecoverySettings": { - "type": "String", - "placeholders": {} - }, - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" + }, + "@hydrateTor": { + "type": "String", + "placeholders": {} + }, + "@pushNotificationsNotAvailable": { + "type": "String", + "placeholders": {} + }, + "@passwordRecovery": { + "type": "String", + "placeholders": {} + }, + "@storeInAppleKeyChain": { + "type": "String", + "placeholders": {} + }, + "@replaceRoomWithNewerVersion": { + "type": "String", + "placeholders": {} + }, + "@hydrate": { + "type": "String", + "placeholders": {} + }, + "@invalidServerName": { + "type": "String", + "placeholders": {} + }, + "@chatPermissions": { + "type": "String", + "placeholders": {} + }, + "@voiceMessage": { + "type": "String", + "placeholders": {} + }, + "@wipeChatBackup": { + "type": "String", + "placeholders": {} + }, + "@sender": { + "type": "String", + "placeholders": {} + }, + "@storeInAndroidKeystore": { + "type": "String", + "placeholders": {} + }, + "@hideRedactedEvents": { + "type": "String", + "placeholders": {} + }, + "@online": { + "type": "String", + "placeholders": {} + }, + "@signInWithPassword": { + "type": "String", + "placeholders": {} + }, + "@ignoredUsers": { + "type": "String", + "placeholders": {} + }, + "@lastActiveAgo": { + "type": "String", + "placeholders": { + "localizedTimeShort": { + "type": "String" + } } - } - }, - "@presenceStyle": { - "type": "String", - "placeholders": {} - }, - "@presencesToggle": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" + }, + "@weSentYouAnEmail": { + "type": "String", + "placeholders": {} + }, + "@offensive": { + "type": "String", + "placeholders": {} + }, + "@needPantalaimonWarning": { + "type": "String", + "placeholders": {} + }, + "@makeAdminDescription": { + "type": "String", + "placeholders": {} + }, + "@edit": { + "type": "String", + "placeholders": {} + }, + "@loadMore": { + "type": "String", + "placeholders": {} + }, + "@noEmotesFound": { + "type": "String", + "placeholders": {} + }, + "@synchronizingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "@transferFromAnotherDevice": { + "type": "String", + "placeholders": {} + }, + "@passwordHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "@pushRules": { + "type": "String", + "placeholders": {} + }, + "@goToTheNewRoom": { + "type": "String", + "placeholders": {} + }, + "@loadingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "@copy": { + "type": "String", + "placeholders": {} + }, + "@saveKeyManuallyDescription": { + "type": "String", + "placeholders": {} + }, + "@none": { + "type": "String", + "placeholders": {} + }, + "@editBundlesForAccount": { + "type": "String", + "placeholders": {} + }, + "@enableEncryption": { + "type": "String", + "placeholders": {} + }, + "@whyIsThisMessageEncrypted": { + "type": "String", + "placeholders": {} + }, + "@unreadChats": { + "type": "String", + "placeholders": { + "unreadCount": { + "type": "int" + } } - } - }, - "@youInvitedToBy": { - "type": "String", - "placeholders": { - "alias": { - "type": "String" + }, + "@rejectedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@invitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" + }, + "@setChatDescription": { + "type": "String", + "placeholders": {} + }, + "@userLeftTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@usersMustKnock": { - "type": "String", - "placeholders": {} - }, - "@noOneCanJoin": { - "type": "String", - "placeholders": {} - }, - "@userWouldLikeToChangeTheChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" + }, + "@spaceName": { + "type": "String", + "placeholders": {} + }, + "@toggleUnread": { + "type": "String", + "placeholders": {} + }, + "@or": { + "type": "String", + "placeholders": {} + }, + "@dehydrateWarning": { + "type": "String", + "placeholders": {} + }, + "@sendOriginal": { + "type": "String", + "placeholders": {} + }, + "@noOtherDevicesFound": { + "type": "String", + "placeholders": {} + }, + "@whoIsAllowedToJoinThisGroup": { + "type": "String", + "placeholders": {} + }, + "@emptyChat": { + "type": "String", + "placeholders": {} + }, + "@seenByUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@noPublicLinkHasBeenCreatedYet": { - "type": "String", - "placeholders": {} - }, - "@knock": { - "type": "String", - "placeholders": {} - }, - "@hidePresences": { - "type": "String", - "placeholders": {} - }, - "@yourGlobalUserIdIs": { - "type": "String", - "placeholders": {} - }, - "@noUsersFoundWithQuery": { - "type": "String", - "placeholders": { - "query": { - "type": "String" + }, + "@storeSecurlyOnThisDevice": { + "type": "String", + "placeholders": {} + }, + "@yourChatBackupHasBeenSetUp": { + "type": "String", + "placeholders": {} + }, + "@redactedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@knocking": { - "type": "String", - "placeholders": {} - }, - "@chatCanBeDiscoveredViaSearchOnServer": { - "type": "String", - "placeholders": { - "server": { - "type": "String" + }, + "@submit": { + "type": "String", + "placeholders": {} + }, + "@videoCallsBetaWarning": { + "type": "String", + "placeholders": {} + }, + "@unmuteChat": { + "type": "String", + "placeholders": {} + }, + "@createdTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@searchChatsRooms": { - "type": "String", - "placeholders": {} - }, - "@nothingFound": { - "type": "String", - "placeholders": {} - }, - "@groupName": { - "type": "String", - "placeholders": {} - }, - "@createGroupAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@groupCanBeFoundViaSearch": { - "type": "String", - "placeholders": {} - }, - "@wrongRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@startConversation": { - "type": "String", - "placeholders": {} - }, - "@commandHint_sendraw": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationTitle": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationBody": { - "type": "String", - "placeholders": {} - }, - "@leaveEmptyToClearStatus": { - "type": "String", - "placeholders": {} - }, - "@select": { - "type": "String", - "placeholders": {} - }, - "@searchForUsers": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourCurrentPassword": { - "type": "String", - "placeholders": {} - }, - "@newPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@passwordsDoNotMatch": { - "type": "String", - "placeholders": {} - }, - "@passwordIsWrong": { - "type": "String", - "placeholders": {} - }, - "@publicLink": { - "type": "String", - "placeholders": {} - }, - "@publicChatAddresses": { - "type": "String", - "placeholders": {} - }, - "@createNewAddress": { - "type": "String", - "placeholders": {} - }, - "@joinSpace": { - "type": "String", - "placeholders": {} - }, - "@publicSpaces": { - "type": "String", - "placeholders": {} - }, - "@addChatOrSubSpace": { - "type": "String", - "placeholders": {} - }, - "@subspace": { - "type": "String", - "placeholders": {} - }, - "@decline": { - "type": "String", - "placeholders": {} - }, - "@thisDevice": { - "type": "String", - "placeholders": {} - }, - "@initAppError": { - "type": "String", - "placeholders": {} - }, - "@userRole": { - "type": "String", - "placeholders": {} - }, - "@minimumPowerLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "String" + }, + "@redactedAnEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@searchIn": { - "type": "String", - "placeholders": { - "chat": { - "type": "String" + }, + "@compareEmojiMatch": { + "type": "String", + "placeholders": {} + }, + "@participant": { + "type": "String", + "placeholders": {} + }, + "@logInTo": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } } - } - }, - "@searchMore": { - "type": "String", - "placeholders": {} - }, - "@gallery": { - "type": "String", - "placeholders": {} - }, - "@files": { - "type": "String", - "placeholders": {} - }, - "@databaseBuildErrorBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" + }, + "@yes": { + "type": "String", + "placeholders": {} + }, + "@containsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@username": { + "type": "String", + "placeholders": {} + }, + "@changedTheRoomAliases": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "@fileIsTooBigForServer": { + "type": "String", + "placeholders": { + "max": { + "type": "String" + } + } + }, + "@homeserver": { + "type": "String", + "placeholders": {} + }, + "@help": { + "type": "String", + "placeholders": {} + }, + "@people": { + "type": "String", + "placeholders": {} + }, + "@leftTheChat": { + "type": "String", + "placeholders": {} + }, + "@verified": { + "type": "String", + "placeholders": {} + }, + "@setStatus": { + "type": "String", + "placeholders": {} + }, + "@groupWith": { + "type": "String", + "placeholders": { + "displayname": { + "type": "String" + } + } + }, + "@callingPermissions": { + "type": "String", + "placeholders": {} + }, + "@delete": { + "type": "String", + "placeholders": {} + }, + "@newMessageInFluffyChat": { + "type": "String", + "placeholders": {} + }, + "@readUpToHere": { + "type": "String", + "placeholders": {} + }, + "@start": { + "type": "String", + "placeholders": {} + }, + "@downloadFile": { + "type": "String", + "placeholders": {} + }, + "@deviceId": { + "type": "String", + "placeholders": {} + }, + "@register": { + "type": "String", + "placeholders": {} + }, + "@unlockOldMessages": { + "type": "String", + "placeholders": {} + }, + "@identity": { + "type": "String", + "placeholders": {} + }, + "@numChats": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "@ignore": { + "type": "String", + "placeholders": {} + }, + "@recording": { + "type": "String", + "placeholders": {} + }, + "@moderator": { + "type": "String", + "placeholders": {} + }, + "@optionalRedactReason": { + "type": "String", + "placeholders": {} + }, + "@waitingPartnerEmoji": { + "type": "String", + "placeholders": {} + }, + "@channelCorruptedDecryptError": { + "type": "String", + "placeholders": {} + }, + "@tryToSendAgain": { + "type": "String", + "placeholders": {} + }, + "@guestsCanJoin": { + "type": "String", + "placeholders": {} + }, + "@ok": { + "type": "String", + "placeholders": {} + }, + "@copyToClipboard": { + "type": "String", + "placeholders": {} + }, + "@dehydrate": { + "type": "String", + "placeholders": {} + }, + "@locationPermissionDeniedNotice": { + "type": "String", + "placeholders": {} + }, + "@send": { + "type": "String", + "placeholders": {} + }, + "@hasWithdrawnTheInvitationFor": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "@visibleForAllParticipants": { + "type": "String", + "placeholders": {} + }, + "@noRoomsFound": { + "type": "String", + "placeholders": {} + }, + "@sendAsText": { + "type": "String" + }, + "@inviteForMe": { + "type": "String", + "placeholders": {} + }, + "@archiveRoomDescription": { + "type": "String", + "placeholders": {} + }, + "@sendSticker": { + "type": "String", + "placeholders": {} + }, + "@switchToAccount": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "@commandInvalid": { + "type": "String" + }, + "@setAsCanonicalAlias": { + "type": "String", + "placeholders": {} + }, + "@whyDoYouWantToReportThis": { + "type": "String", + "placeholders": {} + }, + "@locationDisabledNotice": { + "type": "String", + "placeholders": {} + }, + "@placeCall": { + "type": "String", + "placeholders": {} + }, + "@removedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "@changedTheRoomInvitationLink": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "@newChat": { + "type": "String", + "placeholders": {} + }, + "@notifications": { + "type": "String", + "placeholders": {} + }, + "@commandHint_plain": { + "type": "String", + "description": "Usage hint for the command /plain" + }, + "@emoteSettings": { + "type": "String", + "placeholders": {} + }, + "@experimentalVideoCalls": { + "type": "String", + "placeholders": {} + }, + "@openCamera": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterRecoveryKeyDescription": { + "type": "String", + "placeholders": {} + }, + "@guestsAreForbidden": { + "type": "String", + "placeholders": {} + }, + "@mention": { + "type": "String", + "placeholders": {} + }, + "@openInMaps": { + "type": "String", + "placeholders": {} + }, + "@withTheseAddressesRecoveryDescription": { + "type": "String", + "placeholders": {} + }, + "@inviteContactToGroupQuestion": { + "type": "String", + "placeholders": { + "contact": {}, + "groupName": {} + } + }, + "@emoteExists": { + "type": "String", + "placeholders": {} + }, + "@redactedByBecause": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "reason": { + "type": "String" + } + } + }, + "@isTyping": { + "type": "String", + "placeholders": {} + }, + "@youHaveWithdrawnTheInvitationFor": { + "placeholders": { + "user": { + "type": "String" + } }, - "error": { - "type": "String" + "type": "String" + }, + "@chat": { + "type": "String", + "placeholders": {} + }, + "@group": { + "type": "String", + "placeholders": {} + }, + "@leave": { + "type": "String", + "placeholders": {} + }, + "@skip": { + "type": "String", + "placeholders": {} + }, + "@appearOnTopDetails": { + "type": "String", + "placeholders": {} + }, + "@roomHasBeenUpgraded": { + "type": "String", + "placeholders": {} + }, + "@enterRoom": { + "type": "String", + "placeholders": {} + }, + "@enableEmotesGlobally": { + "type": "String", + "placeholders": {} + }, + "@pleaseChooseAPasscode": { + "type": "String", + "placeholders": {} + }, + "@noPasswordRecoveryDescription": { + "type": "String", + "placeholders": {} + }, + "@reportUser": { + "type": "String", + "placeholders": {} + }, + "@sharedTheLocation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@sessionLostBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" + }, + "@commandHint_send": { + "type": "String", + "description": "Usage hint for the command /send" + }, + "@onlineKeyBackupEnabled": { + "type": "String", + "placeholders": {} + }, + "@unbannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "@confirmEventUnpin": { + "type": "String", + "placeholders": {} + }, + "@youInvitedUser": { + "placeholders": { + "user": { + "type": "String" + } }, - "error": { - "type": "String" + "type": "String" + }, + "@kickedAndBanned": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } } - } - }, - "@restoreSessionBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" + }, + "@noConnectionToTheServer": { + "type": "String", + "placeholders": {} + }, + "@fileHasBeenSavedAt": { + "type": "String", + "placeholders": { + "path": { + "type": "String" + } + } + }, + "@license": { + "type": "String", + "placeholders": {} + }, + "@unbanFromChat": { + "type": "String", + "placeholders": {} + }, + "@commandMissing": { + "type": "String", + "placeholders": { + "command": { + "type": "String" + } }, - "error": { - "type": "String" + "description": "State that {command} is not a valid /command." + }, + "@redactMessageDescription": { + "type": "String", + "placeholders": {} + }, + "@rejoin": { + "type": "String", + "placeholders": {} + }, + "@recoveryKey": { + "type": "String", + "placeholders": {} + }, + "@redactMessage": { + "type": "String", + "placeholders": {} + }, + "@forward": { + "type": "String", + "placeholders": {} + }, + "@commandHint_discardsession": { + "type": "String", + "description": "Usage hint for the command /discardsession" + }, + "@invalidInput": { + "type": "String", + "placeholders": {} + }, + "@hideUnknownEvents": { + "type": "String", + "placeholders": {} + }, + "@dehydrateTorLong": { + "type": "String", + "placeholders": {} + }, + "@yourPublicKey": { + "type": "String", + "placeholders": {} + }, + "@tooManyRequestsWarning": { + "type": "String", + "placeholders": {} + }, + "@invitedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } } - } - }, - "@forwardMessageTo": { - "type": "String", - "placeholders": { - "roomName": { - "type": "String" + }, + "@kickFromChat": { + "type": "String", + "placeholders": {} + }, + "@commandHint_myroomnick": { + "type": "String", + "description": "Usage hint for the command /myroomnick" + }, + "@offline": { + "type": "String", + "placeholders": {} + }, + "@noPermission": { + "type": "String", + "placeholders": {} + }, + "@doNotShowAgain": { + "type": "String", + "placeholders": {} + }, + "@report": { + "type": "String", + "placeholders": {} + }, + "@status": { + "type": "String", + "placeholders": {} + }, + "@compareNumbersMatch": { + "type": "String", + "placeholders": {} + }, + "@groupIsPublic": { + "type": "String", + "placeholders": {} + }, + "@verifyStart": { + "type": "String", + "placeholders": {} + }, + "@memberChanges": { + "type": "String", + "placeholders": {} + }, + "@joinRoom": { + "type": "String", + "placeholders": {} + }, + "@unverified": { + "type": "String", + "placeholders": {} + }, + "@fluffychat": { + "type": "String", + "placeholders": {} + }, + "@howOffensiveIsThisContent": { + "type": "String", + "placeholders": {} + }, + "@serverRequiresEmail": { + "type": "String", + "placeholders": {} + }, + "@hideUnimportantStateEvents": { + "type": "String", + "placeholders": {} + }, + "@screenSharingTitle": { + "type": "String", + "placeholders": {} + }, + "@widgetCustom": { + "type": "String", + "placeholders": {} + }, + "@sentCallInformations": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } } - } - }, - "@sendReadReceipts": { - "type": "String", - "placeholders": {} - }, - "@sendTypingNotificationsDescription": { - "type": "String", - "placeholders": {} - }, - "@sendReadReceiptsDescription": { - "type": "String", - "placeholders": {} - }, - "@formattedMessages": { - "type": "String", - "placeholders": {} - }, - "@formattedMessagesDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUser": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUserDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDevice": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDeviceDescription": { - "type": "String", - "placeholders": {} - }, - "@acceptedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@canceledKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@completedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@isReadyForKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@requestedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@startedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@transparent": { - "type": "String", - "placeholders": {} - }, - "@incomingMessages": { - "type": "String", - "placeholders": {} - }, - "@stickers": { - "type": "String", - "placeholders": {} - }, - "@discover": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ignore": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unignore": { - "type": "String", - "placeholders": {} - }, - "@unreadChatsInApp": { - "type": "String", - "placeholders": { - "appname": { - "type": "String" + }, + "@addToSpaceDescription": { + "type": "String", + "placeholders": {} + }, + "@youBannedUser": { + "placeholders": { + "user": { + "type": "String" + } }, - "unread": { - "type": "String" + "type": "String" + }, + "@theyDontMatch": { + "type": "String", + "placeholders": {} + }, + "@youHaveBeenBannedFromThisChat": { + "type": "String", + "placeholders": {} + }, + "@displaynameHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "@sentAnAudio": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@noDatabaseEncryption": { - "type": "String", - "placeholders": {} - }, - "@thereAreCountUsersBlocked": { - "type": "String", - "placeholders": { + }, + "@editRoomAvatar": { + "type": "String", + "placeholders": {} + }, + "@encrypted": { + "type": "String", + "placeholders": {} + }, + "@commandHint_leave": { + "type": "String", + "description": "Usage hint for the command /leave" + }, + "@commandHint_myroomavatar": { + "type": "String", + "description": "Usage hint for the command /myroomavatar" + }, + "@hasKnocked": { + "placeholders": { + "user": { + "type": "String" + } + }, + "type": "String" + }, + "@publish": { + "type": "String", + "placeholders": {} + }, + "@openLinkInBrowser": { + "type": "String", + "placeholders": {} + }, + "@clearArchive": { + "type": "String", + "placeholders": {} + }, + "@commandHint_react": { + "type": "String", + "description": "Usage hint for the command /react" + }, + "@commandHint_me": { + "type": "String", + "description": "Usage hint for the command /me" + }, + "@pleaseEnterYourUsername": { + "type": "String", + "placeholders": {} + }, + "@messageInfo": { + "type": "String", + "placeholders": {} + }, + "@disableEncryptionWarning": { + "type": "String", + "placeholders": {} + }, + "@directChat": { + "type": "String", + "placeholders": {} + }, + "@encryptionNotEnabled": { + "type": "String", + "placeholders": {} + }, + "@wrongPinEntered": { + "type": "String", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "@lightTheme": { + "type": "String", + "placeholders": {} + }, + "@inviteGroupChat": { + "type": "String", + "placeholders": {} + }, + "@appearOnTop": { + "type": "String", + "placeholders": {} + }, + "@invitePrivateChat": { + "type": "String", + "placeholders": {} + }, + "@verifyTitle": { + "type": "String", + "placeholders": {} + }, + "@foregroundServiceRunning": { + "type": "String", + "placeholders": {} + }, + "@enterAnEmailAddress": { + "type": "String", + "placeholders": {} + }, + "@voiceCall": { + "type": "String", + "placeholders": {} + }, + "@commandHint_kick": { + "type": "String", + "description": "Usage hint for the command /kick" + }, + "@copiedToClipboard": { + "type": "String", + "placeholders": {} + }, + "@createNewSpace": { + "type": "String", + "placeholders": {} + }, + "@commandHint_unban": { + "type": "String", + "description": "Usage hint for the command /unban" + }, + "@unknownEncryptionAlgorithm": { + "type": "String", + "placeholders": {} + }, + "@confirm": { + "type": "String", + "placeholders": {} + }, + "@wasDirectChatDisplayName": { + "type": "String", + "placeholders": { + "oldDisplayName": { + "type": "String" + } + } + }, + "@noChatDescriptionYet": { + "type": "String", + "placeholders": {} + }, + "@defaultPermissionLevel": { + "type": "String", + "placeholders": {} + }, + "@removeFromBundle": { + "type": "String", + "placeholders": {} + }, + "@numUsersTyping": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@fontSize": { + "type": "String", + "placeholders": {} + }, + "@whoCanPerformWhichAction": { + "type": "String", + "placeholders": {} + }, + "@learnMore": { + "type": "String", + "placeholders": {} + }, + "@iHaveClickedOnLink": { + "type": "String", + "placeholders": {} + }, + "@you": { + "type": "String", + "placeholders": {} + }, + "@users": { + "type": "String", + "placeholders": {} + }, + "@openGallery": { + "type": "String", + "placeholders": {} + }, + "@chatDescriptionHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "@search": { + "type": "String", + "placeholders": {} + }, + "@newGroup": { + "type": "String", + "placeholders": {} + }, + "@bundleName": { + "type": "String", + "placeholders": {} + }, + "@dehydrateTor": { + "type": "String", + "placeholders": {} + }, + "@removeFromSpace": { + "type": "String", + "placeholders": {} + }, + "@dateAndTimeOfDay": { + "type": "String", + "placeholders": { + "date": { + "type": "String" + }, + "timeOfDay": { + "type": "String" + } + } + }, + "@commandHint_op": { + "type": "String", + "description": "Usage hint for the command /op" + }, + "@commandHint_join": { + "type": "String", + "description": "Usage hint for the command /join" + }, + "@sourceCode": { + "type": "String", + "placeholders": {} + }, + "@roomUpgradeDescription": { + "type": "String", + "placeholders": {} + }, + "@userSentUnknownEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "type": { + "type": "String" + } + } + }, + "@scanQrCode": { + "type": "String", + "placeholders": {} + }, + "@logout": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterANumber": { + "type": "String", + "placeholders": {} + }, + "@contactHasBeenInvitedToTheGroup": { + "type": "String", + "placeholders": {} + }, + "@youKicked": { + "placeholders": { + "user": { + "type": "String" + } + }, + "type": "String" + }, + "@profileNotFound": { + "type": "String", + "placeholders": {} + }, + "@jump": { + "type": "String", + "placeholders": {} + }, + "@groups": { + "type": "String", + "placeholders": {} + }, + "@reactedWith": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "reaction": { + "type": "String" + } + } + }, + "@sorryThatsNotPossible": { + "type": "String", + "placeholders": {} + }, + "@videoWithSize": { + "type": "String", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "@oopsSomethingWentWrong": { + "type": "String", + "placeholders": {} + }, + "@loadCountMoreParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@shareInviteLink": { + "type": "String", + "placeholders": {} + }, + "@commandHint_markasdm": { + "type": "String", + "placeholders": {} + }, + "@recoveryKeyLost": { + "type": "String", + "placeholders": {} + }, + "@containsUserName": { + "type": "String", + "placeholders": {} + }, + "@messages": { + "type": "String", + "placeholders": {} + }, + "@login": { + "type": "String", + "placeholders": {} + }, + "@deviceKeys": { + "type": "String", + "placeholders": {} + }, + "@waitingPartnerNumbers": { + "type": "String", + "placeholders": {} + }, + "@noGoogleServicesWarning": { + "type": "String", + "placeholders": {} + }, + "@everythingReady": { + "type": "String", + "placeholders": {} + }, + "@emoteKeyboardNoRecents": { + "type": "String", + "placeholders": {} + }, + "@setCustomEmotes": { + "type": "String", + "placeholders": {} + }, + "@startedACall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "@emoteInvalid": { + "type": "String", + "placeholders": {} + }, + "@systemTheme": { + "type": "String", + "placeholders": {} + }, + "@notificationsEnabledForThisAccount": { + "type": "String", + "placeholders": {} + }, + "@deleteMessage": { + "type": "String", + "placeholders": {} + }, + "@visibilityOfTheChatHistory": { + "type": "String", + "placeholders": {} + }, + "@settings": { + "type": "String", + "placeholders": {} + }, + "@setTheme": { + "type": "String", + "placeholders": {} + }, + "@changeTheHomeserver": { + "type": "String", + "placeholders": {} + }, + "@youJoinedTheChat": { + "type": "String", + "placeholders": {} + }, + "@wallpaper": { + "type": "String", + "placeholders": {} + }, + "@openVideoCamera": { + "type": "String", + "placeholders": {} + }, + "@play": { + "type": "String", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "@chatBackupDescription": { + "type": "String", + "placeholders": {} + }, + "@passwordForgotten": { + "type": "String", + "placeholders": {} + }, + "@statusExampleMessage": { + "type": "String", + "placeholders": {} + }, + "@security": { + "type": "String", + "placeholders": {} + }, + "@markAsRead": { + "type": "String", + "placeholders": {} + }, + "@sendAudio": { + "type": "String", + "placeholders": {} + }, + "@widgetName": { + "type": "String", + "placeholders": {} + }, + "@sentASticker": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "@errorAddingWidget": { + "type": "String", + "placeholders": {} + }, + "@commandHint_dm": { + "type": "String", + "description": "Usage hint for the command /dm" + }, + "@reject": { + "type": "String", + "placeholders": {} + }, + "@extremeOffensive": { + "type": "String", + "placeholders": {} + }, + "@editBlockedServers": { + "type": "String", + "placeholders": {} + }, + "@oopsPushError": { + "type": "String", + "placeholders": {} + }, + "@youUnbannedUser": { + "placeholders": { + "user": { + "type": "String" + } + }, + "type": "String" + }, + "@deactivateAccountWarning": { + "type": "String", + "placeholders": {} + }, + "@joinedTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "@visibleForEveryone": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnter4Digits": { + "type": "String", + "placeholders": {} + }, + "@newSpace": { + "type": "String", + "placeholders": {} + }, + "@devices": { + "type": "String", + "placeholders": {} + }, + "@unknownEvent": { + "type": "String", + "placeholders": { + "type": { + "type": "String" + } + } + }, + "@emojis": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterYourPin": { + "type": "String", + "placeholders": {} + }, + "@pleaseChoose": { + "type": "String", + "placeholders": {} + }, + "@share": { + "type": "String", + "placeholders": {} + }, + "@pleaseTryAgainLaterOrChooseDifferentServer": { + "type": "String", + "placeholders": {} + }, + "@createGroup": { + "type": "String", + "placeholders": {} + }, + "@privacy": { + "type": "String", + "placeholders": {} + }, + "@sendImage": { + "type": "String", + "placeholders": {} + }, + "@hydrateTorLong": { + "type": "String", + "placeholders": {} + }, + "@time": { + "type": "String", + "placeholders": {} + }, + "@enterYourHomeserver": { + "type": "String", + "placeholders": {} + }, + "@contentHasBeenReported": { + "type": "String", + "placeholders": {} + }, + "@custom": { + "type": "String", + "placeholders": {} + }, + "@noBackupWarning": { + "type": "String", + "placeholders": {} + }, + "@fromJoining": { + "type": "String", + "placeholders": {} + }, + "@verify": { + "type": "String", + "placeholders": {} + }, + "@sendVideo": { + "type": "String", + "placeholders": {} + }, + "@storeInSecureStorageDescription": { + "type": "String", + "placeholders": {} + }, + "@openChat": { + "type": "String", + "placeholders": {} + }, + "@kickUserDescription": { + "type": "String", + "placeholders": {} + }, + "@sendAMessage": { + "type": "String", + "placeholders": {} + }, + "@pin": { + "type": "String", + "placeholders": {} + }, + "@deleteAccount": { + "type": "String", + "placeholders": {} + }, + "@setInvitationLink": { + "type": "String", + "placeholders": {} + }, + "@pinMessage": { + "type": "String", + "placeholders": {} + }, + "@screenSharingDetail": { + "type": "String", + "placeholders": {} + }, + "@muteChat": { + "type": "String", + "placeholders": {} + }, + "@invite": { + "type": "String", + "placeholders": {} + }, + "@enableMultiAccounts": { + "type": "String", + "placeholders": {} + }, + "@emotePacks": { + "type": "String", + "placeholders": {} + }, + "@indexedDbErrorTitle": { + "type": "String", + "placeholders": {} + }, + "@endedTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "changedTheRoomAliases": "{username} άλλαξε τα ψευδώνυμα του δωματίου", + "changedTheRoomInvitationLink": "{username} άλλαξε τον σύνδεσμο πρόσκλησης", + "changeTheHomeserver": "Αλλαγή του διακομιστή σπιτιού", + "changeTheNameOfTheGroup": "Αλλαγή ονόματος ομάδας", + "channelCorruptedDecryptError": "Ο κρυπτογραφημένος κώδικας έχει καταστραφεί", + "chat": "Συνομιλία", + "yourChatBackupHasBeenSetUp": "Η δημιουργία αντιγράφων ασφαλείας της συνομιλίας σας έχει ρυθμιστεί.", + "chatBackupDescription": "Τα παλιά σας μηνύματα είναι ασφαλή με ένα κλειδί ανάκτησης. Παρακαλώ βεβαιωθείτε ότι δεν το χάνετε.", + "clearArchive": "Διαγραφή αρχείου", + "commandHint_markasdm": "Σημειώστε ως άμεσο μήνυμα για το δοσμένο ID του Matrix", + "commandHint_markasgroup": "Σημειώστε ως ομάδα", + "commandHint_create": "Δημιουργήστε μια κενή ομαδική συνομιλία\nΧρησιμοποιήστε --no-encryption για να απενεργοποιήσετε την κρυπτογράφηση", + "commandHint_discardsession": "Απορρίψτε τη συνεδρία", + "commandHint_dm": "Ξεκινήστε μια άμεση συνομιλία\nΧρησιμοποιήστε --no-encryption για να απενεργοποιήσετε την κρυπτογράφηση", + "commandHint_html": "Αποστείλετε κείμενο μορφοποιημένο σε HTML", + "commandHint_join": "Ενταχθείτε στο δοσμένο δωμάτιο", + "commandHint_kick": "Αφαιρέστε τον δοσμένο χρήστη από αυτό το δωμάτιο", + "commandHint_leave": "Αφήστε αυτό το δωμάτιο", + "commandHint_me": "Περιγράψτε τον εαυτό σας", + "commandHint_myroomavatar": "Ορίστε τη φωτογραφία σας για αυτό το δωμάτιο (με mxc-uri)", + "commandHint_myroomnick": "Ορίστε το όνομα εμφάνισης για αυτό το δωμάτιο", + "commandHint_op": "Ορίστε το επίπεδο ισχύος του χρήστη (προεπιλογή: 50)", + "commandHint_plain": "Αποστολή κειμένου χωρίς μορφοποίηση", + "commandHint_react": "Αποστολή απάντησης ως αντίδραση", + "commandHint_send": "Αποστολή κειμένου", + "commandHint_unban": "Άρση αποκλεισμού του χρήστη από αυτό το δωμάτιο", + "commandInvalid": "Μη έγκυρη εντολή", + "commandMissing": "{command} δεν είναι εντολή.", + "compareEmojiMatch": "Παρακαλώ συγκρίνετε τα emojis", + "compareNumbersMatch": "Παρακαλώ συγκρίνετε τους αριθμούς", + "configureChat": "Ρύθμιση συνομιλίας", + "confirm": "Επιβεβαίωση", + "connect": "Σύνδεση", + "contactHasBeenInvitedToTheGroup": "Ο επαφή έχει προσκληθεί στην ομάδα", + "containsDisplayName": "Περιέχει όνομα εμφάνισης", + "containsUserName": "Περιέχει όνομα χρήστη", + "contentHasBeenReported": "Το περιεχόμενο έχει αναφερθεί στους διαχειριστές του διακομιστή", + "copiedToClipboard": "Αντιγράφηκε στο πρόχειρο", + "copy": "Αντιγραφή", + "copyToClipboard": "Αντιγραφή στο πρόχειρο", + "couldNotDecryptMessage": "Αδύνατη η αποκρυπτογράφηση μηνύματος: {error}", + "checkList": "Λίστα ελέγχου", + "countParticipants": "{count} συμμετέχοντες", + "countInvited": "{count} προσκεκλημένοι", + "create": "Δημιουργία", + "createdTheChat": "💬 {username} δημιούργησε τη συνομιλία", + "createGroup": "Δημιουργία ομάδας", + "createNewSpace": "Νέος χώρος", + "currentlyActive": "Ενεργό αυτήν τη στιγμή", + "darkTheme": "Σκοτεινό", + "dateAndTimeOfDay": "{date}, {timeOfDay}", + "dateWithoutYear": "{month}-{day}", + "dateWithYear": "{year}-{month}-{day}", + "deactivateAccountWarning": "Αυτό θα απενεργοποιήσει τον λογαριασμό σας. Δεν μπορεί να αναιρεθεί! Είστε σίγουροι;", + "defaultPermissionLevel": "Προεπιλεγμένο επίπεδο δικαιωμάτων για νέους χρήστες", + "delete": "Διαγραφή", + "deleteAccount": "Διαγραφή λογαριασμού", + "deleteMessage": "Διαγραφή μηνύματος", + "device": "Συσκευή", + "deviceId": "Ταυτότητα συσκευής", + "devices": "Συσκευές", + "directChats": "Άμεσα μηνύματα", + "allRooms": "Όλες οι ομαδικές συνομιλίες", + "displaynameHasBeenChanged": "Το όνομα εμφανιζόμενου έχει αλλάξει", + "downloadFile": "Λήψη αρχείου", + "edit": "Επεξεργασία", + "editBlockedServers": "Επεξεργασία αποκλεισμένων διακομιστών", + "chatPermissions": "Άδειες συνομιλίας", + "editDisplayname": "Επεξεργασία ονόματος εμφανιζόμενου", + "editRoomAliases": "Επεξεργασία ψευδωνύμων δωματίου", + "editRoomAvatar": "Επεξεργασία εικόνας δωματίου", + "emoteExists": "Το emoji ήδη υπάρχει!", + "emoteInvalid": "Μη έγκυρος κωδικός emoji!", + "emoteKeyboardNoRecents": "Πρόσφατα χρησιμοποιημένα emoji θα εμφανίζονται εδώ...", + "emotePacks": "Πακέτα emoji για το δωμάτιο", + "emoteSettings": "Ρυθμίσεις emoji", + "globalChatId": "Παγκόσμιο ID συνομιλίας", + "accessAndVisibility": "Πρόσβαση και ορατότητα", + "accessAndVisibilityDescription": "Ποιος επιτρέπεται να συμμετάσχει σε αυτήν τη συνομιλία και πώς μπορεί να βρεθεί η συνομιλία.", + "calls": "Κλήσεις", + "customEmojisAndStickers": "Προσαρμοσμένα emojis και αυτοκόλλητα", + "customEmojisAndStickersBody": "Προσθέστε ή μοιραστείτε προσαρμοσμένα emojis ή αυτοκόλλητα που μπορούν να χρησιμοποιηθούν σε οποιαδήποτε συνομιλία.", + "emoteShortcode": "Κωδικός σύντομου emoji", + "emoteWarnNeedToPick": "Πρέπει να επιλέξετε έναν κωδικό emoji και μια εικόνα!", + "emptyChat": "Άδειο chat", + "enableEmotesGlobally": "Ενεργοποίηση πακέτου emoji σε όλο το σύστημα", + "enableEncryption": "Ενεργοποίηση κρυπτογράφησης", + "enableEncryptionWarning": "Δεν θα μπορείτε πλέον να απενεργοποιήσετε την κρυπτογράφηση. Είστε σίγουροι;", + "encrypted": "Κρυπτογραφημένο", + "encryption": "Κρυπτογράφηση", + "encryptionNotEnabled": "Η κρυπτογράφηση δεν είναι ενεργοποιημένη", + "endedTheCall": "{senderName} τερμάτισε την κλήση", + "enterAnEmailAddress": "Εισάγετε μια διεύθυνση email", + "homeserver": "Homeserver", + "enterYourHomeserver": "Εισάγετε το homeserver σας", + "errorObtainingLocation": "Σφάλμα κατά την απόκτηση τοποθεσίας: {error}", + "everythingReady": "Όλα έτοιμα!", + "extremeOffensive": "Απολύτως προσβλητικό", + "fileName": "Όνομα αρχείου", + "fluffychat": "FluffyChat", + "fontSize": "Μέγεθος γραμματοσειράς", + "forward": "Προώθηση", + "fromJoining": "Από την ένταξη", + "fromTheInvitation": "Από την πρόσκληση", + "goToTheNewRoom": "Μεταβείτε στο νέο δωμάτιο", + "group": "Ομάδα", + "chatDescription": "Περιγραφή συνομιλίας", + "chatDescriptionHasBeenChanged": "Η περιγραφή της συνομιλίας άλλαξε", + "groupIsPublic": "Η ομάδα είναι δημόσια", + "groups": "Ομάδες", + "groupWith": "Ομάδα με {displayname}", + "guestsAreForbidden": "Οι επισκέπτες απαγορεύονται", + "guestsCanJoin": "Οι επισκέπτες μπορούν να συμμετάσχουν", + "hasWithdrawnTheInvitationFor": "{username} έχει αποσύρει την πρόσκληση για {targetName}", + "help": "Βοήθεια", + "hideRedactedEvents": "Απόκρυψη επεξεργασμένων γεγονότων", + "hideRedactedMessages": "Απόκρυψη επεξεργασμένων μηνυμάτων", + "hideRedactedMessagesBody": "Αν κάποιος επεξεργαστεί ένα μήνυμα, αυτό το μήνυμα δεν θα είναι πλέον ορατό στη συνομιλία.", + "hideInvalidOrUnknownMessageFormats": "Απόκρυψη μη έγκυρων ή άγνωστων μορφών μηνυμάτων", + "howOffensiveIsThisContent": "Πόσο προσβλητικό είναι αυτό το περιεχόμενο;", + "id": "ID", + "identity": "Ταυτότητα", + "block": "Αποκλεισμός", + "blockedUsers": "Αποκλεισμένοι χρήστες", + "blockListDescription": "Μπορείτε να αποκλείσετε χρήστες που σας ενοχλούν. Δεν θα μπορείτε να λαμβάνετε μηνύματα ή προσκλήσεις δωματίου από τους χρήστες στη λίστα αποκλεισμού σας.", + "blockUsername": "Αγνόηση ονόματος χρήστη", + "iHaveClickedOnLink": "Έχω κάνει κλικ στον σύνδεσμο", + "incorrectPassphraseOrKey": "Λάθος φράση πρόσβασης ή κλειδί ανάκτησης", + "inoffensive": "Ακίνδυνο", + "inviteContact": "Πρόσκληση επαφής", + "inviteContactToGroupQuestion": "Θέλετε να προσκαλέσετε τον {contact} στη συνομιλία \"{groupName}\";", + "inviteContactToGroup": "Πρόσκληση επαφής στο {groupName}", + "noChatDescriptionYet": "Δεν έχει δημιουργηθεί ακόμη περιγραφή συνομιλίας.", + "tryAgain": "Δοκιμάστε ξανά", + "invalidServerName": "Μη έγκυρο όνομα διακομιστή", + "invited": "Προσκαλέστηκε", + "redactMessageDescription": "Το μήνυμα θα διαγραφεί για όλους τους συμμετέχοντες σε αυτή τη συνομιλία. Αυτό δεν μπορεί να αναιρεθεί.", + "optionalRedactReason": "(Προαιρετικό) Λόγος διαγραφής αυτού του μηνύματος...", + "invitedUser": "📩 {username} προσκάλεσε {targetName}", + "invitedUsersOnly": "Μόνο για προσκεκλημένους χρήστες", + "inviteForMe": "Πρόσκληση για μένα", + "inviteText": "{username} σε κάλεσε στο FluffyChat.\n1. Επισκεφθείτε το fluffychat.im και εγκαταστήστε την εφαρμογή \n2. Εγγραφείτε ή συνδεθείτε \n3. Ανοίξτε τον σύνδεσμο πρόσκλησης: \n {link}", + "isTyping": "γράφει…", + "joinedTheChat": "👋 {username} μπήκε στη συζήτηση", + "joinRoom": "Ενταχθείτε στο δωμάτιο", + "kicked": "👞 {username} έδιωξε τον/την {targetName}", + "kickedAndBanned": "🙅 {username} έδιωξε και απαγόρευσε τον/την {targetName}", + "kickFromChat": "Απομάκρυνση από τη συζήτηση", + "lastActiveAgo": "Τελευταία δραστηριότητα: {localizedTimeShort}", + "leave": "Αποχώρηση", + "leftTheChat": "Έφυγε από τη συζήτηση", + "license": "Άδεια", + "lightTheme": "Φωτεινό", + "loadCountMoreParticipants": "Φόρτωση {count} επιπλέον συμμετεχόντων", + "dehydrate": "Εξαγωγή συνεδρίας και διαγραφή συσκευής", + "dehydrateWarning": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Βεβαιωθείτε ότι αποθηκεύετε με ασφάλεια το αρχείο αντιγράφου ασφαλείας.", + "dehydrateTor": "Χρήστες TOR: Εξαγωγή συνεδρίας", + "dehydrateTorLong": "Για χρήστες TOR, συνιστάται να εξάγετε τη συνεδρία πριν κλείσετε το παράθυρο.", + "hydrateTor": "Χρήστες TOR: Εισαγωγή εξαγωγής συνεδρίας", + "hydrateTorLong": "Έχετε εξάγει την συνεδρία σας τελευταία φορά στο TOR; Εισάγετέ το γρήγορα και συνεχίστε τη συνομιλία.", + "hydrate": "Ανάκτηση από αρχείο δημιουργίας αντιγράφων ασφαλείας", + "loadingPleaseWait": "Φόρτωση… Παρακαλώ περιμένετε.", + "loadMore": "Φόρτωση περισσότερων…", + "locationDisabledNotice": "Οι υπηρεσίες τοποθεσίας είναι απενεργοποιημένες. Παρακαλώ ενεργοποιήστε τες για να μπορείτε να μοιραστείτε την τοποθεσία σας.", + "locationPermissionDeniedNotice": "Η άδεια τοποθεσίας απορρίφθηκε. Παρακαλώ δώστε την για να μπορείτε να μοιραστείτε την τοποθεσία σας.", + "login": "Σύνδεση", + "logInTo": "Συνδεθείτε στο {homeserver}", + "logout": "Αποσύνδεση", + "memberChanges": "Αλλαγές μελών", + "mention": "Αναφορά", + "messages": "Μηνύματα", + "messagesStyle": "Μηνύματα:", + "moderator": "Διαχειριστής", + "muteChat": "Αθόρυβη συνομιλία", + "needPantalaimonWarning": "Παρακαλώ να γνωρίζετε ότι χρειάζεστε το Pantalaimon για να χρησιμοποιήσετε κρυπτογράφηση άκρο-σε-άκρο προς το παρόν.", + "newChat": "Νέα συνομιλία", + "newMessageInFluffyChat": "🗨️ Νέο μήνυμα στο FluffyChat", + "newVerificationRequest": "Νέο αίτημα επαλήθευσης!", + "next": "Επόμενο", + "no": "Όχι", + "noConnectionToTheServer": "Δεν υπάρχει σύνδεση με τον διακομιστή", + "noEmotesFound": "Δεν βρέθηκαν εμοτέ. 😝", + "noEncryptionForPublicRooms": "Μπορείτε να ενεργοποιήσετε την κρυπτογράφηση μόνο όταν το δωμάτιο δεν είναι πλέον δημόσια προσβάσιμο.", + "noGoogleServicesWarning": "Το Firebase Cloud Messaging δεν φαίνεται να είναι διαθέσιμο στη συσκευή σας. Για να λαμβάνετε ακόμα ειδοποιήσεις push, προτείνουμε την εγκατάσταση του ntfy. Με το ntfy ή έναν άλλο πάροχο Unified Push μπορείτε να λαμβάνετε ειδοποιήσεις push με ασφαλή τρόπο. Μπορείτε να κατεβάσετε το ntfy από το PlayStore ή από το F-Droid.", + "noMatrixServer": "{server1} δεν είναι διακομιστής matrix, να χρησιμοποιήσω το {server2} αντί αυτού;", + "shareInviteLink": "Μοιραστείτε τον σύνδεσμο πρόσκλησης", + "scanQrCode": "Σάρωση κωδικού QR", + "none": "Κανένα", + "noPasswordRecoveryDescription": "Δεν έχετε προσθέσει ακόμα τρόπο ανάκτησης του κωδικού πρόσβασής σας.", + "noPermission": "Χωρίς άδεια", + "noRoomsFound": "Δεν βρέθηκαν δωμάτια…", + "notifications": "Ειδοποιήσεις", + "notificationsEnabledForThisAccount": "Οι ειδοποιήσεις είναι ενεργοποιημένες για αυτόν τον λογαριασμό", + "numUsersTyping": "{count} χρήστες πληκτρολογούν…", + "obtainingLocation": "Λήψη τοποθεσίας…", + "offensive": "Προσβλητικό", + "offline": "Εκτός σύνδεσης", + "ok": "Εντάξει", + "online": "Σε σύνδεση", + "onlineKeyBackupEnabled": "Ενεργοποιήθηκε η online δημιουργία αντιγράφων ασφαλείας κλειδιών", + "oopsPushError": "Ωχ! Δυστυχώς, προέκυψε σφάλμα κατά την εγκατάσταση των ειδοποιήσεων push.", + "oopsSomethingWentWrong": "Ωχ, κάτι πήγε στραβά...", + "openAppToReadMessages": "Ανοίξτε την εφαρμογή για να διαβάσετε μηνύματα", + "openCamera": "Άνοιγμα κάμερας", + "openVideoCamera": "Άνοιγμα κάμερας για βίντεο", + "oneClientLoggedOut": "Ένας από τους πελάτες σας έχει αποσυνδεθεί", + "addAccount": "Προσθήκη λογαριασμού", + "editBundlesForAccount": "Επεξεργασία πακέτων για αυτόν τον λογαριασμό", + "addToBundle": "Προσθήκη στο πακέτο", + "removeFromBundle": "Αφαίρεση από αυτό το πακέτο", + "bundleName": "Όνομα πακέτου", + "enableMultiAccounts": "(BETA) Ενεργοποίηση πολλαπλών λογαριασμών σε αυτήν τη συσκευή", + "openInMaps": "Άνοιγμα στους χάρτες", + "link": "Σύνδεσμος", + "serverRequiresEmail": "Αυτός ο διακομιστής χρειάζεται να επικυρώσει τη διεύθυνση email σας για εγγραφή.", + "or": "Ή", + "participant": "Συμμετέχων", + "passphraseOrKey": "κωδικός πρόσβασης ή κλειδί ανάκτησης", + "password": "Κωδικός πρόσβασης", + "passwordForgotten": "Ξεχάσατε τον κωδικό πρόσβασης", + "passwordHasBeenChanged": "Ο κωδικός πρόσβασης έχει αλλάξει", + "hideMemberChangesInPublicChats": "Απόκρυψη αλλαγών μελών σε δημόσια chats", + "hideMemberChangesInPublicChatsBody": "Μην εμφανίζετε στο χρονολόγιο του chat αν κάποιος ενταχθεί ή φύγει από ένα δημόσιο chat για βελτίωση της αναγνωσιμότητας.", + "overview": "Επισκόπηση", + "notifyMeFor": "Ειδοποίησέ με για", + "passwordRecoverySettings": "Ρυθμίσεις ανάκτησης κωδικού πρόσβασης", + "passwordRecovery": "Ανάκτηση κωδικού πρόσβασης", + "people": "Άνθρωποι", + "pickImage": "Επιλέξτε μια εικόνα", + "pin": "Καρφίτσωμα", + "play": "Αναπαραγωγή {fileName}", + "pleaseChoose": "Παρακαλώ επιλέξτε", + "pleaseChooseAPasscode": "Παρακαλώ επιλέξτε έναν κωδικό πρόσβασης", + "pleaseClickOnLink": "Παρακαλώ κάντε κλικ στον σύνδεσμο στο email και συνεχίστε.", + "pleaseEnter4Digits": "Παρακαλώ εισάγετε 4 ψηφία ή αφήστε κενό για να απενεργοποιήσετε το κλείδωμα εφαρμογής.", + "pleaseEnterRecoveryKey": "Παρακαλώ εισάγετε το κλειδί ανάκτησης σας:", + "pleaseEnterYourPassword": "Παρακαλώ εισάγετε τον κωδικό πρόσβασής σας", + "pleaseEnterYourPin": "Παρακαλώ εισάγετε το PIN σας", + "pleaseEnterYourUsername": "Παρακαλώ εισάγετε το όνομα χρήστη σας", + "pleaseFollowInstructionsOnWeb": "Παρακαλούμε ακολουθήστε τις οδηγίες στην ιστοσελίδα και πατήστε επόμενο.", + "privacy": "Απόρρητο", + "publicRooms": "Δημόσιες αίθουσες", + "pushRules": "Κανόνες ειδοποιήσεων", + "reason": "Αιτία", + "recording": "Εγγραφή", + "redactedBy": "Επεξεργάστηκε από {username}", + "directChat": "Άμεσο μήνυμα", + "redactedByBecause": "Επεξεργάστηκε από {username} επειδή: \"{reason}\"", + "redactedAnEvent": "{username} επεξεργάστηκε ένα γεγονός", + "redactMessage": "Επεξεργασία μηνύματος", + "register": "Εγγραφή", + "reject": "Απόρριψη", + "rejectedTheInvitation": "{username} απέρριψε την πρόσκληση", + "rejoin": "Επανένταξη", + "removeAllOtherDevices": "Αφαίρεση όλων των άλλων συσκευών", + "removedBy": "Αφαιρέθηκε από {username}", + "removeDevice": "Αφαίρεση συσκευής", + "unbanFromChat": "Άρση αποκλεισμού από τη συνομιλία", + "removeYourAvatar": "Αφαίρεση του προφίλ σας", + "replaceRoomWithNewerVersion": "Αντικαταστήστε το δωμάτιο με νεότερη έκδοση", + "reply": "Απάντηση", + "reportMessage": "Αναφορά μηνύματος", + "requestPermission": "Αίτηση άδειας", + "roomHasBeenUpgraded": "Το δωμάτιο έχει αναβαθμιστεί", + "roomVersion": "Έκδοση δωματίου", + "saveFile": "Αποθήκευση αρχείου", + "search": "Αναζήτηση", + "security": "Ασφάλεια", + "recoveryKey": "Κλειδί ανάκτησης", + "recoveryKeyLost": "Έχετε χάσει το κλειδί ανάκτησης;", + "seenByUser": "Εμφανίστηκε από {username}", + "send": "Αποστολή", + "sendAMessage": "Αποστολή μηνύματος", + "sendAsText": "Αποστολή ως κείμενο", + "sendAudio": "Αποστολή ήχου", + "sendFile": "Αποστολή αρχείου", + "sendImage": "Αποστολή εικόνας", + "sendImages": "Αποστολή {count} εικόνας", + "sendMessages": "Αποστολή μηνυμάτων", + "sendOriginal": "Αποστολή πρωτότυπου", + "sendSticker": "Αποστολή αυτοκόλλητου", + "sendVideo": "Αποστολή βίντεο", + "sentAFile": "📁 {username} έστειλε ένα αρχείο", + "sentAnAudio": "🎤 {username} έστειλε έναν ήχο", + "sentAPicture": "🖼️ {username} έστειλε μια εικόνα", + "sentASticker": "😊 {username} έστειλε ένα αυτοκόλλητο", + "sentAVideo": "🎥 {username} έστειλε ένα βίντεο", + "sentCallInformations": "Ο {senderName} έστειλε πληροφορίες κλήσης", + "separateChatTypes": "Διαχωρισμός άμεσων μηνυμάτων και ομάδων", + "setAsCanonicalAlias": "Ορισμός ως κύριο ψευδώνυμο", + "setCustomEmotes": "Ορισμός προσαρμοσμένων εικονιδίων", + "setChatDescription": "Ορισμός περιγραφής συνομιλίας", + "setInvitationLink": "Ορισμός συνδέσμου πρόσκλησης", + "setPermissionsLevel": "Ορισμός επιπέδου δικαιωμάτων", + "setStatus": "Ορισμός κατάστασης", + "settings": "Ρυθμίσεις", + "share": "Κοινοποίηση", + "sharedTheLocation": "{username} κοινοποίησε την τοποθεσία του", + "shareLocation": "Κοινοποίηση τοποθεσίας", + "showPassword": "Εμφάνιση κωδικού πρόσβασης", + "presenceStyle": "Παρουσία:", + "presencesToggle": "Εμφάνιση μηνυμάτων κατάστασης από άλλους χρήστες", + "singlesignon": "Ενιαία σύνδεση", + "skip": "Παραλείπω", + "sourceCode": "Κώδικας πηγής", + "spaceIsPublic": "Ο χώρος είναι δημόσιος", + "spaceName": "Όνομα χώρου", + "startedACall": "{senderName} ξεκίνησε μια κλήση", + "startFirstChat": "Ξεκινήστε την πρώτη σας συνομιλία", + "status": "Κατάσταση", + "statusExampleMessage": "Πώς είστε σήμερα;", + "submit": "Υποβολή", + "synchronizingPleaseWait": "Συγχρονισμός… Παρακαλώ περιμένετε.", + "synchronizingPleaseWaitCounter": "Συγχρονισμός… ({percentage}%)", + "systemTheme": "Σύστημα", + "theyDontMatch": "Δεν ταιριάζουν", + "theyMatch": "Ταιριάζουν", + "title": "FluffyChat", + "toggleFavorite": "Εναλλαγή Αγαπημένου", + "toggleMuted": "Εναλλαγή σίγασης", + "toggleUnread": "Σήμανση ως διαβασμένο/μη διαβασμένο", + "tooManyRequestsWarning": "Πάρα πολλά αιτήματα. Παρακαλώ δοκιμάστε ξανά αργότερα!", + "transferFromAnotherDevice": "Μεταφορά από άλλη συσκευή", + "tryToSendAgain": "Προσπαθήστε ξανά να στείλετε", + "unavailable": "Μη διαθέσιμο", + "unbannedUser": "{username} άρπαξε τον αποκλεισμό του {targetName}", + "unblockDevice": "Ξεκλειδώστε τη συσκευή", + "unknownDevice": "Άγνωστη συσκευή", + "unknownEncryptionAlgorithm": "Άγνωστος αλγόριθμος κρυπτογράφησης", + "unknownEvent": "Άγνωστο γεγονός '{type}'", + "unmuteChat": "Αναίρεση σίγασης συνομιλίας", + "unpin": "Αφαίρεση από την κορυφή", + "unreadChats": "{unreadCount, plural, =1{1 μη αναγνωσμένο μήνυμα} other{{unreadCount} μη αναγνωσμένα μηνύματα}}", + "userAndOthersAreTyping": "{username} και {count} άλλοι πληκτρολογούν…", + "userAndUserAreTyping": "{username} και {username2} πληκτρολογούν…", + "userIsTyping": "{username} πληκτρολογεί…", + "userLeftTheChat": "🚪 {username} έφυγε από τη συνομιλία", + "username": "Όνομα χρήστη", + "userSentUnknownEvent": "{username} έστειλε ένα γεγονός {type}", + "unverified": "Μη επαληθευμένο", + "verified": "Επαληθευμένο", + "verify": "Επαλήθευση", + "verifyStart": "Ξεκινήστε την επαλήθευση", + "verifySuccess": "Επιτυχής επαλήθευση!", + "verifyTitle": "Επαλήθευση άλλου λογαριασμού", + "videoCall": "Βιντεοκλήση", + "visibilityOfTheChatHistory": "Ορατότητα του ιστορικού συνομιλίας", + "visibleForAllParticipants": "Ορατό για όλους τους συμμετέχοντες", + "visibleForEveryone": "Ορατό για όλους", + "voiceMessage": "Φωνητικό μήνυμα", + "waitingPartnerAcceptRequest": "Αναμονή για αποδοχή του αιτήματος από τον συνεργάτη…", + "waitingPartnerEmoji": "Αναμονή για αποδοχή του emoji από τον συνεργάτη…", + "waitingPartnerNumbers": "Αναμονή για αποδοχή των αριθμών από τον συνεργάτη…", + "wallpaper": "Φόντο:", + "warning": "Προειδοποίηση!", + "weSentYouAnEmail": "Σας στείλαμε ένα email", + "whoCanPerformWhichAction": "Ποιος μπορεί να εκτελέσει ποια ενέργεια", + "whoIsAllowedToJoinThisGroup": "Ποιος επιτρέπεται να συμμετάσχει σε αυτήν την ομάδα", + "whyDoYouWantToReportThis": "Γιατί θέλετε να αναφέρετε αυτό;", + "wipeChatBackup": "Διαγράψτε το αντίγραφο ασφαλείας της συνομιλίας σας για να δημιουργήσετε ένα νέο κλειδί ανάκτησης;", + "withTheseAddressesRecoveryDescription": "Με αυτές τις διευθύνσεις μπορείτε να ανακτήσετε τον κωδικό πρόσβασής σας.", + "writeAMessage": "Γράψτε ένα μήνυμα…", + "yes": "Ναι", + "you": "Εσύ", + "youAreNoLongerParticipatingInThisChat": "Δεν συμμετέχετε πλέον σε αυτήν τη συνομιλία", + "youHaveBeenBannedFromThisChat": "Έχετε αποκλειστεί από αυτήν τη συνομιλία", + "yourPublicKey": "Το δημόσιο κλειδί σας", + "messageInfo": "Πληροφορίες μηνύματος", + "time": "Χρόνος", + "messageType": "Τύπος μηνύματος", + "sender": "Αποστολέας", + "openGallery": "Άνοιγμα γκαλερί", + "removeFromSpace": "Αφαίρεση από τον χώρο", + "addToSpaceDescription": "Επιλέξτε έναν χώρο στον οποίο θα προσθέσετε αυτήν τη συνομιλία.", + "start": "Έναρξη", + "pleaseEnterRecoveryKeyDescription": "Για να ξεκλειδώσετε τα παλιά μηνύματά σας, εισάγετε το κλειδί ανάκτησης που δημιουργήθηκε σε προηγούμενη συνεδρία. Το κλειδί ανάκτησης ΔΕΝ είναι ο κωδικός πρόσβασής σας.", + "publish": "Δημοσίευση", + "videoWithSize": "Βίντεο ({size})", + "openChat": "Άνοιγμα συνομιλίας", + "markAsRead": "Σημείωσε ως διαβασμένο", + "reportUser": "Αναφορά χρήστη", + "dismiss": "Απόρριψη", + "reactedWith": "{sender} αντέδρασε με {reaction}", + "pinMessage": "Καρφίτσωσε στο δωμάτιο", + "confirmEventUnpin": "Είστε βέβαιοι ότι θέλετε να αποσυνδέσετε οριστικά το γεγονός;", + "emojis": "Εμοji", + "placeCall": "Κάντε κλήση", + "voiceCall": "Φωνητική κλήση", + "unsupportedAndroidVersion": "Μη υποστηριζόμενη έκδοση Android", + "unsupportedAndroidVersionLong": "Αυτή η λειτουργία απαιτεί νεότερη έκδοση Android. Παρακαλούμε ελέγξτε για ενημερώσεις ή υποστήριξη Lineage OS.", + "videoCallsBetaWarning": "Παρακαλούμε σημειώστε ότι οι βιντεοκλήσεις βρίσκονται επί του παρόντος σε beta. Μπορεί να μην λειτουργούν όπως αναμένεται ή καθόλου σε όλες τις πλατφόρμες.", + "experimentalVideoCalls": "Πειραματικές βιντεοκλήσεις", + "emailOrUsername": "Email ή όνομα χρήστη", + "indexedDbErrorTitle": "Προβλήματα ιδιωτικής λειτουργίας", + "indexedDbErrorLong": "Η αποθήκευση μηνυμάτων δυστυχώς δεν ενεργοποιείται από προεπιλογή σε ιδιωτική λειτουργία.\nΠαρακαλούμε επισκεφθείτε\n - about:config\n - ορίστε το dom.indexedDB.privateBrowsing.enabled σε true\nΑλλιώς, δεν είναι δυνατή η εκτέλεση του FluffyChat.", + "switchToAccount": "Μεταβείτε στον λογαριασμό {number}", + "nextAccount": "Επόμενος λογαριασμός", + "previousAccount": "Προηγούμενος λογαριασμός", + "addWidget": "Προσθήκη widget", + "widgetVideo": "Βίντεο", + "widgetEtherpad": "Κείμενο σημείωσης", + "widgetJitsi": "Jitsi Meet", + "widgetCustom": "Προσαρμοσμένο", + "widgetName": "Όνομα", + "widgetUrlError": "Αυτό δεν είναι έγκυρη διεύθυνση URL.", + "widgetNameError": "Παρακαλώ δώστε ένα όνομα εμφάνισης.", + "errorAddingWidget": "Σφάλμα κατά την προσθήκη του widget.", + "youRejectedTheInvitation": "Απορρίψατε την πρόσκληση", + "youJoinedTheChat": "Έχετε συμμετάσχει στη συνομιλία", + "youAcceptedTheInvitation": "👍 Έχετε αποδεχθεί την πρόσκληση", + "youBannedUser": "Αποκλείσατε τον {user}", + "youHaveWithdrawnTheInvitationFor": "Έχετε αποσύρει την πρόσκληση για τον {user}", + "youInvitedToBy": "📩 Έχετε προσκληθεί μέσω συνδέσμου στο:\n{alias}", + "youInvitedBy": "📩 Έχετε προσκληθεί από τον {user}", + "invitedBy": "📩 Προσκληθείς από τον {user}", + "youInvitedUser": "📩 Προσκαλέσατε τον {user}", + "youKicked": "👞 Απομακρύνατε τον {user}", + "youKickedAndBanned": "🙅‍♀️ Απομακρύνατε και αποκλείσατε τον {user}", + "youUnbannedUser": "Απελευθερώσατε τον {user}", + "hasKnocked": "🚪 {user} χτύπησε", + "usersMustKnock": "Οι χρήστες πρέπει να χτυπήσουν", + "noOneCanJoin": "Κανείς δεν μπορεί να συμμετάσχει", + "userWouldLikeToChangeTheChat": "{user} θα ήθελε να συμμετάσχει στη συζήτηση.", + "noPublicLinkHasBeenCreatedYet": "Δεν έχει δημιουργηθεί ακόμη δημόσιος σύνδεσμος", + "knock": "Χτύπημα", + "users": "Χρήστες", + "unlockOldMessages": "Ξεκλείδωμα παλαιών μηνυμάτων", + "storeInSecureStorageDescription": "Αποθηκεύστε το κλειδί ανάκαμψης στην ασφαλή αποθήκευση αυτής της συσκευής.", + "saveKeyManuallyDescription": "Αποθηκεύστε αυτό το κλειδί χειροκίνητα ενεργοποιώντας το διάλογο κοινής χρήσης συστήματος ή το πρόχειρο.", + "storeInAndroidKeystore": "Αποθήκευση στο Android KeyStore", + "storeInAppleKeyChain": "Αποθήκευση στο Apple KeyChain", + "storeSecurlyOnThisDevice": "Αποθήκευση με ασφάλεια σε αυτήν τη συσκευή", + "countFiles": "{count} αρχεία", + "user": "Χρήστης", + "custom": "Προσαρμοσμένο", + "foregroundServiceRunning": "Αυτή η ειδοποίηση εμφανίζεται όταν τρέχει η υπηρεσία μπροστά.", + "screenSharingTitle": "κοινή χρήση οθόνης", + "screenSharingDetail": "Μοιράζεστε την οθόνη σας στο FuffyChat", + "callingPermissions": "Άδειες κλήσης", + "callingAccount": "Λογαριασμός κλήσης", + "callingAccountDetails": "Επιτρέπει στο FluffyChat να χρησιμοποιεί την εγγενή εφαρμογή τηλεφωνητή Android.", + "appearOnTop": "Εμφάνιση στην κορυφή", + "appearOnTopDetails": "Επιτρέπει στην εφαρμογή να εμφανίζεται στην κορυφή (δεν χρειάζεται αν έχετε ήδη ρυθμίσει το FluffyChat ως λογαριασμό κλήσης)", + "otherCallingPermissions": "Άδειες μικροφώνου, κάμερας και άλλες άδειες του FluffyChat", + "whyIsThisMessageEncrypted": "Γιατί αυτό το μήνυμα είναι μη αναγνώσιμο;", + "noKeyForThisMessage": "Αυτό μπορεί να συμβεί αν το μήνυμα στάλθηκε πριν συνδεθείτε στον λογαριασμό σας σε αυτήν τη συσκευή.\n\nΕίναι επίσης πιθανό ο αποστολέας να έχει μπλοκάρει τη συσκευή σας ή να υπήρξε κάποιο πρόβλημα με τη σύνδεση στο διαδίκτυο.\n\nΜπορείτε να διαβάσετε το μήνυμα σε μια άλλη συνεδρία; Τότε μπορείτε να μεταφέρετε το μήνυμα από αυτήν! Μεταβείτε στις Ρυθμίσεις > Συσκευές και βεβαιωθείτε ότι οι συσκευές σας έχουν επαληθευτεί η μία την άλλη. Όταν ανοίξετε ξανά το δωμάτιο και και οι δύο συνεδρίες είναι στην μπροστινή γραμμή, τα κλειδιά θα μεταδοθούν αυτόματα.\n\nΔεν θέλετε να χάσετε τα κλειδιά κατά το αποσύνδεση ή την αλλαγή συσκευής; Βεβαιωθείτε ότι έχετε ενεργοποιήσει την εφεδρεία συνομιλίας στις ρυθμίσεις.", + "newGroup": "Νέα ομάδα", + "newSpace": "Νέος χώρος", + "enterSpace": "Εισέλθετε στον χώρο", + "enterRoom": "Εισέλθετε στο δωμάτιο", + "allSpaces": "Όλοι οι χώροι", + "numChats": "{number} συνομιλίες", + "hideUnimportantStateEvents": "Απόκρυψη άσχετων γεγονότων κατάστασης", + "hidePresences": "Απόκρυψη λίστας κατάστασης;", + "doNotShowAgain": "Μην το εμφανίζετε ξανά", + "wasDirectChatDisplayName": "Άδειο chat (ήταν {oldDisplayName})", + "newSpaceDescription": "Οι χώροι σας επιτρέπουν να ενοποιήσετε τις συνομιλίες σας και να δημιουργήσετε ιδιωτικές ή δημόσιες κοινότητες.", + "encryptThisChat": "Κρυπτογραφήστε αυτήν τη συνομιλία", + "disableEncryptionWarning": "Για λόγους ασφαλείας, δεν μπορείτε να απενεργοποιήσετε την κρυπτογράφηση σε μια συνομιλία όπου είχε ενεργοποιηθεί προηγουμένως.", + "sorryThatsNotPossible": "Λυπούμαστε... αυτό δεν είναι δυνατό", + "deviceKeys": "Κλειδιά συσκευής:", + "reopenChat": "Άνοιγμα ξανά συνομιλίας", + "noBackupWarning": "Προειδοποίηση! Χωρίς ενεργοποίηση αντιγράφου ασφαλείας συνομιλίας, θα χάσετε την πρόσβαση στα κρυπτογραφημένα μηνύματά σας. Συνιστάται ιδιαίτερα να ενεργοποιήσετε το αντίγραφο ασφαλείας πριν αποσυνδεθείτε.", + "noOtherDevicesFound": "Δεν βρέθηκαν άλλες συσκευές", + "fileIsTooBigForServer": "Αδύνατη η αποστολή! Ο διακομιστής υποστηρίζει μόνο συνημμένα έως {max}.", + "fileHasBeenSavedAt": "Το αρχείο έχει αποθηκευτεί στο {path}", + "jumpToLastReadMessage": "Μετάβαση στο τελευταίο διαβασμένο μήνυμα", + "readUpToHere": "Διάβασε μέχρι εδώ", + "jump": "Μετάβαση", + "openLinkInBrowser": "Άνοιγμα συνδέσμου στον περιηγητή", + "reportErrorDescription": "😞 Ωχ. Κάτι πήγε στραβά. Αν θέλετε, μπορείτε να αναφέρετε αυτό το σφάλμα στους προγραμματιστές.", + "report": "αναφορά", + "signInWithPassword": "Σύνδεση με κωδικό πρόσβασης", + "pleaseTryAgainLaterOrChooseDifferentServer": "Παρακαλώ δοκιμάστε ξανά αργότερα ή επιλέξτε διαφορετικό διακομιστή.", + "profileNotFound": "Ο χρήστης δεν βρέθηκε στον διακομιστή. Ίσως υπάρχει πρόβλημα σύνδεσης ή ο χρήστης δεν υπάρχει.", + "setTheme": "Ορίστε θέμα:", + "setColorTheme": "Ορίστε χρωματικό θέμα:", + "invite": "Πρόσκληση", + "inviteGroupChat": "📨 Πρόσκληση σε ομαδική συνομιλία", + "invitePrivateChat": "📨 Πρόσκληση σε ιδιωτική συνομιλία", + "invalidInput": "Μη έγκυρη εισαγωγή!", + "wrongPinEntered": "Λάθος PIN εισήχθη! Δοκιμάστε ξανά σε {seconds} δευτερόλεπτα...", + "pleaseEnterANumber": "Παρακαλώ εισάγετε έναν αριθμό μεγαλύτερο από το 0", + "archiveRoomDescription": "Η συνομιλία θα μετακινηθεί στο αρχείο. Άλλοι χρήστες θα μπορούν να δουν ότι έχετε φύγει από τη συνομιλία.", + "roomUpgradeDescription": "Η συνομιλία θα αναδημιουργηθεί με τη νέα έκδοση δωματίου. Όλοι οι συμμετέχοντες θα ειδοποιηθούν ότι πρέπει να μεταβούν στη νέα συνομιλία. Μπορείτε να μάθετε περισσότερα για τις εκδόσεις δωματίων στο https://spec.matrix.org/latest/rooms/", + "removeDevicesDescription": "Θα αποσυνδεθείτε από αυτήν τη συσκευή και δεν θα μπορείτε πλέον να λαμβάνετε μηνύματα.", + "banUserDescription": "Ο χρήστης θα αποκλειστεί από τη συνομιλία και δεν θα μπορεί να εισέλθει ξανά μέχρι να αφαιρεθεί ο αποκλεισμός.", + "unbanUserDescription": "Ο χρήστης θα μπορεί να εισέλθει ξανά στη συνομιλία αν προσπαθήσει.", + "kickUserDescription": "Ο χρήστης αποβάλλεται από τη συνομιλία αλλά δεν αποκλείεται. Στις δημόσιες συνομιλίες, ο χρήστης μπορεί να επανενταχθεί οποιαδήποτε στιγμή.", + "makeAdminDescription": "Αφού κάνετε αυτόν τον χρήστη διαχειριστή, ίσως να μην μπορείτε να αναιρέσετε αυτό, καθώς θα έχει τις ίδιες άδειες με εσάς.", + "pushNotificationsNotAvailable": "Οι ειδοποιήσεις push δεν είναι διαθέσιμες", + "learnMore": "Μάθετε περισσότερα", + "yourGlobalUserIdIs": "Το παγκόσμιο αναγνωριστικό χρήστη σας είναι: ", + "noUsersFoundWithQuery": "Δυστυχώς, δεν βρέθηκε χρήστης με την ερώτηση \"{query}\". Παρακαλώ ελέγξτε αν κάνατε τυπογραφικό λάθος.", + "knocking": "Χτυπάει", + "chatCanBeDiscoveredViaSearchOnServer": "Η συνομιλία μπορεί να βρεθεί μέσω της αναζήτησης στο {server}", + "searchChatsRooms": "Αναζήτηση για #συνομιλίες, @χρήστες...", + "nothingFound": "Δεν βρέθηκε τίποτα...", + "groupName": "Όνομα ομάδας", + "createGroupAndInviteUsers": "Δημιουργήστε μια ομάδα και προσκαλέστε χρήστες", + "groupCanBeFoundViaSearch": "Η ομάδα μπορεί να βρεθεί μέσω αναζήτησης", + "wrongRecoveryKey": "Λυπάμαι... αυτό δεν φαίνεται να είναι το σωστό κλειδί ανάκτησης.", + "startConversation": "Ξεκινήστε συνομιλία", + "commandHint_sendraw": "Αποστολή ακατέργαστου json", + "databaseMigrationTitle": "Η βάση δεδομένων είναι βελτιστοποιημένη", + "databaseMigrationBody": "Παρακαλώ περιμένετε. Αυτό μπορεί να διαρκέσει λίγα λεπτά.", + "leaveEmptyToClearStatus": "Αφήστε κενό για να διαγράψετε την κατάσταση σας.", + "select": "Επιλογή", + "searchForUsers": "Αναζήτηση για @χρήστες...", + "pleaseEnterYourCurrentPassword": "Παρακαλώ εισάγετε τον τρέχοντα κωδικό πρόσβασής σας", + "newPassword": "Νέος κωδικός πρόσβασης", + "pleaseChooseAStrongPassword": "Παρακαλώ επιλέξτε έναν ισχυρό κωδικό πρόσβασης", + "passwordsDoNotMatch": "Οι κωδικοί πρόσβασης δεν ταιριάζουν", + "passwordIsWrong": "Ο εισαγόμενος κωδικός πρόσβασης είναι λανθασμένος", + "publicLink": "Δημόσιος σύνδεσμος", + "publicChatAddresses": "Διευθύνσεις δημόσιων συνομιλιών", + "createNewAddress": "Δημιουργία νέας διεύθυνσης", + "joinSpace": "Συμμετοχή στον χώρο", + "publicSpaces": "Δημόσιοι χώροι", + "addChatOrSubSpace": "Προσθήκη συνομιλίας ή υποχώρου", + "subspace": "Υποχώρος", + "decline": "Απόρριψη", + "thisDevice": "Αυτή η συσκευή:", + "initAppError": "Παρουσιάστηκε σφάλμα κατά την αρχικοποίηση της εφαρμογής", + "userRole": "Ρόλος χρήστη", + "minimumPowerLevel": "{level} είναι το ελάχιστο επίπεδο ισχύος.", + "searchIn": "Αναζήτηση στη συνομιλία \"{chat}\"...", + "searchMore": "Αναζήτηση περισσότερο...", + "gallery": "Πινακοθήκη", + "files": "Αρχεία", + "databaseBuildErrorBody": "Αδυναμία δημιουργίας της βάσης δεδομένων SQLite. Η εφαρμογή προσπαθεί να χρησιμοποιήσει την παλαιά βάση δεδομένων προς το παρόν. Παρακαλούμε αναφέρετε αυτό το σφάλμα στους προγραμματιστές στο {url}. Το μήνυμα σφάλματος είναι: {error}", + "sessionLostBody": "Η συνεδρία σας χάθηκε. Παρακαλούμε αναφέρετε αυτό το σφάλμα στους προγραμματιστές στο {url}. Το μήνυμα σφάλματος είναι: {error}", + "restoreSessionBody": "Η εφαρμογή προσπαθεί τώρα να επαναφέρει τη συνεδρία σας από το αντίγραφο ασφαλείας. Παρακαλούμε αναφέρετε αυτό το σφάλμα στους προγραμματιστές στο {url}. Το μήνυμα σφάλματος είναι: {error}", + "forwardMessageTo": "Αποστολή μηνύματος στο {roomName}?", + "sendReadReceipts": "Αποστολή αποδείξεων ανάγνωσης", + "sendTypingNotificationsDescription": "Οι άλλοι συμμετέχοντες σε μια συνομιλία μπορούν να δουν πότε πληκτρολογείτε ένα νέο μήνυμα.", + "sendReadReceiptsDescription": "Οι άλλοι συμμετέχοντες σε μια συνομιλία μπορούν να δουν πότε έχετε διαβάσει ένα μήνυμα.", + "formattedMessages": "Μορφοποιημένα μηνύματα", + "formattedMessagesDescription": "Εμφάνιση πλούσιου περιεχομένου μηνυμάτων όπως έντονο κείμενο χρησιμοποιώντας markdown.", + "verifyOtherUser": "🔐 Επιβεβαίωση άλλου χρήστη", + "verifyOtherUserDescription": "Αν επιβεβαιώσετε έναν άλλο χρήστη, μπορείτε να είστε σίγουροι ότι γνωρίζετε πραγματικά σε ποιον γράφετε. 💪\n\nΌταν ξεκινάτε μια επιβεβαίωση, εσείς και ο άλλος χρήστης θα δείτε ένα αναδυόμενο παράθυρο στην εφαρμογή. Εκεί θα δείτε μια σειρά από emoji ή αριθμούς που πρέπει να συγκρίνετε μεταξύ σας.\n\nΟ καλύτερος τρόπος να το κάνετε αυτό είναι να συναντηθείτε ή να ξεκινήσετε μια βιντεκλήση. 👭", + "verifyOtherDevice": "🔐 Επιβεβαίωση άλλης συσκευής", + "verifyOtherDeviceDescription": "Όταν επιβεβαιώνετε μια άλλη συσκευή, αυτές οι συσκευές μπορούν να ανταλλάξουν κλειδιά, αυξάνοντας τη συνολική ασφάλειά σας. 💪 Όταν ξεκινάτε μια επιβεβαίωση, θα εμφανιστεί ένα αναδυόμενο παράθυρο στην εφαρμογή και στις δύο συσκευές. Εκεί θα δείτε μια σειρά από emoji ή αριθμούς που πρέπει να συγκρίνετε. Είναι καλύτερο να έχετε και τις δύο συσκευές έτοιμες πριν ξεκινήσετε την επιβεβαίωση. 🤳", + "acceptedKeyVerification": "{sender} αποδέχτηκε την επιβεβαίωση κλειδιού", + "canceledKeyVerification": "{sender} ακύρωσε την επιβεβαίωση κλειδιού", + "completedKeyVerification": "{sender} ολοκλήρωσε την επιβεβαίωση κλειδιού", + "isReadyForKeyVerification": "{sender} είναι έτοιμος/η για επιβεβαίωση κλειδιού", + "requestedKeyVerification": "{sender} ζήτησε επιβεβαίωση κλειδιού", + "startedKeyVerification": "{sender} ξεκίνησε την επιβεβαίωση κλειδιού", + "transparent": "Διαφανές", + "incomingMessages": "Εισερχόμενα μηνύματα", + "stickers": "Αυτοκόλλητα", + "discover": "Ανακάλυψη", + "commandHint_ignore": "Αγνόηση του δοθέντος ταυτότητας matrix", + "commandHint_unignore": "Αναίρεση αγνόησης του δοθέντος ταυτότητας matrix", + "unreadChatsInApp": "{appname}: {unread} μη αναγνωσμένα chats", + "noDatabaseEncryption": "Η κρυπτογράφηση βάσης δεδομένων δεν υποστηρίζεται σε αυτήν την πλατφόρμα", + "thereAreCountUsersBlocked": "Αυτήν τη στιγμή υπάρχουν {count} αποκλεισμένοι χρήστες.", + "restricted": "Περιορισμένο", + "knockRestricted": "Περιορισμός χτυπήματος", + "goToSpace": "Μεταβείτε στον χώρο: {space}", + "markAsUnread": "Σήμανση ως μη αναγνωσμένο", + "userLevel": "{level} - Χρήστης", + "moderatorLevel": "{level} - Διαχειριστής", + "adminLevel": "{level} - Διαχειριστής", + "changeGeneralChatSettings": "Αλλαγή γενικών ρυθμίσεων συνομιλίας", + "inviteOtherUsers": "Πρόσκληση άλλων χρηστών σε αυτήν τη συνομιλία", + "changeTheChatPermissions": "Αλλαγή των δικαιωμάτων συνομιλίας", + "changeTheVisibilityOfChatHistory": "Αλλαγή της ορατότητας του ιστορικού συνομιλίας", + "changeTheCanonicalRoomAlias": "Αλλαγή της κύριας δημόσιας διεύθυνσης συνομιλίας", + "sendRoomNotifications": "Αποστολή ειδοποιήσεων @room", + "changeTheDescriptionOfTheGroup": "Αλλαγή της περιγραφής της συνομιλίας", + "chatPermissionsDescription": "Ορίστε ποιο επίπεδο δύναμης είναι απαραίτητο για ορισμένες ενέργειες σε αυτήν τη συνομιλία. Τα επίπεδα δύναμης 0, 50 και 100 συνήθως αντιπροσωπεύουν χρήστες, διαχειριστές και διαχειριστές, αλλά οποιαδήποτε βαθμίδα είναι δυνατή.", + "updateInstalled": "🎉 Εγκαταστάθηκε η ενημέρωση {version}!", + "changelog": "Αλλαγές", + "sendCanceled": "Η αποστολή ακυρώθηκε", + "loginWithMatrixId": "Σύνδεση με Matrix-ID", + "discoverHomeservers": "Ανακαλύψτε τους διακομιστές", + "whatIsAHomeserver": "Τι είναι ένας διακομιστής σπιτιού;", + "homeserverDescription": "Όλα τα δεδομένα σας αποθηκεύονται στον διακομιστή σπιτιού, όπως και ένας πάροχος email. Μπορείτε να επιλέξετε ποιον διακομιστή σπιτιού θέλετε να χρησιμοποιήσετε, ενώ μπορείτε ακόμα να επικοινωνείτε με όλους. Μάθετε περισσότερα στο https://matrix.org.", + "doesNotSeemToBeAValidHomeserver": "Δεν φαίνεται να είναι συμβατός διακομιστής σπιτιού. Λάθος URL;", + "calculatingFileSize": "Υπολογισμός μεγέθους αρχείου...", + "prepareSendingAttachment": "Ετοιμασία αποστολής συνημμένου...", + "sendingAttachment": "Αποστολή συνημμένου...", + "generatingVideoThumbnail": "Δημιουργία μικρογραφίας βίντεο...", + "compressVideo": "Συμπίεση βίντεο...", + "sendingAttachmentCountOfCount": "Αποστολή συνημμένου {index} από {length}...", + "serverLimitReached": "Έφτασε το όριο του διακομιστή! Περιμένετε {seconds} δευτερόλεπτα...", + "oneOfYourDevicesIsNotVerified": "Ένα από τα συσκευές σας δεν είναι επαληθευμένο", + "noticeChatBackupDeviceVerification": "Σημείωση: Όταν συνδέσετε όλες τις συσκευές σας στο αντίγραφο ασφαλείας συνομιλίας, αυτομάτως επαληθεύονται.", + "continueText": "Συνέχεια", + "welcomeText": "Γεια Γεια 👋 Αυτό είναι το FluffyChat. Μπορείτε να συνδεθείτε σε οποιονδήποτε διακομιστή σπιτιού, ο οποίος είναι συμβατός με το https://matrix.org. Και στη συνέχεια να συνομιλήσετε με οποιονδήποτε. Είναι ένα τεράστιο αποκεντρωμένο δίκτυο μηνυμάτων!", + "blur": "Θάμπωμα:", + "opacity": "Αδιαφάνεια:", + "setWallpaper": "Ορισμός φόντου", + "manageAccount": "Διαχείριση λογαριασμού", + "noContactInformationProvided": "Ο διακομιστής δεν παρέχει έγκυρες πληροφορίες επικοινωνίας", + "contactServerAdmin": "Επικοινωνήστε με τον διαχειριστή του διακομιστή", + "contactServerSecurity": "Επικοινωνία με την ασφάλεια διακομιστή", + "supportPage": "Σελίδα υποστήριξης", + "serverInformation": "Πληροφορίες διακομιστή:", + "name": "Όνομα", + "version": "Έκδοση", + "website": "Ιστοσελίδα", + "compress": "Συμπίεση", + "boldText": "Έντονο κείμενο", + "italicText": "Πλάγιο κείμενο", + "strikeThrough": "Διαγραφή", + "pleaseFillOut": "Παρακαλώ συμπληρώστε", + "invalidUrl": "Μη έγκυρο URL", + "addLink": "Προσθήκη συνδέσμου", + "unableToJoinChat": "Αδύνατη η συμμετοχή στη συνομιλία. Ίσως το άλλο μέρος έχει ήδη κλείσει τη συνομιλία.", + "previous": "Προηγούμενο", + "otherPartyNotLoggedIn": "Το άλλο μέρος δεν είναι αυτήν τη στιγμή συνδεδεμένο και επομένως δεν μπορεί να λάβει μηνύματα!", + "appWantsToUseForLogin": "Χρησιμοποιήστε '{server}' για σύνδεση", + "appWantsToUseForLoginDescription": "Επιτρέπετε στην εφαρμογή και στον ιστότοπο να μοιράζονται πληροφορίες σχετικά με εσάς.", + "open": "Άνοιγμα", + "waitingForServer": "Αναμονή για διακομιστή...", + "appIntroduction": "Το FluffyChat σας επιτρέπει να συνομιλείτε με τους φίλους σας σε διαφορετικά μηνύματα. Μάθετε περισσότερα στο https://matrix.org ή απλώς πατήστε *Συνέχεια*.", + "newChatRequest": "📩 Νέα αίτηση συνομιλίας", + "contentNotificationSettings": "Ρυθμίσεις ειδοποιήσεων περιεχομένου", + "generalNotificationSettings": "Γενικές ρυθμίσεις ειδοποιήσεων", + "roomNotificationSettings": "Ρυθμίσεις ειδοποιήσεων δωματίου", + "userSpecificNotificationSettings": "Εξατομικευμένες ρυθμίσεις ειδοποιήσεων χρήστη", + "otherNotificationSettings": "Άλλες ρυθμίσεις ειδοποιήσεων", + "notificationRuleContainsUserName": "Περιέχει όνομα χρήστη", + "notificationRuleContainsUserNameDescription": "Ειδοποιεί τον χρήστη όταν ένα μήνυμα περιέχει το όνομα χρήστη του.", + "notificationRuleMaster": "Απενεργοποίηση όλων των ειδοποιήσεων", + "notificationRuleMasterDescription": "Παρακάμπτει όλους τους άλλους κανόνες και απενεργοποιεί όλες τις ειδοποιήσεις.", + "notificationRuleSuppressNotices": "Καταστολή αυτόματων μηνυμάτων", + "notificationRuleSuppressNoticesDescription": "Καταστέλλει τις ειδοποιήσεις από αυτοματοποιμένους πελάτες όπως τα bots.", + "notificationRuleInviteForMe": "Πρόσκληση για μένα", + "notificationRuleInviteForMeDescription": "Ειδοποιεί τον χρήστη όταν λαμβάνει πρόσκληση σε δωμάτιο.", + "notificationRuleMemberEvent": "Γεγονός μέλους", + "notificationRuleMemberEventDescription": "Καταστέλλει τις ειδοποιήσεις για γεγονότα μέλους.", + "notificationRuleIsUserMention": "Αναφορά χρήστη", + "notificationRuleIsUserMentionDescription": "Ειδοποιεί τον χρήστη όταν αναφέρεται άμεσα σε μήνυμα.", + "notificationRuleContainsDisplayName": "Περιέχει όνομα εμφάνισης", + "notificationRuleContainsDisplayNameDescription": "Ειδοποιεί τον χρήστη όταν ένα μήνυμα περιέχει το όνομά του.", + "notificationRuleIsRoomMention": "Αναφορά Δωματίου", + "notificationRuleIsRoomMentionDescription": "Ειδοποιεί τον χρήστη όταν υπάρχει αναφορά σε δωμάτιο.", + "notificationRuleRoomnotif": "Ειδοποίηση Δωματίου", + "notificationRuleRoomnotifDescription": "Ειδοποιεί τον χρήστη όταν ένα μήνυμα περιέχει '@room'.", + "notificationRuleTombstone": "Ταφόπλακα", + "notificationRuleTombstoneDescription": "Ειδοποιεί τον χρήστη σχετικά με μηνύματα απενεργοποίησης δωματίου.", + "notificationRuleReaction": "Αντίδραση", + "notificationRuleReactionDescription": "Καταστέλλει τις ειδοποιήσεις για αντιδράσεις.", + "notificationRuleRoomServerAcl": "Πρόσβαση Δωματίου στον Διακομιστή", + "notificationRuleRoomServerAclDescription": "Καταστέλλει τις ειδοποιήσεις για λίστες ελέγχου πρόσβασης (ACL) του διακομιστή δωματίου.", + "notificationRuleSuppressEdits": "Καταστολή Επεξεργασιών", + "notificationRuleSuppressEditsDescription": "Καταστέλλει τις ειδοποιήσεις για επεξεργασμένα μηνύματα.", + "notificationRuleCall": "Κλήση", + "notificationRuleCallDescription": "Ειδοποιεί τον χρήστη σχετικά με κλήσεις.", + "notificationRuleEncryptedRoomOneToOne": "Κρυπτογραφημένο Δωμάτιο Μία προς Μία", + "notificationRuleEncryptedRoomOneToOneDescription": "Ειδοποιεί τον χρήστη σχετικά με μηνύματα σε κρυπτογραφημένα δωμάτια ένα προς ένα.", + "notificationRuleRoomOneToOne": "Δωμάτιο Μία προς Μία", + "notificationRuleRoomOneToOneDescription": "Ειδοποιεί τον χρήστη σχετικά με μηνύματα σε δωμάτια ένα προς ένα.", + "notificationRuleMessage": "Μήνυμα", + "notificationRuleMessageDescription": "Ειδοποιεί τον χρήστη για γενικά μηνύματα.", + "notificationRuleEncrypted": "Κρυπτογραφημένο", + "notificationRuleEncryptedDescription": "Ειδοποιεί τον χρήστη για μηνύματα σε κρυπτογραφημένους δωματίους.", + "notificationRuleJitsi": "Jitsi", + "notificationRuleJitsiDescription": "Ειδοποιεί τον χρήστη για γεγονότα widget Jitsi.", + "notificationRuleServerAcl": "Κατάπνιξε γεγονότα Server ACL", + "notificationRuleServerAclDescription": "Καταπνίγει τις ειδοποιήσεις για γεγονότα Server ACL.", + "unknownPushRule": "Άγνωστος κανόνας ώθησης '{rule}'", + "sentVoiceMessage": "🎙️ {duration} - Φωνητικό μήνυμα από {sender}", + "deletePushRuleCanNotBeUndone": "Εάν διαγράψετε αυτήν τη ρύθμιση ειδοποίησης, αυτό δεν μπορεί να αναιρεθεί.", + "more": "Περισσότερα", + "shareKeysWith": "Μοιραστείτε κλειδιά με...", + "shareKeysWithDescription": "Ποια συσκευές πρέπει να είναι αξιόπιστες ώστε να μπορούν να διαβάσουν τα μηνύματά σας σε κρυπτογραφημένες συνομιλίες;", + "allDevices": "Όλες οι συσκευές", + "crossVerifiedDevicesIfEnabled": "Διασταυρωμένες επαληθευμένες συσκευές αν είναι ενεργοποιημένο", + "crossVerifiedDevices": "Διασταυρωμένες επαληθευμένες συσκευές", + "verifiedDevicesOnly": "Μόνο επαληθευμένες συσκευές", + "takeAPhoto": "Βγάλτε μια φωτογραφία", + "recordAVideo": "Καταγράψτε ένα βίντεο", + "optionalMessage": "(Προαιρετικό) μήνυμα...", + "notSupportedOnThisDevice": "Δεν υποστηρίζεται σε αυτήν τη συσκευή", + "enterNewChat": "Ξεκινήστε νέο chat", + "approve": "Έγκριση", + "youHaveKnocked": "Έχετε χτυπήσει", + "pleaseWaitUntilInvited": "Παρακαλώ περιμένετε τώρα, μέχρι κάποιος από το δωμάτιο να σας προσκαλέσει.", + "commandHint_logout": "Αποσυνδεθείτε από τη τρέχουσα συσκευή σας", + "commandHint_logoutall": "Αποσυνδεθείτε από όλες τις ενεργές συσκευές", + "displayNavigationRail": "Εμφάνιση της γραμμής πλοήγησης σε κινητά", + "customReaction": "Προσαρμοσμένη αντίδραση", + "ignore": "Αποκλεισμός", + "ignoredUsers": "Αποκλεισμένοι χρήστες", + "writeAMessageLangCodes": "Πληκτρολογήστε σε {l1} ή {l2}...", + "requests": "Αιτήματα", + "holdForInfo": "Κάντε κλικ και κρατήστε πατημένο για πληροφορίες λέξης.", + "greenFeedback": "Αυτό θα έβαζα!", + "yellowFeedback": "Χμ, μπορείς να το δοκιμάσεις και να δεις αν λειτουργεί! Για να χρησιμοποιήσεις αυτή τη λέξη, απλώς κάνε κλικ ξανά.", + "redFeedback": "Δεν νομίζω ότι είναι σωστό...", + "itInstructionsTitle": "Μπορώ να σε βοηθήσω με τη μετάφραση!", + "itInstructionsBody": "Μπορείς να κάνεις κλικ και να κρατήσεις πατημένο τις επιλογές για πληροφορίες λέξης.", + "gaTooltip": "L2 χρήση με βοήθεια γραμματικής", + "taTooltip": "L2 χρήση με βοήθεια μετάφρασης", + "unTooltip": "Άλλο", + "interactiveTranslatorSliderHeader": "Διαδραστικός Μεταφραστής", + "interactiveGrammarSliderHeader": "Διαδραστικός Έλεγχος Γραμματικής", + "interactiveTranslatorNotAllowed": "Απενεργοποιημένο", + "interactiveTranslatorAllowed": "Επιλογή Μαθητή", + "interactiveTranslatorRequired": "Απαιτείται", + "notYetSet": "Δεν έχει οριστεί ακόμα", + "waTooltip": "Χρήση L2 χωρίς βοήθεια", + "languageSettings": "Ρυθμίσεις γλώσσας", + "interactiveTranslator": "Βοήθεια μετάφρασης", + "noIdenticalLanguages": "Παρακαλώ επιλέξτε διαφορετικές βασικές και στόχους γλώσσες", + "searchBy": "Αναζήτηση με βάση χώρα και γλώσσες", + "joinWithClassCode": "Εγγραφή στο μάθημα", + "languageLevelPreA1": "Νέος Χαμηλός (Προ Α1)", + "languageLevelA1": "Νέος Μέσος (A1)", + "languageLevelA2": "Αρχάριος Υψηλού Επιπέδου (A2)", + "languageLevelB1": "Μεσαίος Μέτριος (B1)", + "languageLevelB2": "Προχωρημένος Χαμηλού Επιπέδου (B2)", + "languageLevelC1": "Προχωρημένος Μέτριος (C1)", + "languageLevelC2": "Ανώτερος (C2)", + "changeTheNameOfTheClass": "Αλλαγή ονόματος", + "changeTheNameOfTheChat": "Αλλαγή ονόματος της συνομιλίας", + "sorryNoResults": "Λυπούμαστε, δεν βρέθηκαν αποτελέσματα.", + "ignoreInThisText": "Αγνόηση", + "needsItMessage": "Περίμενε, αυτό δεν είναι {targetLanguage}! Χρειάζεστε βοήθεια με τη μετάφραση;", + "countryInformation": "Η χώρα μου", + "targetLanguage": "Γλώσσα στόχος", + "sourceLanguage": "Βασική γλώσσα", + "updateLanguage": "Οι γλώσσες μου", + "whatLanguageYouWantToLearn": "Ποια γλώσσα θέλετε να μάθετε;", + "whatIsYourBaseLanguage": "Ποια είναι η βασική σας γλώσσα;", + "saveChanges": "Αποθήκευση αλλαγών", + "publicProfileTitle": "Να επιτρέπεται η εύρεση του προφίλ μου στην αναζήτηση", + "publicProfileDesc": "Με την ενεργοποίηση, επιτρέπετε σε άλλους χρήστες να βρουν το προφίλ σας στη διεθνή γραμμή αναζήτησης και να στείλουν αιτήματα συνομιλίας. Σε αυτό το σημείο, μπορείτε να επιλέξετε να αποδεχτείτε ή να αρνηθείτε το αίτημα.", + "errorDisableIT": "Η βοήθεια μετάφρασης είναι απενεργοποιημένη.", + "errorDisableIGC": "Η βοήθεια γραμματικής είναι απενεργοποιημένη.", + "errorDisableLanguageAssistance": "Η βοήθεια μετάφρασης και γραμματικής είναι απενεργοποιημένες.", + "errorDisableITUserDesc": "Κάντε κλικ εδώ για να ενημερώσετε τις ρυθμίσεις βοήθειας μετάφρασης", + "errorDisableIGCUserDesc": "Κάντε κλικ εδώ για να ενημερώσετε τις ρυθμίσεις βοήθειας γραμματικής", + "errorDisableLanguageAssistanceUserDesc": "Κάντε κλικ εδώ για να ενημερώσετε τις ρυθμίσεις βοήθειας μετάφρασης και γραμματικής", + "errorDisableITClassDesc": "Η βοήθεια μετάφρασης είναι απενεργοποιημένη για το μάθημα στο οποίο βρίσκεται αυτή η συνομιλία.", + "errorDisableIGCClassDesc": "Η βοήθεια γραμματικής είναι απενεργοποιημένη για το μάθημα στο οποίο βρίσκεται αυτή η συνομιλία.", + "error405Title": "Οι γλώσσες δεν έχουν οριστεί", + "error405Desc": "Ορίστε τις γλώσσες σας στο Μενού > Ρυθμίσεις Μάθησης.", + "termsAndConditions": "Όρους και Προϋποθέσεις", + "andCertifyIAmAtLeast13YearsOfAge": " και πιστοποιώ ότι είμαι τουλάχιστον 16 ετών.", + "error502504Title": "Ω, υπάρχουν πολλοί μαθητές online!", + "error502504Desc": "Τα εργαλεία μετάφρασης και γραμματικής ενδέχεται να είναι αργά ή μη διαθέσιμα ενώ οι bots της Pangea ενημερώνονται.", + "error404Title": "Σφάλμα μετάφρασης!", + "error404Desc": "Ο Pangea Bot δεν είναι σίγουρος πώς να μεταφράσει αυτό...", + "errorPleaseRefresh": "Το ερευνούμε! Παρακαλούμε ανανεώστε και δοκιμάστε ξανά.", + "connectedToStaging": "Συνδεδεμένο με το Staging", + "learningSettings": "Ρυθμίσεις Μάθησης", + "participants": "Συμμετέχοντες", + "clickMessageTitle": "Χρειάζεστε βοήθεια;", + "clickMessageBody": "Κάντε κλικ σε ένα μήνυμα για εργαλεία γλώσσας όπως μετάφραση, αναπαραγωγή και άλλα!", + "allDone": "Ολοκληρώθηκε!", + "vocab": "Λεξιλόγιο", + "low": "Έχουμε αποδείξεις ότι ο χρήστης δεν καταλαβαίνει αυτές τις λέξεις.", + "medium": "Αυτές οι λέξεις έχουν χρησιμοποιηθεί. Δεν είναι σαφές αν οι λέξεις κατανοούνται πλήρως ή όχι.", + "high": "Έχουμε αποδείξεις ότι ο χρήστης καταλαβαίνει αυτές τις λέξεις.", + "subscribe": "Εγγραφή", + "getAccess": "Εγγραφείτε τώρα!", + "subscriptionDesc": "Οι μηνύσεις είναι δωρεάν! Εγγραφείτε για να ξεκλειδώσετε διαδραστική μετάφραση, έλεγχο γραμματικής και αναλυτικά στοιχεία μάθησης.", + "subscriptionManagement": "Διαχείριση Συνδρομής", + "currentSubscription": "Τρέχουσα Συνδρομή", + "cancelSubscription": "Ακυρώστε τη συνδρομή σας", + "selectYourPlan": "Επιλέξτε το πλάνο σας", + "subsciptionPlatformTooltip": "Παρακαλώ συνδεθείτε στη αρχική συσκευή σας για να διαχειριστείτε το πλάνο της συνδρομής σας", + "subscriptionManagementUnavailable": "Η διαχείριση συνδρομής δεν είναι διαθέσιμη", + "paymentMethod": "Μέθοδος Πληρωμής", + "paymentHistory": "Ιστορικό Πληρωμών", + "emptyChatDownloadWarning": "Δεν είναι δυνατή η λήψη κενής συνομιλίας", + "update": "Ενημέρωση", + "toggleImmersionMode": "Λειτουργία Εμβάπτισης", + "toggleImmersionModeDesc": "Όταν ενεργοποιείται, όλα τα μηνύματα εμφανίζονται στη γλώσσα στόχο σας. Αυτή η ρύθμιση είναι πιο χρήσιμη σε ανταλλαγές γλωσσών.", + "itToggleDescription": "Αυτό το εργαλείο εκμάθησης γλωσσών θα εντοπίζει λέξεις στη βασική σας γλώσσα και θα σας βοηθά να τις μεταφράσετε στη γλώσσα στόχο. Αν και σπάνιο, το AI μπορεί να κάνει λάθη στη μετάφραση.", + "igcToggleDescription": "Αυτό το εργαλείο εκμάθησης γλωσσών θα εντοπίζει κοινά ορθογραφικά, γραμματικά και σημεία στίξης λάθη στο μήνυμά σας και θα προτείνει διορθώσεις. Αν και σπάνιο, το AI μπορεί να κάνει λάθη στις διορθώσεις.", + "originalMessage": "Αρχικό Μήνυμα", + "sentMessage": "Αποσταλμένο Μήνυμα", + "useType": "Χρησιμοποιήστε Τύπο", + "notAvailable": "Δεν Διαθέσιμη", + "taAndGaTooltip": "Χρήση L2 με βοήθεια μετάφρασης και γραμματικής", + "definitionsToolName": "Ορισμοί Λέξεων", + "messageTranslationsToolName": "Μεταφράσεις Μηνυμάτων", + "definitionsToolDescription": "Όταν ενεργοποιείται, οι λέξεις με υπογράμμιση μπλε μπορούν να πατηθούν για ορισμούς. Πατήστε μηνύματα για πρόσβαση στους ορισμούς.", + "translationsToolDescrption": "Όταν ενεργοποιείται, πατήστε ένα μήνυμα και το εικονίδιο μετάφρασης για να δείτε ένα μήνυμα στη βασική σας γλώσσα.", + "welcomeBack": "Καλώς ήρθατε ξανά! Αν ήσασταν μέρος του πιλοτικού προγράμματος 2023-2024, παρακαλούμε επικοινωνήστε μαζί μας για τη ειδική σας συνδρομή πιλοτικού προγράμματος. Αν είστε εκπαιδευτικός που έχει αγοράσει (ή η εκπαιδευτική σας ιδιότητα έχει αγοράσει) άδειες για την τάξη σας, επικοινωνήστε μαζί μας για τη συνδρομή εκπαιδευτικού.", + "downloadTxtFile": "Λήψη αρχείου κειμένου", + "downloadCSVFile": "Λήψη αρχείου CSV", + "promotionalSubscriptionDesc": "Έχετε επί του παρόντος μια συνδρομή προώθησης διάρκειας ζωής. Στείλτε μήνυμα στο support@pangea.chat για βοήθεια στην αλλαγή της συνδρομής σας.", + "originalSubscriptionPlatform": "Συνδρομή αγοράστηκε μέσω {purchasePlatform}", + "oneWeekTrial": "Δοκιμαστική περίοδος μιας εβδομάδας", + "downloadXLSXFile": "Λήψη αρχείου Excel", + "unkDisplayName": "Άγνωστο", + "wwCountryDisplayName": "Παγκόσμιος", + "afCountryDisplayName": "Αφγανιστάν", + "axCountryDisplayName": "Νήσοι Αλάντες", + "alCountryDisplayName": "Αλβανία", + "dzCountryDisplayName": "Αλγερία", + "asCountryDisplayName": "Αμερικανική Σαμόα", + "adCountryDisplayName": "Ανδόρα", + "aoCountryDisplayName": "Αγκόλα", + "aiCountryDisplayName": "Ανγκουίλα", + "agCountryDisplayName": "Αντίγκουα και Μπαρμπούντα", + "arCountryDisplayName": "Αργεντινή", + "amCountryDisplayName": "Αρμενία", + "awCountryDisplayName": "Αρούμπα", + "acCountryDisplayName": "Νήσος Ασένσιον", + "auCountryDisplayName": "Αυστραλία", + "atCountryDisplayName": "Αυστρία", + "azCountryDisplayName": "Αζερμπαϊτζάν", + "bsCountryDisplayName": "Μπαχάμες", + "bhCountryDisplayName": "Μπαχρέιν", + "bdCountryDisplayName": "Μπανγκλαντές", + "bbCountryDisplayName": "Μπαρμπάντος", + "byCountryDisplayName": "Λευκορωσία", + "beCountryDisplayName": "Βέλγιο", + "bzCountryDisplayName": "Μπελίζ", + "bjCountryDisplayName": "Μπενίν", + "bmCountryDisplayName": "Βερμούδες", + "btCountryDisplayName": "Μπουτάν", + "boCountryDisplayName": "Βολιβία", + "baCountryDisplayName": "Βοσνία και Ερζεγοβίνη", + "bwCountryDisplayName": "Μποτσουάνα", + "brCountryDisplayName": "Βραζιλία", + "ioCountryDisplayName": "Βρετανικό Εδαφικό Τόξο Ινδικού Ωκεανού", + "vgCountryDisplayName": "Βρετανικές Παρθένοι Νήσοι", + "bnCountryDisplayName": "Μπρουνέι", + "bgCountryDisplayName": "Βουλγαρία", + "bfCountryDisplayName": "Μπουρκίνα Φάσο", + "biCountryDisplayName": "Μπουρούντι", + "khCountryDisplayName": "Καμπότζη", + "cmCountryDisplayName": "Καμερούν", + "caCountryDisplayName": "Καναδάς", + "cvCountryDisplayName": "Πράσινο Ακρωτήρι", + "bqCountryDisplayName": "Καραϊβική Ολλανδία", + "kyCountryDisplayName": "Νήσοι Κέιμαν", + "cfCountryDisplayName": "Κεντροαφρικανική Δημοκρατία", + "tdCountryDisplayName": "Τσαντ", + "clCountryDisplayName": "Χιλή", + "cnCountryDisplayName": "Κίνα", + "cxCountryDisplayName": "Νήσος Χριστουγέννων", + "ccCountryDisplayName": "Νήσοι Κόκος [Κίλιγκ]", + "coCountryDisplayName": "Κολομβία", + "kmCountryDisplayName": "Κομόρες", + "cdCountryDisplayName": "Δημοκρατία του Κονγκό", + "cgCountryDisplayName": "Δημοκρατία του Κονγκό", + "ckCountryDisplayName": "Νήσοι Κουκ", + "crCountryDisplayName": "Κόστα Ρίκα", + "ciCountryDisplayName": "Ακτή Ελεφαντοστού", + "hrCountryDisplayName": "Κροατία", + "cuCountryDisplayName": "Κούβα", + "cwCountryDisplayName": "Κουρασάο", + "cyCountryDisplayName": "Κύπρος", + "czCountryDisplayName": "Τσεχική Δημοκρατία", + "dkCountryDisplayName": "Δανία", + "djCountryDisplayName": "Τζιμπουτί", + "dmCountryDisplayName": "Δομινίκα", + "doCountryDisplayName": "Δομινικανή Δημοκρατία", + "tlCountryDisplayName": "Ανατολικό Τιμόρ", + "ecCountryDisplayName": "Ισημερινός", + "egCountryDisplayName": "Αίγυπτος", + "svCountryDisplayName": "Ελ Σαλβαδόρ", + "gqCountryDisplayName": "Ισημερινή Γουινέα", + "erCountryDisplayName": "Ερυθραία", + "eeCountryDisplayName": "Εσθονία", + "szCountryDisplayName": "Σουαζιλάνδη", + "etCountryDisplayName": "Αιθιοπία", + "fkCountryDisplayName": "Νήσοι Φόκλαντ", + "foCountryDisplayName": "Νήσοι Φερόες", + "fjCountryDisplayName": "Φίτζι", + "fiCountryDisplayName": "Φινλανδία", + "frCountryDisplayName": "Γαλλία", + "gfCountryDisplayName": "Γαλλική Γουιάνα", + "pfCountryDisplayName": "Γαλλική Πολυνησία", + "gaCountryDisplayName": "Γκαμπόν", + "gmCountryDisplayName": "Γκάμπια", + "geCountryDisplayName": "Γεωργία", + "deCountryDisplayName": "Γερμανία", + "ghCountryDisplayName": "Γκάνα", + "giCountryDisplayName": "Γιβραλτάρ", + "grCountryDisplayName": "Ελλάδα", + "glCountryDisplayName": "Γροιλανδία", + "gdCountryDisplayName": "Γρενάδα", + "gpCountryDisplayName": "Γουαδελούπη", + "guCountryDisplayName": "Γκουάμ", + "gtCountryDisplayName": "Γουατεμάλα", + "ggCountryDisplayName": "Γκέρνσεϊ", + "gnCountryDisplayName": "Γουινέα Κονάκρι", + "gwCountryDisplayName": "Γουινέα-Μπισάου", + "gyCountryDisplayName": "Γουιάνα", + "htCountryDisplayName": "Αϊτή", + "hmCountryDisplayName": "Νησί Heard και Νησιά McDonald", + "hnCountryDisplayName": "Ονδούρα", + "hkCountryDisplayName": "Χονγκ Κονγκ", + "huCountryDisplayName": "Ουγγαρία", + "isCountryDisplayName": "Ισλανδία", + "inCountryDisplayName": "Ινδία", + "idCountryDisplayName": "Ινδονησία", + "irCountryDisplayName": "Ιράν", + "iqCountryDisplayName": "Ιράκ", + "ieCountryDisplayName": "Ιρλανδία", + "imCountryDisplayName": "Νήσος του Μαν", + "ilCountryDisplayName": "Ισραήλ", + "itCountryDisplayName": "Ιταλία", + "jmCountryDisplayName": "Τζαμάικα", + "jpCountryDisplayName": "Ιαπωνία", + "jeCountryDisplayName": "Τζέρσεϊ", + "joCountryDisplayName": "Ιορδανία", + "kzCountryDisplayName": "Καζακστάν", + "keCountryDisplayName": "Κένυα", + "kiCountryDisplayName": "Κιριμπάτι", + "xkCountryDisplayName": "Κοσσυφοπέδιο", + "kwCountryDisplayName": "Κουβέιτ", + "kgCountryDisplayName": "Κιργιζία", + "laCountryDisplayName": "Λάος", + "lvCountryDisplayName": "Λετονία", + "lbCountryDisplayName": "Λίβανος", + "lsCountryDisplayName": "Λεσόθο", + "lrCountryDisplayName": "Λιβερία", + "lyCountryDisplayName": "Λιβύη", + "liCountryDisplayName": "Λιχτενστάιν", + "ltCountryDisplayName": "Λιθουανία", + "luCountryDisplayName": "Λουξεμβούργο", + "moCountryDisplayName": "Μακάου", + "mkCountryDisplayName": "Βόρεια Μακεδονία", + "mgCountryDisplayName": "Μαδαγασκάρη", + "mwCountryDisplayName": "Μαλάουι", + "myCountryDisplayName": "Μαλαισία", + "mvCountryDisplayName": "Μαλδίβες", + "mlCountryDisplayName": "Μάλι", + "mtCountryDisplayName": "Μάλτα", + "mhCountryDisplayName": "Νήσοι Μάρσαλ", + "mqCountryDisplayName": "Μαρτινίκα", + "mrCountryDisplayName": "Μαυριτανία", + "muCountryDisplayName": "Μαυρίκιος", + "ytCountryDisplayName": "Μαγιότ", + "mxCountryDisplayName": "Μεξικό", + "fmCountryDisplayName": "Μικρονησία", + "mdCountryDisplayName": "Μολδαβία", + "mcCountryDisplayName": "Μονακό", + "mnCountryDisplayName": "Μογγολία", + "meCountryDisplayName": "Μαυροβούνιο", + "msCountryDisplayName": "Μοντσερράτ", + "maCountryDisplayName": "Μαρόκο", + "mzCountryDisplayName": "Μοζαμβίκη", + "mmCountryDisplayName": "Μιανμάρ (Βιρμανία)", + "naCountryDisplayName": "Ναμίμπια", + "nrCountryDisplayName": "Ναουρού", + "npCountryDisplayName": "Νεπάλ", + "nlCountryDisplayName": "Ολλανδία", + "ncCountryDisplayName": "Νέα Καληδονία", + "nzCountryDisplayName": "Νέα Ζηλανδία", + "niCountryDisplayName": "Νικαράγουα", + "neCountryDisplayName": "Νίγηρας", + "ngCountryDisplayName": "Νιγηρία", + "nuCountryDisplayName": "Νιουέ", + "nfCountryDisplayName": "Νησί Νόρφολκ", + "kpCountryDisplayName": "Βόρεια Κορέα", + "mpCountryDisplayName": "Βόρειες Μαριάνες Νήσοι", + "noCountryDisplayName": "Νορβηγία", + "omCountryDisplayName": "Ομάν", + "pkCountryDisplayName": "Πακιστάν", + "pwCountryDisplayName": "Παλάου", + "psCountryDisplayName": "Παλαιστινιακά Εδάφη", + "paCountryDisplayName": "Παναμάς", + "pgCountryDisplayName": "Παπούα Νέα Γουινέα", + "pyCountryDisplayName": "Παραγουάη", + "peCountryDisplayName": "Περού", + "phCountryDisplayName": "Φιλιππίνες", + "plCountryDisplayName": "Πολωνία", + "ptCountryDisplayName": "Πορτογαλία", + "prCountryDisplayName": "Πουέρτο Ρίκο", + "qaCountryDisplayName": "Κατάρ", + "reCountryDisplayName": "Ρεϋνιόν", + "roCountryDisplayName": "Ρουμανία", + "ruCountryDisplayName": "Ρωσία", + "rwCountryDisplayName": "Ρουάντα", + "blCountryDisplayName": "Άγιος Βαρθολομαίος", + "shCountryDisplayName": "Άγιος Ελένη", + "knCountryDisplayName": "Άγιος Κιτς και Νέβις", + "lcCountryDisplayName": "Άγιος Λουκάς", + "mfCountryDisplayName": "Άγιος Μαρτίνος", + "pmCountryDisplayName": "Άγιος Πέτρος και Μικελόν", + "vcCountryDisplayName": "Άγιος Βικέντιος", + "wsCountryDisplayName": "Σαμόα", + "smCountryDisplayName": "Άγιος Μαρίνος", + "stCountryDisplayName": "Σάο Τόμε και Πρίνσιπε", + "saCountryDisplayName": "Σαουδική Αραβία", + "snCountryDisplayName": "Σενεγάλη", + "rsCountryDisplayName": "Σερβία", + "scCountryDisplayName": "Σεϋχέλλες", + "slCountryDisplayName": "Σιέρα Λεόνε", + "sgCountryDisplayName": "Σιγκαπούρη", + "sxCountryDisplayName": "Σίντ Μαρτέν", + "skCountryDisplayName": "Σλοβακία", + "siCountryDisplayName": "Σλοβενία", + "sbCountryDisplayName": "Νήσοι Σολομώντα", + "soCountryDisplayName": "Σομαλία", + "zaCountryDisplayName": "Νότια Αφρική", + "gsCountryDisplayName": "Νότια Γεωργία και Νότιες Νήσοι Σάντουιτς", + "krCountryDisplayName": "Νότια Κορέα", + "ssCountryDisplayName": "Νότιο Σουδάν", + "esCountryDisplayName": "Ισπανία", + "lkCountryDisplayName": "Σρι Λάνκα", + "sdCountryDisplayName": "Σουδάν", + "srCountryDisplayName": "Σουρινάμ", + "sjCountryDisplayName": "Σβάλμπαρντ και Γιαν Μάγιεν", + "seCountryDisplayName": "Σουηδία", + "chCountryDisplayName": "Ελβετία", + "syCountryDisplayName": "Συρία", + "twCountryDisplayName": "Ταϊβάν", + "tjCountryDisplayName": "Τατζικιστάν", + "tzCountryDisplayName": "Τανζανία", + "thCountryDisplayName": "Ταϊλάνδη", + "tgCountryDisplayName": "Τόγκο", + "tkCountryDisplayName": "Τοκελάου", + "toCountryDisplayName": "Τόνγκα", + "ttCountryDisplayName": "Τρινιντάντ / Τόμπακο", + "tnCountryDisplayName": "Τυνησία", + "trCountryDisplayName": "Τουρκία", + "tmCountryDisplayName": "Τουρκμενιστάν", + "tcCountryDisplayName": "Νησιά Τερκς και Κάικος", + "tvCountryDisplayName": "Τουβαλού", + "viCountryDisplayName": "Αμερικανικές Παρθένοι Νήσοι", + "ugCountryDisplayName": "Ουγκάντα", + "uaCountryDisplayName": "Ουκρανία", + "aeCountryDisplayName": "Ηνωμένα Αραβικά Εμιράτα", + "gbCountryDisplayName": "Ηνωμένο Βασίλειο", + "usCountryDisplayName": "Ηνωμένες Πολιτείες", + "uyCountryDisplayName": "Ουρουγουάη", + "uzCountryDisplayName": "Ουζμπεκιστάν", + "vuCountryDisplayName": "Βανουάτου", + "vaCountryDisplayName": "Βατικανό", + "veCountryDisplayName": "Βενεζουέλα", + "vnCountryDisplayName": "Βιετνάμ", + "wfCountryDisplayName": "Wallis και Futuna", + "ehCountryDisplayName": "Δυτική Σαχάρα", + "yeCountryDisplayName": "Υεμένη", + "zmCountryDisplayName": "Ζάμπια", + "zwCountryDisplayName": "Ζιμπάμπουε", + "pay": "Checkout", + "invitedToSpace": "{user} σας έχει προσκαλέσει να συμμετάσχετε σε ένα μάθημα: {space}! Θέλετε να αποδεχθείτε;", + "youreInvited": "📩 Είστε προσκεκλημένοι!", + "invitedToChat": "{user} σας έχει προσκαλέσει να συμμετάσχετε σε μια συνομιλία: {name}! Θέλετε να αποδεχθείτε;", + "monthlySubscription": "Μηνιαία", + "yearlySubscription": "Ετήσια", + "defaultSubscription": "Συνδρομή Pangea Chat", + "freeTrial": "Δωρεάν Δοκιμή", + "total": "Σύνολο: ", + "noDataFound": "Δεν βρέθηκαν δεδομένα", + "blurMeansTranslateTitle": "Γιατί είναι θολό το μήνυμα;", + "blurMeansTranslateBody": "Ενώ είναι ενεργή η λειτουργία Εμβάθυνσης, τα μηνύματα που αποστέλλονται στη βασική σας γλώσσα θα θολώνουν ενώ το Pangea Bot τα μεταφράζει στη γλώσσα στόχο σας. Η λειτουργία Εμβάθυνσης μπορεί να ενεργοποιείται ή να απενεργοποιείται στις ρυθμίσεις μεμονωμένων και μαθημάτων.", + "bestCorrectionFeedback": "Αυτό είναι σωστό!", + "distractorFeedback": "Δεν είναι ακριβώς σωστό.", + "bestAnswerFeedback": "Αυτό είναι σωστό!", + "definitionDefaultPrompt": "Τι σημαίνει αυτή η λέξη;", + "practiceDefaultPrompt": "Ποια είναι η καλύτερη απάντηση;", + "correctionDefaultPrompt": "Ποια είναι η καλύτερη αντικατάσταση;", + "acceptSelection": "Αποδοχή Διόρθωσης", + "why": "Γιατί;", + "definition": "Ορισμός", + "exampleSentence": "Παράδειγμα πρότασης", + "reportToTeacher": "Σε ποιον θέλετε να αναφέρετε αυτό το μήνυμα;", + "reportMessageTitle": "{reportingUserId} ανέφερε ένα μήνυμα από τον {reportedUserId} στη συνομιλία {roomName}", + "reportMessageBody": "Μήνυμα: {reportedMessage}\nΑιτία: {reason}", + "noTeachersFound": "Δεν βρέθηκαν δάσκαλοι για αναφορά", + "trialExpiration": "Η δωρεάν δοκιμή σας λήγει στις {expiration}", + "freeTrialDesc": "Οι νέοι χρήστες λαμβάνουν μια εβδομάδα δωρεάν δοκιμής του Pangea Chat", + "activateTrial": "Δωρεάν 7-ήμερη Δοκιμή", + "successfullySubscribed": "Έχετε εγγραφεί με επιτυχία!", + "clickToManageSubscription": "Κάντε κλικ εδώ για να διαχειριστείτε τη συνδρομή σας.", + "signUp": "Εγγραφή", + "pleaseChooseAtLeastChars": "Παρακαλώ επιλέξτε τουλάχιστον {min} χαρακτήρες.", + "noEmailWarning": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση email. Διαφορετικά δεν θα μπορείτε να επαναφέρετε τον κωδικό πρόσβασής σας. Αν δεν θέλετε, πατήστε ξανά το κουμπί για να συνεχίσετε.", + "pleaseEnterValidEmail": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση email.", + "pleaseChooseAUsername": "Παρακαλώ επιλέξτε ένα όνομα χρήστη", + "define": "Ορίστε", + "listen": "Ακούστε", + "trialPeriodExpired": "Η δοκιμαστική περίοδος σας έχει λήξει", + "translations": "μεταφράσεις", + "messageAudio": "ήχος μηνύματος", + "definitions": "ορισμοί", + "subscribedToUnlockTools": "Εγγραφείτε για να ξεκλειδώσετε διαδραστική μετάφραση και έλεγχο γραμματικής, αναπαραγωγή ήχου, εξατομικευμένες δραστηριότητες πρακτικής και αναλύσεις μάθησης!", + "translationTooltip": "Μετάφραση", + "speechToTextTooltip": "Μεταγραφή", + "kickBotWarning": "Το κλείσιμο του Pangea Bot θα αφαιρέσει το bot συνομιλίας από αυτήν τη συζήτηση.", + "refresh": "Ανανέωση", + "messageAnalytics": "Ανάλυση μηνυμάτων", + "words": "Λέξεις", + "score": "Βαθμολογία", + "accuracy": "Ακρίβεια", + "points": "Βαθμοί", + "noPaymentInfo": "Δεν απαιτείται πληροφορία πληρωμής!", + "updatePhoneOS": "Ίσως χρειαστεί να ενημερώσετε την έκδοση του λειτουργικού συστήματος της συσκευής σας.", + "wordsPerMinute": "Λέξεις ανά λεπτό", + "tooltipInstructionsTitle": "Δεν είστε σίγουροι τι κάνει αυτό;", + "tooltipInstructionsMobileBody": "Πατήστε και κρατήστε πατημένο αντικείμενα για να δείτε τις συμβουλές εργαλείων.", + "tooltipInstructionsBrowserBody": "Τοποθετήστε το δείκτη πάνω από αντικείμενα για να δείτε τις συμβουλές εργαλείων.", + "chatCapacity": "Χωρητικότητα συνομιλίας", + "roomFull": "Αυτή η αίθουσα είναι ήδη γεμάτη.", + "chatCapacityHasBeenChanged": "Η χωρητικότητα της συνομιλίας άλλαξε", + "chatCapacitySetTooLow": "Η χωρητικότητα της συνομιλίας πρέπει να είναι τουλάχιστον {count}.", + "chatCapacityExplanation": "Η χωρητικότητα της συνομιλίας περιορίζει τον αριθμό των μελών που επιτρέπονται σε μια συνομιλία.", + "tooManyRequest": "Πάρα πολλά αιτήματα, παρακαλώ δοκιμάστε ξανά αργότερα.", + "enterNumber": "Παρακαλώ εισάγετε μια ακέραια τιμή.", + "buildTranslation": "Δημιουργήστε τη μετάφρασή σας από τις επιλογές παραπάνω", + "practice": "Εξάσκηση", + "noLanguagesSet": "Δεν έχουν οριστεί γλώσσες", + "speechToTextBody": "Για φωνητικά μηνύματα, μπορείτε να δείτε μια μεταγραφή καθώς και το σκορ Λέξεων ανά Λεπτό του ομιλητή.", + "versionNotFound": "Δεν βρέθηκε έκδοση", + "fetchingVersion": "Λήψη έκδοσης...", + "versionFetchError": "Σφάλμα κατά τη λήψη έκδοσης", + "versionText": "Έκδοση: {version}+{buildNumber}", + "l1TranslationBody": "Τα μηνύματα στη βασική σας γλώσσα δεν θα μεταφραστούν.", + "deleteSubscriptionWarningTitle": "Έχετε ενεργή συνδρομή", + "deleteSubscriptionWarningBody": "Η διαγραφή του λογαριασμού σας δεν θα ακυρώσει αυτόματα τη συνδρομή σας.", + "manageSubscription": "Διαχείριση συνδρομής", + "error520Title": "Παρακαλώ δοκιμάστε ξανά.", + "error520Desc": "Λυπούμαστε, δεν καταλάβαμε το μήνυμά σας...", + "wordsUsed": "Χρησιμοποιημένες λέξεις", + "level": "Επίπεδο", + "morphsUsed": "Χρησιμοποιημένες μορφές", + "translationChoicesBody": "Κάντε κλικ και κρατήστε μια επιλογή για μια υπόδειξη.", + "grammar": "Γραμματική", + "contactHasBeenInvitedToTheChat": "Ο επαφή έχει προσκληθεί στη συνομιλία", + "inviteChat": "📨 Προσκαλέστε στη συνομιλία", + "chatName": "Όνομα συνομιλίας", + "reportContentIssueTitle": "Αναφορά προβλήματος περιεχομένου", + "feedback": "Προαιρετικά σχόλια", + "reportContentIssueDescription": "Ωχ! Η ΤΝ μπορεί να διευκολύνει εξατομικευμένες εκπαιδευτικές εμπειρίες αλλά... επίσης hallucinate. Παρακαλούμε δώστε τυχόν σχόλια και θα προσπαθήσουμε ξανά.", + "clickTheWordAgainToDeselect": "Κάντε κλικ στη επιλεγμένη λέξη για να την αποεπιλέξετε.", + "l2SupportNa": "Μη διαθέσιμο", + "l2SupportAlpha": "Άλφα", + "l2SupportBeta": "Βήτα", + "l2SupportFull": "Πλήρες", + "missingVoiceTitle": "Απουσία φωνής", + "voiceNotAvailable": "Δεν έχετε εγκατεστημένη φωνή για αυτήν τη γλώσσα.", + "openVoiceSettings": "Άνοιγμα ρυθμίσεων φωνής", + "playAudio": "Αναπαραγωγή", + "stop": "Διακοπή", + "grammarCopyPOSsconj": "Υποδεκτική Συζυγία", + "grammarCopyPOSnum": "Αριθμός", + "grammarCopyPOSverb": "Ρήμα", + "grammarCopyPOSaffix": "Επίθημα", + "grammarCopyPOSpart": "Μόριο", + "grammarCopyPOSadj": "Επίθετο", + "grammarCopyPOScconj": "Συντονιστική Συζυγία", + "grammarCopyPOSpunct": "Σημείο Στίξης", + "grammarCopyPOSadv": "Επίρρημα", + "grammarCopyPOSaux": "Βοηθητικό", + "grammarCopyPOSspace": "Κενό", + "grammarCopyPOSsym": "Σύμβολο", + "grammarCopyPOSdet": "Οριστικό", + "grammarCopyPOSpron": "Αντωνυμία", + "grammarCopyPOSadp": "Επιρρηματική Πρόθεση", + "grammarCopyPOSpropn": "Ονόματος Ιδιότητας", + "grammarCopyPOSnoun": "Ουσιαστικό", + "grammarCopyPOSintj": "Επιφώνημα", + "grammarCopyPOSx": "Άλλο", + "grammarCopyGENDERfem": "Θηλυκό", + "grammarCopyPERSON2": "Δεύτερο Πρόσωπο", + "grammarCopyMOODimp": "Υποτακτική", + "grammarCopyPUNCTTYPEqest": "Ερώτηση", + "grammarCopyASPECTperf": "Τελειωμένο", + "grammarCopyCASEaccnom": "Αιτιατική, Ονομαστική", + "grammarCopyCASEobl": "Παρακειμένη", + "grammarCopyVOICEact": "Ενεργητική", + "grammarCopyPUNCTTYPEbrck": "Παρένθεση", + "grammarCopyNOUNTYPEart": "Άρθρο", + "grammarCopyNUMBERsing": "Ενικός", + "grammarCopyGENDERmasc": "Αρσενικό", + "grammarCopyVERBTYPEmod": "Ρήμα Modal", + "grammarCopyADVTYPEadverbial": "Επιρρηματικός", + "grammarCopyTENSEperi": "Περιφραστικός", + "grammarCopyNUMFORMdigit": "Ψηφίο", + "grammarCopyNOUNTYPEnot_proper": "Μη σωστό", + "grammarCopyNUMTYPEcard": "Αριθμητικό", + "grammarCopyNOUNTYPEprop": "Ιδιωτικό", + "grammarCopyPUNCTTYPEdash": "Παύλα", + "grammarCopyPUNCTTYPEyes": "Ναι", + "grammarCopyPUNCTTYPEsemi": "Άνω τελεία", + "grammarCopyPUNCTTYPEcomm": "Κόμμα", + "grammarCopyMOODcnd": "Υποθετικό", + "grammarCopyCASEacc": "Αιτιατική", + "grammarCopyPARTTYPEpart": "Μερικό", + "grammarCopyTENSEpast": "Παρελθόν", + "grammarCopyDEGREEsup": "Υπερθετικός", + "grammarCopyPUNCTTYPEcolo": "Άνω τελεία", + "grammarCopyPERSON3": "Τρίτο πρόσωπο", + "grammarCopyNUMBERplur": "Πληθυντικός", + "grammarCopyPRONTYPEnpr": "Ιδιωτικό ουσιαστικό", + "grammarCopyPRONTYPEinterrogative": "Ερωτηματική", + "grammarCopyPOLITEinfm": "Α informal", + "grammarCopyADVTYPEtim": "Χρόνος", + "grammarCopyPOLARITYneg": "Αρνητικό", + "grammarCopyNUMTYPEtot": "Σύνολο", + "grammarCopyADVTYPEadnomial": "Επιθετικός", + "grammarCopyASPECTprog": "Προοδευτικό", + "grammarCopyMOODsub": "Υποτακτική", + "grammarCopyVERBFORMcomplementive": "Συμπληρωματική", + "grammarCopyCASEnom": "Ονομαστική", + "grammarCopyTENSEfut": "Μέλλοντας", + "grammarCopyCASEdat": "Δοτική", + "grammarCopyTENSEpres": "Ενεστώτας", + "grammarCopyGENDERneut": "Ουδέτερο", + "grammarCopyPRONTYPErel": "Σχετικός", + "grammarCopyVERBFORMfinalEnding": "Τελευταία κατάληξη", + "grammarCopyPRONTYPEdem": "Δεικτικός", + "grammarCopyPREPCASEpre": "Προθετική", + "grammarCopyVERBFORMfin": "Οριστική", + "grammarCopyDEGREEpos": "Θετικό", + "grammarCopyPUNCTTYPEquot": "Παραθέματα", + "grammarCopyVERBFORMger": "Γερουνδικό", + "grammarCopyVOICEpass": "Παθητική", + "grammarCopyCASEgen": "Γενική", + "grammarCopyTENSEprs": "Ενεστώτας", + "grammarCopyDEFINITEdef": "Οριστική", + "grammarCopyNUMTYPEord": "Τακτική", + "grammarCopyCASEins": "Όργανο", + "grammarCopyVERBFORMinf": "Απαρέμφατο", + "grammarCopyVERBFORMaux": "Βοηθητικό", + "grammarCopyNUMFORMlong": "Μακρύ", + "grammarCopyCASEloc": "Τοπική", + "grammarCopyMOODind": "Εγκλιτική", + "grammarCopyDEGREEcmp": "Συγκριτική", + "grammarCopyCASErelativeCase": "Σχετική", + "grammarCopyPUNCTTYPEexcl": "Ευφημιστική", + "grammarCopyPERSON1": "Πρώτο πρόσωπο", + "grammarCopyPUNCTSIDEini": "Αρχικό", + "grammarCopyGENDERperson": "Άτομο", + "grammarCopyFOREIGNyes": "Ξένη", + "grammarCopyVOICEvoice": "Φωνή", + "grammarCopyVERBTYPEverbType": "Ρήμα", + "grammarCopyPOSSpass": "Κτητική", + "grammarCopyPREPCASEprepCase": "Προθετικό", + "grammarCopyNUMTYPEnumType": "Αριθμητικό", + "grammarCopyNOUNTYPEnounType": "Ουσιαστικό", + "grammarCopyREFLEXreflex": "Ανακλαστικό", + "grammarCopyPRONTYPEpronType": "Αντωνυμία", + "grammarCopyPUNCTSIDEpunctSide": "Πλευρά Σημείωσης Στίξης", + "grammarCopyVERBFORMverbForm": "Ρήμα", + "grammarCopyGENDERgender": "Γένος", + "grammarCopyMOODmood": "Τρόπος", + "grammarCopyASPECTaspect": "Πλευρά", + "grammarCopyPUNCTTYPEpunctType": "Στίξη", + "grammarCopyTENSEtense": "Χρόνος", + "grammarCopyDEGREEdegree": "Βαθμός", + "grammarCopyPOLITEpolite": "Ευγένεια", + "grammarCopyADVTYPEadvType": "Επίρρημα", + "grammarCopyNUMFORMnumber": "Αριθμός", + "grammarCopyCONJTYPEconjType": "Σύνδεσμος", + "grammarCopyPOLARITYpolarity": "Πολικότητα", + "grammarCopyCASEcase": "Πτώση", + "grammarCopyDEFINITEdefinite": "Οριστικότητα", + "grammarCopyNUMFORMnumForm": "Αριθμητικό", + "grammarCopyPRONTYPEadn": "Επιθετικό", + "grammarCopyVOCvoc": "Κλητική", + "grammarCopyCMPLcmpl": "Συμπληρωματικός", + "grammarCopyADVadv": "Επιρρηματικός", + "grammarCopyMOODjus": "Επιτακτική", + "grammarCopyGENDERcom": "Κοινό", + "grammarCopyREFLEXrflx": "Ανακλαστικό", + "grammarCopyPARTTYPEpar": "Μερικό", + "grammarCopySPCspc": "Συγκεκριμένο", + "grammarCopyTENSEpqp": "Πλεορισμένο", + "grammarCopyREFLEXref": "Ανακλαστικό", + "grammarCopyPUNCTTYPEnshrt": "Σύντομο", + "grammarCopyNUMBERdual": "Διπλό", + "grammarCopyNUMFORMlng": "Μακρύ", + "grammarCopyVOICEmid": "Μεσαίο", + "grammarCopyINTRELintRel": "Ερωτηματικό, Σχετικό", + "grammarCopyINTint": "Ερωτηματικό", + "grammarCopyVOICEcaus": "Αιτιατικό", + "grammarCopyUnknown": "Άγνωστο", + "grammarCopyEVIDENTevident": "Επικυρωτική", + "grammarCopyNUMFORMnumberPsor": "Αριθμός Κατόχου", + "grammarCopyASPECThab": "Συνήθης", + "grammarCopyCASEabl": "Αποθετική", + "grammarCopyCASEall": "Πολλαπλασιαστική", + "grammarCopyCASEess": "Εξωτική", + "grammarCopyCASEtra": "Μεταβατική", + "grammarCopyCASEequ": "Ισοδύναμη", + "grammarCopyCASEdis": "Διανεμητική", + "grammarCopyCASEabs": "Απόλυτη", + "grammarCopyCASEerg": "Εργατική", + "grammarCopyCASEcau": "Αιτιακή", + "grammarCopyCASEben": "Ωφελική", + "grammarCopyCASEtem": "Χρονική", + "grammarCopyCONJTYPEcoord": "Συντονιστική", + "grammarCopyDEFINITEcons": "Κατασκευαστική Κατάσταση", + "grammarCopyDEGREEabs": "Απόλυτο Βαθμό", + "grammarCopyEVIDENTfh": "Πραγματική Επικυρωτικότητα", + "grammarCopyEVIDENTnfh": "Μη-πραγματική Επικυρωτικότητα", + "grammarCopyMOODopt": "Ευχή", + "grammarCopyMOODadm": "Επιτιμητικό", + "grammarCopyMOODdes": "Επιθυμητικό", + "grammarCopyMOODnec": "Αναγκαστικό", + "grammarCopyMOODpot": "Δυναμικό", + "grammarCopyMOODprp": "Προτατικό", + "grammarCopyMOODqot": "Παραθετικό", + "grammarCopyNUMFORMword": "Μορφή Λέξης", + "grammarCopyNUMFORMroman": "Ρωμαϊκό Αριθμητικό", + "grammarCopyNUMFORMletter": "Μορφή Γράμματος", + "grammarCopyNUMTYPEmult": "Πολλαπλασιαστικό", + "grammarCopyNUMTYPEfrac": "Κλασματικό", + "grammarCopyNUMTYPEsets": "Σετ", + "grammarCopyNUMTYPErange": "Εύρος", + "grammarCopyNUMTYPEdist": "Διανεμητικό", + "grammarCopyNUMBERtri": "Δοκιμαστικό", + "grammarCopyNUMBERpauc": "Ποακού", + "grammarCopyNUMBERgrpa": "Μεγαλύτερο Ποακού", + "grammarCopyNUMBERgrpl": "Μεγαλύτερο Πληθυντικό", + "grammarCopyNUMBERinv": "Αντίστροφο", + "grammarCopyPERSON0": "Μηδέν", + "grammarCopyPERSON4": "Τέταρτος", + "grammarCopyPOLITEform": "Επίσημο", + "grammarCopyPOLITEelev": "Αυξημένο", + "grammarCopyPOLITEhumb": "Ταπεινό", + "grammarCopyPRONTYPEemp": "Διεισδυτικό", + "grammarCopyPRONTYPEexc": "Ερωτηματικό", + "grammarCopyPRONTYPErcp": "Ανταποδοτικό", + "grammarCopyPRONTYPEintRelPronType": "Ερωτηματικό-Σχετικό", + "grammarCopyTENSEaor": "Αόριστος", + "grammarCopyTENSEeps": "Επιδεξιωματικός", + "grammarCopyTENSEprosp": "Προοπτικός", + "grammarCopyVERBFORMpart": "Μέρος", + "grammarCopyVERBFORMconv": "Συνεκδοχή", + "grammarCopyVERBFORMvnoun": "Ρηματικό Ουσιαστικό", + "grammarCopyVOICEantip": "Αντιπαραθετικός", + "grammarCopyVOICEcauVoice": "Αιτιατική", + "grammarCopyVOICedir": "Άμεσος", + "grammarCopyVOICEinvVoice": "Αντίστροφος", + "grammarCopyVOICErcpVoice": "Ανταποδοτικός", + "grammarCopyPOS": "Μέρος του Λόγου", + "grammarCopyGENDER": "Γένος", + "grammarCopyPERSON": "Άτομο", + "grammarCopyMOOD": "Τρόπος", + "grammarCopyPUNCTTYPE": "Τύπος Σημείωσης", + "grammarCopyASPECT": "Πλευρά", + "grammarCopyCASE": "Πτώση", + "grammarCopyVOICE": "Φωνή", + "grammarCopyNOUNTYPE": "Τύπος Ουσιαστικού", + "grammarCopyVERBTYPE": "Τύπος Ρήματος", + "grammarCopyADVTYPE": "Τύπος Επιρρήματος", + "grammarCopyNUMFORM": "Μορφή Αριθμού", + "grammarCopyNUMTYPE": "Τύπος Αριθμού", + "grammarCopyNUMBER": "Αριθμός", + "grammarCopyDEFINITE": "Οριστικότητα", + "grammarCopyDEGREE": "Βαθμός", + "grammarCopyEVIDENT": "Ενδεικτικότητα", + "grammarCopyFOREIGN": "Ξένο", + "grammarCopyPOLARITY": "Πολικότητα", + "grammarCopyPOLITE": "Ευγένεια", + "grammarCopyPREPCASE": "Προθετική Πτώση", + "grammarCopyPRONTYPE": "Τύπος Αντωνυμίας", + "grammarCopyPUNCTSIDE": "Πλευρά Σημείωσης Στίξης", + "grammarCopyREFLEX": "Ανακλαστική", + "grammarCopyTENSE": "Χρόνος", + "grammarCopyVERBFORM": "Μορφή Ρήματος", + "grammarCopyCONJTYPE": "Τύπος Συνδέσμου", + "grammarCopySPC": "Ειδικότητα", + "grammarCopyPARTTYPE": "Τύπος Μερισμού", + "grammarCopyINTREL": "Ερωτηματική-Σχετική", + "grammarCopyUNKNOWN": "Άγνωστο", + "grammarCopyNUMBERPSOR": "Αριθμός Κυριότητας", + "grammarCopyPOSS": "Κτητική", + "grammarCopyASPECTimp": "Ατελής Όψη", + "grammarCopyCASEvoc": "Κλητική", + "grammarCopyCASEcom": "Κοινοτική", + "grammarCopyCASEpar": "Μεριστική", + "grammarCopyCASEadv": "Επιρρηματική", + "grammarCopyCASEref": "Αναφορική", + "grammarCopyCASErel": "Σχετική", + "grammarCopyCASEsub": "Υποθετική", + "grammarCopyCASEsup": "Υπερεστιακή", + "grammarCopyCASEaccdat": "Αιτιατική-Δοτική", + "grammarCopyCASEpre": "Προθετική", + "grammarCopyCONJTYPEsub": "Υποτακτική", + "grammarCopyCONJTYPEcmp": "Συγκριτική", + "grammarCopyDEFINITEind": "Αόριστη", + "grammarCopyMOODint": "Ερωτηματική Έννοια", + "grammarCopyNOUNTYPEcomm": "Κοινό Ουσιαστικό", + "grammarCopyNUMBERPSORsing": "Ενικός του Κατόχου", + "grammarCopyNUMBERPSORplur": "Πληθυντικός του Κατόχου", + "grammarCopyNUMBERPSORdual": "Διπλός του Κατόχου", + "grammarCopyPOLARITYpos": "Θετική Πολικότητα", + "grammarCopyPOSSyes": "Κτητική", + "grammarCopyPREPCASEnpr": "Μη-προθετική", + "grammarCopyPRONTYPEprs": "Προσωπική", + "grammarCopyPRONTYPEint": "Ερωτηματική", + "grammarCopyPRONTYPEtot": "Ολική", + "grammarCopyPRONTYPEneg": "Αρνητική", + "grammarCopyPRONTYPEart": "Άρθρο", + "grammarCopyPRONTYPEind": "Αόριστος", + "grammarCopyPRONTYPEintrel": "Ερωτηματικός-Σχετικός", + "grammarCopyPUNCTSIDEfin": "Τελευταίο Στίξη", + "grammarCopyPUNCTTYPEperi": "Τελεία", + "grammarCopyREFLEXyes": "Ανακλαστικό", + "grammarCopyTENSEimp": "Παρατατικός", + "grammarCopyVERBFORMsup": "Υπερσυντέλικος", + "grammarCopyVERBFORMadn": "Επιθετικός", + "grammarCopyVERBFORMlng": "Μακρύς", + "grammarCopyVERBFORMshrt": "Βραχύς", + "grammarCopyVERBTYPEcaus": "Αιτιατικό Ρήμα", + "grammarCopyVOICEcau": "Αιτιατική", + "grammarCopyVOICEdir": "Άμεση", + "grammarCopyVOICEinv": "Αντίστροφη", + "grammarCopyVOICErcp": "Αμοιβαία", + "other": "Άλλο", + "levelShort": "ΕΠΙΠΕΔΟ {level}", + "clickBestOption": "Επιλέξτε τις καλύτερες επιλογές για να μεταφράσετε το μήνυμά σας!", + "completeActivitiesToUnlock": "Ολοκληρώστε τουλάχιστον μια δραστηριότητα για να ξεκλειδώσετε τη μετάφραση!", + "noCapacityLimit": "Χωρίς όριο χωρητικότητας", + "downloadGroupText": "Λήψη κειμένου ομάδας", + "notificationsOn": "Ειδοποιήσεις ενεργές", + "notificationsOff": "Ειδοποιήσεις απενεργοποιημένες", + "createChatAndInviteUsers": "Δημιουργία συνομιλίας και πρόσκληση χρηστών", + "updatedNewSpaceDescription": "Τα μαθήματα σας επιτρέπουν να ενοποιήσετε τις συνομιλίες σας και να δημιουργήσετε ιδιωτικές ή δημόσιες κοινότητες.", + "joinWithCode": "Συμμετοχή με κωδικό", + "enterCodeToJoin": "Εισάγετε κωδικό για να συμμετάσχετε", + "updateNow": "Ενημέρωση τώρα", + "updateLater": "Αργότερα", + "constructUseWaDesc": "Χρησιμοποιείται χωρίς βοήθεια", + "constructUseGaDesc": "Βοήθεια γραμματικής", + "constructUseTaDesc": "Βοήθεια μετάφρασης", + "constructUseUnkDesc": "Άγνωστο", + "constructUseCorITDesc": "Ορθό στη μετάφραση", + "constructUseIgnITDesc": "Παραβλέπεται στη μετάφραση", + "constructUseIncITDesc": "Λάθος στη μετάφραση", + "constructUseIgnIGCDesc": "Παραβλέπεται στη διόρθωση γραμματικής", + "constructUseCorIGCDesc": "Ορθό στη διόρθωση γραμματικής", + "constructUseIncIGCDesc": "Λάθος στη διόρθωση γραμματικής", + "constructUseCorPADesc": "Ορθό στη δραστηριότητα σημασίας λέξης", + "constructUseIgnPADesc": "Παραβλέπεται στη δραστηριότητα σημασίας λέξης", + "constructUseIncPADesc": "Λάθος στη δραστηριότητα σημασίας λέξης", + "constructUseCorWLDesc": "Ορθό στη δραστηριότητα ακρόασης λέξης", + "constructUseIncWLDesc": "Λάθος στη δραστηριότητα ακρόασης λέξης", + "constructUseIngWLDesc": "Παραβλέπεται στη δραστηριότητα ακρόασης λέξης", + "constructUseCorHWLDesc": "Ορθό στη δραστηριότητα κρυφής λέξης", + "constructUseIncHWLDesc": "Λάθος στην κρυφή δραστηριότητα λέξης", + "constructUseIgnHWLDesc": "Αγνοήθηκε στην κρυφή δραστηριότητα λέξης", + "constructUseCorLDesc": "Σωστό στην δραστηριότητα λήμματος", + "constructUseIncLDesc": "Λάθος στην δραστηριότητα λήμματος", + "constructUseIgnLDesc": "Αγνοήθηκε στην δραστηριότητα λήμματος", + "constructUseCorMDesc": "Σωστό στην δραστηριότητα γραμματικής", + "constructUseIncMDesc": "Λάθος στην δραστηριότητα γραμματικής", + "constructUseIgnMDesc": "Αγνοήθηκε στην δραστηριότητα γραμματικής", + "constructUseEmojiDesc": "Σωστό στην δραστηριότητα emoji", + "constructUseCollected": "Συλλέχθηκε στη συνομιλία", + "constructUseNanDesc": "Μη εφαρμόσιμο", + "xpIntoLevel": "{currentXP} / {maxXP} Πόντοι XP", + "enableTTSToolName": "Ενεργοποιημένη φωνητική ανάγνωση κειμένου", + "enableTTSToolDescription": "Επιτρέψτε στην εφαρμογή να παράγει έξοδο φωνητικής ανάγνωσης για τμήματα κειμένου στη γλώσσα στόχο σας.", + "yourUsername": "Το όνομα χρήστη σας", + "yourEmail": "Το email σας", + "iWantToLearn": "Θέλω να μάθω", + "pleaseEnterEmail": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση email.", + "myBaseLanguage": "Η βασική μου γλώσσα", + "meaningSectionHeader": "Νόημα:", + "formSectionHeader": "Μορφές που χρησιμοποιούνται στα chat:", + "writingExercisesTooltip": "Γράψιμο", + "listeningExercisesTooltip": "Ακρόαση", + "readingExercisesTooltip": "Ανάγνωση", + "meaningNotFound": "Δεν ήταν δυνατή η εύρεση του νοήματος.", + "chooseBaseForm": "Επιλέξτε τη βασική μορφή", + "notTheCodeError": "Λυπούμαστε, αυτό δεν είναι ο κώδικας!", + "totalXP": "Συνολικά XP", + "numLemmas": "Συνολικός αριθμός λημμάτων", + "numLemmasUsedCorrectly": "Αριθμός λημμάτων που χρησιμοποιήθηκαν σωστά τουλάχιστον μία φορά", + "numLemmasUsedIncorrectly": "Αριθμός λημμάτων που χρησιμοποιήθηκαν σωστά 0 φορές", + "numLemmasSmallXP": "Αριθμός λημμάτων με 0 - 30 XP", + "numLemmasMediumXP": "Αριθμός λημμάτων με 31 - 200 XP", + "numLemmasLargeXP": "Αριθμός λημμάτων με > 200 XP", + "numGrammarConcepts": "Αριθμός γραμματικών εννοιών", + "listGrammarConcepts": "Γραμματικές έννοιες", + "listGrammarConceptsUsedCorrectly": "Γραμματικές έννοιες που χρησιμοποιήθηκαν σωστά σε αρχικά μηνύματα τουλάχιστον το 80% του χρόνου", + "listGrammarConceptsUsedIncorrectly": "Γραμματικές έννοιες που χρησιμοποιήθηκαν σωστά σε αρχικά μηνύματα λιγότερο από το 80% του χρόνου", + "listGrammarConceptsUseCorrectlySystemGenerated": "Γραμματικές έννοιες που επιλέχθηκαν σωστά από προτάσεις συστήματος τουλάχιστον το 80% του χρόνου", + "listGrammarConceptsUseIncorrectlySystemGenerated": "Γραμματικές έννοιες που επιλέχθηκαν σωστά από προτάσεις συστήματος λιγότερο από το 80% του χρόνου", + "listGrammarConceptsSmallXP": "Γλωσσικά concepts με 0-50 XP", + "listGrammarConceptsMediumXP": "Γλωσσικά concepts με 51-200 XP", + "listGrammarConceptsLargeXP": "Γλωσσικά concepts 201-500 XP", + "listGrammarConceptsHugeXP": "Γλωσσικά concepts >500 XP", + "numMessagesSent": "Αριθμός αποστελλόμενων μηνυμάτων", + "numWordsTyped": "Αριθμός πληκτρολογημένων λέξεων στα αρχικά μηνύματα", + "numCorrectChoices": "Αριθμός σωστών λέξεων που επιλέχθηκαν από προτάσεις συστήματος", + "numIncorrectChoices": "Αριθμός λανθασμένων λέξεων που επιλέχθηκαν από προτάσεις συστήματος", + "commaSeparatedFile": "CSV", + "excelFile": "Excel", + "fileType": "Τύπος αρχείου", + "download": "Λήψη", + "analyticsNotAvailable": "Ανάλυση χρήστη δεν διατίθεται", + "downloading": "Λήψη σε εξέλιξη...", + "failedFetchUserAnalytics": "Αποτυχία λήψης ανάλυσης χρήστη", + "downloadComplete": "Η λήψη ολοκληρώθηκε!", + "whatIsTheMorphTag": "Τι είναι το {morphologicalFeature} του '{wordForm}'?", + "dataAvailable": "Διαθεσιμότητα δεδομένων", + "available": "Διαθέσιμο", + "pangeaBotIsFallible": "Ο Pangea Bot κάνει και λάθη!", + "whatIsMeaning": "Τι σημαίνει το '{lemma}'?", + "pickAnEmoji": "Ποιο είναι το αγαπημένο σου emoji για το '{lemma}'?", + "chooseLemmaMeaningInstructionsBody": "Ταιριάξτε τις σημασίες με τις λέξεις στο μήνυμα!", + "doubleClickToEdit": "Διπλό κλικ για επεξεργασία.", + "activityPlannerTitle": "Προγραμματιστής Δραστηριοτήτων", + "topicLabel": "Θέμα", + "topicPlaceholder": "Επιλέξτε ένα θέμα...", + "modeLabel": "Τύπος δραστηριότητας", + "modePlaceholder": "Επιλέξτε μια λειτουργία...", + "learningObjectiveLabel": "Στόχος μάθησης", + "learningObjectivePlaceholder": "Επιλέξτε στόχο μάθησης...", + "languageOfInstructionsLabel": "Γλώσσα οδηγιών δραστηριότητας", + "targetLanguageLabel": "Στόχος γλώσσα", + "cefrLevelLabel": "Επίπεδο CEFR", + "generateActivitiesButton": "Δημιουργία Δραστηριότητας", + "launchActivityButton": "Έναρξη Δραστηριότητας", + "image": "Εικόνα", + "video": "Βίντεο", + "nan": "Μη εφαρμόσιμο", + "activityPlannerOverviewInstructionsBody": "Επιλέξτε ένα θέμα, τρόπο, μαθησιακό στόχο και δημιουργήστε μια δραστηριότητα για τη συνομιλία!", + "activityTitle": "Τίτλος Δραστηριότητας", + "addVocabulary": "Προσθήκη λεξιλογίου", + "instructions": "Οδηγίες", + "numberOfLearners": "Αριθμός μαθητών", + "mustBeInteger": "Πρέπει να είναι ακέραιος π.χ. 1, 2, 3, ...", + "constructUsePvmDesc": "Παράγεται σε φωνητικό μήνυμα", + "leaveSpaceDescription": "Αφήνοντας το μάθημα, θα αφήσετε όλες τις συνομιλίες μέσα σε αυτό. Οι άλλοι χρήστες θα δουν ότι έχετε φύγει από το μάθημα.", + "constructUseCorMmDesc": "Ορθό μήνυμα σημασίας", + "constructUseIncMmDesc": "Λάθος μήνυμα σημασίας", + "constructUseIgnMmDesc": "Αγνοημένο μήνυμα σημασίας", + "clickForMeaningActivity": "Κάντε κλικ εδώ για μια Πρόκληση Νοήματος", + "meaning": "Νόημα", + "chatWith": "Ομάδα με {displayname}", + "clickOnEmailLink": "Παρακαλώ κάντε κλικ στον σύνδεσμο στο email και συνεχίστε.\n\nΕλέγξτε τον φάκελο spam αν το email δεν έχει φτάσει.", + "dontForgetPassword": "Μην ξεχάσετε τον κωδικό πρόσβασής σας!", + "enableAutocorrectToolName": "Ενεργοποίηση αυτόματης διόρθωσης συσκευής", + "enableAutocorrectDescription": "Αν η συσκευή σας υποστηρίζει τη γλώσσα που μαθαίνετε, μπορείτε να ενεργοποιήσετε την αυτόματη διόρθωση για να διορθώνει κοινά λάθη καθώς πληκτρολογείτε.", + "ttsDisbledTitle": "Η φωνητική ανάγνωση απενεργοποιήθηκε", + "ttsDisabledBody": "Μπορείτε να ενεργοποιήσετε τη φωνητική ανάγνωση στις ρυθμίσεις εκμάθησης", + "noSpaceDescriptionYet": "Δεν έχει δημιουργηθεί ακόμη περιγραφή μαθήματος.", + "tooLargeToSend": "Αυτό το μήνυμα είναι πολύ μεγάλο για αποστολή", + "exitWithoutSaving": "Είστε βέβαιοι ότι θέλετε να φύγετε χωρίς να αποθηκεύσετε;", + "enableAutocorrectPopupTitle": "Προσθέστε το πληκτρολόγιο της γλώσσας στόχου πηγαίνοντας στα:", + "enableAutocorrectPopupSteps": " • Ρυθμίσεις\n • Γενικά\n • Πληκτρολόγιο\n • Πληκτρολόγια\n • Προσθήκη νέου πληκτρολογίου", + "enableAutocorrectPopupDescription": "Μόλις επιλεγεί η γλώσσα, μπορείτε να κάνετε κλικ στο μικρό εικονίδιο του κόσμου στο κάτω αριστερό μέρος του πληκτρολογίου σας για να ενεργοποιήσετε το νέο εγκατεστημένο πληκτρολόγιο.", + "downloadGboardTitle": "Κατεβάστε το Gboard από το Google Play Store για να ενεργοποιήσετε την αυτόματη διόρθωση και άλλες λειτουργίες πληκτρολογίου:", + "downloadGboardSteps": " • Κατεβάστε το Gboard\n • Ανοίξτε την εφαρμογή\n • Γλώσσες\n • Προσθήκη πληκτρολογίου\n • Επιλογή γλώσσας\n • Επιλογή τύπου πληκτρολογίου\n • Τέλος", + "downloadGboardDescription": "Μόλις επιλεγεί η γλώσσα, μπορείτε να κάνετε κλικ στο μικρό εικονίδιο του κόσμου στο κάτω αριστερό μέρος του πληκτρολογίου σας για να ενεργοποιήσετε το νέο εγκατεστημένο πληκτρολόγιο.", + "enableAutocorrectWarning": "Προειδοποίηση! Απαιτείται η προσθήκη του πληκτρολογίου της γλώσσας στόχου", + "displayName": "Εμφανιζόμενο όνομα", + "leaveRoomDescription": "Πρόκειται να φύγετε από αυτήν τη συνομιλία. Οι άλλοι χρήστες θα δουν ότι έχετε φύγει.", + "confirmUserId": "Παρακαλούμε επιβεβαιώστε το όνομα χρήστη Pangea Chat για να διαγράψετε τον λογαριασμό σας.", + "startingToday": "Από σήμερα", + "oneWeekFreeTrial": "Μια εβδομάδα δωρεάν δοκιμής", + "paidSubscriptionStarts": "Ξεκινάει {startDate}", + "cancelInSubscriptionSettings": "• Ακύρωση οποιαδήποτε στιγμή στις ρυθμίσεις συνδρομής", + "cancelToAvoidCharges": "• Ακύρωση πριν από {trialEnds} για να αποφύγετε χρεώσεις", + "downloadGboard": "Κατεβάστε το Gboard", + "autocorrectNotAvailable": "Δυστυχώς, η πλατφόρμα σας δεν υποστηρίζεται αυτήν τη στιγμή για αυτήν τη λειτουργία. Μείνετε συντονισμένοι για περαιτέρω ανάπτυξη!", + "pleaseUpdateApp": "Παρακαλούμε ενημερώστε την εφαρμογή για να συνεχίσετε.", + "chooseEmojiInstructionsBody": "Ταιριάξτε emojis με τις λέξεις που καλύτερα αντιπροσωπεύουν. Μην ανησυχείτε! Δεν θα χάσετε πόντους αν διαφωνείτε. 😅", + "analyticsVocabListBody": "Αυτή είναι όλη η λεξιλογική σας λίστα! Καθώς κερδίζετε XP για κάθε λέξη, θα μετατρέπονται από σπορά σε πλήρη άνθιση. Κάντε κλικ σε οποιαδήποτε λέξη για περισσότερες λεπτομέρειες.", + "morphAnalyticsListBody": "Αυτές είναι όλες οι γραμματικές έννοιες στη γλώσσα που μαθαίνετε! Θα τις ξεκλειδώσετε καθώς τις συναντάτε κατά τη διάρκεια της συνομιλίας. Κάντε κλικ για λεπτομέρειες.", + "knockSpaceSuccess": "Έχετε ζητήσει να συμμετάσχετε σε αυτό το μάθημα! Ένας διαχειριστής θα απαντήσει στο αίτημά σας μόλις το λάβει 😀", + "chooseWordAudioInstructionsBody": "Ακούστε το πλήρες μήνυμα. Στη συνέχεια, ταιριάξτε τα ηχητικά με τις λέξεις.", + "chooseMorphsInstructionsBody": "Κάντε κλικ στα κομμάτια παζλ για γραμματικές ερωτήσεις!", + "pleaseEnterInt": "Παρακαλούμε εισάγετε έναν αριθμό", + "home": "Αρχική", + "join": "Συμμετοχή", + "readingAssistanceOverviewBody": "Κάντε κλικ στα κουμπιά παρακάτω για mini-games με αντιστοίχιση emoji, ήχων, σημασιών λέξεων και γραμματικών εννοιών. Ή κάντε κλικ σε οποιαδήποτε λέξη για λεπτομέρειες.", + "levelSummaryPopupTitle": "Περίληψη Επιπέδου {level}", + "resetInstructionTooltipsTitle": "Επαναφορά οδηγιών βοήθειας", + "resetInstructionTooltipsDesc": "Κάντε κλικ για να εμφανίσετε ξανά τις οδηγίες βοήθειας, όπως για έναν ολοκαίνουργιο χρήστη.", + "selectForGrammar": "Επιλέξτε ένα εικονίδιο γραμματικής για δραστηριότητες και λεπτομέρειες.", + "randomize": "Τυχαία επιλογή", + "clear": "Καθαρισμός", + "makeYourOwnActivity": "Δημιουργήστε τη δική σας δραστηριότητα", + "featuredActivities": "Προτεινόμενες", + "save": "Αποθήκευση", + "startChat": "Ξεκίνα μια συνομιλία", + "translationProblem": "Πρόβλημα μετάφρασης", + "askToJoin": "Ζήτησε να συμμετάσχεις", + "emptyChatWarningTitle": "Η συνομιλία είναι κενή", + "emptyChatWarningDesc": "Δεν έχεις προσκαλέσει κανέναν στη συνομιλία σου. Πήγαινε στις ρυθμίσεις συνομιλίας για να προσκαλέσεις τις επαφές σου ή το Bot. Μπορείς επίσης να το κάνεις αργότερα.", + "areYouLikeMe": "Είσαι σαν εμένα;", + "tryAgainLater": "Πολλές προσπάθειες. Παρακαλώ δοκίμασε ξανά σε 5 λεπτά.", + "enterSpaceCode": "Εισάγετε τον κωδικό μαθήματος", + "shareSpaceLink": "Μοιράσου τον σύνδεσμο", + "byUsingPangeaChat": "Χρησιμοποιώντας το Pangea Chat, συμφωνώ με το ", + "details": "Λεπτομέρειες", + "languageLevelPreA1Desc": "Ποτέ δεν έμαθα ή χρησιμοποίησα τη γλώσσα.", + "languageLevelA1Desc": "Μπορώ να καταλάβω και να χρησιμοποιήσω μερικές οικείες καθημερινές εκφράσεις και πολύ βασικές φράσεις.", + "languageLevelA2Desc": "Μπορώ να καταλάβω προτάσεις και συχνά χρησιμοποιούμενες εκφράσεις που σχετίζονται με περιοχές άμεσης σημασίας.", + "languageLevelB1Desc": "Μπορώ να αντιμετωπίσω τις περισσότερες οικείες καταστάσεις και μπορώ να παράγω απλό συνδεδεμένο κείμενο σε οικεία θέματα.", + "languageLevelB2Desc": "Μπορώ να καταλάβω τις κύριες ιδέες σύνθετων κειμένων και να αλληλεπιδράσω με βαθμό ευφράδειας και αυθορμητισμού.", + "languageLevelC1Desc": "Μπορώ να εκφράσω ιδέες με ευφράδεια και αυθορμητισμό χωρίς μεγάλη δυσκολία και να καταλάβω ένα ευρύ φάσμα απαιτητικών κειμένων.", + "languageLevelC2Desc": "Μπορώ να καταλάβω σχεδόν τα πάντα που ακούω ή διαβάζω και να εκφράζομαι με ευφράδεια και ακρίβεια.", + "newVocab": "Νέες λέξεις", + "newGrammar": "Νέες γραμματικές έννοιες", + "choosePracticeMode": "Κάνε κλικ σε μία από τις παραπάνω επιλογές για να ξεκινήσεις μια δραστηριότητα εξάσκησης", + "ban": "Απαγόρευση", + "unban": "Άρση απαγόρευσης", + "kick": "Απομάκρυνση", + "lemma": "Λήμμα", + "grammarFeature": "Χαρακτηριστικό γραμματικής", + "grammarTag": "Ετικέτα γραμματικής", + "forms": "Μορφές", + "exampleMessages": "Παραδείγματα μηνυμάτων", + "timesUsedIndependently": "Χρησιμοποιήθηκε αυτόνομα", + "timesUsedWithAssistance": "Χρησιμοποιήθηκε με βοήθεια", + "shareInviteCode": "Μοιραστείτε τον κωδικό πρόσκλησης: {code}", + "leaderboard": "Κατάταξη", + "skipForNow": "Παραλείψτε προς το παρόν", + "permissions": "Άδειες", + "spaceChildPermission": "Ποιος μπορεί να προσθέσει νέες συνομιλίες σε αυτό το μάθημα", + "addEnvironmentOverride": "Προσθήκη παραμέτρου περιβάλλοντος", + "defaultOption": "Προεπιλογή", + "deleteChatDesc": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτήν τη συνομιλία; Θα διαγραφεί για όλους τους συμμετέχοντες και όλα τα μηνύματα μέσα στη συνομιλία δεν θα είναι πλέον διαθέσιμα για πρακτική ή αναλυτικά δεδομένα μάθησης.", + "deleteSpaceDesc": "Το μάθημα και οποιεσδήποτε επιλεγμένες συνομιλίες θα διαγραφούν για όλους τους συμμετέχοντες και όλα τα μηνύματα μέσα στη συνομιλία δεν θα είναι πλέον διαθέσιμα για πρακτική ή αναλυτικά δεδομένα μάθησης. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", + "launch": "Έναρξη", + "searchChats": "Αναζήτηση συνομιλιών", + "maxFifty": "Μέγιστο 50", + "configureSpace": "Διαμόρφωση μαθήματος", + "pinMessages": "Καρφιτσώστε μηνύματα", + "setJoinRules": "Ορίστε κανόνες συμμετοχής", + "changeGeneralSettings": "Αλλάξτε τις γενικές ρυθμίσεις", + "inviteOtherUsersToRoom": "Προσκαλέστε άλλους χρήστες", + "changeTheNameOfTheSpace": "Αλλάξτε το όνομα του μαθήματος", + "changeTheDescription": "Αλλάξτε την περιγραφή", + "changeThePermissions": "Αλλάξτε τα δικαιώματα", + "introductions": "Εισαγωγές", + "announcements": "Ανακοινώσεις", + "activities": "Δραστηριότητες", + "access": "Πρόσβαση", + "activitySuggestionTimeoutMessage": "Δουλεύουμε σκληρά για να δημιουργήσουμε περισσότερες δραστηριότητες για εσάς, παρακαλούμε ελέγξτε ξανά σε ένα λεπτό", + "howSpaceCanBeFound": "Πώς μπορεί να βρεθεί αυτό το μάθημα", + "private": "Ιδιωτικό", + "cannotBeFoundInSearch": "Δεν μπορεί να βρεθεί στην αναζήτηση", + "public": "Δημόσιο", + "visibleToCommunity": "Ορατό στην ευρύτερη κοινότητα Pangea Chat μέσω \"Βρες ένα μάθημα\"", + "howSpaceCanBeJoined": "Πώς μπορεί να ενταχθεί αυτό το μάθημα", + "canBeFoundVia": "Μπορεί να βρεθεί μέσω:", + "canBeFoundViaInvitation": "• πρόσκληση", + "canBeFoundViaCodeOrLink": "• κωδικός ή σύνδεσμος", + "canBeFoundViaKnock": "• αίτημα συμμετοχής και έγκριση διαχειριστή", + "youHaveLeveledUp": "Έχετε ανέβει επίπεδο!", + "sendActivities": "Αποστολή δραστηριοτήτων", + "groupChat": "Ομαδική Συνομιλία", + "directMessage": "Άμεσο μήνυμα", + "newDirectMessage": "Νέο άμεσο μήνυμα", + "speakingExercisesTooltip": "Ομιλία", + "noChatsFoundHereYet": "Δεν βρέθηκαν συνομιλίες εδώ ακόμα", + "duration": "Διάρκεια", + "transcriptionFailed": "Αποτυχία μεταγραφής ήχου", + "aUserIsKnocking": "Ένας χρήστης ζητά να συμμετάσχει στο μάθημά σας", + "usersAreKnocking": "{users} χρήστες ζητούν να συμμετάσχουν στο μάθημά σας", + "failedToFetchTranscription": "Αποτυχία λήψης μεταγραφής", + "deleteEmptySpaceDesc": "Το μάθημα θα διαγραφεί για όλους τους συμμετέχοντες. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", + "regenerate": "Αναδημιουργία", + "mySavedActivities": "Αποθηκευμένες Δραστηριότητες μου", + "noSavedActivities": "Δεν υπάρχουν αποθηκευμένες δραστηριότητες", + "saveActivity": "Αποθήκευση αυτής της δραστηριότητας", + "failedToPlayVideo": "Αποτυχία αναπαραγωγής βίντεο", + "done": "Ολοκληρώθηκε", + "inThisSpace": "Σε αυτό το μάθημα", + "myContacts": "Οι επαφές μου", + "inviteAllInSpace": "Πρόσκληση όλων στο μάθημα", + "spaceParticipantsHaveBeenInvitedToTheChat": "Όλοι οι συμμετέχοντες στο μάθημα έχουν προσκληθεί στη συνομιλία", + "numKnocking": "{count} χτυπήματα", + "numInvited": "{count} προσκεκλημένοι", + "saved": "Αποθηκευμένο", + "reset": "Επαναφορά", + "errorGenerateActivityMessage": "Αποτυχία δημιουργίας δραστηριότητας", + "errorRegenerateActivityMessage": "Αποτυχία αναδημιουργίας δραστηριότητας", + "errorLaunchActivityMessage": "Αποτυχία εκκίνησης δραστηριότητας", + "errorFetchingActivitiesMessage": "Αποτυχία λήψης δραστηριοτήτων", + "errorFetchingDefinition": "Αποτυχία λήψης ορισμού", + "errorProcessAnalytics": "Αποτυχία επεξεργασίας αναλυτικών στοιχείων", + "errorDownloading": "Η λήψη απέτυχε", + "errorFetchingLevelSummary": "Αποτυχία λήψης περίληψης επιπέδου", + "errorLoadingSpaceChildren": "Αποτυχία φόρτωσης συνομιλιών εντός αυτού του μαθήματος", + "unexpectedError": "Απρόβλεπτο σφάλμα.", + "pleaseReload": "Παρακαλώ επαναφορτώστε και δοκιμάστε ξανά.", + "translationError": "Σφάλμα μετάφρασης", + "errorFetchingTranslation": "Αποτυχία λήψης μετάφρασης", + "errorFetchingActivity": "Αποτυχία λήψης δραστηριότητας", + "check": "Έλεγχος", + "unableToFindRoom": "Δεν βρέθηκε συνομιλία ή μάθημα με αυτόν τον κωδικό. Παρακαλώ δοκιμάστε ξανά.", + "numCompletedActivities": "Αριθμός ολοκληρωμένων δραστηριοτήτων", + "viewingAnalytics": "Προβολή {visible}/{users} Αναλύσεων", + "request": "Αίτημα", + "requestAll": "Ζήτηση Όλα", + "confirmMessageUnpin": "Είστε σίγουροι ότι θέλετε να αποσυνδέσετε αυτό το μήνυμα;", + "createActivityPlan": "Δημιουργία νέου σχεδίου δραστηριότητας", + "saveAndLaunch": "Αποθήκευση και Εκκίνηση", + "launchToSpace": "Εκκίνηση στην πορεία", + "numberOfActivities": "Αριθμός συνεδριών δραστηριότητας", + "maximumActivityParticipants": "Κάθε δραστηριότητα μπορεί να έχει το μέγιστο {count} συμμετέχοντα(ους).", + "pending": "Εκκρεμεί", + "inactive": "Ανενεργό", + "confirmRole": "Επιβεβαίωση ρόλου", + "openRoleLabel": "ΑΝΟΙΧΤΟ", + "joinedTheActivity": "👋 {username} συμμετείχε ως {role}", + "finishedTheActivity": "🎯 {username} ολοκλήρωσε αυτήν τη δραστηριότητα", + "archiveToAnalytics": "Προσθήκη στις Ολοκληρωμένες Δραστηριότητες μου", + "activitySummaryError": "Οι περιλήψεις δραστηριοτήτων δεν είναι διαθέσιμες", + "requestSummaries": "Αιτήματα περιλήψεων", + "generatingNewActivities": "Είστε ο πρώτος χρήστης αυτής της ζεύξης γλωσσών! Παρακαλούμε δώστε μας ένα λεπτό, ετοιμάζουμε δραστηριότητες αποκλειστικά για εσάς.", + "requestAccessTitle": "Ζητήστε πρόσβαση σε αναλύσεις;", + "requestAccessDesc": "Θα θέλατε να ζητήσετε πρόσβαση για να δείτε τα αναλυτικά στοιχεία των συμμετεχόντων;\n\nΑν οι συμμετέχοντες συμφωνήσουν, οι διαχειριστές αυτού του μαθήματος θα μπορούν να δουν:\n • συνολικό λεξιλόγιο\n • συνολικές γραμματικές έννοιες\n • συνολικές συνεδρίες δραστηριότητας που ολοκληρώθηκαν\n • τις συγκεκριμένες γραμματικές έννοιες που χρησιμοποιήθηκαν, σωστά και λανθασμένα\n\nΔεν θα μπορούν να δουν:\n • μηνύματα σε συνομιλίες εκτός του μαθήματος\n • λίστα λεξιλογίου", + "requestAccess": "Αίτηση πρόσβασης ({count})", + "analyticsInactiveTitle": "Οι αιτήσεις σε ανενεργούς χρήστες δεν μπορούν να σταλούν", + "analyticsInactiveDesc": "Οι ανενεργοί χρήστες που δεν έχουν συνδεθεί από τότε που εισήχθη αυτή η λειτουργία δεν θα δουν το αίτημά σας.\n\nΤο κουμπί Αίτημα θα εμφανιστεί μόλις επιστρέψουν. Μπορείτε να ξαναστείλετε το αίτημα αργότερα κάνοντας κλικ στο κουμπί Αίτημα κάτω από το όνομά τους όταν είναι διαθέσιμο.", + "accessRequestedTitle": "Αίτημα πρόσβασης στα αναλυτικά στοιχεία", + "accessRequestedDesc": "Ζητώντας admin(s): {admin} \n\nΟι διαχειριστές από το «{space}» ζητούν να δουν τις αναλύσεις μάθησής σας.\n\nΑν συμφωνείτε, θα μπορούν να δουν:\n • το συνολικό λεξιλόγιο\n • τις συνολικές έννοιες γραμματικής\n • τις συνολικές συνεδρίες δραστηριότητας που ολοκληρώθηκαν\n • τις συγκεκριμένες έννοιες γραμματικής που χρησιμοποιήθηκαν, σωστά και λανθασμένα\n\nΔεν θα μπορούν να δουν:\n • μηνύματα σε συνομιλίες εκτός του μαθήματος\n • τη λίστα λεξιλογίου", + "adminRequestedAccess": "Οι διαχειριστές ζήτησαν να δουν τα αναλυτικά σας στοιχεία.", + "lastUpdated": "Ενημερώθηκε\n{time}", + "activityFinishedMessage": "Όλα Ολοκληρώθηκαν!", + "endForAll": "Τέλος για όλους", + "newCourse": "Νέο μάθημα", + "numModules": "{num} ενότητες", + "coursePlan": "Πλάνο Μαθήματος", + "editCourseLater": "Μπορείτε να επεξεργαστείτε τον τίτλο, τις περιγραφές και την εικόνα του μαθήματος αργότερα.", + "createCourse": "Δημιουργία μαθήματος", + "stats": "Στατιστικά", + "createGroupChat": "Δημιουργία ομαδικής συνομιλίας", + "editCourse": "Επεξεργασία μαθήματος", + "inviteDesc": "Με όνομα χρήστη, με κωδικό ή σύνδεσμο", + "editCourseDesc": "Εδώ μπορείτε να επεξεργαστείτε τον τίτλο, την περιγραφή και άλλα στοιχεία του μαθήματος.", + "permissionsDesc": "Ορίστε δικαιώματα, όπως ποιος μπορεί να προσκαλεί χρήστες, να στέλνει μηνύματα, να δημιουργεί συνομιλίες κ.λπ.", + "accessDesc": "Μπορείτε να κάνετε το μάθημά σας ανοιχτό στον κόσμο! Ή, να το κρατήσετε ιδιωτικό και ασφαλές.", + "createGroupChatDesc": "Ενώ οι συνεδρίες δραστηριοτήτων ξεκινούν και τελειώνουν, οι ομαδικές συνομιλίες θα παραμένουν ανοιχτές για τακτική επικοινωνία.", + "deleteDesc": "Μόνο διαχειριστές μπορούν να διαγράψουν ένα μάθημα. Αυτή είναι μια καταστροφική ενέργεια που αφαιρεί όλους τους χρήστες και διαγράφει όλες τις επιλεγμένες συνομιλίες εντός του μαθήματος. Προχωρήστε με προσοχή.", + "noCourseFound": "Ωχ, αυτό το μάθημα χρειάζεται ένα πλάνο!\n\nΤα πλάνα μαθημάτων είναι μια σειρά θεμάτων και δραστηριοτήτων συζήτησης.", + "additionalParticipants": "+ {num} άλλοι", + "directMessages": "Άμεσες Μηνύσεις", + "whatNow": "Τι τώρα;", + "chooseNextActivity": "Επιλέξτε την επόμενη δραστηριότητά σας!", + "letsGo": "Πάμε", + "chooseRole": "Επιλέξτε ρόλο!", + "chooseRoleToParticipate": "Επιλέξτε ρόλο για συμμετοχή!", + "waitingToFillRole": "Αναμονή για συμπλήρωση {num} ρόλων...", + "pingParticipants": "Ειδοποιήστε τους συμμετέχοντες του μαθήματος", + "playWithBot": "Παίξτε με το Pangea Bot", + "inviteFriends": "Προσκαλέστε φίλους", + "waitNotDone": "Περίμενε, δεν έχω τελειώσει!", + "waitingForOthersToFinish": "Αναμονή για τους υπόλοιπους να τελειώσουν...", + "generatingSummary": "Ανάλυση συνομιλίας και δημιουργία αποτελεσμάτων", + "findCourse": "Βρείτε ένα μάθημα", + "pingParticipantsNotification": "{user} ψάχνει χρήστες να συμμετάσχουν στη συνεδρία δραστηριότητας στο {room}", + "course": "Μάθημα", + "courses": "Μαθήματα", + "courseName": "Όνομα μαθήματος", + "createNewCourse": "Νέο μάθημα", + "goToCourse": "Πήγαινε στο μάθημα: {course}", + "activityComplete": "Αυτή η δραστηριότητα έχει ολοκληρωθεί. Η περίληψη της δραστηριότητας θα πρέπει να είναι διαθέσιμη παρακάτω.", + "startNewSession": "Ξεκινήστε νέα συνεδρία", + "joinOpenSession": "Συμμετοχή σε ανοιχτή συνεδρία", + "less": "λιγότερο", + "activityNotFound": "Δεν βρέθηκε δραστηριότητα", + "levelUp": "Επίπεδο πάνω", + "myActivities": "Οι δραστηριότητές μου", + "openToJoin": "Ανοιχτό για συμμετοχή", + "results": "Αποτελέσματα", + "activityDone": "Ολοκληρώθηκε η δραστηριότητα!", + "promoCodeInfo": "Οι κωδικοί προώθησης μπορούν να εισαχθούν στη σελίδα που ακολουθεί", + "editsComingSoon": "Η δυνατότητα επεξεργασίας πόλεων και δραστηριοτήτων έρχεται σύντομα.", + "editing": "Επεξεργασία", + "activityNeedsOneMember": "Ωχ! Αυτή η δραστηριότητα χρειάζεται 1 ακόμη άτομο.", + "activityNeedsMembers": "Ωχ! Αυτή η δραστηριότητα χρειάζεται {num} ακόμη άτομα.", + "inviteFriendsToCourse": "Πρόσκληση φίλων στο μάθημά μου", + "subscribeToUnlockActivitySummaries": "Εγγραφή για ξεκλείδωμα περιλήψεων δραστηριοτήτων", + "subscribeToUnlockDefinitions": "Εγγραφή για ξεκλείδωμα ορισμών", + "subscribeToUnlockTranscriptions": "Εγγραφή για ξεκλείδωμα μεταγραφών", + "pingSent": "🔔 Η ειδοποίηση μαθήματος εστάλη! 🔔", + "courseTitle": "Τίτλος μαθήματος", + "courseDesc": "Περιγραφή μαθήματος", + "courseSavedSuccessfully": "Το μάθημα αποθηκεύτηκε με επιτυχία", + "addCoursePlan": "Προσθήκη σχεδίου μαθήματος", + "activityStatsButtonInstruction": "Κάντε κλικ εδώ για να δείτε τα στατιστικά της δραστηριότητάς σας και να κλείσετε τη δραστηριότητα όταν τελειώσετε", + "loginToAccount": "Συνδεθείτε στον λογαριασμό μου", + "appDescription": "Μάθε μια γλώσσα\nενώ στέλνεις μηνύματα στους φίλους σου.", + "languages": "Γλώσσες", + "chooseLanguage": "Επιλέξτε μια γλώσσα στόχου.", + "planTrip": "Προγραμματίστε το ταξίδι σας", + "howAreYouTraveling": "Πώς ταξιδεύετε;", + "unlockPrivateTrip": "Ξεκλειδώστε ένα ιδιωτικό ταξίδι", + "joinPublicTrip": "Συμμετάσχετε σε ένα δημόσιο ταξίδι", + "startOwnTrip": "Ξεκινήστε το δικό μου", + "tripPlanDesc": "Τα ταξίδια είναι μαθήματα. Κάθε ένα έχει 8-10 διαδοχικά θέματα με μια σειρά δραστηριοτήτων μάθησης γλώσσας με βάση τις εργασίες.", + "unlockPrivateTripTitle": "Ξεκλειδώστε ιδιωτικό ταξίδι", + "browsePublicTrips": "Περιηγηθείτε σε δημόσια ταξίδια", + "startOwnTripTitle": "Ξεκινήστε το δικό μου ταξίδι", + "courseCode": "Ποιος είναι ο μυστικός κωδικός;", + "courseCodeHint": "Κωδικός ή σύνδεσμος ταξιδιού", + "unlockMyTrip": "Ξεκλειδώστε το ταξίδι μου", + "signupOption": "Πώς θέλετε να εγγραφείτε;", + "withApple": "Με την Apple", + "withGoogle": "Με Google", + "withEmail": "Με Email", + "createAccount": "Δημιουργία λογαριασμού", + "loginWithEmail": "Σύνδεση με email", + "usernameOrEmail": "Όνομα χρήστη ή email", + "email": "Email", + "forgotPassword": "Ξεχάσατε τον κωδικό;", + "endActivity": "Τέλος δραστηριότητας", + "allLanguages": "Όλες οι γλώσσες", + "chatListTooltip": "Εδώ θα βρείτε τα άμεσα μηνύματά σας! Κάντε κλικ σε οποιονδήποτε avatar χρήστη και \"ξεκινήστε συνομιλία\" για να στείλετε ένα DM.", + "directMessageBotTitle": "Άμεσο μήνυμα Pangea Bot", + "feedbackTitle": "Ανατροφοδότηση δραστηριότητας", + "feedbackHint": "Η ανατροφοδότησή σας", + "feedbackButton": "Υποβολή ανατροφοδότησης", + "directMessageBotDesc": "Το να μιλάς με ανθρώπους είναι πιο διασκεδαστικό, αλλά... η ΤΝ είναι πάντα έτοιμη!", + "inviteYourFriends": "Προσκαλέστε τους φίλους σας", + "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", + "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", + "@@locale": "el", + "@@last_modified": "2026-02-05 10:10:14.390437", + "@checkList": { + "type": "String", + "placeholders": {} + }, + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@globalChatId": { + "type": "String", + "placeholders": {} + }, + "@accessAndVisibility": { + "type": "String", + "placeholders": {} + }, + "@accessAndVisibilityDescription": { + "type": "String", + "placeholders": {} + }, + "@calls": { + "type": "String", + "placeholders": {} + }, + "@customEmojisAndStickers": { + "type": "String", + "placeholders": {} + }, + "@customEmojisAndStickersBody": { + "type": "String", + "placeholders": {} + }, + "@hideRedactedMessages": { + "type": "String", + "placeholders": {} + }, + "@hideRedactedMessagesBody": { + "type": "String", + "placeholders": {} + }, + "@hideInvalidOrUnknownMessageFormats": { + "type": "String", + "placeholders": {} + }, + "@block": { + "type": "String", + "placeholders": {} + }, + "@blockedUsers": { + "type": "String", + "placeholders": {} + }, + "@blockListDescription": { + "type": "String", + "placeholders": {} + }, + "@blockUsername": { + "type": "String", + "placeholders": {} + }, + "@hideMemberChangesInPublicChats": { + "type": "String", + "placeholders": {} + }, + "@hideMemberChangesInPublicChatsBody": { + "type": "String", + "placeholders": {} + }, + "@overview": { + "type": "String", + "placeholders": {} + }, + "@notifyMeFor": { + "type": "String", + "placeholders": {} + }, + "@passwordRecoverySettings": { + "type": "String", + "placeholders": {} + }, + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@presenceStyle": { + "type": "String", + "placeholders": {} + }, + "@presencesToggle": { + "type": "String", + "placeholders": {} + }, + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "@youInvitedToBy": { + "type": "String", + "placeholders": { + "alias": { + "type": "String" + } + } + }, + "@invitedBy": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } + }, + "@usersMustKnock": { + "type": "String", + "placeholders": {} + }, + "@noOneCanJoin": { + "type": "String", + "placeholders": {} + }, + "@userWouldLikeToChangeTheChat": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } + }, + "@noPublicLinkHasBeenCreatedYet": { + "type": "String", + "placeholders": {} + }, + "@knock": { + "type": "String", + "placeholders": {} + }, + "@hidePresences": { + "type": "String", + "placeholders": {} + }, + "@yourGlobalUserIdIs": { + "type": "String", + "placeholders": {} + }, + "@noUsersFoundWithQuery": { + "type": "String", + "placeholders": { + "query": { + "type": "String" + } + } + }, + "@knocking": { + "type": "String", + "placeholders": {} + }, + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "@searchChatsRooms": { + "type": "String", + "placeholders": {} + }, + "@nothingFound": { + "type": "String", + "placeholders": {} + }, + "@groupName": { + "type": "String", + "placeholders": {} + }, + "@createGroupAndInviteUsers": { + "type": "String", + "placeholders": {} + }, + "@groupCanBeFoundViaSearch": { + "type": "String", + "placeholders": {} + }, + "@wrongRecoveryKey": { + "type": "String", + "placeholders": {} + }, + "@startConversation": { + "type": "String", + "placeholders": {} + }, + "@commandHint_sendraw": { + "type": "String", + "placeholders": {} + }, + "@databaseMigrationTitle": { + "type": "String", + "placeholders": {} + }, + "@databaseMigrationBody": { + "type": "String", + "placeholders": {} + }, + "@leaveEmptyToClearStatus": { + "type": "String", + "placeholders": {} + }, + "@select": { + "type": "String", + "placeholders": {} + }, + "@searchForUsers": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterYourCurrentPassword": { + "type": "String", + "placeholders": {} + }, + "@newPassword": { + "type": "String", + "placeholders": {} + }, + "@pleaseChooseAStrongPassword": { + "type": "String", + "placeholders": {} + }, + "@passwordsDoNotMatch": { + "type": "String", + "placeholders": {} + }, + "@passwordIsWrong": { + "type": "String", + "placeholders": {} + }, + "@publicLink": { + "type": "String", + "placeholders": {} + }, + "@publicChatAddresses": { + "type": "String", + "placeholders": {} + }, + "@createNewAddress": { + "type": "String", + "placeholders": {} + }, + "@joinSpace": { + "type": "String", + "placeholders": {} + }, + "@publicSpaces": { + "type": "String", + "placeholders": {} + }, + "@addChatOrSubSpace": { + "type": "String", + "placeholders": {} + }, + "@subspace": { + "type": "String", + "placeholders": {} + }, + "@decline": { + "type": "String", + "placeholders": {} + }, + "@thisDevice": { + "type": "String", + "placeholders": {} + }, + "@initAppError": { + "type": "String", + "placeholders": {} + }, + "@userRole": { + "type": "String", + "placeholders": {} + }, + "@minimumPowerLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "String" + } + } + }, + "@searchIn": { + "type": "String", + "placeholders": { + "chat": { + "type": "String" + } + } + }, + "@searchMore": { + "type": "String", + "placeholders": {} + }, + "@gallery": { + "type": "String", + "placeholders": {} + }, + "@files": { + "type": "String", + "placeholders": {} + }, + "@databaseBuildErrorBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "@sessionLostBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "@restoreSessionBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "@forwardMessageTo": { + "type": "String", + "placeholders": { + "roomName": { + "type": "String" + } + } + }, + "@sendReadReceipts": { + "type": "String", + "placeholders": {} + }, + "@sendTypingNotificationsDescription": { + "type": "String", + "placeholders": {} + }, + "@sendReadReceiptsDescription": { + "type": "String", + "placeholders": {} + }, + "@formattedMessages": { + "type": "String", + "placeholders": {} + }, + "@formattedMessagesDescription": { + "type": "String", + "placeholders": {} + }, + "@verifyOtherUser": { + "type": "String", + "placeholders": {} + }, + "@verifyOtherUserDescription": { + "type": "String", + "placeholders": {} + }, + "@verifyOtherDevice": { + "type": "String", + "placeholders": {} + }, + "@verifyOtherDeviceDescription": { + "type": "String", + "placeholders": {} + }, + "@acceptedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "@canceledKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "@completedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "@isReadyForKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "@requestedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "@startedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "@transparent": { + "type": "String", + "placeholders": {} + }, + "@incomingMessages": { + "type": "String", + "placeholders": {} + }, + "@stickers": { + "type": "String", + "placeholders": {} + }, + "@discover": { + "type": "String", + "placeholders": {} + }, + "@commandHint_ignore": { + "type": "String", + "placeholders": {} + }, + "@commandHint_unignore": { + "type": "String", + "placeholders": {} + }, + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "@noDatabaseEncryption": { + "type": "String", + "placeholders": {} + }, + "@thereAreCountUsersBlocked": { + "type": "String", "count": {} - } - }, - "@restricted": { - "type": "String", - "placeholders": {} - }, - "@knockRestricted": { - "type": "String", - "placeholders": {} - }, - "@goToSpace": { - "type": "String", - "placeholders": { - "space": {} - } - }, - "@markAsUnread": { - "type": "String", - "placeholders": {} - }, - "@userLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" + }, + "@restricted": { + "type": "String", + "placeholders": {} + }, + "@knockRestricted": { + "type": "String", + "placeholders": {} + }, + "@goToSpace": { + "type": "String", + "placeholders": { + "space": {} } - } - }, - "@moderatorLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" + }, + "@markAsUnread": { + "type": "String", + "placeholders": {} + }, + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } } - } - }, - "@adminLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" + }, + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } } - } - }, - "@changeGeneralChatSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsers": { - "type": "String", - "placeholders": {} - }, - "@changeTheChatPermissions": { - "type": "String", - "placeholders": {} - }, - "@changeTheVisibilityOfChatHistory": { - "type": "String", - "placeholders": {} - }, - "@changeTheCanonicalRoomAlias": { - "type": "String", - "placeholders": {} - }, - "@sendRoomNotifications": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescriptionOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "@chatPermissionsDescription": { - "type": "String", - "placeholders": {} - }, - "@updateInstalled": { - "type": "String", - "placeholders": { - "version": { - "type": "String" + }, + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } } - } - }, - "@changelog": { - "type": "String", - "placeholders": {} - }, - "@sendCanceled": { - "type": "String", - "placeholders": {} - }, - "@loginWithMatrixId": { - "type": "String", - "placeholders": {} - }, - "@discoverHomeservers": { - "type": "String", - "placeholders": {} - }, - "@whatIsAHomeserver": { - "type": "String", - "placeholders": {} - }, - "@homeserverDescription": { - "type": "String", - "placeholders": {} - }, - "@doesNotSeemToBeAValidHomeserver": { - "type": "String", - "placeholders": {} - }, - "@calculatingFileSize": { - "type": "String", - "placeholders": {} - }, - "@prepareSendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@generatingVideoThumbnail": { - "type": "String", - "placeholders": {} - }, - "@compressVideo": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachmentCountOfCount": { - "type": "integer", - "placeholders": { - "index": { - "type": "int" - }, - "length": { - "type": "int" + }, + "@changeGeneralChatSettings": { + "type": "String", + "placeholders": {} + }, + "@inviteOtherUsers": { + "type": "String", + "placeholders": {} + }, + "@changeTheChatPermissions": { + "type": "String", + "placeholders": {} + }, + "@changeTheVisibilityOfChatHistory": { + "type": "String", + "placeholders": {} + }, + "@changeTheCanonicalRoomAlias": { + "type": "String", + "placeholders": {} + }, + "@sendRoomNotifications": { + "type": "String", + "placeholders": {} + }, + "@changeTheDescriptionOfTheGroup": { + "type": "String", + "placeholders": {} + }, + "@chatPermissionsDescription": { + "type": "String", + "placeholders": {} + }, + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } } - } - }, - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" + }, + "@changelog": { + "type": "String", + "placeholders": {} + }, + "@sendCanceled": { + "type": "String", + "placeholders": {} + }, + "@loginWithMatrixId": { + "type": "String", + "placeholders": {} + }, + "@discoverHomeservers": { + "type": "String", + "placeholders": {} + }, + "@whatIsAHomeserver": { + "type": "String", + "placeholders": {} + }, + "@homeserverDescription": { + "type": "String", + "placeholders": {} + }, + "@doesNotSeemToBeAValidHomeserver": { + "type": "String", + "placeholders": {} + }, + "@calculatingFileSize": { + "type": "String", + "placeholders": {} + }, + "@prepareSendingAttachment": { + "type": "String", + "placeholders": {} + }, + "@sendingAttachment": { + "type": "String", + "placeholders": {} + }, + "@generatingVideoThumbnail": { + "type": "String", + "placeholders": {} + }, + "@compressVideo": { + "type": "String", + "placeholders": {} + }, + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } } - } - }, - "@oneOfYourDevicesIsNotVerified": { - "type": "String", - "placeholders": {} - }, - "@noticeChatBackupDeviceVerification": { - "type": "String", - "placeholders": {} - }, - "@continueText": { - "type": "String", - "placeholders": {} - }, - "@welcomeText": { - "type": "String", - "placeholders": {} - }, - "@blur": { - "type": "String", - "placeholders": {} - }, - "@opacity": { - "type": "String", - "placeholders": {} - }, - "@setWallpaper": { - "type": "String", - "placeholders": {} - }, - "@manageAccount": { - "type": "String", - "placeholders": {} - }, - "@noContactInformationProvided": { - "type": "String", - "placeholders": {} - }, - "@contactServerAdmin": { - "type": "String", - "placeholders": {} - }, - "@contactServerSecurity": { - "type": "String", - "placeholders": {} - }, - "@supportPage": { - "type": "String", - "placeholders": {} - }, - "@serverInformation": { - "type": "String", - "placeholders": {} - }, - "@name": { - "type": "String", - "placeholders": {} - }, - "@version": { - "type": "String", - "placeholders": {} - }, - "@website": { - "type": "String", - "placeholders": {} - }, - "@compress": { - "type": "String", - "placeholders": {} - }, - "@boldText": { - "type": "String", - "placeholders": {} - }, - "@italicText": { - "type": "String", - "placeholders": {} - }, - "@strikeThrough": { - "type": "String", - "placeholders": {} - }, - "@pleaseFillOut": { - "type": "String", - "placeholders": {} - }, - "@invalidUrl": { - "type": "String", - "placeholders": {} - }, - "@addLink": { - "type": "String", - "placeholders": {} - }, - "@unableToJoinChat": { - "type": "String", - "placeholders": {} - }, - "@previous": { - "type": "String", - "placeholders": {} - }, - "@otherPartyNotLoggedIn": { - "type": "String", - "placeholders": {} - }, - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" + }, + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } } - } - }, - "@appWantsToUseForLoginDescription": { - "type": "String", - "placeholders": {} - }, - "@open": { - "type": "String", - "placeholders": {} - }, - "@waitingForServer": { - "type": "String", - "placeholders": {} - }, - "@appIntroduction": { - "type": "String", - "placeholders": {} - }, - "@newChatRequest": { - "type": "String", - "placeholders": {} - }, - "@contentNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@generalNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@roomNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@userSpecificNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@otherNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMaster": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMasterDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNotices": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNoticesDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMe": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEvent": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEventDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReaction": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReactionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEditsDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCall": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCallDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessage": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncrypted": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsi": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" + }, + "@oneOfYourDevicesIsNotVerified": { + "type": "String", + "placeholders": {} + }, + "@noticeChatBackupDeviceVerification": { + "type": "String", + "placeholders": {} + }, + "@continueText": { + "type": "String", + "placeholders": {} + }, + "@welcomeText": { + "type": "String", + "placeholders": {} + }, + "@blur": { + "type": "String", + "placeholders": {} + }, + "@opacity": { + "type": "String", + "placeholders": {} + }, + "@setWallpaper": { + "type": "String", + "placeholders": {} + }, + "@manageAccount": { + "type": "String", + "placeholders": {} + }, + "@noContactInformationProvided": { + "type": "String", + "placeholders": {} + }, + "@contactServerAdmin": { + "type": "String", + "placeholders": {} + }, + "@contactServerSecurity": { + "type": "String", + "placeholders": {} + }, + "@supportPage": { + "type": "String", + "placeholders": {} + }, + "@serverInformation": { + "type": "String", + "placeholders": {} + }, + "@name": { + "type": "String", + "placeholders": {} + }, + "@version": { + "type": "String", + "placeholders": {} + }, + "@website": { + "type": "String", + "placeholders": {} + }, + "@compress": { + "type": "String", + "placeholders": {} + }, + "@boldText": { + "type": "String", + "placeholders": {} + }, + "@italicText": { + "type": "String", + "placeholders": {} + }, + "@strikeThrough": { + "type": "String", + "placeholders": {} + }, + "@pleaseFillOut": { + "type": "String", + "placeholders": {} + }, + "@invalidUrl": { + "type": "String", + "placeholders": {} + }, + "@addLink": { + "type": "String", + "placeholders": {} + }, + "@unableToJoinChat": { + "type": "String", + "placeholders": {} + }, + "@previous": { + "type": "String", + "placeholders": {} + }, + "@otherPartyNotLoggedIn": { + "type": "String", + "placeholders": {} + }, + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" + }, + "@appWantsToUseForLoginDescription": { + "type": "String", + "placeholders": {} + }, + "@open": { + "type": "String", + "placeholders": {} + }, + "@waitingForServer": { + "type": "String", + "placeholders": {} + }, + "@appIntroduction": { + "type": "String", + "placeholders": {} + }, + "@newChatRequest": { + "type": "String", + "placeholders": {} + }, + "@contentNotificationSettings": { + "type": "String", + "placeholders": {} + }, + "@generalNotificationSettings": { + "type": "String", + "placeholders": {} + }, + "@roomNotificationSettings": { + "type": "String", + "placeholders": {} + }, + "@userSpecificNotificationSettings": { + "type": "String", + "placeholders": {} + }, + "@otherNotificationSettings": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleContainsUserName": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleContainsUserNameDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleMaster": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleMasterDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleSuppressNotices": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleSuppressNoticesDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleInviteForMe": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleInviteForMeDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleMemberEvent": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleMemberEventDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleIsUserMention": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleIsUserMentionDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleContainsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleContainsDisplayNameDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleIsRoomMention": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleIsRoomMentionDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleRoomnotif": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleRoomnotifDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleTombstone": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleTombstoneDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleReaction": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleReactionDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleRoomServerAcl": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleRoomServerAclDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleSuppressEdits": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleSuppressEditsDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleCall": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleCallDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleEncryptedRoomOneToOne": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleEncryptedRoomOneToOneDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleRoomOneToOne": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleRoomOneToOneDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleMessage": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleMessageDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleEncrypted": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleEncryptedDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleJitsi": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleJitsiDescription": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleServerAcl": { + "type": "String", + "placeholders": {} + }, + "@notificationRuleServerAclDescription": { + "type": "String", + "placeholders": {} + }, + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } } - } - }, - "@deletePushRuleCanNotBeUndone": { - "type": "String", - "placeholders": {} - }, - "@more": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWith": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@allDevices": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@takeAPhoto": { - "type": "String", - "placeholders": {} - }, - "@recordAVideo": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@enterNewChat": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, - "@youHaveKnocked": { - "type": "String", - "placeholders": {} - }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, - "@writeAMessageLangCodes": { - "type": "String", - "placeholders": { - "l1": { - "type": "String" - }, - "l2": { - "type": "String" + }, + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "duration": { + "type": "String" + }, + "sender": { + "type": "String" + } } - } - }, - "@requests": { - "type": "String", - "placeholders": {} - }, - "@holdForInfo": { - "type": "String", - "placeholders": {} - }, - "@greenFeedback": { - "type": "String", - "placeholders": {} - }, - "@yellowFeedback": { - "type": "String", - "placeholders": {} - }, - "@redFeedback": { - "type": "String", - "placeholders": {} - }, - "@itInstructionsTitle": { - "type": "String", - "placeholders": {} - }, - "@itInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@gaTooltip": { - "type": "String", - "placeholders": {} - }, - "@taTooltip": { - "type": "String", - "placeholders": {} - }, - "@unTooltip": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorSliderHeader": { - "type": "String", - "placeholders": {} - }, - "@interactiveGrammarSliderHeader": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorNotAllowed": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorAllowed": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslatorRequired": { - "type": "String", - "placeholders": {} - }, - "@notYetSet": { - "type": "String", - "placeholders": {} - }, - "@waTooltip": { - "type": "String", - "placeholders": {} - }, - "@languageSettings": { - "type": "String", - "placeholders": {} - }, - "@interactiveTranslator": { - "type": "String", - "placeholders": {} - }, - "@noIdenticalLanguages": { - "type": "String", - "placeholders": {} - }, - "@searchBy": { - "type": "String", - "placeholders": {} - }, - "@joinWithClassCode": { - "type": "String", - "placeholders": {} - }, - "@languageLevelPreA1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA2": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB2": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC1": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC2": { - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheClass": { - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheChat": { - "type": "String", - "placeholders": {} - }, - "@sorryNoResults": { - "type": "String", - "placeholders": {} - }, - "@ignoreInThisText": { - "type": "String", - "placeholders": {} - }, - "@needsItMessage": { - "type": "String", - "placeholders": { - "targetLanguage": { - "type": "String" + }, + "@deletePushRuleCanNotBeUndone": { + "type": "String", + "placeholders": {} + }, + "@more": { + "type": "String", + "placeholders": {} + }, + "@shareKeysWith": { + "type": "String", + "placeholders": {} + }, + "@shareKeysWithDescription": { + "type": "String", + "placeholders": {} + }, + "@allDevices": { + "type": "String", + "placeholders": {} + }, + "@crossVerifiedDevicesIfEnabled": { + "type": "String", + "placeholders": {} + }, + "@crossVerifiedDevices": { + "type": "String", + "placeholders": {} + }, + "@verifiedDevicesOnly": { + "type": "String", + "placeholders": {} + }, + "@takeAPhoto": { + "type": "String", + "placeholders": {} + }, + "@recordAVideo": { + "type": "String", + "placeholders": {} + }, + "@optionalMessage": { + "type": "String", + "placeholders": {} + }, + "@notSupportedOnThisDevice": { + "type": "String", + "placeholders": {} + }, + "@enterNewChat": { + "type": "String", + "placeholders": {} + }, + "@approve": { + "type": "String", + "placeholders": {} + }, + "@youHaveKnocked": { + "type": "String", + "placeholders": {} + }, + "@pleaseWaitUntilInvited": { + "type": "String", + "placeholders": {} + }, + "@commandHint_logout": { + "type": "String", + "placeholders": {} + }, + "@commandHint_logoutall": { + "type": "String", + "placeholders": {} + }, + "@displayNavigationRail": { + "type": "String", + "placeholders": {} + }, + "@customReaction": { + "type": "String", + "placeholders": {} + }, + "@writeAMessageLangCodes": { + "type": "String", + "placeholders": { + "l1": { + "type": "String" + }, + "l2": { + "type": "String" + } } - } - }, - "@countryInformation": { - "type": "String", - "placeholders": {} - }, - "@targetLanguage": { - "type": "String", - "placeholders": {} - }, - "@sourceLanguage": { - "type": "String", - "placeholders": {} - }, - "@updateLanguage": { - "type": "String", - "placeholders": {} - }, - "@whatLanguageYouWantToLearn": { - "type": "String", - "placeholders": {} - }, - "@whatIsYourBaseLanguage": { - "type": "String", - "placeholders": {} - }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, - "@publicProfileTitle": { - "type": "String", - "placeholders": {} - }, - "@publicProfileDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIT": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIGC": { - "type": "String", - "placeholders": {} - }, - "@errorDisableLanguageAssistance": { - "type": "String", - "placeholders": {} - }, - "@errorDisableITUserDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIGCUserDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableLanguageAssistanceUserDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableITClassDesc": { - "type": "String", - "placeholders": {} - }, - "@errorDisableIGCClassDesc": { - "type": "String", - "placeholders": {} - }, - "@error405Title": { - "type": "String", - "placeholders": {} - }, - "@error405Desc": { - "type": "String", - "placeholders": {} - }, - "@termsAndConditions": { - "type": "String", - "placeholders": {} - }, - "@andCertifyIAmAtLeast13YearsOfAge": { - "type": "String", - "placeholders": {} - }, - "@error502504Title": { - "type": "String", - "placeholders": {} - }, - "@error502504Desc": { - "type": "String", - "placeholders": {} - }, - "@error404Title": { - "type": "String", - "placeholders": {} - }, - "@error404Desc": { - "type": "String", - "placeholders": {} - }, - "@errorPleaseRefresh": { - "type": "String", - "placeholders": {} - }, - "@connectedToStaging": { - "type": "String", - "placeholders": {} - }, - "@learningSettings": { - "type": "String", - "placeholders": {} - }, - "@participants": { - "type": "String", - "placeholders": {} - }, - "@clickMessageTitle": { - "type": "String", - "placeholders": {} - }, - "@clickMessageBody": { - "type": "String", - "placeholders": {} - }, - "@allDone": { - "type": "String", - "placeholders": {} - }, - "@vocab": { - "type": "String", - "placeholders": {} - }, - "@low": { - "type": "String", - "placeholders": {} - }, - "@medium": { - "type": "String", - "placeholders": {} - }, - "@high": { - "type": "String", - "placeholders": {} - }, - "@subscribe": { - "type": "String", - "placeholders": {} - }, - "@getAccess": { - "type": "String", - "placeholders": {} - }, - "@subscriptionDesc": { - "type": "String", - "placeholders": {} - }, - "@subscriptionManagement": { - "type": "String", - "placeholders": {} - }, - "@currentSubscription": { - "type": "String", - "placeholders": {} - }, - "@cancelSubscription": { - "type": "String", - "placeholders": {} - }, - "@selectYourPlan": { - "type": "String", - "placeholders": {} - }, - "@subsciptionPlatformTooltip": { - "type": "String", - "placeholders": {} - }, - "@subscriptionManagementUnavailable": { - "type": "String", - "placeholders": {} - }, - "@paymentMethod": { - "type": "String", - "placeholders": {} - }, - "@paymentHistory": { - "type": "String", - "placeholders": {} - }, - "@emptyChatDownloadWarning": { - "type": "String", - "placeholders": {} - }, - "@update": { - "type": "String", - "placeholders": {} - }, - "@toggleImmersionMode": { - "type": "String", - "placeholders": {} - }, - "@toggleImmersionModeDesc": { - "type": "String", - "placeholders": {} - }, - "@itToggleDescription": { - "type": "String", - "placeholders": {} - }, - "@igcToggleDescription": { - "type": "String", - "placeholders": {} - }, - "@originalMessage": { - "type": "String", - "placeholders": {} - }, - "@sentMessage": { - "type": "String", - "placeholders": {} - }, - "@useType": { - "type": "String", - "placeholders": {} - }, - "@notAvailable": { - "type": "String", - "placeholders": {} - }, - "@taAndGaTooltip": { - "type": "String", - "placeholders": {} - }, - "@definitionsToolName": { - "type": "String", - "placeholders": {} - }, - "@messageTranslationsToolName": { - "type": "String", - "placeholders": {} - }, - "@definitionsToolDescription": { - "type": "String", - "placeholders": {} - }, - "@translationsToolDescrption": { - "type": "String", - "placeholders": {} - }, - "@welcomeBack": { - "type": "String", - "placeholders": {} - }, - "@downloadTxtFile": { - "type": "String", - "placeholders": {} - }, - "@downloadCSVFile": { - "type": "String", - "placeholders": {} - }, - "@promotionalSubscriptionDesc": { - "type": "String", - "placeholders": {} - }, - "@originalSubscriptionPlatform": { - "type": "String", - "placeholders": { - "purchasePlatform": { - "type": "String" + }, + "@requests": { + "type": "String", + "placeholders": {} + }, + "@holdForInfo": { + "type": "String", + "placeholders": {} + }, + "@greenFeedback": { + "type": "String", + "placeholders": {} + }, + "@yellowFeedback": { + "type": "String", + "placeholders": {} + }, + "@redFeedback": { + "type": "String", + "placeholders": {} + }, + "@itInstructionsTitle": { + "type": "String", + "placeholders": {} + }, + "@itInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@gaTooltip": { + "type": "String", + "placeholders": {} + }, + "@taTooltip": { + "type": "String", + "placeholders": {} + }, + "@unTooltip": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorSliderHeader": { + "type": "String", + "placeholders": {} + }, + "@interactiveGrammarSliderHeader": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorNotAllowed": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorAllowed": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslatorRequired": { + "type": "String", + "placeholders": {} + }, + "@notYetSet": { + "type": "String", + "placeholders": {} + }, + "@waTooltip": { + "type": "String", + "placeholders": {} + }, + "@languageSettings": { + "type": "String", + "placeholders": {} + }, + "@interactiveTranslator": { + "type": "String", + "placeholders": {} + }, + "@noIdenticalLanguages": { + "type": "String", + "placeholders": {} + }, + "@searchBy": { + "type": "String", + "placeholders": {} + }, + "@joinWithClassCode": { + "type": "String", + "placeholders": {} + }, + "@languageLevelPreA1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA2": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB2": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC1": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC2": { + "type": "String", + "placeholders": {} + }, + "@changeTheNameOfTheClass": { + "type": "String", + "placeholders": {} + }, + "@changeTheNameOfTheChat": { + "type": "String", + "placeholders": {} + }, + "@sorryNoResults": { + "type": "String", + "placeholders": {} + }, + "@ignoreInThisText": { + "type": "String", + "placeholders": {} + }, + "@needsItMessage": { + "type": "String", + "placeholders": { + "targetLanguage": { + "type": "String" + } } - } - }, - "@oneWeekTrial": { - "type": "String", - "placeholders": {} - }, - "@downloadXLSXFile": { - "type": "String", - "placeholders": {} - }, - "@unkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@afCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@axCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@alCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@asCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@adCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@aoCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@aiCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@agCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@arCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@amCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@awCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@acCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@auCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@atCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@azCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bhCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@byCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@beCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@btCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@boCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@baCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@brCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ioCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@biCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@khCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@caCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@clCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cxCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ccCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@coCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ckCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@crCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ciCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cuCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@czCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@djCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@doCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tlCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ecCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@egCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@svCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@erCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@eeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@szCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@etCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@foCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fiCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@frCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@geCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@deCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ghCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@giCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@grCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@glCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@guCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gtCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ggCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@htCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@huCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@isCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@inCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@idCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@irCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@iqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ieCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@imCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ilCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@itCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@joCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@keCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kiCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@xkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@laCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@liCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ltCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@luCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@moCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@myCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mlCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mtCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mhCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mqCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@muCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ytCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mxCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@meCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@maCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@naCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nrCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@npCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nlCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ncCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@niCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@neCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ngCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nuCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mpCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@noCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@omCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@psCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@peCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@phCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@plCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@prCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@qaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@reCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@roCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ruCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@blCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@shCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@knCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@smCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@stCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@saCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@snCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@scCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@slCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sxCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@skCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@siCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@soCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gsCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@krCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ssCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sdCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@srCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@seCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@chCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@syCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@twCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tjCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@thCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tgCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tkCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@toCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ttCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@trCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tcCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tvCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@viCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ugCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@aeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gbCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@usCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uyCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uzCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vuCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vaCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@veCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@vnCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wfCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ehCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yeCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zmCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zwCountryDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pay": { - "type": "String", - "placeholders": {} - }, - "@invitedToSpace": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - }, - "space": { - "type": "String" + }, + "@countryInformation": { + "type": "String", + "placeholders": {} + }, + "@targetLanguage": { + "type": "String", + "placeholders": {} + }, + "@sourceLanguage": { + "type": "String", + "placeholders": {} + }, + "@updateLanguage": { + "type": "String", + "placeholders": {} + }, + "@whatLanguageYouWantToLearn": { + "type": "String", + "placeholders": {} + }, + "@whatIsYourBaseLanguage": { + "type": "String", + "placeholders": {} + }, + "@saveChanges": { + "type": "String", + "placeholders": {} + }, + "@publicProfileTitle": { + "type": "String", + "placeholders": {} + }, + "@publicProfileDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIT": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIGC": { + "type": "String", + "placeholders": {} + }, + "@errorDisableLanguageAssistance": { + "type": "String", + "placeholders": {} + }, + "@errorDisableITUserDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIGCUserDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableLanguageAssistanceUserDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableITClassDesc": { + "type": "String", + "placeholders": {} + }, + "@errorDisableIGCClassDesc": { + "type": "String", + "placeholders": {} + }, + "@error405Title": { + "type": "String", + "placeholders": {} + }, + "@error405Desc": { + "type": "String", + "placeholders": {} + }, + "@termsAndConditions": { + "type": "String", + "placeholders": {} + }, + "@andCertifyIAmAtLeast13YearsOfAge": { + "type": "String", + "placeholders": {} + }, + "@error502504Title": { + "type": "String", + "placeholders": {} + }, + "@error502504Desc": { + "type": "String", + "placeholders": {} + }, + "@error404Title": { + "type": "String", + "placeholders": {} + }, + "@error404Desc": { + "type": "String", + "placeholders": {} + }, + "@errorPleaseRefresh": { + "type": "String", + "placeholders": {} + }, + "@connectedToStaging": { + "type": "String", + "placeholders": {} + }, + "@learningSettings": { + "type": "String", + "placeholders": {} + }, + "@participants": { + "type": "String", + "placeholders": {} + }, + "@clickMessageTitle": { + "type": "String", + "placeholders": {} + }, + "@clickMessageBody": { + "type": "String", + "placeholders": {} + }, + "@allDone": { + "type": "String", + "placeholders": {} + }, + "@vocab": { + "type": "String", + "placeholders": {} + }, + "@low": { + "type": "String", + "placeholders": {} + }, + "@medium": { + "type": "String", + "placeholders": {} + }, + "@high": { + "type": "String", + "placeholders": {} + }, + "@subscribe": { + "type": "String", + "placeholders": {} + }, + "@getAccess": { + "type": "String", + "placeholders": {} + }, + "@subscriptionDesc": { + "type": "String", + "placeholders": {} + }, + "@subscriptionManagement": { + "type": "String", + "placeholders": {} + }, + "@currentSubscription": { + "type": "String", + "placeholders": {} + }, + "@cancelSubscription": { + "type": "String", + "placeholders": {} + }, + "@selectYourPlan": { + "type": "String", + "placeholders": {} + }, + "@subsciptionPlatformTooltip": { + "type": "String", + "placeholders": {} + }, + "@subscriptionManagementUnavailable": { + "type": "String", + "placeholders": {} + }, + "@paymentMethod": { + "type": "String", + "placeholders": {} + }, + "@paymentHistory": { + "type": "String", + "placeholders": {} + }, + "@emptyChatDownloadWarning": { + "type": "String", + "placeholders": {} + }, + "@update": { + "type": "String", + "placeholders": {} + }, + "@toggleImmersionMode": { + "type": "String", + "placeholders": {} + }, + "@toggleImmersionModeDesc": { + "type": "String", + "placeholders": {} + }, + "@itToggleDescription": { + "type": "String", + "placeholders": {} + }, + "@igcToggleDescription": { + "type": "String", + "placeholders": {} + }, + "@originalMessage": { + "type": "String", + "placeholders": {} + }, + "@sentMessage": { + "type": "String", + "placeholders": {} + }, + "@useType": { + "type": "String", + "placeholders": {} + }, + "@notAvailable": { + "type": "String", + "placeholders": {} + }, + "@taAndGaTooltip": { + "type": "String", + "placeholders": {} + }, + "@definitionsToolName": { + "type": "String", + "placeholders": {} + }, + "@messageTranslationsToolName": { + "type": "String", + "placeholders": {} + }, + "@definitionsToolDescription": { + "type": "String", + "placeholders": {} + }, + "@translationsToolDescrption": { + "type": "String", + "placeholders": {} + }, + "@welcomeBack": { + "type": "String", + "placeholders": {} + }, + "@downloadTxtFile": { + "type": "String", + "placeholders": {} + }, + "@downloadCSVFile": { + "type": "String", + "placeholders": {} + }, + "@promotionalSubscriptionDesc": { + "type": "String", + "placeholders": {} + }, + "@originalSubscriptionPlatform": { + "type": "String", + "placeholders": { + "purchasePlatform": { + "type": "String" + } } - } - }, - "@youreInvited": { - "type": "String", - "placeholders": {} - }, - "@invitedToChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - }, - "name": { - "type": "String" + }, + "@oneWeekTrial": { + "type": "String", + "placeholders": {} + }, + "@downloadXLSXFile": { + "type": "String", + "placeholders": {} + }, + "@unkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@afCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@axCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@alCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@asCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@adCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@aoCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@aiCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@agCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@arCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@amCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@awCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@acCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@auCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@atCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@azCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bhCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@byCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@beCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@btCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@boCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@baCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@brCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ioCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@biCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@khCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@caCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@clCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cxCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ccCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@coCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ckCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@crCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ciCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cuCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@czCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@djCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@doCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tlCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ecCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@egCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@svCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@erCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@eeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@szCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@etCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@foCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fiCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@frCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@geCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@deCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ghCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@giCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@grCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@glCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@guCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gtCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ggCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@htCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@huCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@isCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@inCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@idCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@irCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@iqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ieCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@imCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ilCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@itCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@joCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@keCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kiCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@xkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@laCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@liCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ltCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@luCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@moCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@myCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mlCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mtCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mhCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mqCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@muCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ytCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mxCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@meCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@maCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@naCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nrCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@npCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nlCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ncCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@niCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@neCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ngCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nuCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mpCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@noCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@omCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@psCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@peCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@phCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@plCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@prCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@qaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@reCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@roCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ruCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@blCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@shCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@knCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@smCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@stCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@saCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@snCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@scCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@slCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sxCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@skCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@siCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@soCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gsCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@krCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ssCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sdCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@srCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@seCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@chCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@syCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@twCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tjCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@thCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tgCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tkCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@toCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ttCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@trCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tcCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tvCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@viCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ugCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@aeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gbCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@usCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uyCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uzCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vuCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vaCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@veCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@vnCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wfCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ehCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yeCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zmCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zwCountryDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pay": { + "type": "String", + "placeholders": {} + }, + "@invitedToSpace": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + }, + "space": { + "type": "String" + } } - } - }, - "@monthlySubscription": { - "type": "String", - "placeholders": {} - }, - "@yearlySubscription": { - "type": "String", - "placeholders": {} - }, - "@defaultSubscription": { - "type": "String", - "placeholders": {} - }, - "@freeTrial": { - "type": "String", - "placeholders": {} - }, - "@total": { - "type": "String", - "placeholders": {} - }, - "@noDataFound": { - "type": "String", - "placeholders": {} - }, - "@blurMeansTranslateTitle": { - "type": "String", - "placeholders": {} - }, - "@blurMeansTranslateBody": { - "type": "String", - "placeholders": {} - }, - "@bestCorrectionFeedback": { - "type": "String", - "placeholders": {} - }, - "@distractorFeedback": { - "type": "String", - "placeholders": {} - }, - "@bestAnswerFeedback": { - "type": "String", - "placeholders": {} - }, - "@definitionDefaultPrompt": { - "type": "String", - "placeholders": {} - }, - "@practiceDefaultPrompt": { - "type": "String", - "placeholders": {} - }, - "@correctionDefaultPrompt": { - "type": "String", - "placeholders": {} - }, - "@acceptSelection": { - "type": "String", - "placeholders": {} - }, - "@why": { - "type": "String", - "placeholders": {} - }, - "@definition": { - "type": "String", - "placeholders": {} - }, - "@exampleSentence": { - "type": "String", - "placeholders": {} - }, - "@reportToTeacher": { - "type": "String", - "placeholders": {} - }, - "@reportMessageTitle": { - "type": "String", - "placeholders": { - "reportingUserId": { - "type": "String" - }, - "reportedUserId": { - "type": "String" - }, - "roomName": { - "type": "String" + }, + "@youreInvited": { + "type": "String", + "placeholders": {} + }, + "@invitedToChat": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + }, + "name": { + "type": "String" + } } - } - }, - "@reportMessageBody": { - "type": "String", - "placeholders": { - "reportedMessage": { - "type": "String" - }, - "reason": { - "type": "String" + }, + "@monthlySubscription": { + "type": "String", + "placeholders": {} + }, + "@yearlySubscription": { + "type": "String", + "placeholders": {} + }, + "@defaultSubscription": { + "type": "String", + "placeholders": {} + }, + "@freeTrial": { + "type": "String", + "placeholders": {} + }, + "@total": { + "type": "String", + "placeholders": {} + }, + "@noDataFound": { + "type": "String", + "placeholders": {} + }, + "@blurMeansTranslateTitle": { + "type": "String", + "placeholders": {} + }, + "@blurMeansTranslateBody": { + "type": "String", + "placeholders": {} + }, + "@bestCorrectionFeedback": { + "type": "String", + "placeholders": {} + }, + "@distractorFeedback": { + "type": "String", + "placeholders": {} + }, + "@bestAnswerFeedback": { + "type": "String", + "placeholders": {} + }, + "@definitionDefaultPrompt": { + "type": "String", + "placeholders": {} + }, + "@practiceDefaultPrompt": { + "type": "String", + "placeholders": {} + }, + "@correctionDefaultPrompt": { + "type": "String", + "placeholders": {} + }, + "@acceptSelection": { + "type": "String", + "placeholders": {} + }, + "@why": { + "type": "String", + "placeholders": {} + }, + "@definition": { + "type": "String", + "placeholders": {} + }, + "@exampleSentence": { + "type": "String", + "placeholders": {} + }, + "@reportToTeacher": { + "type": "String", + "placeholders": {} + }, + "@reportMessageTitle": { + "type": "String", + "placeholders": { + "reportingUserId": { + "type": "String" + }, + "reportedUserId": { + "type": "String" + }, + "roomName": { + "type": "String" + } } - } - }, - "@noTeachersFound": { - "type": "String", - "placeholders": {} - }, - "@trialExpiration": { - "type": "String", - "placeholders": { - "expiration": { - "type": "String" + }, + "@reportMessageBody": { + "type": "String", + "placeholders": { + "reportedMessage": { + "type": "String" + }, + "reason": { + "type": "String" + } } - } - }, - "@freeTrialDesc": { - "type": "String", - "placeholders": {} - }, - "@activateTrial": { - "type": "String", - "placeholders": {} - }, - "@successfullySubscribed": { - "type": "String", - "placeholders": {} - }, - "@clickToManageSubscription": { - "type": "String", - "placeholders": {} - }, - "@signUp": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAtLeastChars": { - "type": "String", - "placeholders": { - "min": { - "type": "String" + }, + "@noTeachersFound": { + "type": "String", + "placeholders": {} + }, + "@trialExpiration": { + "type": "String", + "placeholders": { + "expiration": { + "type": "String" + } } - } - }, - "@noEmailWarning": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterValidEmail": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAUsername": { - "type": "String", - "placeholders": {} - }, - "@define": { - "type": "String", - "placeholders": {} - }, - "@listen": { - "type": "String", - "placeholders": {} - }, - "@trialPeriodExpired": { - "type": "String", - "placeholders": {} - }, - "@translations": { - "type": "String", - "placeholders": {} - }, - "@messageAudio": { - "type": "String", - "placeholders": {} - }, - "@definitions": { - "type": "String", - "placeholders": {} - }, - "@subscribedToUnlockTools": { - "type": "String", - "placeholders": {} - }, - "@translationTooltip": { - "type": "String", - "placeholders": {} - }, - "@speechToTextTooltip": { - "type": "String", - "placeholders": {} - }, - "@kickBotWarning": { - "type": "String", - "placeholders": {} - }, - "@refresh": { - "type": "String", - "placeholders": {} - }, - "@messageAnalytics": { - "type": "String", - "placeholders": {} - }, - "@words": { - "type": "String", - "placeholders": {} - }, - "@score": { - "type": "String", - "placeholders": {} - }, - "@accuracy": { - "type": "String", - "placeholders": {} - }, - "@points": { - "type": "String", - "placeholders": {} - }, - "@noPaymentInfo": { - "type": "String", - "placeholders": {} - }, - "@updatePhoneOS": { - "type": "String", - "placeholders": {} - }, - "@wordsPerMinute": { - "type": "String", - "placeholders": {} - }, - "@tooltipInstructionsTitle": { - "type": "String", - "placeholders": {} - }, - "@tooltipInstructionsMobileBody": { - "type": "String", - "placeholders": {} - }, - "@tooltipInstructionsBrowserBody": { - "type": "String", - "placeholders": {} - }, - "@chatCapacity": { - "type": "String", - "placeholders": {} - }, - "@roomFull": { - "type": "String", - "placeholders": {} - }, - "@chatCapacityHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@chatCapacitySetTooLow": { - "type": "int", - "placeholders": { - "count": { - "type": "int" + }, + "@freeTrialDesc": { + "type": "String", + "placeholders": {} + }, + "@activateTrial": { + "type": "String", + "placeholders": {} + }, + "@successfullySubscribed": { + "type": "String", + "placeholders": {} + }, + "@clickToManageSubscription": { + "type": "String", + "placeholders": {} + }, + "@signUp": { + "type": "String", + "placeholders": {} + }, + "@pleaseChooseAtLeastChars": { + "type": "String", + "placeholders": { + "min": { + "type": "String" + } } - } - }, - "@chatCapacityExplanation": { - "type": "String", - "placeholders": {} - }, - "@tooManyRequest": { - "type": "String", - "placeholders": {} - }, - "@enterNumber": { - "type": "String", - "placeholders": {} - }, - "@buildTranslation": { - "type": "String", - "placeholders": {} - }, - "@practice": { - "type": "String", - "placeholders": {} - }, - "@noLanguagesSet": { - "type": "String", - "placeholders": {} - }, - "@speechToTextBody": { - "type": "String", - "placeholders": {} - }, - "@versionNotFound": { - "type": "String", - "placeholders": {} - }, - "@fetchingVersion": { - "type": "String", - "placeholders": {} - }, - "@versionFetchError": { - "type": "String", - "placeholders": {} - }, - "@versionText": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - }, - "buildNumber": { - "type": "String" + }, + "@noEmailWarning": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterValidEmail": { + "type": "String", + "placeholders": {} + }, + "@pleaseChooseAUsername": { + "type": "String", + "placeholders": {} + }, + "@define": { + "type": "String", + "placeholders": {} + }, + "@listen": { + "type": "String", + "placeholders": {} + }, + "@trialPeriodExpired": { + "type": "String", + "placeholders": {} + }, + "@translations": { + "type": "String", + "placeholders": {} + }, + "@messageAudio": { + "type": "String", + "placeholders": {} + }, + "@definitions": { + "type": "String", + "placeholders": {} + }, + "@subscribedToUnlockTools": { + "type": "String", + "placeholders": {} + }, + "@translationTooltip": { + "type": "String", + "placeholders": {} + }, + "@speechToTextTooltip": { + "type": "String", + "placeholders": {} + }, + "@kickBotWarning": { + "type": "String", + "placeholders": {} + }, + "@refresh": { + "type": "String", + "placeholders": {} + }, + "@messageAnalytics": { + "type": "String", + "placeholders": {} + }, + "@words": { + "type": "String", + "placeholders": {} + }, + "@score": { + "type": "String", + "placeholders": {} + }, + "@accuracy": { + "type": "String", + "placeholders": {} + }, + "@points": { + "type": "String", + "placeholders": {} + }, + "@noPaymentInfo": { + "type": "String", + "placeholders": {} + }, + "@updatePhoneOS": { + "type": "String", + "placeholders": {} + }, + "@wordsPerMinute": { + "type": "String", + "placeholders": {} + }, + "@tooltipInstructionsTitle": { + "type": "String", + "placeholders": {} + }, + "@tooltipInstructionsMobileBody": { + "type": "String", + "placeholders": {} + }, + "@tooltipInstructionsBrowserBody": { + "type": "String", + "placeholders": {} + }, + "@chatCapacity": { + "type": "String", + "placeholders": {} + }, + "@roomFull": { + "type": "String", + "placeholders": {} + }, + "@chatCapacityHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "@chatCapacitySetTooLow": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } } - } - }, - "@l1TranslationBody": { - "type": "String", - "placeholders": {} - }, - "@deleteSubscriptionWarningTitle": { - "type": "String", - "placeholders": {} - }, - "@deleteSubscriptionWarningBody": { - "type": "String", - "placeholders": {} - }, - "@manageSubscription": { - "type": "String", - "placeholders": {} - }, - "@error520Title": { - "type": "String", - "placeholders": {} - }, - "@error520Desc": { - "type": "String", - "placeholders": {} - }, - "@wordsUsed": { - "type": "String", - "placeholders": {} - }, - "@level": { - "type": "String", - "placeholders": {} - }, - "@morphsUsed": { - "type": "String", - "placeholders": {} - }, - "@translationChoicesBody": { - "type": "String", - "placeholders": {} - }, - "@grammar": { - "type": "String", - "placeholders": {} - }, - "@contactHasBeenInvitedToTheChat": { - "type": "String", - "placeholders": {} - }, - "@inviteChat": { - "type": "String", - "placeholders": {} - }, - "@chatName": { - "type": "String", - "placeholders": {} - }, - "@reportContentIssueTitle": { - "type": "String", - "placeholders": {} - }, - "@feedback": { - "type": "String", - "placeholders": {} - }, - "@reportContentIssueDescription": { - "type": "String", - "placeholders": {} - }, - "@clickTheWordAgainToDeselect": { - "type": "String", - "placeholders": {} - }, - "@l2SupportNa": { - "type": "String", - "placeholders": {} - }, - "@l2SupportAlpha": { - "type": "String", - "placeholders": {} - }, - "@l2SupportBeta": { - "type": "String", - "placeholders": {} - }, - "@l2SupportFull": { - "type": "String", - "placeholders": {} - }, - "@missingVoiceTitle": { - "type": "String", - "placeholders": {} - }, - "@voiceNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@openVoiceSettings": { - "type": "String", - "placeholders": {} - }, - "@playAudio": { - "type": "String", - "placeholders": {} - }, - "@stop": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSsconj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSnum": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSverb": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSaffix": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSadj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOScconj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpunct": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSadv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSaux": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSspace": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSsym": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSdet": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpron": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSadp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSpropn": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSnoun": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSintj": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSx": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERfem": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON2": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODimp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEqest": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTperf": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEaccnom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEobl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEact": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEbrck": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERsing": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERmasc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPEmod": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEadverbial": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEperi": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMdigit": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEnot_proper": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEcard": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEprop": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEdash": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEsemi": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEcomm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODcnd": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEacc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPARTTYPEpart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEpast": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEsup": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEcolo": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON3": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERplur": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEnpr": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEinterrogative": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEinfm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEtim": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITYneg": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEtot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEadnomial": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTprog": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODsub": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMcomplementive": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEnom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEfut": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEdat": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEpres": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERneut": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPErel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMfinalEnding": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEdem": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASEpre": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMfin": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEpos": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEquot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMger": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEpass": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEgen": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEprs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEdef": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEord": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEins": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMinf": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMaux": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMlong": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEloc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODind": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEcmp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASErelativeCase": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEexcl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON1": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDEini": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERperson": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyFOREIGNyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEvoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPEverbType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSSpass": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASEprepCase": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEnumType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEnounType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXreflex": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEpronType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDEpunctSide": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMverbForm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERgender": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODmood": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTaspect": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEpunctType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEtense": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEdegree": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEpolite": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPEadvType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMnumber": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEconjType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITYpolarity": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEcase": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEdefinite": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMnumForm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEadn": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOCvoc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCMPLcmpl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVadv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODjus": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDERcom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXrflx": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPARTTYPEpar": { - "type": "String", - "placeholders": {} - }, - "@grammarCopySPCspc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEpqp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXref": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEnshrt": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERdual": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMlng": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEmid": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyINTRELintRel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyINTint": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEcaus": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyUnknown": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENTevident": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMnumberPsor": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECThab": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEabl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEall": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEess": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEtra": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEequ": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEdis": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEabs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEerg": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEcau": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEben": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEtem": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEcoord": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEcons": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREEabs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENTfh": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENTnfh": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODopt": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODadm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODdes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODnec": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODpot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODprp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODqot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMword": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMroman": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORMletter": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEmult": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEfrac": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEsets": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPErange": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPEdist": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERtri": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERpauc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERgrpa": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERgrpl": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERinv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON0": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON4": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEform": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEelev": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITEhumb": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEemp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEexc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPErcp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEintRelPronType": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEaor": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEeps": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEprosp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMpart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMconv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMvnoun": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEantip": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEcauVoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICedir": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEinvVoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICErcpVoice": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOS": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyGENDER": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPERSON": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOOD": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECT": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyADVTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMFORM": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBER": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEGREE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyEVIDENT": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyFOREIGN": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITY": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLITE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEX": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORM": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopySPC": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPARTTYPE": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyINTREL": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyUNKNOWN": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSOR": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSS": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyASPECTimp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEvoc": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEcom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEpar": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEadv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEref": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASErel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEsub": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEsup": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEaccdat": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCASEpre": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEsub": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyCONJTYPEcmp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyDEFINITEind": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyMOODint": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNOUNTYPEcomm": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSORsing": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSORplur": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyNUMBERPSORdual": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOLARITYpos": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSSyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPREPCASEnpr": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEprs": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEint": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEtot": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEneg": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEart": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEind": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPRONTYPEintrel": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTSIDEfin": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPUNCTTYPEperi": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyREFLEXyes": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyTENSEimp": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMsup": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMadn": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMlng": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBFORMshrt": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVERBTYPEcaus": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEcau": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEdir": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICEinv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyVOICErcp": { - "type": "String", - "placeholders": {} - }, - "@other": { - "type": "String", - "placeholders": {} - }, - "@levelShort": { - "type": "String", - "placeholders": { - "level": { - "type": "int" + }, + "@chatCapacityExplanation": { + "type": "String", + "placeholders": {} + }, + "@tooManyRequest": { + "type": "String", + "placeholders": {} + }, + "@enterNumber": { + "type": "String", + "placeholders": {} + }, + "@buildTranslation": { + "type": "String", + "placeholders": {} + }, + "@practice": { + "type": "String", + "placeholders": {} + }, + "@noLanguagesSet": { + "type": "String", + "placeholders": {} + }, + "@speechToTextBody": { + "type": "String", + "placeholders": {} + }, + "@versionNotFound": { + "type": "String", + "placeholders": {} + }, + "@fetchingVersion": { + "type": "String", + "placeholders": {} + }, + "@versionFetchError": { + "type": "String", + "placeholders": {} + }, + "@versionText": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + }, + "buildNumber": { + "type": "String" + } } - } - }, - "@clickBestOption": { - "type": "String", - "placeholders": {} - }, - "@completeActivitiesToUnlock": { - "type": "String", - "placeholders": {} - }, - "@noCapacityLimit": { - "type": "String", - "placeholders": {} - }, - "@downloadGroupText": { - "type": "String", - "placeholders": {} - }, - "@notificationsOn": { - "type": "String", - "placeholders": {} - }, - "@notificationsOff": { - "type": "String", - "placeholders": {} - }, - "@createChatAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@updatedNewSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@joinWithCode": { - "type": "String", - "placeholders": {} - }, - "@enterCodeToJoin": { - "type": "String", - "placeholders": {} - }, - "@updateNow": { - "type": "String", - "placeholders": {} - }, - "@updateLater": { - "type": "String", - "placeholders": {} - }, - "@constructUseWaDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseGaDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseTaDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseUnkDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorITDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnITDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncITDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnIGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorIGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncIGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorPADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnPADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncPADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIngWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorHWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncHWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnHWLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnLDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseEmojiDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCollected": { - "type": "String", - "placeholders": {} - }, - "@constructUseNanDesc": { - "type": "String", - "placeholders": {} - }, - "@xpIntoLevel": { - "type": "String", - "placeholders": { - "currentXP": { - "type": "int" - }, - "maxXP": { - "type": "int" + }, + "@l1TranslationBody": { + "type": "String", + "placeholders": {} + }, + "@deleteSubscriptionWarningTitle": { + "type": "String", + "placeholders": {} + }, + "@deleteSubscriptionWarningBody": { + "type": "String", + "placeholders": {} + }, + "@manageSubscription": { + "type": "String", + "placeholders": {} + }, + "@error520Title": { + "type": "String", + "placeholders": {} + }, + "@error520Desc": { + "type": "String", + "placeholders": {} + }, + "@wordsUsed": { + "type": "String", + "placeholders": {} + }, + "@level": { + "type": "String", + "placeholders": {} + }, + "@morphsUsed": { + "type": "String", + "placeholders": {} + }, + "@translationChoicesBody": { + "type": "String", + "placeholders": {} + }, + "@grammar": { + "type": "String", + "placeholders": {} + }, + "@contactHasBeenInvitedToTheChat": { + "type": "String", + "placeholders": {} + }, + "@inviteChat": { + "type": "String", + "placeholders": {} + }, + "@chatName": { + "type": "String", + "placeholders": {} + }, + "@reportContentIssueTitle": { + "type": "String", + "placeholders": {} + }, + "@feedback": { + "type": "String", + "placeholders": {} + }, + "@reportContentIssueDescription": { + "type": "String", + "placeholders": {} + }, + "@clickTheWordAgainToDeselect": { + "type": "String", + "placeholders": {} + }, + "@l2SupportNa": { + "type": "String", + "placeholders": {} + }, + "@l2SupportAlpha": { + "type": "String", + "placeholders": {} + }, + "@l2SupportBeta": { + "type": "String", + "placeholders": {} + }, + "@l2SupportFull": { + "type": "String", + "placeholders": {} + }, + "@missingVoiceTitle": { + "type": "String", + "placeholders": {} + }, + "@voiceNotAvailable": { + "type": "String", + "placeholders": {} + }, + "@openVoiceSettings": { + "type": "String", + "placeholders": {} + }, + "@playAudio": { + "type": "String", + "placeholders": {} + }, + "@stop": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSsconj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSnum": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSverb": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSaffix": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSadj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOScconj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpunct": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSadv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSaux": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSspace": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSsym": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSdet": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpron": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSadp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSpropn": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSnoun": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSintj": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSx": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERfem": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON2": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODimp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEqest": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTperf": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEaccnom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEobl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEact": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEbrck": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERsing": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERmasc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPEmod": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEadverbial": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEperi": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMdigit": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEnot_proper": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEcard": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEprop": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEdash": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEsemi": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEcomm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODcnd": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEacc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPARTTYPEpart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEpast": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEsup": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEcolo": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON3": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERplur": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEnpr": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEinterrogative": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEinfm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEtim": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITYneg": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEtot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEadnomial": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTprog": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODsub": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMcomplementive": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEnom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEfut": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEdat": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEpres": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERneut": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPErel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMfinalEnding": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEdem": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASEpre": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMfin": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEpos": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEquot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMger": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEpass": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEgen": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEprs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEdef": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEord": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEins": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMinf": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMaux": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMlong": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEloc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODind": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEcmp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASErelativeCase": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEexcl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON1": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDEini": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERperson": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyFOREIGNyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEvoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPEverbType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSSpass": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASEprepCase": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEnumType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEnounType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXreflex": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEpronType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDEpunctSide": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMverbForm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERgender": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODmood": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTaspect": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEpunctType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEtense": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEdegree": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEpolite": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPEadvType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMnumber": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEconjType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITYpolarity": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEcase": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEdefinite": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMnumForm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEadn": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOCvoc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCMPLcmpl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVadv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODjus": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDERcom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXrflx": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPARTTYPEpar": { + "type": "String", + "placeholders": {} + }, + "@grammarCopySPCspc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEpqp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXref": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEnshrt": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERdual": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMlng": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEmid": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyINTRELintRel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyINTint": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEcaus": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyUnknown": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENTevident": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMnumberPsor": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECThab": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEabl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEall": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEess": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEtra": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEequ": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEdis": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEabs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEerg": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEcau": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEben": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEtem": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEcoord": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEcons": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREEabs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENTfh": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENTnfh": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODopt": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODadm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODdes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODnec": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODpot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODprp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODqot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMword": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMroman": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORMletter": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEmult": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEfrac": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEsets": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPErange": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPEdist": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERtri": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERpauc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERgrpa": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERgrpl": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERinv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON0": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON4": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEform": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEelev": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITEhumb": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEemp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEexc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPErcp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEintRelPronType": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEaor": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEeps": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEprosp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMpart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMconv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMvnoun": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEantip": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEcauVoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICedir": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEinvVoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICErcpVoice": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOS": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyGENDER": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPERSON": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOOD": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECT": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyADVTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMFORM": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBER": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEGREE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyEVIDENT": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyFOREIGN": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITY": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLITE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEX": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORM": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopySPC": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPARTTYPE": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyINTREL": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyUNKNOWN": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSOR": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSS": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyASPECTimp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEvoc": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEcom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEpar": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEadv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEref": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASErel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEsub": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEsup": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEaccdat": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCASEpre": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEsub": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyCONJTYPEcmp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyDEFINITEind": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyMOODint": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNOUNTYPEcomm": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSORsing": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSORplur": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyNUMBERPSORdual": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOLARITYpos": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSSyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPREPCASEnpr": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEprs": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEint": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEtot": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEneg": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEart": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEind": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPRONTYPEintrel": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTSIDEfin": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPUNCTTYPEperi": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyREFLEXyes": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyTENSEimp": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMsup": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMadn": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMlng": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBFORMshrt": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVERBTYPEcaus": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEcau": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEdir": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICEinv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyVOICErcp": { + "type": "String", + "placeholders": {} + }, + "@other": { + "type": "String", + "placeholders": {} + }, + "@levelShort": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } } - } - }, - "@enableTTSToolName": { - "type": "String", - "placeholders": {} - }, - "@enableTTSToolDescription": { - "type": "String", - "placeholders": {} - }, - "@yourUsername": { - "type": "String", - "placeholders": {} - }, - "@yourEmail": { - "type": "String", - "placeholders": {} - }, - "@iWantToLearn": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterEmail": { - "type": "String", - "placeholders": {} - }, - "@myBaseLanguage": { - "type": "String", - "placeholders": {} - }, - "@meaningSectionHeader": { - "type": "String", - "placeholders": {} - }, - "@formSectionHeader": { - "type": "String", - "placeholders": {} - }, - "@writingExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@listeningExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@readingExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@meaningNotFound": { - "type": "String", - "placeholders": {} - }, - "@chooseBaseForm": { - "type": "String", - "placeholders": {} - }, - "@notTheCodeError": { - "type": "String", - "placeholders": {} - }, - "@totalXP": { - "type": "String", - "placeholders": {} - }, - "@numLemmas": { - "type": "String", - "placeholders": {} - }, - "@numLemmasUsedCorrectly": { - "type": "String", - "placeholders": {} - }, - "@numLemmasUsedIncorrectly": { - "type": "String", - "placeholders": {} - }, - "@numLemmasSmallXP": { - "type": "String", - "placeholders": {} - }, - "@numLemmasMediumXP": { - "type": "String", - "placeholders": {} - }, - "@numLemmasLargeXP": { - "type": "String", - "placeholders": {} - }, - "@numGrammarConcepts": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConcepts": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUsedCorrectly": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUsedIncorrectly": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUseCorrectlySystemGenerated": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsUseIncorrectlySystemGenerated": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsSmallXP": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsMediumXP": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsLargeXP": { - "type": "String", - "placeholders": {} - }, - "@listGrammarConceptsHugeXP": { - "type": "String", - "placeholders": {} - }, - "@numMessagesSent": { - "type": "String", - "placeholders": {} - }, - "@numWordsTyped": { - "type": "String", - "placeholders": {} - }, - "@numCorrectChoices": { - "type": "String", - "placeholders": {} - }, - "@numIncorrectChoices": { - "type": "String", - "placeholders": {} - }, - "@commaSeparatedFile": { - "type": "String", - "placeholders": {} - }, - "@excelFile": { - "type": "String", - "placeholders": {} - }, - "@fileType": { - "type": "String", - "placeholders": {} - }, - "@download": { - "type": "String", - "placeholders": {} - }, - "@analyticsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@downloading": { - "type": "String", - "placeholders": {} - }, - "@failedFetchUserAnalytics": { - "type": "String", - "placeholders": {} - }, - "@downloadComplete": { - "type": "String", - "placeholders": {} - }, - "@whatIsTheMorphTag": { - "type": "String", - "placeholders": { - "morphologicalFeature": { - "type": "String" - }, - "wordForm": { - "type": "String" + }, + "@clickBestOption": { + "type": "String", + "placeholders": {} + }, + "@completeActivitiesToUnlock": { + "type": "String", + "placeholders": {} + }, + "@noCapacityLimit": { + "type": "String", + "placeholders": {} + }, + "@downloadGroupText": { + "type": "String", + "placeholders": {} + }, + "@notificationsOn": { + "type": "String", + "placeholders": {} + }, + "@notificationsOff": { + "type": "String", + "placeholders": {} + }, + "@createChatAndInviteUsers": { + "type": "String", + "placeholders": {} + }, + "@updatedNewSpaceDescription": { + "type": "String", + "placeholders": {} + }, + "@joinWithCode": { + "type": "String", + "placeholders": {} + }, + "@enterCodeToJoin": { + "type": "String", + "placeholders": {} + }, + "@updateNow": { + "type": "String", + "placeholders": {} + }, + "@updateLater": { + "type": "String", + "placeholders": {} + }, + "@constructUseWaDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseGaDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseTaDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseUnkDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorITDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnITDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncITDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnIGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorIGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncIGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorPADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnPADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncPADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIngWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorHWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncHWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnHWLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnLDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseEmojiDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCollected": { + "type": "String", + "placeholders": {} + }, + "@constructUseNanDesc": { + "type": "String", + "placeholders": {} + }, + "@xpIntoLevel": { + "type": "String", + "placeholders": { + "currentXP": { + "type": "int" + }, + "maxXP": { + "type": "int" + } } - } - }, - "@dataAvailable": { - "type": "String", - "placeholders": {} - }, - "@available": { - "type": "String", - "placeholders": {} - }, - "@pangeaBotIsFallible": { - "type": "String", - "placeholders": {} - }, - "@whatIsMeaning": { - "type": "String", - "placeholders": { - "lemma": { - "type": "String" + }, + "@enableTTSToolName": { + "type": "String", + "placeholders": {} + }, + "@enableTTSToolDescription": { + "type": "String", + "placeholders": {} + }, + "@yourUsername": { + "type": "String", + "placeholders": {} + }, + "@yourEmail": { + "type": "String", + "placeholders": {} + }, + "@iWantToLearn": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterEmail": { + "type": "String", + "placeholders": {} + }, + "@myBaseLanguage": { + "type": "String", + "placeholders": {} + }, + "@meaningSectionHeader": { + "type": "String", + "placeholders": {} + }, + "@formSectionHeader": { + "type": "String", + "placeholders": {} + }, + "@writingExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@listeningExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@readingExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@meaningNotFound": { + "type": "String", + "placeholders": {} + }, + "@chooseBaseForm": { + "type": "String", + "placeholders": {} + }, + "@notTheCodeError": { + "type": "String", + "placeholders": {} + }, + "@totalXP": { + "type": "String", + "placeholders": {} + }, + "@numLemmas": { + "type": "String", + "placeholders": {} + }, + "@numLemmasUsedCorrectly": { + "type": "String", + "placeholders": {} + }, + "@numLemmasUsedIncorrectly": { + "type": "String", + "placeholders": {} + }, + "@numLemmasSmallXP": { + "type": "String", + "placeholders": {} + }, + "@numLemmasMediumXP": { + "type": "String", + "placeholders": {} + }, + "@numLemmasLargeXP": { + "type": "String", + "placeholders": {} + }, + "@numGrammarConcepts": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConcepts": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUsedCorrectly": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUsedIncorrectly": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUseCorrectlySystemGenerated": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsUseIncorrectlySystemGenerated": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsSmallXP": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsMediumXP": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsLargeXP": { + "type": "String", + "placeholders": {} + }, + "@listGrammarConceptsHugeXP": { + "type": "String", + "placeholders": {} + }, + "@numMessagesSent": { + "type": "String", + "placeholders": {} + }, + "@numWordsTyped": { + "type": "String", + "placeholders": {} + }, + "@numCorrectChoices": { + "type": "String", + "placeholders": {} + }, + "@numIncorrectChoices": { + "type": "String", + "placeholders": {} + }, + "@commaSeparatedFile": { + "type": "String", + "placeholders": {} + }, + "@excelFile": { + "type": "String", + "placeholders": {} + }, + "@fileType": { + "type": "String", + "placeholders": {} + }, + "@download": { + "type": "String", + "placeholders": {} + }, + "@analyticsNotAvailable": { + "type": "String", + "placeholders": {} + }, + "@downloading": { + "type": "String", + "placeholders": {} + }, + "@failedFetchUserAnalytics": { + "type": "String", + "placeholders": {} + }, + "@downloadComplete": { + "type": "String", + "placeholders": {} + }, + "@whatIsTheMorphTag": { + "type": "String", + "placeholders": { + "morphologicalFeature": { + "type": "String" + }, + "wordForm": { + "type": "String" + } } - } - }, - "@pickAnEmoji": { - "type": "String", - "placeholders": { - "lemma": { - "type": "String" + }, + "@dataAvailable": { + "type": "String", + "placeholders": {} + }, + "@available": { + "type": "String", + "placeholders": {} + }, + "@pangeaBotIsFallible": { + "type": "String", + "placeholders": {} + }, + "@whatIsMeaning": { + "type": "String", + "placeholders": { + "lemma": { + "type": "String" + } } - } - }, - "@chooseLemmaMeaningInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@doubleClickToEdit": { - "type": "String", - "placeholders": {} - }, - "@activityPlannerTitle": { - "type": "String", - "placeholders": {} - }, - "@topicLabel": { - "type": "String", - "placeholders": {} - }, - "@topicPlaceholder": { - "type": "String", - "placeholders": {} - }, - "@modeLabel": { - "type": "String", - "placeholders": {} - }, - "@modePlaceholder": { - "type": "String", - "placeholders": {} - }, - "@learningObjectiveLabel": { - "type": "String", - "placeholders": {} - }, - "@learningObjectivePlaceholder": { - "type": "String", - "placeholders": {} - }, - "@languageOfInstructionsLabel": { - "type": "String", - "placeholders": {} - }, - "@targetLanguageLabel": { - "type": "String", - "placeholders": {} - }, - "@cefrLevelLabel": { - "type": "String", - "placeholders": {} - }, - "@generateActivitiesButton": { - "type": "String", - "placeholders": {} - }, - "@launchActivityButton": { - "type": "String", - "placeholders": {} - }, - "@image": { - "type": "String", - "placeholders": {} - }, - "@video": { - "type": "String", - "placeholders": {} - }, - "@nan": { - "type": "String", - "placeholders": {} - }, - "@activityPlannerOverviewInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@activityTitle": { - "type": "String", - "placeholders": {} - }, - "@addVocabulary": { - "type": "String", - "placeholders": {} - }, - "@instructions": { - "type": "String", - "placeholders": {} - }, - "@numberOfLearners": { - "type": "String", - "placeholders": {} - }, - "@mustBeInteger": { - "type": "String", - "placeholders": {} - }, - "@constructUsePvmDesc": { - "type": "String", - "placeholders": {} - }, - "@leaveSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorMmDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncMmDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIgnMmDesc": { - "type": "String", - "placeholders": {} - }, - "@clickForMeaningActivity": { - "type": "String", - "placeholders": {} - }, - "@meaning": { - "type": "String", - "placeholders": {} - }, - "@chatWith": { - "type": "String", - "placeholders": { - "displayname": { - "type": "String" + }, + "@pickAnEmoji": { + "type": "String", + "placeholders": { + "lemma": { + "type": "String" + } } - } - }, - "@clickOnEmailLink": { - "type": "String", - "placeholders": {} - }, - "@dontForgetPassword": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectToolName": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectDescription": { - "type": "String", - "placeholders": {} - }, - "@ttsDisbledTitle": { - "type": "String", - "placeholders": {} - }, - "@ttsDisabledBody": { - "type": "String", - "placeholders": {} - }, - "@noSpaceDescriptionYet": { - "type": "String", - "placeholders": {} - }, - "@tooLargeToSend": { - "type": "String", - "placeholders": {} - }, - "@exitWithoutSaving": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectPopupTitle": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectPopupSteps": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectPopupDescription": { - "type": "String", - "placeholders": {} - }, - "@downloadGboardTitle": { - "type": "String", - "placeholders": {} - }, - "@downloadGboardSteps": { - "type": "String", - "placeholders": {} - }, - "@downloadGboardDescription": { - "type": "String", - "placeholders": {} - }, - "@enableAutocorrectWarning": { - "type": "String", - "placeholders": {} - }, - "@displayName": { - "type": "String", - "placeholders": {} - }, - "@leaveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@confirmUserId": { - "type": "String", - "placeholders": {} - }, - "@startingToday": { - "type": "String", - "placeholders": {} - }, - "@oneWeekFreeTrial": { - "type": "String", - "placeholders": {} - }, - "@paidSubscriptionStarts": { - "type": "String", - "placeholders": { - "startDate": { - "type": "String" + }, + "@chooseLemmaMeaningInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@doubleClickToEdit": { + "type": "String", + "placeholders": {} + }, + "@activityPlannerTitle": { + "type": "String", + "placeholders": {} + }, + "@topicLabel": { + "type": "String", + "placeholders": {} + }, + "@topicPlaceholder": { + "type": "String", + "placeholders": {} + }, + "@modeLabel": { + "type": "String", + "placeholders": {} + }, + "@modePlaceholder": { + "type": "String", + "placeholders": {} + }, + "@learningObjectiveLabel": { + "type": "String", + "placeholders": {} + }, + "@learningObjectivePlaceholder": { + "type": "String", + "placeholders": {} + }, + "@languageOfInstructionsLabel": { + "type": "String", + "placeholders": {} + }, + "@targetLanguageLabel": { + "type": "String", + "placeholders": {} + }, + "@cefrLevelLabel": { + "type": "String", + "placeholders": {} + }, + "@generateActivitiesButton": { + "type": "String", + "placeholders": {} + }, + "@launchActivityButton": { + "type": "String", + "placeholders": {} + }, + "@image": { + "type": "String", + "placeholders": {} + }, + "@video": { + "type": "String", + "placeholders": {} + }, + "@nan": { + "type": "String", + "placeholders": {} + }, + "@activityPlannerOverviewInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@activityTitle": { + "type": "String", + "placeholders": {} + }, + "@addVocabulary": { + "type": "String", + "placeholders": {} + }, + "@instructions": { + "type": "String", + "placeholders": {} + }, + "@numberOfLearners": { + "type": "String", + "placeholders": {} + }, + "@mustBeInteger": { + "type": "String", + "placeholders": {} + }, + "@constructUsePvmDesc": { + "type": "String", + "placeholders": {} + }, + "@leaveSpaceDescription": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorMmDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncMmDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIgnMmDesc": { + "type": "String", + "placeholders": {} + }, + "@clickForMeaningActivity": { + "type": "String", + "placeholders": {} + }, + "@meaning": { + "type": "String", + "placeholders": {} + }, + "@chatWith": { + "type": "String", + "placeholders": { + "displayname": { + "type": "String" + } } - } - }, - "@cancelInSubscriptionSettings": { - "type": "String", - "placeholders": {} - }, - "@cancelToAvoidCharges": { - "type": "String", - "placeholders": { - "trialEnds": { - "type": "String" + }, + "@clickOnEmailLink": { + "type": "String", + "placeholders": {} + }, + "@dontForgetPassword": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectToolName": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectDescription": { + "type": "String", + "placeholders": {} + }, + "@ttsDisbledTitle": { + "type": "String", + "placeholders": {} + }, + "@ttsDisabledBody": { + "type": "String", + "placeholders": {} + }, + "@noSpaceDescriptionYet": { + "type": "String", + "placeholders": {} + }, + "@tooLargeToSend": { + "type": "String", + "placeholders": {} + }, + "@exitWithoutSaving": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectPopupTitle": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectPopupSteps": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectPopupDescription": { + "type": "String", + "placeholders": {} + }, + "@downloadGboardTitle": { + "type": "String", + "placeholders": {} + }, + "@downloadGboardSteps": { + "type": "String", + "placeholders": {} + }, + "@downloadGboardDescription": { + "type": "String", + "placeholders": {} + }, + "@enableAutocorrectWarning": { + "type": "String", + "placeholders": {} + }, + "@displayName": { + "type": "String", + "placeholders": {} + }, + "@leaveRoomDescription": { + "type": "String", + "placeholders": {} + }, + "@confirmUserId": { + "type": "String", + "placeholders": {} + }, + "@startingToday": { + "type": "String", + "placeholders": {} + }, + "@oneWeekFreeTrial": { + "type": "String", + "placeholders": {} + }, + "@paidSubscriptionStarts": { + "type": "String", + "placeholders": { + "startDate": { + "type": "String" + } } - } - }, - "@downloadGboard": { - "type": "String", - "placeholders": {} - }, - "@autocorrectNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@pleaseUpdateApp": { - "type": "String", - "placeholders": {} - }, - "@chooseEmojiInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@analyticsVocabListBody": { - "type": "String", - "placeholders": {} - }, - "@morphAnalyticsListBody": { - "type": "String", - "placeholders": {} - }, - "@knockSpaceSuccess": { - "type": "String", - "placeholders": {} - }, - "@chooseWordAudioInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@chooseMorphsInstructionsBody": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterInt": { - "type": "String", - "placeholders": {} - }, - "@home": { - "type": "String", - "placeholders": {} - }, - "@join": { - "type": "String", - "placeholders": {} - }, - "@readingAssistanceOverviewBody": { - "type": "String", - "placeholders": {} - }, - "@levelSummaryPopupTitle": { - "type": "String", - "placeholders": { - "level": { - "type": "int" + }, + "@cancelInSubscriptionSettings": { + "type": "String", + "placeholders": {} + }, + "@cancelToAvoidCharges": { + "type": "String", + "placeholders": { + "trialEnds": { + "type": "String" + } } - } - }, - "@resetInstructionTooltipsTitle": { - "type": "String", - "placeholders": {} - }, - "@resetInstructionTooltipsDesc": { - "type": "String", - "placeholders": {} - }, - "@selectForGrammar": { - "type": "String", - "placeholders": {} - }, - "@randomize": { - "type": "String", - "placeholders": {} - }, - "@clear": { - "type": "String", - "placeholders": {} - }, - "@makeYourOwnActivity": { - "type": "String", - "placeholders": {} - }, - "@featuredActivities": { - "type": "String", - "placeholders": {} - }, - "@save": { - "type": "String", - "placeholders": {} - }, - "@startChat": { - "type": "String", - "placeholders": {} - }, - "@translationProblem": { - "type": "String", - "placeholders": {} - }, - "@askToJoin": { - "type": "String", - "placeholders": {} - }, - "@emptyChatWarningTitle": { - "type": "String", - "placeholders": {} - }, - "@emptyChatWarningDesc": { - "type": "String", - "placeholders": {} - }, - "@areYouLikeMe": { - "type": "String", - "placeholders": {} - }, - "@tryAgainLater": { - "type": "String", - "placeholders": {} - }, - "@enterSpaceCode": { - "type": "String", - "placeholders": {} - }, - "@shareSpaceLink": { - "type": "String", - "placeholders": {} - }, - "@byUsingPangeaChat": { - "type": "String", - "placeholders": {} - }, - "@details": { - "type": "String", - "placeholders": {} - }, - "@languageLevelPreA1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelA2Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelB2Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC1Desc": { - "type": "String", - "placeholders": {} - }, - "@languageLevelC2Desc": { - "type": "String", - "placeholders": {} - }, - "@newVocab": { - "type": "String", - "placeholders": {} - }, - "@newGrammar": { - "type": "String", - "placeholders": {} - }, - "@choosePracticeMode": { - "type": "String", - "placeholders": {} - }, - "@ban": { - "type": "String", - "placeholders": {} - }, - "@unban": { - "type": "String", - "placeholders": {} - }, - "@kick": { - "type": "String", - "placeholders": {} - }, - "@lemma": { - "type": "String", - "placeholders": {} - }, - "@grammarFeature": { - "type": "String", - "placeholders": {} - }, - "@grammarTag": { - "type": "String", - "placeholders": {} - }, - "@forms": { - "type": "String", - "placeholders": {} - }, - "@exampleMessages": { - "type": "String", - "placeholders": {} - }, - "@timesUsedIndependently": { - "type": "String", - "placeholders": {} - }, - "@timesUsedWithAssistance": { - "type": "String", - "placeholders": {} - }, - "@shareInviteCode": { - "type": "String", - "placeholders": { - "code": { - "type": "String" + }, + "@downloadGboard": { + "type": "String", + "placeholders": {} + }, + "@autocorrectNotAvailable": { + "type": "String", + "placeholders": {} + }, + "@pleaseUpdateApp": { + "type": "String", + "placeholders": {} + }, + "@chooseEmojiInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@analyticsVocabListBody": { + "type": "String", + "placeholders": {} + }, + "@morphAnalyticsListBody": { + "type": "String", + "placeholders": {} + }, + "@knockSpaceSuccess": { + "type": "String", + "placeholders": {} + }, + "@chooseWordAudioInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@chooseMorphsInstructionsBody": { + "type": "String", + "placeholders": {} + }, + "@pleaseEnterInt": { + "type": "String", + "placeholders": {} + }, + "@home": { + "type": "String", + "placeholders": {} + }, + "@join": { + "type": "String", + "placeholders": {} + }, + "@readingAssistanceOverviewBody": { + "type": "String", + "placeholders": {} + }, + "@levelSummaryPopupTitle": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } } - } - }, - "@leaderboard": { - "type": "String", - "placeholders": {} - }, - "@skipForNow": { - "type": "String", - "placeholders": {} - }, - "@permissions": { - "type": "String", - "placeholders": {} - }, - "@spaceChildPermission": { - "type": "String", - "placeholders": {} - }, - "@addEnvironmentOverride": { - "type": "String", - "placeholders": {} - }, - "@defaultOption": { - "type": "String", - "placeholders": {} - }, - "@deleteChatDesc": { - "type": "String", - "placeholders": {} - }, - "@deleteSpaceDesc": { - "type": "String", - "placeholders": {} - }, - "@launch": { - "type": "String", - "placeholders": {} - }, - "@searchChats": { - "type": "String", - "placeholders": {} - }, - "@maxFifty": { - "type": "String", - "placeholders": {} - }, - "@configureSpace": { - "type": "String", - "placeholders": {} - }, - "@pinMessages": { - "type": "String", - "placeholders": {} - }, - "@setJoinRules": { - "type": "String", - "placeholders": {} - }, - "@changeGeneralSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsersToRoom": { - "type": "String", - "placeholders": {} - }, - "@changeTheNameOfTheSpace": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescription": { - "type": "String", - "placeholders": {} - }, - "@changeThePermissions": { - "type": "String", - "placeholders": {} - }, - "@introductions": { - "type": "String", - "placeholders": {} - }, - "@announcements": { - "type": "String", - "placeholders": {} - }, - "@activities": { - "type": "String", - "placeholders": {} - }, - "@access": { - "type": "String", - "placeholders": {} - }, - "@activitySuggestionTimeoutMessage": { - "type": "String", - "placeholders": {} - }, - "@howSpaceCanBeFound": { - "type": "String", - "placeholders": {} - }, - "@private": { - "type": "String", - "placeholders": {} - }, - "@cannotBeFoundInSearch": { - "type": "String", - "placeholders": {} - }, - "@public": { - "type": "String", - "placeholders": {} - }, - "@visibleToCommunity": { - "type": "String", - "placeholders": {} - }, - "@howSpaceCanBeJoined": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundVia": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundViaInvitation": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundViaCodeOrLink": { - "type": "String", - "placeholders": {} - }, - "@canBeFoundViaKnock": { - "type": "String", - "placeholders": {} - }, - "@youHaveLeveledUp": { - "type": "String", - "placeholders": {} - }, - "@sendActivities": { - "type": "String", - "placeholders": {} - }, - "@groupChat": { - "type": "String", - "placeholders": {} - }, - "@directMessage": { - "type": "String", - "placeholders": {} - }, - "@newDirectMessage": { - "type": "String", - "placeholders": {} - }, - "@speakingExercisesTooltip": { - "type": "String", - "placeholders": {} - }, - "@noChatsFoundHereYet": { - "type": "String", - "placeholders": {} - }, - "@duration": { - "type": "String", - "placeholders": {} - }, - "@transcriptionFailed": { - "type": "String", - "placeholders": {} - }, - "@aUserIsKnocking": { - "type": "String", - "placeholders": {} - }, - "@usersAreKnocking": { - "type": "int", - "placeholders": { - "users": { - "type": "int" + }, + "@resetInstructionTooltipsTitle": { + "type": "String", + "placeholders": {} + }, + "@resetInstructionTooltipsDesc": { + "type": "String", + "placeholders": {} + }, + "@selectForGrammar": { + "type": "String", + "placeholders": {} + }, + "@randomize": { + "type": "String", + "placeholders": {} + }, + "@clear": { + "type": "String", + "placeholders": {} + }, + "@makeYourOwnActivity": { + "type": "String", + "placeholders": {} + }, + "@featuredActivities": { + "type": "String", + "placeholders": {} + }, + "@save": { + "type": "String", + "placeholders": {} + }, + "@startChat": { + "type": "String", + "placeholders": {} + }, + "@translationProblem": { + "type": "String", + "placeholders": {} + }, + "@askToJoin": { + "type": "String", + "placeholders": {} + }, + "@emptyChatWarningTitle": { + "type": "String", + "placeholders": {} + }, + "@emptyChatWarningDesc": { + "type": "String", + "placeholders": {} + }, + "@areYouLikeMe": { + "type": "String", + "placeholders": {} + }, + "@tryAgainLater": { + "type": "String", + "placeholders": {} + }, + "@enterSpaceCode": { + "type": "String", + "placeholders": {} + }, + "@shareSpaceLink": { + "type": "String", + "placeholders": {} + }, + "@byUsingPangeaChat": { + "type": "String", + "placeholders": {} + }, + "@details": { + "type": "String", + "placeholders": {} + }, + "@languageLevelPreA1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelA2Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelB2Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC1Desc": { + "type": "String", + "placeholders": {} + }, + "@languageLevelC2Desc": { + "type": "String", + "placeholders": {} + }, + "@newVocab": { + "type": "String", + "placeholders": {} + }, + "@newGrammar": { + "type": "String", + "placeholders": {} + }, + "@choosePracticeMode": { + "type": "String", + "placeholders": {} + }, + "@ban": { + "type": "String", + "placeholders": {} + }, + "@unban": { + "type": "String", + "placeholders": {} + }, + "@kick": { + "type": "String", + "placeholders": {} + }, + "@lemma": { + "type": "String", + "placeholders": {} + }, + "@grammarFeature": { + "type": "String", + "placeholders": {} + }, + "@grammarTag": { + "type": "String", + "placeholders": {} + }, + "@forms": { + "type": "String", + "placeholders": {} + }, + "@exampleMessages": { + "type": "String", + "placeholders": {} + }, + "@timesUsedIndependently": { + "type": "String", + "placeholders": {} + }, + "@timesUsedWithAssistance": { + "type": "String", + "placeholders": {} + }, + "@shareInviteCode": { + "type": "String", + "placeholders": { + "code": { + "type": "String" + } } - } - }, - "@failedToFetchTranscription": { - "type": "String", - "placeholders": {} - }, - "@deleteEmptySpaceDesc": { - "type": "String", - "placeholders": {} - }, - "@regenerate": { - "type": "String", - "placeholders": {} - }, - "@mySavedActivities": { - "type": "String", - "placeholders": {} - }, - "@noSavedActivities": { - "type": "String", - "placeholders": {} - }, - "@saveActivity": { - "type": "String", - "placeholders": {} - }, - "@failedToPlayVideo": { - "type": "String", - "placeholders": {} - }, - "@done": { - "type": "String", - "placeholders": {} - }, - "@inThisSpace": { - "type": "String", - "placeholders": {} - }, - "@myContacts": { - "type": "String", - "placeholders": {} - }, - "@inviteAllInSpace": { - "type": "String", - "placeholders": {} - }, - "@spaceParticipantsHaveBeenInvitedToTheChat": { - "type": "String", - "placeholders": {} - }, - "@numKnocking": { - "type": "String", - "placeholders": { - "count": { - "type": "int" + }, + "@leaderboard": { + "type": "String", + "placeholders": {} + }, + "@skipForNow": { + "type": "String", + "placeholders": {} + }, + "@permissions": { + "type": "String", + "placeholders": {} + }, + "@spaceChildPermission": { + "type": "String", + "placeholders": {} + }, + "@addEnvironmentOverride": { + "type": "String", + "placeholders": {} + }, + "@defaultOption": { + "type": "String", + "placeholders": {} + }, + "@deleteChatDesc": { + "type": "String", + "placeholders": {} + }, + "@deleteSpaceDesc": { + "type": "String", + "placeholders": {} + }, + "@launch": { + "type": "String", + "placeholders": {} + }, + "@searchChats": { + "type": "String", + "placeholders": {} + }, + "@maxFifty": { + "type": "String", + "placeholders": {} + }, + "@configureSpace": { + "type": "String", + "placeholders": {} + }, + "@pinMessages": { + "type": "String", + "placeholders": {} + }, + "@setJoinRules": { + "type": "String", + "placeholders": {} + }, + "@changeGeneralSettings": { + "type": "String", + "placeholders": {} + }, + "@inviteOtherUsersToRoom": { + "type": "String", + "placeholders": {} + }, + "@changeTheNameOfTheSpace": { + "type": "String", + "placeholders": {} + }, + "@changeTheDescription": { + "type": "String", + "placeholders": {} + }, + "@changeThePermissions": { + "type": "String", + "placeholders": {} + }, + "@introductions": { + "type": "String", + "placeholders": {} + }, + "@announcements": { + "type": "String", + "placeholders": {} + }, + "@activities": { + "type": "String", + "placeholders": {} + }, + "@access": { + "type": "String", + "placeholders": {} + }, + "@activitySuggestionTimeoutMessage": { + "type": "String", + "placeholders": {} + }, + "@howSpaceCanBeFound": { + "type": "String", + "placeholders": {} + }, + "@private": { + "type": "String", + "placeholders": {} + }, + "@cannotBeFoundInSearch": { + "type": "String", + "placeholders": {} + }, + "@public": { + "type": "String", + "placeholders": {} + }, + "@visibleToCommunity": { + "type": "String", + "placeholders": {} + }, + "@howSpaceCanBeJoined": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundVia": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundViaInvitation": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundViaCodeOrLink": { + "type": "String", + "placeholders": {} + }, + "@canBeFoundViaKnock": { + "type": "String", + "placeholders": {} + }, + "@youHaveLeveledUp": { + "type": "String", + "placeholders": {} + }, + "@sendActivities": { + "type": "String", + "placeholders": {} + }, + "@groupChat": { + "type": "String", + "placeholders": {} + }, + "@directMessage": { + "type": "String", + "placeholders": {} + }, + "@newDirectMessage": { + "type": "String", + "placeholders": {} + }, + "@speakingExercisesTooltip": { + "type": "String", + "placeholders": {} + }, + "@noChatsFoundHereYet": { + "type": "String", + "placeholders": {} + }, + "@duration": { + "type": "String", + "placeholders": {} + }, + "@transcriptionFailed": { + "type": "String", + "placeholders": {} + }, + "@aUserIsKnocking": { + "type": "String", + "placeholders": {} + }, + "@usersAreKnocking": { + "type": "int", + "placeholders": { + "users": { + "type": "int" + } } - } - }, - "@numInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" + }, + "@failedToFetchTranscription": { + "type": "String", + "placeholders": {} + }, + "@deleteEmptySpaceDesc": { + "type": "String", + "placeholders": {} + }, + "@regenerate": { + "type": "String", + "placeholders": {} + }, + "@mySavedActivities": { + "type": "String", + "placeholders": {} + }, + "@noSavedActivities": { + "type": "String", + "placeholders": {} + }, + "@saveActivity": { + "type": "String", + "placeholders": {} + }, + "@failedToPlayVideo": { + "type": "String", + "placeholders": {} + }, + "@done": { + "type": "String", + "placeholders": {} + }, + "@inThisSpace": { + "type": "String", + "placeholders": {} + }, + "@myContacts": { + "type": "String", + "placeholders": {} + }, + "@inviteAllInSpace": { + "type": "String", + "placeholders": {} + }, + "@spaceParticipantsHaveBeenInvitedToTheChat": { + "type": "String", + "placeholders": {} + }, + "@numKnocking": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } } - } - }, - "@saved": { - "type": "String", - "placeholders": {} - }, - "@reset": { - "type": "String", - "placeholders": {} - }, - "@errorGenerateActivityMessage": { - "type": "String", - "placeholders": {} - }, - "@errorRegenerateActivityMessage": { - "type": "String", - "placeholders": {} - }, - "@errorLaunchActivityMessage": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingActivitiesMessage": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingDefinition": { - "type": "String", - "placeholders": {} - }, - "@errorProcessAnalytics": { - "type": "String", - "placeholders": {} - }, - "@errorDownloading": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingLevelSummary": { - "type": "String", - "placeholders": {} - }, - "@errorLoadingSpaceChildren": { - "type": "String", - "placeholders": {} - }, - "@unexpectedError": { - "type": "String", - "placeholders": {} - }, - "@pleaseReload": { - "type": "String", - "placeholders": {} - }, - "@translationError": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingTranslation": { - "type": "String", - "placeholders": {} - }, - "@errorFetchingActivity": { - "type": "String", - "placeholders": {} - }, - "@check": { - "type": "String", - "placeholders": {} - }, - "@unableToFindRoom": { - "type": "String", - "placeholders": {} - }, - "@numCompletedActivities": { - "type": "String", - "placeholders": {} - }, - "@viewingAnalytics": { - "type": "String", - "placeholders": { - "visible": { - "type": "int" - }, - "users": { - "type": "int" + }, + "@numInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } } - } - }, - "@request": { - "type": "String", - "placeholders": {} - }, - "@requestAll": { - "type": "String", - "placeholders": {} - }, - "@confirmMessageUnpin": { - "type": "String", - "placeholders": {} - }, - "@createActivityPlan": { - "type": "String", - "placeholders": {} - }, - "@saveAndLaunch": { - "type": "String", - "placeholders": {} - }, - "@launchToSpace": { - "type": "String", - "placeholders": {} - }, - "@numberOfActivities": { - "type": "String", - "placeholders": {} - }, - "@maximumActivityParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" + }, + "@saved": { + "type": "String", + "placeholders": {} + }, + "@reset": { + "type": "String", + "placeholders": {} + }, + "@errorGenerateActivityMessage": { + "type": "String", + "placeholders": {} + }, + "@errorRegenerateActivityMessage": { + "type": "String", + "placeholders": {} + }, + "@errorLaunchActivityMessage": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingActivitiesMessage": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingDefinition": { + "type": "String", + "placeholders": {} + }, + "@errorProcessAnalytics": { + "type": "String", + "placeholders": {} + }, + "@errorDownloading": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingLevelSummary": { + "type": "String", + "placeholders": {} + }, + "@errorLoadingSpaceChildren": { + "type": "String", + "placeholders": {} + }, + "@unexpectedError": { + "type": "String", + "placeholders": {} + }, + "@pleaseReload": { + "type": "String", + "placeholders": {} + }, + "@translationError": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingTranslation": { + "type": "String", + "placeholders": {} + }, + "@errorFetchingActivity": { + "type": "String", + "placeholders": {} + }, + "@check": { + "type": "String", + "placeholders": {} + }, + "@unableToFindRoom": { + "type": "String", + "placeholders": {} + }, + "@numCompletedActivities": { + "type": "String", + "placeholders": {} + }, + "@viewingAnalytics": { + "type": "String", + "placeholders": { + "visible": { + "type": "int" + }, + "users": { + "type": "int" + } } - } - }, - "@pending": { - "type": "String", - "placeholders": {} - }, - "@inactive": { - "type": "String", - "placeholders": {} - }, - "@confirmRole": { - "type": "String", - "placeholders": {} - }, - "@openRoleLabel": { - "type": "String", - "placeholders": {} - }, - "@joinedTheActivity": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "role": { - "type": "String" + }, + "@request": { + "type": "String", + "placeholders": {} + }, + "@requestAll": { + "type": "String", + "placeholders": {} + }, + "@confirmMessageUnpin": { + "type": "String", + "placeholders": {} + }, + "@createActivityPlan": { + "type": "String", + "placeholders": {} + }, + "@saveAndLaunch": { + "type": "String", + "placeholders": {} + }, + "@launchToSpace": { + "type": "String", + "placeholders": {} + }, + "@numberOfActivities": { + "type": "String", + "placeholders": {} + }, + "@maximumActivityParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } } - } - }, - "@finishedTheActivity": { - "type": "String", - "placeholders": { - "username": { - "type": "String" + }, + "@pending": { + "type": "String", + "placeholders": {} + }, + "@inactive": { + "type": "String", + "placeholders": {} + }, + "@confirmRole": { + "type": "String", + "placeholders": {} + }, + "@openRoleLabel": { + "type": "String", + "placeholders": {} + }, + "@joinedTheActivity": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "role": { + "type": "String" + } } - } - }, - "@archiveToAnalytics": { - "type": "String", - "placeholders": {} - }, - "@activitySummaryError": { - "type": "String", - "placeholders": {} - }, - "@requestSummaries": { - "type": "String", - "placeholders": {} - }, - "@generatingNewActivities": { - "type": "String", - "placeholders": {} - }, - "@requestAccessTitle": { - "type": "String", - "placeholders": {} - }, - "@requestAccessDesc": { - "type": "String", - "placeholders": {} - }, - "@requestAccess": { - "type": "String", - "placeholders": { - "count": { - "type": "int" + }, + "@finishedTheActivity": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } } - } - }, - "@analyticsInactiveTitle": { - "type": "String", - "placeholders": {} - }, - "@analyticsInactiveDesc": { - "type": "String", - "placeholders": {} - }, - "@accessRequestedTitle": { - "type": "String", - "placeholders": {} - }, - "@accessRequestedDesc": { - "type": "String", - "placeholders": { - "admin": { - "type": "String" - }, - "space": { - "type": "String" + }, + "@archiveToAnalytics": { + "type": "String", + "placeholders": {} + }, + "@activitySummaryError": { + "type": "String", + "placeholders": {} + }, + "@requestSummaries": { + "type": "String", + "placeholders": {} + }, + "@generatingNewActivities": { + "type": "String", + "placeholders": {} + }, + "@requestAccessTitle": { + "type": "String", + "placeholders": {} + }, + "@requestAccessDesc": { + "type": "String", + "placeholders": {} + }, + "@requestAccess": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } } - } - }, - "@adminRequestedAccess": { - "type": "String", - "placeholders": {} - }, - "@lastUpdated": { - "type": "String", - "placeholders": { - "time": { - "type": "String" + }, + "@analyticsInactiveTitle": { + "type": "String", + "placeholders": {} + }, + "@analyticsInactiveDesc": { + "type": "String", + "placeholders": {} + }, + "@accessRequestedTitle": { + "type": "String", + "placeholders": {} + }, + "@accessRequestedDesc": { + "type": "String", + "placeholders": { + "admin": { + "type": "String" + }, + "space": { + "type": "String" + } } - } - }, - "@activityFinishedMessage": { - "type": "String", - "placeholders": {} - }, - "@endForAll": { - "type": "String", - "placeholders": {} - }, - "@newCourse": { - "type": "String", - "placeholders": {} - }, - "@numModules": { - "type": "int", - "placeholders": { - "num": { - "type": "int" + }, + "@adminRequestedAccess": { + "type": "String", + "placeholders": {} + }, + "@lastUpdated": { + "type": "String", + "placeholders": { + "time": { + "type": "String" + } } - } - }, - "@coursePlan": { - "type": "String", - "placeholders": {} - }, - "@editCourseLater": { - "type": "String", - "placeholders": {} - }, - "@createCourse": { - "type": "String", - "placeholders": {} - }, - "@stats": { - "type": "String", - "placeholders": {} - }, - "@createGroupChat": { - "type": "String", - "placeholders": {} - }, - "@editCourse": { - "type": "String", - "placeholders": {} - }, - "@inviteDesc": { - "type": "String", - "placeholders": {} - }, - "@editCourseDesc": { - "type": "String", - "placeholders": {} - }, - "@permissionsDesc": { - "type": "String", - "placeholders": {} - }, - "@accessDesc": { - "type": "String", - "placeholders": {} - }, - "@createGroupChatDesc": { - "type": "String", - "placeholders": {} - }, - "@deleteDesc": { - "type": "String", - "placeholders": {} - }, - "@noCourseFound": { - "type": "String", - "placeholders": {} - }, - "@additionalParticipants": { - "type": "int", - "placeholders": { - "num": { - "type": "int" + }, + "@activityFinishedMessage": { + "type": "String", + "placeholders": {} + }, + "@endForAll": { + "type": "String", + "placeholders": {} + }, + "@newCourse": { + "type": "String", + "placeholders": {} + }, + "@numModules": { + "type": "int", + "placeholders": { + "num": { + "type": "int" + } } - } - }, - "@directMessages": { - "type": "String", - "placeholders": {} - }, - "@whatNow": { - "type": "String", - "placeholders": {} - }, - "@chooseNextActivity": { - "type": "String", - "placeholders": {} - }, - "@letsGo": { - "type": "String", - "placeholders": {} - }, - "@chooseRole": { - "type": "String", - "placeholders": {} - }, - "@chooseRoleToParticipate": { - "type": "String", - "placeholders": {} - }, - "@waitingToFillRole": { - "type": "int", - "placeholders": { - "num": { - "type": "int" + }, + "@coursePlan": { + "type": "String", + "placeholders": {} + }, + "@editCourseLater": { + "type": "String", + "placeholders": {} + }, + "@createCourse": { + "type": "String", + "placeholders": {} + }, + "@stats": { + "type": "String", + "placeholders": {} + }, + "@createGroupChat": { + "type": "String", + "placeholders": {} + }, + "@editCourse": { + "type": "String", + "placeholders": {} + }, + "@inviteDesc": { + "type": "String", + "placeholders": {} + }, + "@editCourseDesc": { + "type": "String", + "placeholders": {} + }, + "@permissionsDesc": { + "type": "String", + "placeholders": {} + }, + "@accessDesc": { + "type": "String", + "placeholders": {} + }, + "@createGroupChatDesc": { + "type": "String", + "placeholders": {} + }, + "@deleteDesc": { + "type": "String", + "placeholders": {} + }, + "@noCourseFound": { + "type": "String", + "placeholders": {} + }, + "@additionalParticipants": { + "type": "int", + "placeholders": { + "num": { + "type": "int" + } } - } - }, - "@pingParticipants": { - "type": "String", - "placeholders": {} - }, - "@playWithBot": { - "type": "String", - "placeholders": {} - }, - "@inviteFriends": { - "type": "String", - "placeholders": {} - }, - "@waitNotDone": { - "type": "String", - "placeholders": {} - }, - "@waitingForOthersToFinish": { - "type": "String", - "placeholders": {} - }, - "@generatingSummary": { - "type": "String", - "placeholders": {} - }, - "@findCourse": { - "type": "String", - "placeholders": {} - }, - "@pingParticipantsNotification": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - }, - "room": { - "type": "String" + }, + "@directMessages": { + "type": "String", + "placeholders": {} + }, + "@whatNow": { + "type": "String", + "placeholders": {} + }, + "@chooseNextActivity": { + "type": "String", + "placeholders": {} + }, + "@letsGo": { + "type": "String", + "placeholders": {} + }, + "@chooseRole": { + "type": "String", + "placeholders": {} + }, + "@chooseRoleToParticipate": { + "type": "String", + "placeholders": {} + }, + "@waitingToFillRole": { + "type": "int", + "placeholders": { + "num": { + "type": "int" + } } - } - }, - "@course": { - "type": "String", - "placeholders": {} - }, - "@courses": { - "type": "String", - "placeholders": {} - }, - "@courseName": { - "type": "String", - "placeholders": {} - }, - "@createNewCourse": { - "type": "String", - "placeholders": {} - }, - "@goToCourse": { - "type": "String", - "placeholders": { - "course": {} - } - }, - "@activityComplete": { - "type": "String", - "placeholders": {} - }, - "@startNewSession": { - "type": "String", - "placeholders": {} - }, - "@joinOpenSession": { - "type": "String", - "placeholders": {} - }, - "@less": { - "type": "String", - "placeholders": {} - }, - "@activityNotFound": { - "type": "String", - "placeholders": {} - }, - "@levelUp": { - "type": "String", - "placeholders": {} - }, - "@myActivities": { - "type": "String", - "placeholders": {} - }, - "@openToJoin": { - "type": "String", - "placeholders": {} - }, - "@results": { - "type": "String", - "placeholders": {} - }, - "@activityDone": { - "type": "String", - "placeholders": {} - }, - "@promoCodeInfo": { - "type": "String", - "placeholders": {} - }, - "@editsComingSoon": { - "type": "String", - "placeholders": {} - }, - "@editing": { - "type": "String", - "placeholders": {} - }, - "@activityNeedsOneMember": { - "type": "String", - "placeholders": {} - }, - "@activityNeedsMembers": { - "type": "String", - "placeholders": { - "num": { - "type": "int" + }, + "@pingParticipants": { + "type": "String", + "placeholders": {} + }, + "@playWithBot": { + "type": "String", + "placeholders": {} + }, + "@inviteFriends": { + "type": "String", + "placeholders": {} + }, + "@waitNotDone": { + "type": "String", + "placeholders": {} + }, + "@waitingForOthersToFinish": { + "type": "String", + "placeholders": {} + }, + "@generatingSummary": { + "type": "String", + "placeholders": {} + }, + "@findCourse": { + "type": "String", + "placeholders": {} + }, + "@pingParticipantsNotification": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + }, + "room": { + "type": "String" + } } - } - }, - "@inviteFriendsToCourse": { - "type": "String", - "placeholders": {} - }, - "@subscribeToUnlockActivitySummaries": { - "type": "String", - "placeholders": {} - }, - "@subscribeToUnlockDefinitions": { - "type": "String", - "placeholders": {} - }, - "@subscribeToUnlockTranscriptions": { - "type": "String", - "placeholders": {} - }, - "@pingSent": { - "type": "String", - "placeholders": {} - }, - "@courseTitle": { - "type": "String", - "placeholders": {} - }, - "@courseDesc": { - "type": "String", - "placeholders": {} - }, - "@courseSavedSuccessfully": { - "type": "String", - "placeholders": {} - }, - "@addCoursePlan": { - "type": "String", - "placeholders": {} - }, - "@activityStatsButtonInstruction": { - "type": "String", - "placeholders": {} - }, - "@loginToAccount": { - "type": "String", - "placeholders": {} - }, - "@appDescription": { - "type": "String", - "placeholders": {} - }, - "@languages": { - "type": "String", - "placeholders": {} - }, - "@chooseLanguage": { - "type": "String", - "placeholders": {} - }, - "@planTrip": { - "type": "String", - "placeholders": {} - }, - "@howAreYouTraveling": { - "type": "String", - "placeholders": {} - }, - "@unlockPrivateTrip": { - "type": "String", - "placeholders": {} - }, - "@joinPublicTrip": { - "type": "String", - "placeholders": {} - }, - "@startOwnTrip": { - "type": "String", - "placeholders": {} - }, - "@tripPlanDesc": { - "type": "String", - "placeholders": {} - }, - "@unlockPrivateTripTitle": { - "type": "String", - "placeholders": {} - }, - "@browsePublicTrips": { - "type": "String", - "placeholders": {} - }, - "@startOwnTripTitle": { - "type": "String", - "placeholders": {} - }, - "@courseCode": { - "type": "String", - "placeholders": {} - }, - "@courseCodeHint": { - "type": "String", - "placeholders": {} - }, - "@unlockMyTrip": { - "type": "String", - "placeholders": {} - }, - "@signupOption": { - "type": "String", - "placeholders": {} - }, - "@withApple": { - "type": "String", - "placeholders": {} - }, - "@withGoogle": { - "type": "String", - "placeholders": {} - }, - "@withEmail": { - "type": "String", - "placeholders": {} - }, - "@createAccount": { - "type": "String", - "placeholders": {} - }, - "@loginWithEmail": { - "type": "String", - "placeholders": {} - }, - "@usernameOrEmail": { - "type": "String", - "placeholders": {} - }, - "@email": { - "type": "String", - "placeholders": {} - }, - "@forgotPassword": { - "type": "String", - "placeholders": {} - }, - "@endActivity": { - "type": "String", - "placeholders": {} - }, - "@allLanguages": { - "type": "String", - "placeholders": {} - }, - "@chatListTooltip": { - "type": "String", - "placeholders": {} - }, - "@directMessageBotTitle": { - "type": "String", - "placeholders": {} - }, - "@feedbackTitle": { - "type": "String", - "placeholders": {} - }, - "@feedbackHint": { - "type": "String", - "placeholders": {} - }, - "@feedbackButton": { - "type": "String", - "placeholders": {} - }, - "@directMessageBotDesc": { - "type": "String", - "placeholders": {} - }, - "@inviteYourFriends": { - "type": "String", - "placeholders": {} - }, - "@playWithAI": { - "type": "String", - "placeholders": {} - }, - "@courseStartDesc": { - "type": "String", - "placeholders": {} - }, - "feedbackRespDesc": "Ελέγξτε ξανά αύριο για ενημερώσεις δραστηριότητας.", - "activityDropdownDesc": "Όταν τελειώσετε με αυτή τη δραστηριότητα, κάντε κλικ παρακάτω", - "languageMismatchTitle": "Αντιφάσεις γλώσσας", - "languageMismatchDesc": "Η γλώσσα στόχος σας δεν ταιριάζει με τη γλώσσα αυτής της δραστηριότητας. Θέλετε να ενημερώσετε τη γλώσσα στόχο;", - "reportWordIssueTooltip": "Αναφορά προβλήματος με τις πληροφορίες της λέξης", - "tokenInfoFeedbackDialogTitle": "Ανατροφοδότηση Πληροφοριών Λέξης", - "noPublicCoursesFound": "Δεν βρέθηκαν δημόσια μαθήματα. Θα θέλατε να δημιουργήσετε ένα;", - "noCourseTemplatesFound": "Δεν βρήκαμε μαθήματα για τη γλώσσα στόχο σας. Μπορείτε να συνομιλήσετε με το Pangea Bot εν τω μεταξύ, και να επιστρέψετε αργότερα για περισσότερα μαθήματα.", - "botActivityJoinFailMessage": "Ο Pangea Bot καθυστερεί να απαντήσει. Παρακαλώ δοκιμάστε ξανά αργότερα, ή προσκαλέστε έναν φίλο.", - "unsubscribedResponseError": "Αυτή η λειτουργία απαιτεί συνδρομή", - "leaveDesc": "Αφήστε αυτόν τον χώρο και όλες τις συνομιλίες μέσα σε αυτόν", - "selectAll": "Επιλογή όλων", - "deselectAll": "Αποεπιλογή όλων", - "@feedbackRespDesc": { - "type": "String", - "placeholders": {} - }, - "@activityDropdownDesc": { - "type": "String", - "placeholders": {} - }, - "@languageMismatchTitle": { - "type": "String", - "placeholders": {} - }, - "@languageMismatchDesc": { - "type": "String", - "placeholders": {} - }, - "@reportWordIssueTooltip": { - "type": "String", - "placeholders": {} - }, - "@tokenInfoFeedbackDialogTitle": { - "type": "String", - "placeholders": {} - }, - "@noPublicCoursesFound": { - "type": "String", - "placeholders": {} - }, - "@noCourseTemplatesFound": { - "type": "String", - "placeholders": {} - }, - "@botActivityJoinFailMessage": { - "type": "String", - "placeholders": {} - }, - "@unsubscribedResponseError": { - "type": "String", - "placeholders": {} - }, - "@leaveDesc": { - "type": "String", - "placeholders": {} - }, - "@selectAll": { - "type": "String", - "placeholders": {} - }, - "@deselectAll": { - "type": "String", - "placeholders": {} - }, - "startOwn": "Ξεκίνα το δικό σου", - "joinCourseDesc": "Κάθε μάθημα έχει 8-10 διαδοχικά θέματα με μια σειρά δραστηριοτήτων μάθησης γλώσσας βασισμένων σε εργασίες.", - "newMessageInPangeaChat": "🔊 Νέο μήνυμα στο Pangea Chat", - "shareCourse": "Μοιράσου το μάθημα", - "addCourse": "Πρόσθεσε ένα μάθημα", - "joinPublicCourse": "Συνδεθείτε σε δημόσιο μάθημα", - "vocabLevelsDesc": "Εδώ θα προστεθούν οι λέξεις λεξιλογίου μόλις τις αναβαθμίσετε!", - "highlightVocabTooltip": "Επισημάνετε τις λέξεις-στόχους παρακάτω στέλνοντάς τες ή εξασκώντας τες στη συνομιλία", - "@startOwn": { - "type": "String", - "placeholders": {} - }, - "@joinCourseDesc": { - "type": "String", - "placeholders": {} - }, - "@newMessageInPangeaChat": { - "type": "String", - "placeholders": {} - }, - "@shareCourse": { - "type": "String", - "placeholders": {} - }, - "@addCourse": { - "type": "String", - "placeholders": {} - }, - "@joinPublicCourse": { - "type": "String", - "placeholders": {} - }, - "@vocabLevelsDesc": { - "type": "String", - "placeholders": {} - }, - "emptyChatSearch": "Δεν βρέθηκαν άμεσες μηνύματα ή συνομιλίες. Βεβαιωθείτε ότι η αναζήτησή σας είναι σωστά γραμμένη.", - "activityAnalyticsTooltipBody": "Αυτές είναι οι αποθηκευμένες δραστηριότητές σας για ανασκόπηση και πρακτική.", - "numSavedActivities": "Αριθμός αποθηκευμένων δραστηριοτήτων", - "saveActivityTitle": "Αποθήκευση δραστηριότητας", - "saveActivityDesc": "Καλή δουλειά! Αποθηκεύστε αυτή τη δραστηριότητα για μελλοντική ανασκόπηση και πρακτική", - "levelInfoTooltip": "Εδώ μπορείτε να δείτε όλους τους πόντους που έχετε κερδίσει και πώς!", - "alreadyInCourseWithID": "Είστε ήδη σε ένα μάθημα με αυτό το σχέδιο. Θέλετε να δημιουργήσετε ένα μάθημα με το ίδιο σχέδιο ή να πάτε στο υπάρχον μάθημα;", - "goToExistingCourse": "Πηγαίνετε στο υπάρχον μάθημα", - "emojiView": "Προβολή emoji", - "feedbackDialogDesc": "Κάνω λάθη κι εγώ! Οτιδήποτε μπορεί να με βοηθήσει να βελτιωθώ;", - "contactHasBeenInvitedToTheCourse": "Η επαφή έχει προσκληθεί στο μάθημα", - "activityStatsButtonTooltip": "Πληροφορίες δραστηριότητας", - "allow": "Επιτρέπω", - "deny": "Αρνούμαι", - "enabledRenewal": "Ενεργοποίηση Ανανέωσης Συνδρομής", - "subscriptionEndsOn": "Η Συνδρομή Λήγει Στις", - "subscriptionRenewsOn": "Η Συνδρομή Ανανεώνεται Στις", - "waitForSubscriptionChanges": "Οι αλλαγές στη συνδρομή σας μπορεί να χρειαστούν λίγο χρόνο για να εμφανιστούν στην εφαρμογή.", - "subscribeReadingAssistance": "Εγγραφείτε για να ξεκλειδώσετε τα εργαλεία μηνυμάτων", - "aceDisplayName": "Αχινέζικα", - "achDisplayName": "Ακόλι", - "afDisplayName": "Αφρικάανς", - "akDisplayName": "Ακάν", - "alzDisplayName": "Αλούρ", - "amDisplayName": "Αμχαρικά", - "arDisplayName": "Αραβικά", - "asDisplayName": "Ασαμέζ", - "awaDisplayName": "Αουάντι", - "ayDisplayName": "Αϊμάρα", - "azDisplayName": "Αζερικά", - "baDisplayName": "Μπασκίρ", - "banDisplayName": "Μπαλινέζικα", - "bbcDisplayName": "Μπατάκ Τόμπα", - "beDisplayName": "Λευκορωσικά", - "bemDisplayName": "Μπέμπα", - "bewDisplayName": "Μπεταβί", - "bgDisplayName": "Βουλγαρικά", - "bhoDisplayName": "Μποχτζπούρι", - "bikDisplayName": "Μπικολ", - "bmDisplayName": "Μπαμπάρα", - "bnDisplayName": "Μπενγκάλι", - "bnBDDisplayName": "Μπενγκάλι (Μπαγκλαντές)", - "bnINDisplayName": "Μπενγκάλι (Ινδία)", - "brDisplayName": "Βρετονικά", - "bsDisplayName": "Βοσνιακά", - "btsDisplayName": "Μπατάκ Σιμαλούνγκουν", - "btxDisplayName": "Μπατάκ Καρό", - "buaDisplayName": "Μπουριάτ", - "caDisplayName": "Καταλανικά", - "cebDisplayName": "Σεμπουάνο", - "cggDisplayName": "Χίγκα", - "chmDisplayName": "Μάρι", - "ckbDisplayName": "Κεντρικά Κουρδικά", - "cnhDisplayName": "Χάκα Τσιν", - "coDisplayName": "Κορσικανικά", - "crhDisplayName": "Κριμαϊκά Τουρκικά", - "crsDisplayName": "Σεσέλβα Κρεολικά Γαλλικά", - "csDisplayName": "Τσέχικα", - "cvDisplayName": "Τσουβάς", - "cyDisplayName": "Ουαλικά", - "daDisplayName": "Δανέζικα", - "deDisplayName": "Γερμανικά", - "dinDisplayName": "Ντίγκα", - "doiDisplayName": "Ντόγκρι", - "dovDisplayName": "Ντόμπε", - "dzDisplayName": "Ντζονγκκά", - "eeDisplayName": "Εβέ", - "enDisplayName": "Αγγλικά", - "enAUDisplayName": "Αγγλικά (Αυστραλία)", - "enGBDisplayName": "Αγγλικά (Ηνωμένο Βασίλειο)", - "enINDisplayName": "Αγγλικά (Ινδία)", - "enUSDisplayName": "Αγγλικά (ΗΠΑ)", - "eoDisplayName": "Εσπεράντο", - "esDisplayName": "Ισπανικά", - "esESDisplayName": "Ισπανικά (Ισπανία)", - "esMXDisplayName": "Ισπανικά (Μεξικό)", - "euDisplayName": "Βάσκικα", - "faDisplayName": "Περσικά", - "ffDisplayName": "Φουλάχ", - "fiDisplayName": "Φινλανδικά", - "filDisplayName": "Φιλιππινέζικα", - "fjDisplayName": "Φιτζιανικά", - "foDisplayName": "Φαροέζικα", - "frDisplayName": "Γαλλικά", - "frCADisplayName": "Γαλλικά (Καναδάς)", - "frFRDisplayName": "Γαλλικά (Γαλλία)", - "fyDisplayName": "Δυτικά Φριζιανά", - "gaDisplayName": "Ιρλανδικά", - "gaaDisplayName": "Γκα", - "gdDisplayName": "Σκωτικά Γαελικά", - "glDisplayName": "Γαλικιανά", - "gnDisplayName": "Γκουαρανί", - "gomDisplayName": "Γκόαν Κονκάνι", - "guDisplayName": "Γκουτζαράτι", - "haDisplayName": "Χάουσα", - "hawDisplayName": "Χαβανέζικα", - "heDisplayName": "Εβραϊκά", - "hiDisplayName": "Χίντι", - "hilDisplayName": "Χιλιγκάινον", - "hmnDisplayName": "Χμονγκ", - "hneDisplayName": "Χαττισγκάρχι", - "hrDisplayName": "Κροατικά", - "hrxDisplayName": "Χουνσρικ", - "htDisplayName": "Αϊτινός Κρεόλ", - "huDisplayName": "Ουγγρικά", - "hyDisplayName": "Αρμενικά", - "idDisplayName": "Ινδονησιακά", - "igDisplayName": "Ίγκο", - "iloDisplayName": "Ιλόκο", - "isDisplayName": "Ισλανδικά", - "itDisplayName": "Ιταλικά", - "jaDisplayName": "Ιαπωνικά", - "jvDisplayName": "Ιαβανικά", - "kaDisplayName": "Γεωργιανά", - "kkDisplayName": "Καζακικά", - "kmDisplayName": "Χμερ", - "knDisplayName": "Καννάδα", - "koDisplayName": "Κορεατικά", - "kokDisplayName": "Κονκάνι", - "kriDisplayName": "Κρίο", - "ksDisplayName": "Κασμίρι", - "ktuDisplayName": "Κιτούμπα (Δημοκρατική Δημοκρατία του Κονγκό)", - "kuDisplayName": "Κουρδικά", - "kyDisplayName": "Κιργιζικά", - "laDisplayName": "Λατινικά", - "lbDisplayName": "Λουξεμβουργιανά", - "lgDisplayName": "Γκάντα", - "liDisplayName": "Λιμβουργιανά", - "lijDisplayName": "Λιγουριανά", - "lmoDisplayName": "Λομβαρδικά", - "lnDisplayName": "Λινγκάλα", - "loDisplayName": "Λάο", - "ltDisplayName": "Λιθουανικά", - "ltgDisplayName": "Λατγαλικά", - "luoDisplayName": "Λούο (Κένυα και Τανζανία)", - "lusDisplayName": "Μίζο", - "lvDisplayName": "Λετονικά", - "maiDisplayName": "Μαϊθίλι", - "makDisplayName": "Μακασάρ", - "mgDisplayName": "Μαλαγασί", - "miDisplayName": "Μάορι", - "minDisplayName": "Μιναγκαμπάου", - "mkDisplayName": "Μακεδονικά", - "mlDisplayName": "Μαλαγιαλάμ", - "mnDisplayName": "Μογγολικά", - "mniDisplayName": "Μανιπούρι", - "mrDisplayName": "Μαραθί", - "msDisplayName": "Μαλάι", - "msArabDisplayName": "Μαλάι (Αραβικά)", - "msMYDisplayName": "Μαλάι (Μαλαισία)", - "mtDisplayName": "Μαλτέζικα", - "mwrDisplayName": "Μαραθί", - "myDisplayName": "Βιρμανικά", - "nanDisplayName": "Μιν Ναν", - "nbDisplayName": "Νορβηγικά (Μποκμάλ)", - "neDisplayName": "Νεπαλέζικα", - "newDisplayName": "Νεουαρί", - "nlDisplayName": "Ολλανδικά", - "nlBEDisplayName": "Φλαμανδικά", - "noDisplayName": "Νορβηγικά", - "nrDisplayName": "Νότιο Ντεμπέλε", - "nsoDisplayName": "Βόρειος Σόθο", - "nusDisplayName": "Νούερ", - "nyDisplayName": "Νιάντζα", - "ocDisplayName": "Οξιτανικά", - "omDisplayName": "Ορόμο", - "orDisplayName": "Οντία", - "paDisplayName": "Παντζάμπι", - "paArabDisplayName": "Παντζάμπι (Σαχμουκί)", - "paINDisplayName": "Παντζάμπι (Γκουρμούκι)", - "pagDisplayName": "Πανγκασινάν", - "pamDisplayName": "Πάμπανγκα", - "papDisplayName": "Πάπιεντο", - "plDisplayName": "Πολωνικά", - "psDisplayName": "Παστού", - "ptDisplayName": "Πορτογαλικά", - "ptBRDisplayName": "Πορτογαλικά (Βραζιλία)", - "ptPTDisplayName": "Πορτογαλικά (Πορτογαλία)", - "quDisplayName": "Κετσούα", - "rajDisplayName": "Ρατζαστάνι", - "rnDisplayName": "Ρούντι", - "roDisplayName": "Ρουμανικά", - "roMDDisplayName": "Μολδαβικά", - "romDisplayName": "Ρομά", - "ruDisplayName": "Ρωσικά", - "rwDisplayName": "Κινυαρουάντα", - "saDisplayName": "Σανσκριτικά", - "satDisplayName": "Σαντάλι", - "scnDisplayName": "Σικελικά", - "sdDisplayName": "Σιντί", - "sgDisplayName": "Σάνγκο", - "shnDisplayName": "Σαν", - "siDisplayName": "Σινχάλα", - "skDisplayName": "Σλοβακικά", - "slDisplayName": "Σλοβενικά", - "smDisplayName": "Σαμοά", - "snDisplayName": "Σόνα", - "soDisplayName": "Σομαλικά", - "sqDisplayName": "Αλβανικά", - "srDisplayName": "Σερβικά", - "srMEDisplayName": "Μαυροβούνιο", - "ssDisplayName": "Σουαχίλι", - "stDisplayName": "Νότιος Σόθο", - "suDisplayName": "Σουντανησιακά", - "svDisplayName": "Σουηδικά", - "swDisplayName": "Σουαχίλι", - "szlDisplayName": "Σιλεσιανά", - "taDisplayName": "Ταμίλ", - "teDisplayName": "Τελούγκου", - "tetDisplayName": "Τετούμ", - "tgDisplayName": "Τατζικικά", - "thDisplayName": "Ταϊλανδέζικα", - "tiDisplayName": "Τιγκρινιά", - "tkDisplayName": "Τουρκμενικά", - "tlDisplayName": "Ταγκάλογ", - "tnDisplayName": "Τσουάνα", - "trDisplayName": "Τουρκικά", - "tsDisplayName": "Τσόνγκα", - "ttDisplayName": "Τατάρ", - "ugDisplayName": "Ουιγούρο", - "ukDisplayName": "Ουκρανικά", - "urDisplayName": "Ουρντού", - "urINDisplayName": "Ουρντού (Ινδία)", - "urPKDisplayName": "Ουρντού (Πακιστάν)", - "uzDisplayName": "Ουζμπεκικά", - "viDisplayName": "Βιετναμέζικα", - "wuuDisplayName": "Γου", - "xhDisplayName": "Χόσα", - "yiDisplayName": "Γίντις", - "yoDisplayName": "Γιορούμπα", - "yuaDisplayName": "Γιουκατέκο", - "yueDisplayName": "Καντονέζικα", - "yueCNDisplayName": "Καντονέζικα (Κίνα)", - "yueHKDisplayName": "Καντονέζικα (Χονγκ Κονγκ)", - "zhDisplayName": "Κινέζικα", - "zhCNDisplayName": "Κινέζικα (Απλοποιημένα)", - "zhTWDisplayName": "Κινέζικα (Παραδοσιακά)", - "zuDisplayName": "Ζουλού", - "@emptyChatSearch": { - "type": "String", - "placeholders": {} - }, - "@activityAnalyticsTooltipBody": { - "type": "String", - "placeholders": {} - }, - "@numSavedActivities": { - "type": "String", - "placeholders": {} - }, - "@saveActivityTitle": { - "type": "String", - "placeholders": {} - }, - "@saveActivityDesc": { - "type": "String", - "placeholders": {} - }, - "@levelInfoTooltip": { - "type": "String", - "placeholders": {} - }, - "@alreadyInCourseWithID": { - "type": "String", - "placeholders": {} - }, - "@goToExistingCourse": { - "type": "String", - "placeholders": {} - }, - "@emojiView": { - "type": "String", - "placeholders": {} - }, - "@feedbackDialogDesc": { - "type": "String", - "placeholders": {} - }, - "@contactHasBeenInvitedToTheCourse": { - "type": "String", - "placeholders": {} - }, - "@activityStatsButtonTooltip": { - "type": "String", - "placeholders": {} - }, - "@allow": { - "type": "String", - "placeholders": {} - }, - "@deny": { - "type": "String", - "placeholders": {} - }, - "@enabledRenewal": { - "type": "String", - "placeholders": {} - }, - "@subscriptionEndsOn": { - "type": "String", - "placeholders": {} - }, - "@subscriptionRenewsOn": { - "type": "String", - "placeholders": {} - }, - "@waitForSubscriptionChanges": { - "type": "String", - "placeholders": {} - }, - "@subscribeReadingAssistance": { - "type": "String", - "placeholders": {} - }, - "@aceDisplayName": { - "type": "String", - "placeholders": {} - }, - "@achDisplayName": { - "type": "String", - "placeholders": {} - }, - "@afDisplayName": { - "type": "String", - "placeholders": {} - }, - "@akDisplayName": { - "type": "String", - "placeholders": {} - }, - "@alzDisplayName": { - "type": "String", - "placeholders": {} - }, - "@amDisplayName": { - "type": "String", - "placeholders": {} - }, - "@arDisplayName": { - "type": "String", - "placeholders": {} - }, - "@asDisplayName": { - "type": "String", - "placeholders": {} - }, - "@awaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ayDisplayName": { - "type": "String", - "placeholders": {} - }, - "@azDisplayName": { - "type": "String", - "placeholders": {} - }, - "@baDisplayName": { - "type": "String", - "placeholders": {} - }, - "@banDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bbcDisplayName": { - "type": "String", - "placeholders": {} - }, - "@beDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bemDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bewDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bhoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bikDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bmDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnBDDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bnINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@brDisplayName": { - "type": "String", - "placeholders": {} - }, - "@bsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@btsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@btxDisplayName": { - "type": "String", - "placeholders": {} - }, - "@buaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@caDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cebDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cggDisplayName": { - "type": "String", - "placeholders": {} - }, - "@chmDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ckbDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cnhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@coDisplayName": { - "type": "String", - "placeholders": {} - }, - "@crhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@crsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@csDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cvDisplayName": { - "type": "String", - "placeholders": {} - }, - "@cyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@daDisplayName": { - "type": "String", - "placeholders": {} - }, - "@deDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dinDisplayName": { - "type": "String", - "placeholders": {} - }, - "@doiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dovDisplayName": { - "type": "String", - "placeholders": {} - }, - "@dzDisplayName": { - "type": "String", - "placeholders": {} - }, - "@eeDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enAUDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enGBDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@enUSDisplayName": { - "type": "String", - "placeholders": {} - }, - "@eoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esESDisplayName": { - "type": "String", - "placeholders": {} - }, - "@esMXDisplayName": { - "type": "String", - "placeholders": {} - }, - "@euDisplayName": { - "type": "String", - "placeholders": {} - }, - "@faDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ffDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@filDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fjDisplayName": { - "type": "String", - "placeholders": {} - }, - "@foDisplayName": { - "type": "String", - "placeholders": {} - }, - "@frDisplayName": { - "type": "String", - "placeholders": {} - }, - "@frCADisplayName": { - "type": "String", - "placeholders": {} - }, - "@frFRDisplayName": { - "type": "String", - "placeholders": {} - }, - "@fyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gaaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gdDisplayName": { - "type": "String", - "placeholders": {} - }, - "@glDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@gomDisplayName": { - "type": "String", - "placeholders": {} - }, - "@guDisplayName": { - "type": "String", - "placeholders": {} - }, - "@haDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hawDisplayName": { - "type": "String", - "placeholders": {} - }, - "@heDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hilDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hmnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hneDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hrxDisplayName": { - "type": "String", - "placeholders": {} - }, - "@htDisplayName": { - "type": "String", - "placeholders": {} - }, - "@huDisplayName": { - "type": "String", - "placeholders": {} - }, - "@hyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@idDisplayName": { - "type": "String", - "placeholders": {} - }, - "@igDisplayName": { - "type": "String", - "placeholders": {} - }, - "@iloDisplayName": { - "type": "String", - "placeholders": {} - }, - "@isDisplayName": { - "type": "String", - "placeholders": {} - }, - "@itDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@jvDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kmDisplayName": { - "type": "String", - "placeholders": {} - }, - "@knDisplayName": { - "type": "String", - "placeholders": {} - }, - "@koDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kokDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kriDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ksDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ktuDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kuDisplayName": { - "type": "String", - "placeholders": {} - }, - "@kyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@laDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lbDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@liDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lijDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lmoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@loDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ltDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ltgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@luoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lusDisplayName": { - "type": "String", - "placeholders": {} - }, - "@lvDisplayName": { - "type": "String", - "placeholders": {} - }, - "@maiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@makDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@miDisplayName": { - "type": "String", - "placeholders": {} - }, - "@minDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mniDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msArabDisplayName": { - "type": "String", - "placeholders": {} - }, - "@msMYDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mtDisplayName": { - "type": "String", - "placeholders": {} - }, - "@mwrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@myDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nanDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nbDisplayName": { - "type": "String", - "placeholders": {} - }, - "@neDisplayName": { - "type": "String", - "placeholders": {} - }, - "@newDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nlBEDisplayName": { - "type": "String", - "placeholders": {} - }, - "@noDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nrDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nsoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nusDisplayName": { - "type": "String", - "placeholders": {} - }, - "@nyDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ocDisplayName": { - "type": "String", - "placeholders": {} - }, - "@omDisplayName": { - "type": "String", - "placeholders": {} - }, - "@orDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paArabDisplayName": { - "type": "String", - "placeholders": {} - }, - "@paINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pagDisplayName": { - "type": "String", - "placeholders": {} - }, - "@pamDisplayName": { - "type": "String", - "placeholders": {} - }, - "@papDisplayName": { - "type": "String", - "placeholders": {} - }, - "@plDisplayName": { - "type": "String", - "placeholders": {} - }, - "@psDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptBRDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ptPTDisplayName": { - "type": "String", - "placeholders": {} - }, - "@quDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rajDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@roDisplayName": { - "type": "String", - "placeholders": {} - }, - "@roMDDisplayName": { - "type": "String", - "placeholders": {} - }, - "@romDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ruDisplayName": { - "type": "String", - "placeholders": {} - }, - "@rwDisplayName": { - "type": "String", - "placeholders": {} - }, - "@saDisplayName": { - "type": "String", - "placeholders": {} - }, - "@satDisplayName": { - "type": "String", - "placeholders": {} - }, - "@scnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sdDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@shnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@siDisplayName": { - "type": "String", - "placeholders": {} - }, - "@skDisplayName": { - "type": "String", - "placeholders": {} - }, - "@slDisplayName": { - "type": "String", - "placeholders": {} - }, - "@smDisplayName": { - "type": "String", - "placeholders": {} - }, - "@snDisplayName": { - "type": "String", - "placeholders": {} - }, - "@soDisplayName": { - "type": "String", - "placeholders": {} - }, - "@sqDisplayName": { - "type": "String", - "placeholders": {} - }, - "@srDisplayName": { - "type": "String", - "placeholders": {} - }, - "@srMEDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ssDisplayName": { - "type": "String", - "placeholders": {} - }, - "@stDisplayName": { - "type": "String", - "placeholders": {} - }, - "@suDisplayName": { - "type": "String", - "placeholders": {} - }, - "@svDisplayName": { - "type": "String", - "placeholders": {} - }, - "@swDisplayName": { - "type": "String", - "placeholders": {} - }, - "@szlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@taDisplayName": { - "type": "String", - "placeholders": {} - }, - "@teDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tetDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tgDisplayName": { - "type": "String", - "placeholders": {} - }, - "@thDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tkDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tlDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tnDisplayName": { - "type": "String", - "placeholders": {} - }, - "@trDisplayName": { - "type": "String", - "placeholders": {} - }, - "@tsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ttDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ugDisplayName": { - "type": "String", - "placeholders": {} - }, - "@ukDisplayName": { - "type": "String", - "placeholders": {} - }, - "@urDisplayName": { - "type": "String", - "placeholders": {} - }, - "@urINDisplayName": { - "type": "String", - "placeholders": {} - }, - "@urPKDisplayName": { - "type": "String", - "placeholders": {} - }, - "@uzDisplayName": { - "type": "String", - "placeholders": {} - }, - "@viDisplayName": { - "type": "String", - "placeholders": {} - }, - "@wuuDisplayName": { - "type": "String", - "placeholders": {} - }, - "@xhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yiDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yoDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yuaDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yueDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yueCNDisplayName": { - "type": "String", - "placeholders": {} - }, - "@yueHKDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zhDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zhCNDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zhTWDisplayName": { - "type": "String", - "placeholders": {} - }, - "@zuDisplayName": { - "type": "String", - "placeholders": {} - }, - "teacherModeTitle": "Λειτουργία Δασκάλου", - "teacherModeDesc": "Εναλλαγή για να ξεκλειδώσετε όλα τα θέματα και τις δραστηριότητες. Μόνο για διαχειριστές μαθημάτων.", - "@teacherModeTitle": { - "type": "String", - "placeholders": {} - }, - "@teacherModeDesc": { - "type": "String", - "placeholders": {} - }, - "failedToLoadFeedback": "Αποτυχία φόρτωσης ανατροφοδότησης.", - "unreadPlus": "99+", - "noSavedActivitiesYet": "Οι δραστηριότητες θα εμφανιστούν εδώ μόλις ολοκληρωθούν και αποθηκευτούν.", - "@failedToLoadFeedback": { - "type": "String", - "placeholders": {} - }, - "@unreadPlus": { - "type": "String", - "placeholders": {} - }, - "@noSavedActivitiesYet": { - "type": "String", - "placeholders": {} - }, - "changeCourse": "Αλλαγή μαθήματος", - "changeCourseDesc": "Εδώ μπορείτε να αλλάξετε το σχέδιο μαθήματος αυτού του μαθήματος.", - "@changeCourse": { - "type": "String", - "placeholders": {} - }, - "@changeCourseDesc": { - "type": "String", - "placeholders": {} - }, - "practiceActivityCompleted": "Η δραστηριότητα πρακτικής ολοκληρώθηκε", - "@practiceActivityCompleted": { - "type": "String", - "placeholders": {} - }, - "introChatTitle": "Δημιουργία Συνομιλίας Εισαγωγών", - "introChatDesc": "Οποιοσδήποτε στον χώρο μπορεί να δημοσιεύσει.", - "announcementsChatTitle": "Συνομιλία Ανακοινώσεων", - "announcementsChatDesc": "Μόνο ο διαχειριστής του χώρου μπορεί να δημοσιεύσει.", - "@introChatTitle": { - "type": "String", - "placeholders": {} - }, - "@introChatDesc": { - "type": "String", - "placeholders": {} - }, - "@announcementsChatTitle": { - "type": "String", - "placeholders": {} - }, - "@announcementsChatDesc": { - "type": "String", - "placeholders": {} - }, - "notStartedActivitiesTitle": "Ανοιχτές συνεδρίες ({num})", - "inProgressActivitiesTitle": "Γίνεται τώρα ({num})", - "completedActivitiesTitle": "Ολοκληρώθηκε ({num})", - "pickDifferentActivity": "Επιλέξτε μια διαφορετική δραστηριότητα", - "inOngoingActivity": "Έχετε μια τρέχουσα δραστηριότητα!", - "@notStartedActivitiesTitle": { - "type": "String", - "placeholders": { - "num": { - "type": "int" + }, + "@course": { + "type": "String", + "placeholders": {} + }, + "@courses": { + "type": "String", + "placeholders": {} + }, + "@courseName": { + "type": "String", + "placeholders": {} + }, + "@createNewCourse": { + "type": "String", + "placeholders": {} + }, + "@goToCourse": { + "type": "String", + "placeholders": { + "course": {} } - } - }, - "@inProgressActivitiesTitle": { - "type": "String", - "placeholders": { - "num": { - "type": "int" + }, + "@activityComplete": { + "type": "String", + "placeholders": {} + }, + "@startNewSession": { + "type": "String", + "placeholders": {} + }, + "@joinOpenSession": { + "type": "String", + "placeholders": {} + }, + "@less": { + "type": "String", + "placeholders": {} + }, + "@activityNotFound": { + "type": "String", + "placeholders": {} + }, + "@levelUp": { + "type": "String", + "placeholders": {} + }, + "@myActivities": { + "type": "String", + "placeholders": {} + }, + "@openToJoin": { + "type": "String", + "placeholders": {} + }, + "@results": { + "type": "String", + "placeholders": {} + }, + "@activityDone": { + "type": "String", + "placeholders": {} + }, + "@promoCodeInfo": { + "type": "String", + "placeholders": {} + }, + "@editsComingSoon": { + "type": "String", + "placeholders": {} + }, + "@editing": { + "type": "String", + "placeholders": {} + }, + "@activityNeedsOneMember": { + "type": "String", + "placeholders": {} + }, + "@activityNeedsMembers": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } } - } - }, - "@completedActivitiesTitle": { - "type": "String", - "placeholders": { - "num": { - "type": "int" + }, + "@inviteFriendsToCourse": { + "type": "String", + "placeholders": {} + }, + "@subscribeToUnlockActivitySummaries": { + "type": "String", + "placeholders": {} + }, + "@subscribeToUnlockDefinitions": { + "type": "String", + "placeholders": {} + }, + "@subscribeToUnlockTranscriptions": { + "type": "String", + "placeholders": {} + }, + "@pingSent": { + "type": "String", + "placeholders": {} + }, + "@courseTitle": { + "type": "String", + "placeholders": {} + }, + "@courseDesc": { + "type": "String", + "placeholders": {} + }, + "@courseSavedSuccessfully": { + "type": "String", + "placeholders": {} + }, + "@addCoursePlan": { + "type": "String", + "placeholders": {} + }, + "@activityStatsButtonInstruction": { + "type": "String", + "placeholders": {} + }, + "@loginToAccount": { + "type": "String", + "placeholders": {} + }, + "@appDescription": { + "type": "String", + "placeholders": {} + }, + "@languages": { + "type": "String", + "placeholders": {} + }, + "@chooseLanguage": { + "type": "String", + "placeholders": {} + }, + "@planTrip": { + "type": "String", + "placeholders": {} + }, + "@howAreYouTraveling": { + "type": "String", + "placeholders": {} + }, + "@unlockPrivateTrip": { + "type": "String", + "placeholders": {} + }, + "@joinPublicTrip": { + "type": "String", + "placeholders": {} + }, + "@startOwnTrip": { + "type": "String", + "placeholders": {} + }, + "@tripPlanDesc": { + "type": "String", + "placeholders": {} + }, + "@unlockPrivateTripTitle": { + "type": "String", + "placeholders": {} + }, + "@browsePublicTrips": { + "type": "String", + "placeholders": {} + }, + "@startOwnTripTitle": { + "type": "String", + "placeholders": {} + }, + "@courseCode": { + "type": "String", + "placeholders": {} + }, + "@courseCodeHint": { + "type": "String", + "placeholders": {} + }, + "@unlockMyTrip": { + "type": "String", + "placeholders": {} + }, + "@signupOption": { + "type": "String", + "placeholders": {} + }, + "@withApple": { + "type": "String", + "placeholders": {} + }, + "@withGoogle": { + "type": "String", + "placeholders": {} + }, + "@withEmail": { + "type": "String", + "placeholders": {} + }, + "@createAccount": { + "type": "String", + "placeholders": {} + }, + "@loginWithEmail": { + "type": "String", + "placeholders": {} + }, + "@usernameOrEmail": { + "type": "String", + "placeholders": {} + }, + "@email": { + "type": "String", + "placeholders": {} + }, + "@forgotPassword": { + "type": "String", + "placeholders": {} + }, + "@endActivity": { + "type": "String", + "placeholders": {} + }, + "@allLanguages": { + "type": "String", + "placeholders": {} + }, + "@chatListTooltip": { + "type": "String", + "placeholders": {} + }, + "@directMessageBotTitle": { + "type": "String", + "placeholders": {} + }, + "@feedbackTitle": { + "type": "String", + "placeholders": {} + }, + "@feedbackHint": { + "type": "String", + "placeholders": {} + }, + "@feedbackButton": { + "type": "String", + "placeholders": {} + }, + "@directMessageBotDesc": { + "type": "String", + "placeholders": {} + }, + "@inviteYourFriends": { + "type": "String", + "placeholders": {} + }, + "@playWithAI": { + "type": "String", + "placeholders": {} + }, + "@courseStartDesc": { + "type": "String", + "placeholders": {} + }, + "feedbackRespDesc": "Ελέγξτε ξανά αύριο για ενημερώσεις δραστηριότητας.", + "activityDropdownDesc": "Όταν τελειώσετε με αυτή τη δραστηριότητα, κάντε κλικ παρακάτω", + "languageMismatchTitle": "Αντιφάσεις γλώσσας", + "languageMismatchDesc": "Η γλώσσα στόχος σας δεν ταιριάζει με τη γλώσσα αυτής της δραστηριότητας. Θέλετε να ενημερώσετε τη γλώσσα στόχο;", + "reportWordIssueTooltip": "Αναφορά προβλήματος με τις πληροφορίες της λέξης", + "tokenInfoFeedbackDialogTitle": "Ανατροφοδότηση Πληροφοριών Λέξης", + "noPublicCoursesFound": "Δεν βρέθηκαν δημόσια μαθήματα. Θα θέλατε να δημιουργήσετε ένα;", + "noCourseTemplatesFound": "Δεν βρήκαμε μαθήματα για τη γλώσσα στόχο σας. Μπορείτε να συνομιλήσετε με το Pangea Bot εν τω μεταξύ, και να επιστρέψετε αργότερα για περισσότερα μαθήματα.", + "botActivityJoinFailMessage": "Ο Pangea Bot καθυστερεί να απαντήσει. Παρακαλώ δοκιμάστε ξανά αργότερα, ή προσκαλέστε έναν φίλο.", + "unsubscribedResponseError": "Αυτή η λειτουργία απαιτεί συνδρομή", + "leaveDesc": "Αφήστε αυτόν τον χώρο και όλες τις συνομιλίες μέσα σε αυτόν", + "selectAll": "Επιλογή όλων", + "deselectAll": "Αποεπιλογή όλων", + "@feedbackRespDesc": { + "type": "String", + "placeholders": {} + }, + "@activityDropdownDesc": { + "type": "String", + "placeholders": {} + }, + "@languageMismatchTitle": { + "type": "String", + "placeholders": {} + }, + "@languageMismatchDesc": { + "type": "String", + "placeholders": {} + }, + "@reportWordIssueTooltip": { + "type": "String", + "placeholders": {} + }, + "@tokenInfoFeedbackDialogTitle": { + "type": "String", + "placeholders": {} + }, + "@noPublicCoursesFound": { + "type": "String", + "placeholders": {} + }, + "@noCourseTemplatesFound": { + "type": "String", + "placeholders": {} + }, + "@botActivityJoinFailMessage": { + "type": "String", + "placeholders": {} + }, + "@unsubscribedResponseError": { + "type": "String", + "placeholders": {} + }, + "@leaveDesc": { + "type": "String", + "placeholders": {} + }, + "@selectAll": { + "type": "String", + "placeholders": {} + }, + "@deselectAll": { + "type": "String", + "placeholders": {} + }, + "startOwn": "Ξεκίνα το δικό σου", + "joinCourseDesc": "Κάθε μάθημα έχει 8-10 διαδοχικά θέματα με μια σειρά δραστηριοτήτων μάθησης γλώσσας βασισμένων σε εργασίες.", + "newMessageInPangeaChat": "🔊 Νέο μήνυμα στο Pangea Chat", + "shareCourse": "Μοιράσου το μάθημα", + "addCourse": "Πρόσθεσε ένα μάθημα", + "joinPublicCourse": "Συνδεθείτε σε δημόσιο μάθημα", + "vocabLevelsDesc": "Εδώ θα προστεθούν οι λέξεις λεξιλογίου μόλις τις αναβαθμίσετε!", + "highlightVocabTooltip": "Επισημάνετε τις λέξεις-στόχους παρακάτω στέλνοντάς τες ή εξασκώντας τες στη συνομιλία", + "@startOwn": { + "type": "String", + "placeholders": {} + }, + "@joinCourseDesc": { + "type": "String", + "placeholders": {} + }, + "@newMessageInPangeaChat": { + "type": "String", + "placeholders": {} + }, + "@shareCourse": { + "type": "String", + "placeholders": {} + }, + "@addCourse": { + "type": "String", + "placeholders": {} + }, + "@joinPublicCourse": { + "type": "String", + "placeholders": {} + }, + "@vocabLevelsDesc": { + "type": "String", + "placeholders": {} + }, + "emptyChatSearch": "Δεν βρέθηκαν άμεσες μηνύματα ή συνομιλίες. Βεβαιωθείτε ότι η αναζήτησή σας είναι σωστά γραμμένη.", + "activityAnalyticsTooltipBody": "Αυτές είναι οι αποθηκευμένες δραστηριότητές σας για ανασκόπηση και πρακτική.", + "numSavedActivities": "Αριθμός αποθηκευμένων δραστηριοτήτων", + "saveActivityTitle": "Αποθήκευση δραστηριότητας", + "saveActivityDesc": "Καλή δουλειά! Αποθηκεύστε αυτή τη δραστηριότητα για μελλοντική ανασκόπηση και πρακτική", + "levelInfoTooltip": "Εδώ μπορείτε να δείτε όλους τους πόντους που έχετε κερδίσει και πώς!", + "alreadyInCourseWithID": "Είστε ήδη σε ένα μάθημα με αυτό το σχέδιο. Θέλετε να δημιουργήσετε ένα μάθημα με το ίδιο σχέδιο ή να πάτε στο υπάρχον μάθημα;", + "goToExistingCourse": "Πηγαίνετε στο υπάρχον μάθημα", + "emojiView": "Προβολή emoji", + "feedbackDialogDesc": "Κάνω λάθη κι εγώ! Οτιδήποτε μπορεί να με βοηθήσει να βελτιωθώ;", + "contactHasBeenInvitedToTheCourse": "Η επαφή έχει προσκληθεί στο μάθημα", + "activityStatsButtonTooltip": "Πληροφορίες δραστηριότητας", + "allow": "Επιτρέπω", + "deny": "Αρνούμαι", + "enabledRenewal": "Ενεργοποίηση Ανανέωσης Συνδρομής", + "subscriptionEndsOn": "Η Συνδρομή Λήγει Στις", + "subscriptionRenewsOn": "Η Συνδρομή Ανανεώνεται Στις", + "waitForSubscriptionChanges": "Οι αλλαγές στη συνδρομή σας μπορεί να χρειαστούν λίγο χρόνο για να εμφανιστούν στην εφαρμογή.", + "subscribeReadingAssistance": "Εγγραφείτε για να ξεκλειδώσετε τα εργαλεία μηνυμάτων", + "aceDisplayName": "Αχινέζικα", + "achDisplayName": "Ακόλι", + "afDisplayName": "Αφρικάανς", + "akDisplayName": "Ακάν", + "alzDisplayName": "Αλούρ", + "amDisplayName": "Αμχαρικά", + "arDisplayName": "Αραβικά", + "asDisplayName": "Ασαμέζ", + "awaDisplayName": "Αουάντι", + "ayDisplayName": "Αϊμάρα", + "azDisplayName": "Αζερικά", + "baDisplayName": "Μπασκίρ", + "banDisplayName": "Μπαλινέζικα", + "bbcDisplayName": "Μπατάκ Τόμπα", + "beDisplayName": "Λευκορωσικά", + "bemDisplayName": "Μπέμπα", + "bewDisplayName": "Μπεταβί", + "bgDisplayName": "Βουλγαρικά", + "bhoDisplayName": "Μποχτζπούρι", + "bikDisplayName": "Μπικολ", + "bmDisplayName": "Μπαμπάρα", + "bnDisplayName": "Μπενγκάλι", + "bnBDDisplayName": "Μπενγκάλι (Μπαγκλαντές)", + "bnINDisplayName": "Μπενγκάλι (Ινδία)", + "brDisplayName": "Βρετονικά", + "bsDisplayName": "Βοσνιακά", + "btsDisplayName": "Μπατάκ Σιμαλούνγκουν", + "btxDisplayName": "Μπατάκ Καρό", + "buaDisplayName": "Μπουριάτ", + "caDisplayName": "Καταλανικά", + "cebDisplayName": "Σεμπουάνο", + "cggDisplayName": "Χίγκα", + "chmDisplayName": "Μάρι", + "ckbDisplayName": "Κεντρικά Κουρδικά", + "cnhDisplayName": "Χάκα Τσιν", + "coDisplayName": "Κορσικανικά", + "crhDisplayName": "Κριμαϊκά Τουρκικά", + "crsDisplayName": "Σεσέλβα Κρεολικά Γαλλικά", + "csDisplayName": "Τσέχικα", + "cvDisplayName": "Τσουβάς", + "cyDisplayName": "Ουαλικά", + "daDisplayName": "Δανέζικα", + "deDisplayName": "Γερμανικά", + "dinDisplayName": "Ντίγκα", + "doiDisplayName": "Ντόγκρι", + "dovDisplayName": "Ντόμπε", + "dzDisplayName": "Ντζονγκκά", + "eeDisplayName": "Εβέ", + "enDisplayName": "Αγγλικά", + "enAUDisplayName": "Αγγλικά (Αυστραλία)", + "enGBDisplayName": "Αγγλικά (Ηνωμένο Βασίλειο)", + "enINDisplayName": "Αγγλικά (Ινδία)", + "enUSDisplayName": "Αγγλικά (ΗΠΑ)", + "eoDisplayName": "Εσπεράντο", + "esDisplayName": "Ισπανικά", + "esESDisplayName": "Ισπανικά (Ισπανία)", + "esMXDisplayName": "Ισπανικά (Μεξικό)", + "euDisplayName": "Βάσκικα", + "faDisplayName": "Περσικά", + "ffDisplayName": "Φουλάχ", + "fiDisplayName": "Φινλανδικά", + "filDisplayName": "Φιλιππινέζικα", + "fjDisplayName": "Φιτζιανικά", + "foDisplayName": "Φαροέζικα", + "frDisplayName": "Γαλλικά", + "frCADisplayName": "Γαλλικά (Καναδάς)", + "frFRDisplayName": "Γαλλικά (Γαλλία)", + "fyDisplayName": "Δυτικά Φριζιανά", + "gaDisplayName": "Ιρλανδικά", + "gaaDisplayName": "Γκα", + "gdDisplayName": "Σκωτικά Γαελικά", + "glDisplayName": "Γαλικιανά", + "gnDisplayName": "Γκουαρανί", + "gomDisplayName": "Γκόαν Κονκάνι", + "guDisplayName": "Γκουτζαράτι", + "haDisplayName": "Χάουσα", + "hawDisplayName": "Χαβανέζικα", + "heDisplayName": "Εβραϊκά", + "hiDisplayName": "Χίντι", + "hilDisplayName": "Χιλιγκάινον", + "hmnDisplayName": "Χμονγκ", + "hneDisplayName": "Χαττισγκάρχι", + "hrDisplayName": "Κροατικά", + "hrxDisplayName": "Χουνσρικ", + "htDisplayName": "Αϊτινός Κρεόλ", + "huDisplayName": "Ουγγρικά", + "hyDisplayName": "Αρμενικά", + "idDisplayName": "Ινδονησιακά", + "igDisplayName": "Ίγκο", + "iloDisplayName": "Ιλόκο", + "isDisplayName": "Ισλανδικά", + "itDisplayName": "Ιταλικά", + "jaDisplayName": "Ιαπωνικά", + "jvDisplayName": "Ιαβανικά", + "kaDisplayName": "Γεωργιανά", + "kkDisplayName": "Καζακικά", + "kmDisplayName": "Χμερ", + "knDisplayName": "Καννάδα", + "koDisplayName": "Κορεατικά", + "kokDisplayName": "Κονκάνι", + "kriDisplayName": "Κρίο", + "ksDisplayName": "Κασμίρι", + "ktuDisplayName": "Κιτούμπα (Δημοκρατική Δημοκρατία του Κονγκό)", + "kuDisplayName": "Κουρδικά", + "kyDisplayName": "Κιργιζικά", + "laDisplayName": "Λατινικά", + "lbDisplayName": "Λουξεμβουργιανά", + "lgDisplayName": "Γκάντα", + "liDisplayName": "Λιμβουργιανά", + "lijDisplayName": "Λιγουριανά", + "lmoDisplayName": "Λομβαρδικά", + "lnDisplayName": "Λινγκάλα", + "loDisplayName": "Λάο", + "ltDisplayName": "Λιθουανικά", + "ltgDisplayName": "Λατγαλικά", + "luoDisplayName": "Λούο (Κένυα και Τανζανία)", + "lusDisplayName": "Μίζο", + "lvDisplayName": "Λετονικά", + "maiDisplayName": "Μαϊθίλι", + "makDisplayName": "Μακασάρ", + "mgDisplayName": "Μαλαγασί", + "miDisplayName": "Μάορι", + "minDisplayName": "Μιναγκαμπάου", + "mkDisplayName": "Μακεδονικά", + "mlDisplayName": "Μαλαγιαλάμ", + "mnDisplayName": "Μογγολικά", + "mniDisplayName": "Μανιπούρι", + "mrDisplayName": "Μαραθί", + "msDisplayName": "Μαλάι", + "msArabDisplayName": "Μαλάι (Αραβικά)", + "msMYDisplayName": "Μαλάι (Μαλαισία)", + "mtDisplayName": "Μαλτέζικα", + "mwrDisplayName": "Μαραθί", + "myDisplayName": "Βιρμανικά", + "nanDisplayName": "Μιν Ναν", + "nbDisplayName": "Νορβηγικά (Μποκμάλ)", + "neDisplayName": "Νεπαλέζικα", + "newDisplayName": "Νεουαρί", + "nlDisplayName": "Ολλανδικά", + "nlBEDisplayName": "Φλαμανδικά", + "noDisplayName": "Νορβηγικά", + "nrDisplayName": "Νότιο Ντεμπέλε", + "nsoDisplayName": "Βόρειος Σόθο", + "nusDisplayName": "Νούερ", + "nyDisplayName": "Νιάντζα", + "ocDisplayName": "Οξιτανικά", + "omDisplayName": "Ορόμο", + "orDisplayName": "Οντία", + "paDisplayName": "Παντζάμπι", + "paArabDisplayName": "Παντζάμπι (Σαχμουκί)", + "paINDisplayName": "Παντζάμπι (Γκουρμούκι)", + "pagDisplayName": "Πανγκασινάν", + "pamDisplayName": "Πάμπανγκα", + "papDisplayName": "Πάπιεντο", + "plDisplayName": "Πολωνικά", + "psDisplayName": "Παστού", + "ptDisplayName": "Πορτογαλικά", + "ptBRDisplayName": "Πορτογαλικά (Βραζιλία)", + "ptPTDisplayName": "Πορτογαλικά (Πορτογαλία)", + "quDisplayName": "Κετσούα", + "rajDisplayName": "Ρατζαστάνι", + "rnDisplayName": "Ρούντι", + "roDisplayName": "Ρουμανικά", + "roMDDisplayName": "Μολδαβικά", + "romDisplayName": "Ρομά", + "ruDisplayName": "Ρωσικά", + "rwDisplayName": "Κινυαρουάντα", + "saDisplayName": "Σανσκριτικά", + "satDisplayName": "Σαντάλι", + "scnDisplayName": "Σικελικά", + "sdDisplayName": "Σιντί", + "sgDisplayName": "Σάνγκο", + "shnDisplayName": "Σαν", + "siDisplayName": "Σινχάλα", + "skDisplayName": "Σλοβακικά", + "slDisplayName": "Σλοβενικά", + "smDisplayName": "Σαμοά", + "snDisplayName": "Σόνα", + "soDisplayName": "Σομαλικά", + "sqDisplayName": "Αλβανικά", + "srDisplayName": "Σερβικά", + "srMEDisplayName": "Μαυροβούνιο", + "ssDisplayName": "Σουαχίλι", + "stDisplayName": "Νότιος Σόθο", + "suDisplayName": "Σουντανησιακά", + "svDisplayName": "Σουηδικά", + "swDisplayName": "Σουαχίλι", + "szlDisplayName": "Σιλεσιανά", + "taDisplayName": "Ταμίλ", + "teDisplayName": "Τελούγκου", + "tetDisplayName": "Τετούμ", + "tgDisplayName": "Τατζικικά", + "thDisplayName": "Ταϊλανδέζικα", + "tiDisplayName": "Τιγκρινιά", + "tkDisplayName": "Τουρκμενικά", + "tlDisplayName": "Ταγκάλογ", + "tnDisplayName": "Τσουάνα", + "trDisplayName": "Τουρκικά", + "tsDisplayName": "Τσόνγκα", + "ttDisplayName": "Τατάρ", + "ugDisplayName": "Ουιγούρο", + "ukDisplayName": "Ουκρανικά", + "urDisplayName": "Ουρντού", + "urINDisplayName": "Ουρντού (Ινδία)", + "urPKDisplayName": "Ουρντού (Πακιστάν)", + "uzDisplayName": "Ουζμπεκικά", + "viDisplayName": "Βιετναμέζικα", + "wuuDisplayName": "Γου", + "xhDisplayName": "Χόσα", + "yiDisplayName": "Γίντις", + "yoDisplayName": "Γιορούμπα", + "yuaDisplayName": "Γιουκατέκο", + "yueDisplayName": "Καντονέζικα", + "yueCNDisplayName": "Καντονέζικα (Κίνα)", + "yueHKDisplayName": "Καντονέζικα (Χονγκ Κονγκ)", + "zhDisplayName": "Κινέζικα", + "zhCNDisplayName": "Κινέζικα (Απλοποιημένα)", + "zhTWDisplayName": "Κινέζικα (Παραδοσιακά)", + "zuDisplayName": "Ζουλού", + "@emptyChatSearch": { + "type": "String", + "placeholders": {} + }, + "@activityAnalyticsTooltipBody": { + "type": "String", + "placeholders": {} + }, + "@numSavedActivities": { + "type": "String", + "placeholders": {} + }, + "@saveActivityTitle": { + "type": "String", + "placeholders": {} + }, + "@saveActivityDesc": { + "type": "String", + "placeholders": {} + }, + "@levelInfoTooltip": { + "type": "String", + "placeholders": {} + }, + "@alreadyInCourseWithID": { + "type": "String", + "placeholders": {} + }, + "@goToExistingCourse": { + "type": "String", + "placeholders": {} + }, + "@emojiView": { + "type": "String", + "placeholders": {} + }, + "@feedbackDialogDesc": { + "type": "String", + "placeholders": {} + }, + "@contactHasBeenInvitedToTheCourse": { + "type": "String", + "placeholders": {} + }, + "@activityStatsButtonTooltip": { + "type": "String", + "placeholders": {} + }, + "@allow": { + "type": "String", + "placeholders": {} + }, + "@deny": { + "type": "String", + "placeholders": {} + }, + "@enabledRenewal": { + "type": "String", + "placeholders": {} + }, + "@subscriptionEndsOn": { + "type": "String", + "placeholders": {} + }, + "@subscriptionRenewsOn": { + "type": "String", + "placeholders": {} + }, + "@waitForSubscriptionChanges": { + "type": "String", + "placeholders": {} + }, + "@subscribeReadingAssistance": { + "type": "String", + "placeholders": {} + }, + "@aceDisplayName": { + "type": "String", + "placeholders": {} + }, + "@achDisplayName": { + "type": "String", + "placeholders": {} + }, + "@afDisplayName": { + "type": "String", + "placeholders": {} + }, + "@akDisplayName": { + "type": "String", + "placeholders": {} + }, + "@alzDisplayName": { + "type": "String", + "placeholders": {} + }, + "@amDisplayName": { + "type": "String", + "placeholders": {} + }, + "@arDisplayName": { + "type": "String", + "placeholders": {} + }, + "@asDisplayName": { + "type": "String", + "placeholders": {} + }, + "@awaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ayDisplayName": { + "type": "String", + "placeholders": {} + }, + "@azDisplayName": { + "type": "String", + "placeholders": {} + }, + "@baDisplayName": { + "type": "String", + "placeholders": {} + }, + "@banDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bbcDisplayName": { + "type": "String", + "placeholders": {} + }, + "@beDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bemDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bewDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bhoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bikDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bmDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnBDDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bnINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@brDisplayName": { + "type": "String", + "placeholders": {} + }, + "@bsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@btsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@btxDisplayName": { + "type": "String", + "placeholders": {} + }, + "@buaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@caDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cebDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cggDisplayName": { + "type": "String", + "placeholders": {} + }, + "@chmDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ckbDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cnhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@coDisplayName": { + "type": "String", + "placeholders": {} + }, + "@crhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@crsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@csDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cvDisplayName": { + "type": "String", + "placeholders": {} + }, + "@cyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@daDisplayName": { + "type": "String", + "placeholders": {} + }, + "@deDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dinDisplayName": { + "type": "String", + "placeholders": {} + }, + "@doiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dovDisplayName": { + "type": "String", + "placeholders": {} + }, + "@dzDisplayName": { + "type": "String", + "placeholders": {} + }, + "@eeDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enAUDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enGBDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@enUSDisplayName": { + "type": "String", + "placeholders": {} + }, + "@eoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esESDisplayName": { + "type": "String", + "placeholders": {} + }, + "@esMXDisplayName": { + "type": "String", + "placeholders": {} + }, + "@euDisplayName": { + "type": "String", + "placeholders": {} + }, + "@faDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ffDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@filDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fjDisplayName": { + "type": "String", + "placeholders": {} + }, + "@foDisplayName": { + "type": "String", + "placeholders": {} + }, + "@frDisplayName": { + "type": "String", + "placeholders": {} + }, + "@frCADisplayName": { + "type": "String", + "placeholders": {} + }, + "@frFRDisplayName": { + "type": "String", + "placeholders": {} + }, + "@fyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gaaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gdDisplayName": { + "type": "String", + "placeholders": {} + }, + "@glDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@gomDisplayName": { + "type": "String", + "placeholders": {} + }, + "@guDisplayName": { + "type": "String", + "placeholders": {} + }, + "@haDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hawDisplayName": { + "type": "String", + "placeholders": {} + }, + "@heDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hilDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hmnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hneDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hrxDisplayName": { + "type": "String", + "placeholders": {} + }, + "@htDisplayName": { + "type": "String", + "placeholders": {} + }, + "@huDisplayName": { + "type": "String", + "placeholders": {} + }, + "@hyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@idDisplayName": { + "type": "String", + "placeholders": {} + }, + "@igDisplayName": { + "type": "String", + "placeholders": {} + }, + "@iloDisplayName": { + "type": "String", + "placeholders": {} + }, + "@isDisplayName": { + "type": "String", + "placeholders": {} + }, + "@itDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@jvDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kmDisplayName": { + "type": "String", + "placeholders": {} + }, + "@knDisplayName": { + "type": "String", + "placeholders": {} + }, + "@koDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kokDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kriDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ksDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ktuDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kuDisplayName": { + "type": "String", + "placeholders": {} + }, + "@kyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@laDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lbDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@liDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lijDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lmoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@loDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ltDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ltgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@luoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lusDisplayName": { + "type": "String", + "placeholders": {} + }, + "@lvDisplayName": { + "type": "String", + "placeholders": {} + }, + "@maiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@makDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@miDisplayName": { + "type": "String", + "placeholders": {} + }, + "@minDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mniDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msArabDisplayName": { + "type": "String", + "placeholders": {} + }, + "@msMYDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mtDisplayName": { + "type": "String", + "placeholders": {} + }, + "@mwrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@myDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nanDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nbDisplayName": { + "type": "String", + "placeholders": {} + }, + "@neDisplayName": { + "type": "String", + "placeholders": {} + }, + "@newDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nlBEDisplayName": { + "type": "String", + "placeholders": {} + }, + "@noDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nrDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nsoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nusDisplayName": { + "type": "String", + "placeholders": {} + }, + "@nyDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ocDisplayName": { + "type": "String", + "placeholders": {} + }, + "@omDisplayName": { + "type": "String", + "placeholders": {} + }, + "@orDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paArabDisplayName": { + "type": "String", + "placeholders": {} + }, + "@paINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pagDisplayName": { + "type": "String", + "placeholders": {} + }, + "@pamDisplayName": { + "type": "String", + "placeholders": {} + }, + "@papDisplayName": { + "type": "String", + "placeholders": {} + }, + "@plDisplayName": { + "type": "String", + "placeholders": {} + }, + "@psDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptBRDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ptPTDisplayName": { + "type": "String", + "placeholders": {} + }, + "@quDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rajDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@roDisplayName": { + "type": "String", + "placeholders": {} + }, + "@roMDDisplayName": { + "type": "String", + "placeholders": {} + }, + "@romDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ruDisplayName": { + "type": "String", + "placeholders": {} + }, + "@rwDisplayName": { + "type": "String", + "placeholders": {} + }, + "@saDisplayName": { + "type": "String", + "placeholders": {} + }, + "@satDisplayName": { + "type": "String", + "placeholders": {} + }, + "@scnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sdDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@shnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@siDisplayName": { + "type": "String", + "placeholders": {} + }, + "@skDisplayName": { + "type": "String", + "placeholders": {} + }, + "@slDisplayName": { + "type": "String", + "placeholders": {} + }, + "@smDisplayName": { + "type": "String", + "placeholders": {} + }, + "@snDisplayName": { + "type": "String", + "placeholders": {} + }, + "@soDisplayName": { + "type": "String", + "placeholders": {} + }, + "@sqDisplayName": { + "type": "String", + "placeholders": {} + }, + "@srDisplayName": { + "type": "String", + "placeholders": {} + }, + "@srMEDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ssDisplayName": { + "type": "String", + "placeholders": {} + }, + "@stDisplayName": { + "type": "String", + "placeholders": {} + }, + "@suDisplayName": { + "type": "String", + "placeholders": {} + }, + "@svDisplayName": { + "type": "String", + "placeholders": {} + }, + "@swDisplayName": { + "type": "String", + "placeholders": {} + }, + "@szlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@taDisplayName": { + "type": "String", + "placeholders": {} + }, + "@teDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tetDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tgDisplayName": { + "type": "String", + "placeholders": {} + }, + "@thDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tkDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tlDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tnDisplayName": { + "type": "String", + "placeholders": {} + }, + "@trDisplayName": { + "type": "String", + "placeholders": {} + }, + "@tsDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ttDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ugDisplayName": { + "type": "String", + "placeholders": {} + }, + "@ukDisplayName": { + "type": "String", + "placeholders": {} + }, + "@urDisplayName": { + "type": "String", + "placeholders": {} + }, + "@urINDisplayName": { + "type": "String", + "placeholders": {} + }, + "@urPKDisplayName": { + "type": "String", + "placeholders": {} + }, + "@uzDisplayName": { + "type": "String", + "placeholders": {} + }, + "@viDisplayName": { + "type": "String", + "placeholders": {} + }, + "@wuuDisplayName": { + "type": "String", + "placeholders": {} + }, + "@xhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yiDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yoDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yuaDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yueDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yueCNDisplayName": { + "type": "String", + "placeholders": {} + }, + "@yueHKDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zhDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zhCNDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zhTWDisplayName": { + "type": "String", + "placeholders": {} + }, + "@zuDisplayName": { + "type": "String", + "placeholders": {} + }, + "teacherModeTitle": "Λειτουργία Δασκάλου", + "teacherModeDesc": "Εναλλαγή για να ξεκλειδώσετε όλα τα θέματα και τις δραστηριότητες. Μόνο για διαχειριστές μαθημάτων.", + "@teacherModeTitle": { + "type": "String", + "placeholders": {} + }, + "@teacherModeDesc": { + "type": "String", + "placeholders": {} + }, + "failedToLoadFeedback": "Αποτυχία φόρτωσης ανατροφοδότησης.", + "unreadPlus": "99+", + "noSavedActivitiesYet": "Οι δραστηριότητες θα εμφανιστούν εδώ μόλις ολοκληρωθούν και αποθηκευτούν.", + "@failedToLoadFeedback": { + "type": "String", + "placeholders": {} + }, + "@unreadPlus": { + "type": "String", + "placeholders": {} + }, + "@noSavedActivitiesYet": { + "type": "String", + "placeholders": {} + }, + "changeCourse": "Αλλαγή μαθήματος", + "changeCourseDesc": "Εδώ μπορείτε να αλλάξετε το σχέδιο μαθήματος αυτού του μαθήματος.", + "@changeCourse": { + "type": "String", + "placeholders": {} + }, + "@changeCourseDesc": { + "type": "String", + "placeholders": {} + }, + "practiceActivityCompleted": "Η δραστηριότητα πρακτικής ολοκληρώθηκε", + "@practiceActivityCompleted": { + "type": "String", + "placeholders": {} + }, + "introChatTitle": "Δημιουργία Συνομιλίας Εισαγωγών", + "introChatDesc": "Οποιοσδήποτε στον χώρο μπορεί να δημοσιεύσει.", + "announcementsChatTitle": "Συνομιλία Ανακοινώσεων", + "announcementsChatDesc": "Μόνο ο διαχειριστής του χώρου μπορεί να δημοσιεύσει.", + "@introChatTitle": { + "type": "String", + "placeholders": {} + }, + "@introChatDesc": { + "type": "String", + "placeholders": {} + }, + "@announcementsChatTitle": { + "type": "String", + "placeholders": {} + }, + "@announcementsChatDesc": { + "type": "String", + "placeholders": {} + }, + "notStartedActivitiesTitle": "Ανοιχτές συνεδρίες ({num})", + "inProgressActivitiesTitle": "Γίνεται τώρα ({num})", + "completedActivitiesTitle": "Ολοκληρώθηκε ({num})", + "pickDifferentActivity": "Επιλέξτε μια διαφορετική δραστηριότητα", + "inOngoingActivity": "Έχετε μια τρέχουσα δραστηριότητα!", + "@notStartedActivitiesTitle": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } } - } - }, - "@pickDifferentActivity": { - "type": "String", - "placeholders": {} - }, - "messageLanguageMismatchMessage": "Η γλώσσα στόχου σας δεν ταιριάζει με αυτό το μήνυμα. Θέλετε να ενημερώσετε τη γλώσσα στόχου σας;", - "@messageLanguageMismatchMessage": { - "type": "String", - "placeholders": {} - }, - "courseParticipantTooltip": "Αυτοί είναι όλοι σε αυτό το μάθημα. Κάντε κλικ στο avatar οποιουδήποτε χρήστη και \"ξεκινήστε συνομιλία\" για να στείλετε ένα DM.", - "@courseParticipantTooltip": { - "type": "String", - "placeholders": {} - }, - "blockLemmaConfirmation": "Αυτή η λέξη λεξιλογίου θα αφαιρεθεί μόνιμα από την ανάλυσή σας", - "woman": "Γυναίκα", - "man": "Άνδρας", - "otherGender": "Άλλο", - "unselectedGender": "Επιλέξτε μια επιλογή φύλου", - "gender": "Φύλο", - "chatParticipantTooltip": "Αυτοί είναι όλοι σε αυτή τη συνομιλία. Κάντε κλικ στο avatar οποιουδήποτε χρήστη και \"ξεκινήστε συνομιλία\" για να στείλετε ένα DM.", - "@blockLemmaConfirmation": { - "type": "String", - "placeholders": {} - }, - "@woman": { - "type": "String", - "placeholders": {} - }, - "@man": { - "type": "String", - "placeholders": {} - }, - "@otherGender": { - "type": "String", - "placeholders": {} - }, - "@unselectedGender": { - "type": "String", - "placeholders": {} - }, - "@gender": { - "type": "String", - "placeholders": {} - }, - "@chatParticipantTooltip": { - "type": "String", - "placeholders": {} - }, - "@inOngoingActivity": { - "type": "String", - "placeholders": {} - }, - "modeDisabled": "Τα εργαλεία μάθησης είναι απενεργοποιημένα για μηνύματα που δεν είναι στη γλώσσα στόχου σας.", - "vocabEmoji": "Εικόνισμα λεξιλογίου", - "@modeDisabled": { - "type": "String", - "placeholders": {} - }, - "@vocabEmoji": { - "type": "String", - "placeholders": {} - }, - "requestRegeneration": "Αίτημα αναγέννησης", - "optionalRegenerateReason": "(Προαιρετικό) Λόγος", - "@requestRegeneration": { - "type": "String", - "placeholders": {} - }, - "@optionalRegenerateReason": { - "type": "String", - "placeholders": {} - }, - "emojiSelectedSnackbar": "Έχετε ορίσει το emoji για {lemma}! Θα χρησιμοποιήσουμε αυτό το emoji για να εκπροσωπήσουμε τη λέξη σε πρακτικές δραστηριότητες στο εξής.", - "@emojiSelectedSnackbar": { - "type": "String", - "placeholders": { - "lemma": { - "type": "String" + }, + "@inProgressActivitiesTitle": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } } - } - }, - "ssoDialogTitle": "Περιμένοντας την ολοκλήρωση της σύνδεσης", - "ssoDialogDesc": "Ανοίξαμε μια νέα καρτέλα ώστε να μπορέσετε να συνδεθείτε με ασφάλεια.", - "ssoDialogHelpText": "🤔 Αν δεν είδατε τη νέα καρτέλα, παρακαλώ ελέγξτε τον αποκλειστή αναδυόμενων παραθύρων σας.", - "@ssoDialogTitle": { - "type": "String", - "placeholders": {} - }, - "@ssoDialogDesc": { - "type": "String", - "placeholders": {} - }, - "@ssoDialogHelpText": { - "type": "String", - "placeholders": {} - }, - "disableLanguageToolsTitle": "Απενεργοποίηση εργαλείων γλώσσας", - "disableLanguageToolsDesc": "Θα θέλατε να απενεργοποιήσετε την αυτόματη βοήθεια γλώσσας;", - "@disableLanguageToolsTitle": { - "type": "String", - "placeholders": {} - }, - "@disableLanguageToolsDesc": { - "type": "String", - "placeholders": {} - }, - "recordingPermissionDenied": "Η άδεια απορρίφθηκε. Ενεργοποιήστε τις άδειες εγγραφής για να καταγράψετε ηχητικά μηνύματα.", - "genericWebRecordingError": "Κάτι πήγε στραβά. Συνιστούμε να χρησιμοποιείτε τον περιηγητή Chrome κατά την εγγραφή μηνυμάτων.", - "@recordingPermissionDenied": { - "type": "String", - "placeholders": {} - }, - "@genericWebRecordingError": { - "type": "String", - "placeholders": {} - }, - "screenSizeWarning": "Για την καλύτερη εμπειρία χρήσης αυτής της εφαρμογής, παρακαλώ επεκτείνετε το μέγεθος της οθόνης σας.", - "@screenSizeWarning": { - "type": "String", - "placeholders": {} - }, - "activitiesToUnlockTopicTitle": "Δραστηριότητες για Ξεκλείδωμα Επόμενου Θέματος", - "activitiesToUnlockTopicDesc": "Ορίστε τον αριθμό των δραστηριοτήτων για να ξεκλειδώσετε το επόμενο θέμα του μαθήματος", - "@activitiesToUnlockTopicTitle": { - "type": "String", - "placeholders": {} - }, - "@activitiesToUnlockTopicDesc": { - "type": "String", - "placeholders": {} - }, - "constructUseCorLMDesc": "Διόρθωση πρακτικής ορισμού λεξιλογίου", - "constructUseIncLMDesc": "Λάθος πρακτική ορισμού λεξιλογίου", - "constructUseCorLADesc": "Διόρθωση πρακτικής ήχου λεξιλογίου", - "constructUseIncLADesc": "Λάθος πρακτική ήχου λεξιλογίου", - "constructUseBonus": "Μπόνους κατά τη διάρκεια της πρακτικής λεξιλογίου", - "practiceVocab": "Πρακτική λεξιλογίου", - "selectMeaning": "Επιλέξτε την έννοια", - "selectAudio": "Επιλέξτε τον αντίστοιχο ήχο", - "congratulations": "Συγχαρητήρια!", - "anotherRound": "Μια ακόμη γύρος", - "noActivityRequest": "Δεν υπάρχει τρέχουσα αίτηση δραστηριότητας.", - "mustHave10Words": "Πρέπει να έχετε τουλάχιστον 10 λέξεις λεξιλογίου για να τις εξασκήσετε. Δοκιμάστε να μιλήσετε με έναν φίλο ή με τον Pangea Bot για να ανακαλύψετε περισσότερα!", - "botSettings": "Ρυθμίσεις Bot", - "activitySettingsOverrideWarning": "Η γλώσσα και το επίπεδο γλώσσας καθορίζονται από το σχέδιο δραστηριότητας", - "voice": "Φωνή", - "@constructUseCorLMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncLMDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorLADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncLADesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseBonus": { - "type": "String", - "placeholders": {} - }, - "@practiceVocab": { - "type": "String", - "placeholders": {} - }, - "@selectMeaning": { - "type": "String", - "placeholders": {} - }, - "@selectAudio": { - "type": "String", - "placeholders": {} - }, - "@congratulations": { - "type": "String", - "placeholders": {} - }, - "@anotherRound": { - "type": "String", - "placeholders": {} - }, - "@noActivityRequest": { - "type": "String", - "placeholders": {} - }, - "@mustHave10Words": { - "type": "String", - "placeholders": {} - }, - "@botSettings": { - "type": "String", - "placeholders": {} - }, - "@activitySettingsOverrideWarning": { - "type": "String", - "placeholders": {} - }, - "@voice": { - "type": "String", - "placeholders": {} - }, - "youLeftTheChat": "🚪 Αφήσατε τη συνομιλία", - "@youLeftTheChat": { - "type": "String", - "placeholders": {} - }, - "downloadInitiated": "Η λήψη ξεκίνησε", - "webDownloadPermissionMessage": "Εάν ο περιηγητής σας μπλοκάρει τις λήψεις, παρακαλώ ενεργοποιήστε τις λήψεις για αυτόν τον ιστότοπο.", - "@downloadInitiated": { - "type": "String", - "placeholders": {} - }, - "@webDownloadPermissionMessage": { - "type": "String", - "placeholders": {} - }, - "exitPractice": "Η πρόοδος της συνεδρίας πρακτικής σας δεν θα αποθηκευτεί.", - "practiceGrammar": "Πρακτική γραμματικής", - "notEnoughToPractice": "Στείλτε περισσότερα μηνύματα για να ξεκλειδώσετε την πρακτική", - "constructUseCorGCDesc": "Πρακτική κατηγορίας σωστής γραμματικής", - "constructUseIncGCDesc": "Πρακτική κατηγορίας λανθαστής γραμματικής", - "@exitPractice": { - "type": "String", - "placeholders": {} - }, - "@practiceGrammar": { - "type": "String", - "placeholders": {} - }, - "@notEnoughToPractice": { - "type": "String", - "placeholders": {} - }, - "@constructUseCorGCDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncGCDesc": { - "type": "String", - "placeholders": {} - }, - "constructUseCorGEDesc": "Πρακτική διόρθωσης γραμματικών λαθών", - "constructUseIncGEDesc": "Πρακτική λανθασμένων γραμματικών λαθών", - "fillInBlank": "Συμπληρώστε το κενό με τη σωστή επιλογή", - "learn": "Μάθετε", - "languageUpdated": "Η γλώσσα στόχος ενημερώθηκε!", - "@constructUseCorGEDesc": { - "type": "String", - "placeholders": {} - }, - "@constructUseIncGEDesc": { - "type": "String", - "placeholders": {} - }, - "@fillInBlank": { - "type": "String", - "placeholders": {} - }, - "@learn": { - "type": "String", - "placeholders": {} - }, - "@languageUpdated": { - "type": "String", - "placeholders": {} - }, - "voiceDropdownTitle": "Φωνή Pangea Bot", - "@voiceDropdownTitle": { - "type": "String", - "placeholders": {} - }, - "knockDesc": "Το αίτημά σας έχει σταλεί στον διαχειριστή του μαθήματος! Θα σας επιτρέψουν να μπείτε αν το εγκρίνουν.", - "@knockDesc": { - "type": "String", - "placeholders": {} - }, - "joinSpaceOnboardingDesc": "Έχετε έναν κωδικό πρόσκλησης ή σύνδεσμο για ένα δημόσιο μάθημα;", - "welcomeUser": "Καλώς ήρθατε {user}", - "@joinSpaceOnboardingDesc": { - "type": "String", - "placeholders": {} - }, - "@welcomeUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" + }, + "@completedActivitiesTitle": { + "type": "String", + "placeholders": { + "num": { + "type": "int" + } } + }, + "@pickDifferentActivity": { + "type": "String", + "placeholders": {} + }, + "messageLanguageMismatchMessage": "Η γλώσσα στόχου σας δεν ταιριάζει με αυτό το μήνυμα. Θέλετε να ενημερώσετε τη γλώσσα στόχου σας;", + "@messageLanguageMismatchMessage": { + "type": "String", + "placeholders": {} + }, + "courseParticipantTooltip": "Αυτοί είναι όλοι σε αυτό το μάθημα. Κάντε κλικ στο avatar οποιουδήποτε χρήστη και \"ξεκινήστε συνομιλία\" για να στείλετε ένα DM.", + "@courseParticipantTooltip": { + "type": "String", + "placeholders": {} + }, + "blockLemmaConfirmation": "Αυτή η λέξη λεξιλογίου θα αφαιρεθεί μόνιμα από την ανάλυσή σας", + "woman": "Γυναίκα", + "man": "Άνδρας", + "otherGender": "Άλλο", + "unselectedGender": "Επιλέξτε μια επιλογή φύλου", + "gender": "Φύλο", + "chatParticipantTooltip": "Αυτοί είναι όλοι σε αυτή τη συνομιλία. Κάντε κλικ στο avatar οποιουδήποτε χρήστη και \"ξεκινήστε συνομιλία\" για να στείλετε ένα DM.", + "@blockLemmaConfirmation": { + "type": "String", + "placeholders": {} + }, + "@woman": { + "type": "String", + "placeholders": {} + }, + "@man": { + "type": "String", + "placeholders": {} + }, + "@otherGender": { + "type": "String", + "placeholders": {} + }, + "@unselectedGender": { + "type": "String", + "placeholders": {} + }, + "@gender": { + "type": "String", + "placeholders": {} + }, + "@chatParticipantTooltip": { + "type": "String", + "placeholders": {} + }, + "@inOngoingActivity": { + "type": "String", + "placeholders": {} + }, + "modeDisabled": "Τα εργαλεία μάθησης είναι απενεργοποιημένα για μηνύματα που δεν είναι στη γλώσσα στόχου σας.", + "vocabEmoji": "Εικόνισμα λεξιλογίου", + "@modeDisabled": { + "type": "String", + "placeholders": {} + }, + "@vocabEmoji": { + "type": "String", + "placeholders": {} + }, + "requestRegeneration": "Αίτημα αναγέννησης", + "optionalRegenerateReason": "(Προαιρετικό) Λόγος", + "@requestRegeneration": { + "type": "String", + "placeholders": {} + }, + "@optionalRegenerateReason": { + "type": "String", + "placeholders": {} + }, + "emojiSelectedSnackbar": "Έχετε ορίσει το emoji για {lemma}! Θα χρησιμοποιήσουμε αυτό το emoji για να εκπροσωπήσουμε τη λέξη σε πρακτικές δραστηριότητες στο εξής.", + "@emojiSelectedSnackbar": { + "type": "String", + "placeholders": { + "lemma": { + "type": "String" + } + } + }, + "ssoDialogTitle": "Περιμένοντας την ολοκλήρωση της σύνδεσης", + "ssoDialogDesc": "Ανοίξαμε μια νέα καρτέλα ώστε να μπορέσετε να συνδεθείτε με ασφάλεια.", + "ssoDialogHelpText": "🤔 Αν δεν είδατε τη νέα καρτέλα, παρακαλώ ελέγξτε τον αποκλειστή αναδυόμενων παραθύρων σας.", + "@ssoDialogTitle": { + "type": "String", + "placeholders": {} + }, + "@ssoDialogDesc": { + "type": "String", + "placeholders": {} + }, + "@ssoDialogHelpText": { + "type": "String", + "placeholders": {} + }, + "disableLanguageToolsTitle": "Απενεργοποίηση εργαλείων γλώσσας", + "disableLanguageToolsDesc": "Θα θέλατε να απενεργοποιήσετε την αυτόματη βοήθεια γλώσσας;", + "@disableLanguageToolsTitle": { + "type": "String", + "placeholders": {} + }, + "@disableLanguageToolsDesc": { + "type": "String", + "placeholders": {} + }, + "recordingPermissionDenied": "Η άδεια απορρίφθηκε. Ενεργοποιήστε τις άδειες εγγραφής για να καταγράψετε ηχητικά μηνύματα.", + "genericWebRecordingError": "Κάτι πήγε στραβά. Συνιστούμε να χρησιμοποιείτε τον περιηγητή Chrome κατά την εγγραφή μηνυμάτων.", + "@recordingPermissionDenied": { + "type": "String", + "placeholders": {} + }, + "@genericWebRecordingError": { + "type": "String", + "placeholders": {} + }, + "screenSizeWarning": "Για την καλύτερη εμπειρία χρήσης αυτής της εφαρμογής, παρακαλώ επεκτείνετε το μέγεθος της οθόνης σας.", + "@screenSizeWarning": { + "type": "String", + "placeholders": {} + }, + "activitiesToUnlockTopicTitle": "Δραστηριότητες για Ξεκλείδωμα Επόμενου Θέματος", + "activitiesToUnlockTopicDesc": "Ορίστε τον αριθμό των δραστηριοτήτων για να ξεκλειδώσετε το επόμενο θέμα του μαθήματος", + "@activitiesToUnlockTopicTitle": { + "type": "String", + "placeholders": {} + }, + "@activitiesToUnlockTopicDesc": { + "type": "String", + "placeholders": {} + }, + "constructUseCorLMDesc": "Διόρθωση πρακτικής ορισμού λεξιλογίου", + "constructUseIncLMDesc": "Λάθος πρακτική ορισμού λεξιλογίου", + "constructUseCorLADesc": "Διόρθωση πρακτικής ήχου λεξιλογίου", + "constructUseIncLADesc": "Λάθος πρακτική ήχου λεξιλογίου", + "constructUseBonus": "Μπόνους κατά τη διάρκεια της πρακτικής λεξιλογίου", + "practiceVocab": "Πρακτική λεξιλογίου", + "selectMeaning": "Επιλέξτε την έννοια", + "selectAudio": "Επιλέξτε τον αντίστοιχο ήχο", + "congratulations": "Συγχαρητήρια!", + "anotherRound": "Μια ακόμη γύρος", + "noActivityRequest": "Δεν υπάρχει τρέχουσα αίτηση δραστηριότητας.", + "quit": "Έξοδος", + "congratulationsYouveCompletedPractice": "Συγχαρητήρια! Έχετε ολοκληρώσει την πρακτική συνεδρία.", + "mustHave10Words": "Πρέπει να έχετε τουλάχιστον 10 λέξεις λεξιλογίου για να τις εξασκήσετε. Δοκιμάστε να μιλήσετε με έναν φίλο ή με τον Pangea Bot για να ανακαλύψετε περισσότερα!", + "botSettings": "Ρυθμίσεις Bot", + "activitySettingsOverrideWarning": "Η γλώσσα και το επίπεδο γλώσσας καθορίζονται από το σχέδιο δραστηριότητας", + "voice": "Φωνή", + "@constructUseCorLMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncLMDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorLADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncLADesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseBonus": { + "type": "String", + "placeholders": {} + }, + "@practiceVocab": { + "type": "String", + "placeholders": {} + }, + "@selectMeaning": { + "type": "String", + "placeholders": {} + }, + "@selectAudio": { + "type": "String", + "placeholders": {} + }, + "@congratulations": { + "type": "String", + "placeholders": {} + }, + "@anotherRound": { + "type": "String", + "placeholders": {} + }, + "@noActivityRequest": { + "type": "String", + "placeholders": {} + }, + "@quit": { + "type": "String", + "placeholders": {} + }, + "@congratulationsYouveCompletedPractice": { + "type": "String", + "placeholders": {} + }, + "@mustHave10Words": { + "type": "String", + "placeholders": {} + }, + "@botSettings": { + "type": "String", + "placeholders": {} + }, + "@activitySettingsOverrideWarning": { + "type": "String", + "placeholders": {} + }, + "@voice": { + "type": "String", + "placeholders": {} + }, + "youLeftTheChat": "🚪 Αφήσατε τη συνομιλία", + "@youLeftTheChat": { + "type": "String", + "placeholders": {} + }, + "downloadInitiated": "Η λήψη ξεκίνησε", + "webDownloadPermissionMessage": "Εάν ο περιηγητής σας μπλοκάρει τις λήψεις, παρακαλώ ενεργοποιήστε τις λήψεις για αυτόν τον ιστότοπο.", + "@downloadInitiated": { + "type": "String", + "placeholders": {} + }, + "@webDownloadPermissionMessage": { + "type": "String", + "placeholders": {} + }, + "exitPractice": "Η πρόοδος της συνεδρίας πρακτικής σας δεν θα αποθηκευτεί.", + "practiceGrammar": "Πρακτική γραμματικής", + "notEnoughToPractice": "Στείλτε περισσότερα μηνύματα για να ξεκλειδώσετε την πρακτική", + "constructUseCorGCDesc": "Πρακτική κατηγορίας σωστής γραμματικής", + "constructUseIncGCDesc": "Πρακτική κατηγορίας λανθαστής γραμματικής", + "@exitPractice": { + "type": "String", + "placeholders": {} + }, + "@practiceGrammar": { + "type": "String", + "placeholders": {} + }, + "@notEnoughToPractice": { + "type": "String", + "placeholders": {} + }, + "@constructUseCorGCDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGCDesc": { + "type": "String", + "placeholders": {} + }, + "constructUseCorGEDesc": "Πρακτική διόρθωσης γραμματικών λαθών", + "constructUseIncGEDesc": "Πρακτική λανθασμένων γραμματικών λαθών", + "fillInBlank": "Συμπληρώστε το κενό με τη σωστή επιλογή", + "learn": "Μάθετε", + "languageUpdated": "Η γλώσσα στόχος ενημερώθηκε!", + "@constructUseCorGEDesc": { + "type": "String", + "placeholders": {} + }, + "@constructUseIncGEDesc": { + "type": "String", + "placeholders": {} + }, + "@fillInBlank": { + "type": "String", + "placeholders": {} + }, + "@learn": { + "type": "String", + "placeholders": {} + }, + "@languageUpdated": { + "type": "String", + "placeholders": {} + }, + "voiceDropdownTitle": "Φωνή Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} + }, + "knockDesc": "Το αίτημά σας έχει σταλεί στον διαχειριστή του μαθήματος! Θα σας επιτρέψουν να μπείτε αν το εγκρίνουν.", + "@knockDesc": { + "type": "String", + "placeholders": {} + }, + "joinSpaceOnboardingDesc": "Έχετε έναν κωδικό πρόσκλησης ή σύνδεσμο για ένα δημόσιο μάθημα;", + "welcomeUser": "Καλώς ήρθατε {user}", + "@joinSpaceOnboardingDesc": { + "type": "String", + "placeholders": {} + }, + "@welcomeUser": { + "type": "String", + "placeholders": { + "user": { + "type": "String" + } + } + }, + "publicInviteDescChat": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτήν την συνομιλία.", + "publicInviteDescSpace": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτόν τον χώρο.", + "@publicInviteDescChat": { + "type": "String", + "placeholders": {} + }, + "@publicInviteDescSpace": { + "type": "String", + "placeholders": {} + }, + "enableNotificationsTitle": "Η Pangea Chat είναι μια εφαρμογή μηνυμάτων, οπότε οι ειδοποιήσεις είναι σημαντικές!", + "enableNotificationsDesc": "Επιτρέψτε τις ειδοποιήσεις", + "@enableNotificationsTitle": { + "type": "String", + "placeholders": {} + }, + "@enableNotificationsDesc": { + "type": "String", + "placeholders": {} + }, + "useActivityImageAsChatBackground": "Χρησιμοποιήστε την εικόνα δραστηριότητας ως φόντο συνομιλίας", + "@useActivityImageAsChatBackground": { + "type": "String", + "placeholders": {} + }, + "chatWithSupport": "Συνομιλία με Υποστήριξη", + "@chatWithSupport": { + "type": "String", + "placeholders": {} + }, + "newCourseAccess": "Από προεπιλογή, τα μαθήματα είναι δημόσια αναζητήσιμα και απαιτούν έγκριση διαχειριστή για να συμμετάσχετε. Μπορείτε να επεξεργαστείτε αυτές τις ρυθμίσεις οποιαδήποτε στιγμή.", + "@newCourseAccess": { + "type": "String", + "placeholders": {} + }, + "onboardingLanguagesTitle": "Ποια γλώσσα μαθαίνετε;", + "searchLanguagesHint": "Αναζητήστε γλώσσες στόχου", + "@onboardingLanguagesTitle": { + "type": "String", + "placeholders": {} + }, + "@searchLanguagesHint": { + "type": "String", + "placeholders": {} + }, + "supportSubtitle": "Ερωτήσεις; Είμαστε εδώ για να βοηθήσουμε!", + "@supportSubtitle": { + "type": "String", + "placeholders": {} + }, + "courseLoadingError": "Κάτι πήγε στραβά και εργαζόμαστε σκληρά για να το διορθώσουμε. Έλεγξε ξανά αργότερα.", + "@courseLoadingError": { + "type": "String", + "placeholders": {} + }, + "autoIGCToolName": "Ενεργοποίηση βοήθειας γραφής", + "autoIGCToolDescription": "Αυτόματα εκτελέστε τα εργαλεία Pangea Chat για να διορθώσετε τα αποσταλμένα μηνύματα στη γλώσσα στόχο.", + "@autoIGCToolName": { + "type": "String", + "placeholders": {} + }, + "@autoIGCToolDescription": { + "type": "String", + "placeholders": {} + }, + "emptyAudioError": "Η ηχογράφηση απέτυχε. Παρακαλώ ελέγξτε τις άδειες ήχου σας και δοκιμάστε ξανά.", + "@emptyAudioError": { + "type": "String", + "placeholders": {} + }, + "grammarCopyPOSidiom": "Ιδιωματισμός", + "grammarCopyPOSphrasalv": "Φραστικό Ρήμα", + "grammarCopyPOScompn": "Σύνθετο", + "@grammarCopyPOSidiom": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOSphrasalv": { + "type": "String", + "placeholders": {} + }, + "@grammarCopyPOScompn": { + "type": "String", + "placeholders": {} } - }, - "publicInviteDescChat": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτήν την συνομιλία.", - "publicInviteDescSpace": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτόν τον χώρο.", - "@publicInviteDescChat": { - "type": "String", - "placeholders": {} - }, - "@publicInviteDescSpace": { - "type": "String", - "placeholders": {} - }, - "enableNotificationsTitle": "Η Pangea Chat είναι μια εφαρμογή μηνυμάτων, οπότε οι ειδοποιήσεις είναι σημαντικές!", - "enableNotificationsDesc": "Επιτρέψτε τις ειδοποιήσεις", - "@enableNotificationsTitle": { - "type": "String", - "placeholders": {} - }, - "@enableNotificationsDesc": { - "type": "String", - "placeholders": {} - }, - "useActivityImageAsChatBackground": "Χρησιμοποιήστε την εικόνα δραστηριότητας ως φόντο συνομιλίας", - "@useActivityImageAsChatBackground": { - "type": "String", - "placeholders": {} - }, - "chatWithSupport": "Συνομιλία με Υποστήριξη", - "@chatWithSupport": { - "type": "String", - "placeholders": {} - }, - "newCourseAccess": "Από προεπιλογή, τα μαθήματα είναι δημόσια αναζητήσιμα και απαιτούν έγκριση διαχειριστή για να συμμετάσχετε. Μπορείτε να επεξεργαστείτε αυτές τις ρυθμίσεις οποιαδήποτε στιγμή.", - "@newCourseAccess": { - "type": "String", - "placeholders": {} - }, - "onboardingLanguagesTitle": "Ποια γλώσσα μαθαίνετε;", - "searchLanguagesHint": "Αναζητήστε γλώσσες στόχου", - "@onboardingLanguagesTitle": { - "type": "String", - "placeholders": {} - }, - "@searchLanguagesHint": { - "type": "String", - "placeholders": {} - }, - "supportSubtitle": "Ερωτήσεις; Είμαστε εδώ για να βοηθήσουμε!", - "@supportSubtitle": { - "type": "String", - "placeholders": {} - }, - "courseLoadingError": "Κάτι πήγε στραβά και εργαζόμαστε σκληρά για να το διορθώσουμε. Έλεγξε ξανά αργότερα.", - "@courseLoadingError": { - "type": "String", - "placeholders": {} - }, - "autoIGCToolName": "Ενεργοποίηση βοήθειας γραφής", - "autoIGCToolDescription": "Αυτόματα εκτελέστε τα εργαλεία Pangea Chat για να διορθώσετε τα αποσταλμένα μηνύματα στη γλώσσα στόχο.", - "@autoIGCToolName": { - "type": "String", - "placeholders": {} - }, - "@autoIGCToolDescription": { - "type": "String", - "placeholders": {} - }, - "emptyAudioError": "Η ηχογράφηση απέτυχε. Παρακαλώ ελέγξτε τις άδειες ήχου σας και δοκιμάστε ξανά.", - "@emptyAudioError": { - "type": "String", - "placeholders": {} - }, - "aboutMeHint": "Σχετικά με εμένα", - "@aboutMeHint": { - "type": "String", - "placeholders": {} - }, - "grammarCopyPOSidiom": "Ιδιωματισμός", - "grammarCopyPOSphrasalv": "Φραστικό Ρήμα", - "grammarCopyPOScompn": "Σύνθετο", - "@grammarCopyPOSidiom": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOSphrasalv": { - "type": "String", - "placeholders": {} - }, - "@grammarCopyPOScompn": { - "type": "String", - "placeholders": {} - }, - "perfectPractice": "Τέλεια πρακτική!", - "greatPractice": "Υπέροχη πρακτική!", - "usedNoHints": "Καλή δουλειά που δεν χρησιμοποίησες καθόλου υποδείξεις!", - "youveCompletedPractice": "Έχεις ολοκληρώσει την πρακτική, συνέχισε έτσι για να γίνεις καλύτερος!", - "@perfectPractice": { - "type": "String", - "placeholders": {} - }, - "@greatPractice": { - "type": "String", - "placeholders": {} - }, - "@usedNoHints": { - "type": "String", - "placeholders": {} - }, - "@youveCompletedPractice": { - "type": "String", - "placeholders": {} - }, - "changeEmail": "Αλλαγή email", - "withTheseAddressesDescription": "Με αυτές τις διευθύνσεις email μπορείτε να συνδεθείτε, να ανακτήσετε τον κωδικό πρόσβασής σας και να διαχειριστείτε τις συνδρομές.", - "noAddressDescription": "Δεν έχετε προσθέσει καμία διεύθυνση email μέχρι στιγμής.", - "@changeEmail": { - "type": "String", - "placeholders": {} - }, - "@withTheseAddressesDescription": { - "type": "String", - "placeholders": {} - }, - "@noAddressDescription": { - "type": "String", - "placeholders": {} - }, - "spanTypeGrammar": "Γραμματική", - "spanTypeWordChoice": "Επιλογή Λέξεων", - "spanTypeSpelling": "Ορθογραφία", - "spanTypePunctuation": "Σημεία στίξης", - "spanTypeStyle": "Στυλ", - "spanTypeFluency": "Ροή", - "spanTypeAccents": "Τονισμοί", - "spanTypeCapitalization": "Κεφαλαία", - "spanTypeCorrection": "Διόρθωση", - "spanFeedbackTitle": "Αναφορά προβλήματος διόρθωσης", - "@spanTypeGrammar": { - "type": "String", - "placeholders": {} - }, - "@spanTypeWordChoice": { - "type": "String", - "placeholders": {} - }, - "@spanTypeSpelling": { - "type": "String", - "placeholders": {} - }, - "@spanTypePunctuation": { - "type": "String", - "placeholders": {} - }, - "@spanTypeStyle": { - "type": "String", - "placeholders": {} - }, - "@spanTypeFluency": { - "type": "String", - "placeholders": {} - }, - "@spanTypeAccents": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCapitalization": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCorrection": { - "type": "String", - "placeholders": {} - }, - "@spanFeedbackTitle": { - "type": "String", - "placeholders": {} - } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 627d0d5db..24c2f695a 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -10,9 +10,13 @@ "notAnImage": "Not an image file.", "@notAnImage": {}, "setCustomPermissionLevel": "Set custom permission level", + "@setCustomPermissionLevel": {}, "setPermissionsLevelDescription": "Please choose a predefined role below or enter a custom permission level between 0 and 100.", + "@setPermissionsLevelDescription": {}, "ignoreUser": "Ignore user", + "@ignoreUser": {}, "normalUser": "Normal user", + "@normalUser": {}, "remove": "Remove", "@remove": { "type": "String", @@ -29,6 +33,7 @@ "replace": "Replace", "@replace": {}, "about": "About", + "@about": {}, "aboutHomeserver": "About {homeserver}", "@aboutHomeserver": { "type": "String", @@ -107,6 +112,7 @@ "placeholders": {} }, "commandHint_roomupgrade": "Upgrade this room to the given room version", + "@commandHint_roomupgrade": {}, "commandHint_googly": "Send some googly eyes", "@commandHint_googly": {}, "commandHint_cuddle": "Send a cuddle", @@ -149,7 +155,7 @@ } } }, - "anyoneCanJoin": "Anyone can join! However, admin can kick and ban whoever misbehaves. Those who are banned may not return!", + "anyoneCanJoin": "Anyone can join", "@anyoneCanJoin": { "type": "String", "placeholders": {} @@ -243,11 +249,17 @@ } }, "noMoreChatsFound": "No more chats found...", + "@noMoreChatsFound": {}, "noChatsFoundHere": "No chats found here yet. Start a new chat with someone by using the button below. ⤵️", + "@noChatsFoundHere": {}, "joinedChats": "Joined chats", + "@joinedChats": {}, "unread": "Unread", + "@unread": {}, "space": "Space", + "@space": {}, "spaces": "Spaces", + "@spaces": {}, "banFromChat": "Ban from chat", "@banFromChat": { "type": "String", @@ -313,6 +325,7 @@ } } }, + "changedTheChatDescription": "{username} changed the chat description", "changedTheChatDescriptionTo": "{username} changed the chat description to: '{description}'", "@changedTheChatDescriptionTo": { "type": "String", @@ -325,6 +338,7 @@ } } }, + "changedTheChatName": "{username} changed the chat name", "changedTheChatNameTo": "{username} changed the chat name to: '{chatname}'", "@changedTheChatNameTo": { "type": "String", @@ -490,7 +504,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "Your old messages are secured with a recovery key. Please make sure you don't lose it.", + "chatBackupDescription": "Your messages are secured with a recovery key. Please make sure you don't lose it.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -697,6 +711,7 @@ } }, "checkList": "Check list", + "@checkList": {}, "countParticipants": "{count} participants", "@countParticipants": { "type": "String", @@ -2724,15 +2739,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Please try again later or choose a different server.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Sign in with {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "profileNotFound": "The user could not be found on the server. Maybe there is a connection problem or the user doesn't exist.", "@profileNotFound": {}, "setTheme": "Set theme:", @@ -2741,9 +2747,9 @@ "@setColorTheme": {}, "invite": "Invite", "@invite": {}, - "inviteGroupChat": "📨 Invite group chat", + "inviteGroupChat": "📨 Group chat invite", "@inviteGroupChat": {}, - "invitePrivateChat": "📨 Invite private chat", + "invitePrivateChat": "📨 Private chat invite", "@invitePrivateChat": {}, "invalidInput": "Invalid input!", "@invalidInput": {}, @@ -3038,6 +3044,7 @@ "space": {} }, "markAsUnread": "Mark as unread", + "@markAsUnread": {}, "userLevel": "{level} - User", "@userLevel": { "type": "String", @@ -3066,13 +3073,21 @@ } }, "changeGeneralChatSettings": "Change general chat settings", + "@changeGeneralChatSettings": {}, "inviteOtherUsers": "Invite other users to this chat", + "@inviteOtherUsers": {}, "changeTheChatPermissions": "Change the chat permissions", + "@changeTheChatPermissions": {}, "changeTheVisibilityOfChatHistory": "Change the visibility of the chat history", + "@changeTheVisibilityOfChatHistory": {}, "changeTheCanonicalRoomAlias": "Change the main public chat address", + "@changeTheCanonicalRoomAlias": {}, "sendRoomNotifications": "Send a @room notifications", + "@sendRoomNotifications": {}, "changeTheDescriptionOfTheGroup": "Change the description of the chat", + "@changeTheDescriptionOfTheGroup": {}, "chatPermissionsDescription": "Define which power level is necessary for certain actions in this chat. The power levels 0, 50 and 100 are usually representing users, moderators and admins, but any gradation is possible.", + "@chatPermissionsDescription": {}, "updateInstalled": "🎉 Update {version} installed!", "@updateInstalled": { "type": "String", @@ -3083,17 +3098,29 @@ } }, "changelog": "Changelog", + "@changelog": {}, "sendCanceled": "Sending canceled", + "@sendCanceled": {}, "loginWithMatrixId": "Login with Matrix-ID", + "@loginWithMatrixId": {}, "discoverHomeservers": "Discover homeservers", + "@discoverHomeservers": {}, "whatIsAHomeserver": "What is a homeserver?", + "@whatIsAHomeserver": {}, "homeserverDescription": "All your data is stored on the homeserver, just like an email provider. You can choose which homeserver you want to use, while you can still communicate with everyone. Learn more at at https://matrix.org.", + "@homeserverDescription": {}, "doesNotSeemToBeAValidHomeserver": "Doesn't seem to be a compatible homeserver. Wrong URL?", + "@doesNotSeemToBeAValidHomeserver": {}, "calculatingFileSize": "Calculating file size...", + "@calculatingFileSize": {}, "prepareSendingAttachment": "Prepare sending attachment...", + "@prepareSendingAttachment": {}, "sendingAttachment": "Sending attachment...", + "@sendingAttachment": {}, "generatingVideoThumbnail": "Generating video thumbnail...", + "@generatingVideoThumbnail": {}, "compressVideo": "Compressing video...", + "@compressVideo": {}, "sendingAttachmentCountOfCount": "Sending attachment {index} of {length}...", "@sendingAttachmentCountOfCount": { "type": "integer", @@ -3116,31 +3143,57 @@ } }, "oneOfYourDevicesIsNotVerified": "One of your devices is not verified", + "@oneOfYourDevicesIsNotVerified": {}, "noticeChatBackupDeviceVerification": "Note: When you connect all your devices to the chat backup, they are automatically verified.", + "@noticeChatBackupDeviceVerification": {}, "continueText": "Continue", + "@continueText": {}, "welcomeText": "Hey Hey 👋 This is FluffyChat. You can sign in to any homeserver, which is compatible with https://matrix.org. And then chat with anyone. It's a huge decentralized messaging network!", + "@welcomeText": {}, "blur": "Blur:", + "@blur": {}, "opacity": "Opacity:", + "@opacity": {}, "setWallpaper": "Set wallpaper", + "@setWallpaper": {}, "manageAccount": "Manage account", + "@manageAccount": {}, "noContactInformationProvided": "Server does not provide any valid contact information", + "@noContactInformationProvided": {}, "contactServerAdmin": "Contact server admin", + "@contactServerAdmin": {}, "contactServerSecurity": "Contact server security", + "@contactServerSecurity": {}, "supportPage": "Support page", + "@supportPage": {}, "serverInformation": "Server information:", + "@serverInformation": {}, "name": "Name", + "@name": {}, "version": "Version", + "@version": {}, "website": "Website", + "@website": {}, "compress": "Compress", + "@compress": {}, "boldText": "Bold text", + "@boldText": {}, "italicText": "Italic text", + "@italicText": {}, "strikeThrough": "Strikethrough", + "@strikeThrough": {}, "pleaseFillOut": "Please fill out", + "@pleaseFillOut": {}, "invalidUrl": "Invalid url", + "@invalidUrl": {}, "addLink": "Add link", + "@addLink": {}, "unableToJoinChat": "Unable to join chat. Maybe the other party has already closed the conversation.", + "@unableToJoinChat": {}, "previous": "Previous", + "@previous": {}, "otherPartyNotLoggedIn": "The other party is currently not logged in and therefore cannot receive messages!", + "@otherPartyNotLoggedIn": {}, "appWantsToUseForLogin": "Use '{server}' to log in", "@appWantsToUseForLogin": { "type": "String", @@ -3151,55 +3204,105 @@ } }, "appWantsToUseForLoginDescription": "You hereby allow the app and website to share information about you.", + "@appWantsToUseForLoginDescription": {}, "open": "Open", + "@open": {}, "waitingForServer": "Waiting for server...", + "@waitingForServer": {}, "appIntroduction": "FluffyChat lets you chat with your friends across different messengers. Learn more at https://matrix.org or just tap *Continue*.", + "@appIntroduction": {}, "newChatRequest": "📩 New chat request", + "@newChatRequest": {}, "contentNotificationSettings": "Content notification settings", + "@contentNotificationSettings": {}, "generalNotificationSettings": "General notification settings", + "@generalNotificationSettings": {}, "roomNotificationSettings": "Room notification settings", + "@roomNotificationSettings": {}, "userSpecificNotificationSettings": "User specific notification settings", + "@userSpecificNotificationSettings": {}, "otherNotificationSettings": "Other notification settings", + "@otherNotificationSettings": {}, "notificationRuleContainsUserName": "Contains User Name", + "@notificationRuleContainsUserName": {}, "notificationRuleContainsUserNameDescription": "Notifies the user when a message contains their username.", + "@notificationRuleContainsUserNameDescription": {}, "notificationRuleMaster": "Mute all notifications", + "@notificationRuleMaster": {}, "notificationRuleMasterDescription": "Overrides all other rules and disables all notifications.", + "@notificationRuleMasterDescription": {}, "notificationRuleSuppressNotices": "Suppress Automated Messages", + "@notificationRuleSuppressNotices": {}, "notificationRuleSuppressNoticesDescription": "Suppresses notifications from automated clients like bots.", + "@notificationRuleSuppressNoticesDescription": {}, "notificationRuleInviteForMe": "Invite for Me", + "@notificationRuleInviteForMe": {}, "notificationRuleInviteForMeDescription": "Notifies the user when they are invited to a room.", + "@notificationRuleInviteForMeDescription": {}, "notificationRuleMemberEvent": "Member Event", + "@notificationRuleMemberEvent": {}, "notificationRuleMemberEventDescription": "Suppresses notifications for membership events.", + "@notificationRuleMemberEventDescription": {}, "notificationRuleIsUserMention": "User Mention", + "@notificationRuleIsUserMention": {}, "notificationRuleIsUserMentionDescription": "Notifies the user when they are directly mentioned in a message.", + "@notificationRuleIsUserMentionDescription": {}, "notificationRuleContainsDisplayName": "Contains Display Name", + "@notificationRuleContainsDisplayName": {}, "notificationRuleContainsDisplayNameDescription": "Notifies the user when a message contains their display name.", + "@notificationRuleContainsDisplayNameDescription": {}, "notificationRuleIsRoomMention": "Room Mention", + "@notificationRuleIsRoomMention": {}, "notificationRuleIsRoomMentionDescription": "Notifies the user when there is a room mention.", + "@notificationRuleIsRoomMentionDescription": {}, "notificationRuleRoomnotif": "Room Notification", + "@notificationRuleRoomnotif": {}, "notificationRuleRoomnotifDescription": "Notifies the user when a message contains '@room'.", + "@notificationRuleRoomnotifDescription": {}, "notificationRuleTombstone": "Tombstone", + "@notificationRuleTombstone": {}, "notificationRuleTombstoneDescription": "Notifies the user about room deactivation messages.", + "@notificationRuleTombstoneDescription": {}, "notificationRuleReaction": "Reaction", + "@notificationRuleReaction": {}, "notificationRuleReactionDescription": "Suppresses notifications for reactions.", + "@notificationRuleReactionDescription": {}, "notificationRuleRoomServerAcl": "Room Server ACL", + "@notificationRuleRoomServerAcl": {}, "notificationRuleRoomServerAclDescription": "Suppresses notifications for room server access control lists (ACL).", + "@notificationRuleRoomServerAclDescription": {}, "notificationRuleSuppressEdits": "Suppress Edits", + "@notificationRuleSuppressEdits": {}, "notificationRuleSuppressEditsDescription": "Suppresses notifications for edited messages.", + "@notificationRuleSuppressEditsDescription": {}, "notificationRuleCall": "Call", + "@notificationRuleCall": {}, "notificationRuleCallDescription": "Notifies the user about calls.", + "@notificationRuleCallDescription": {}, "notificationRuleEncryptedRoomOneToOne": "Encrypted Room One-to-One", + "@notificationRuleEncryptedRoomOneToOne": {}, "notificationRuleEncryptedRoomOneToOneDescription": "Notifies the user about messages in encrypted one-to-one rooms.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, "notificationRuleRoomOneToOne": "Room One-to-One", + "@notificationRuleRoomOneToOne": {}, "notificationRuleRoomOneToOneDescription": "Notifies the user about messages in one-to-one rooms.", + "@notificationRuleRoomOneToOneDescription": {}, "notificationRuleMessage": "Message", + "@notificationRuleMessage": {}, "notificationRuleMessageDescription": "Notifies the user about general messages.", + "@notificationRuleMessageDescription": {}, "notificationRuleEncrypted": "Encrypted", + "@notificationRuleEncrypted": {}, "notificationRuleEncryptedDescription": "Notifies the user about messages in encrypted rooms.", + "@notificationRuleEncryptedDescription": {}, "notificationRuleJitsi": "Jitsi", + "@notificationRuleJitsi": {}, "notificationRuleJitsiDescription": "Notifies the user about Jitsi widget events.", + "@notificationRuleJitsiDescription": {}, "notificationRuleServerAcl": "Suppress Server ACL Events", + "@notificationRuleServerAcl": {}, "notificationRuleServerAclDescription": "Suppresses notifications for Server ACL events.", + "@notificationRuleServerAclDescription": {}, "unknownPushRule": "Unknown push rule '{rule}'", "@unknownPushRule": { "type": "String", @@ -3222,25 +3325,173 @@ } }, "deletePushRuleCanNotBeUndone": "If you delete this notification setting, this can not be undone.", + "@deletePushRuleCanNotBeUndone": {}, "more": "More", + "@more": {}, "shareKeysWith": "Share keys with...", + "@shareKeysWith": {}, "shareKeysWithDescription": "Which devices should be trusted so that they can read along your messages in encrypted chats?", + "@shareKeysWithDescription": {}, "allDevices": "All devices", + "@allDevices": {}, "crossVerifiedDevicesIfEnabled": "Cross verified devices if enabled", + "@crossVerifiedDevicesIfEnabled": {}, "crossVerifiedDevices": "Cross verified devices", + "@crossVerifiedDevices": {}, "verifiedDevicesOnly": "Verified devices only", + "@verifiedDevicesOnly": {}, "takeAPhoto": "Take a photo", + "@takeAPhoto": {}, "recordAVideo": "Record a video", + "@recordAVideo": {}, "optionalMessage": "(Optional) message...", + "@optionalMessage": {}, "notSupportedOnThisDevice": "Not supported on this device", + "@notSupportedOnThisDevice": {}, "enterNewChat": "Enter new chat", + "@enterNewChat": {}, "approve": "Approve", + "@approve": {}, "youHaveKnocked": "You have knocked", + "@youHaveKnocked": {}, "pleaseWaitUntilInvited": "Please wait now, until someone from the room invites you.", + "@pleaseWaitUntilInvited": {}, "commandHint_logout": "Logout your current device", + "@commandHint_logout": {}, "commandHint_logoutall": "Logout all active devices", + "@commandHint_logoutall": {}, "displayNavigationRail": "Show navigation rail on mobile", + "@displayNavigationRail": {}, "customReaction": "Custom reaction", + "@customReaction": {}, + "moreEvents": "More events", + "@moreEvents": {}, + "declineInvitation": "Decline invitation", + "@declineInvitation": {}, + "noMessagesYet": "No messages yet", + "longPressToRecordVoiceMessage": "Long press to record voice message.", + "pause": "Pause", + "resume": "Resume", + "newSubSpace": "New sub space", + "moveToDifferentSpace": "Move to different space", + "moveUp": "Move up", + "moveDown": "Move down", + "removeFromSpaceDescription": "The chat will be removed from the space but still appear in your chat list.", + "countChats": "{chats} chats", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Space member of {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Space member of {spaces} can knock", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Donate", + "startedAPoll": "{username} started a poll.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Poll", + "startPoll": "Start poll", + "endPoll": "End poll", + "answersVisible": "Answers visible", + "answersHidden": "Answers hidden", + "pollQuestion": "Poll question", + "answerOption": "Answer option", + "addAnswerOption": "Add answer option", + "allowMultipleAnswers": "Allow multiple answers", + "pollHasBeenEnded": "Poll has been ended", + "countVotes": "{count, plural, =1{One vote} other{{count} votes}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Answers will be visible when poll has ended", + "replyInThread": "Reply in thread", + "countReplies": "{count, plural, =1{One reply} other{{count} replies}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Thread", + "backToMainChat": "Back to main chat", + "saveChanges": "Save changes", + "createSticker": "Create sticker or emoji", + "useAsSticker": "Use as sticker", + "useAsEmoji": "Use as emoji", + "stickerPackNameAlreadyExists": "Sticker pack name already exists", + "newStickerPack": "New sticker pack", + "stickerPackName": "Sticker pack name", + "attribution": "Attribution", + "skipChatBackup": "Skip chat backup", + "skipChatBackupWarning": "Are you sure? Without enabling the chat backup you may lose access to your messages if you switch your device.", + "loadingMessages": "Loading messages", + "setupChatBackup": "Set up chat backup", + "noMoreResultsFound": "No more results found", + "chatSearchedUntil": "Chat searched until {time}", + "@chatSearchedUntil": { + "type": "String", + "placeholders": { + "time": { + "type": "String" + } + } + }, + "federationBaseUrl": "Federation Base URL", + "@federationBaseUrl": {}, + "clientWellKnownInformation": "Client-Well-Known Information:", + "@clientWellKnownInformation": {}, + "baseUrl": "Base URL", + "@baseUrl": {}, + "identityServer": "Identity Server:", + "@identityServer": {}, + "versionWithNumber": "Version: {version}", + "@versionWithNumber": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "logs": "Logs", + "@logs": {}, + "advancedConfigs": "Advanced Configs", + "@advancedConfigs": {}, + "advancedConfigurations": "Advanced configurations", + "@advancedConfigurations": {}, + "signInWithLabel": "Sign in with:", "ignore": "Block", "ignoredUsers": "Blocked users", "writeAMessageLangCodes": "Type in {l1} or {l2}...", @@ -3301,7 +3552,6 @@ "updateLanguage": "My languages", "whatLanguageYouWantToLearn": "What language do you want to learn?", "whatIsYourBaseLanguage": "What is your base language?", - "saveChanges": "Save changes", "publicProfileTitle": "Allow my profile to be found in search", "publicProfileDesc": "By turning on, you enable other users to find your profile in the global search bar and send requests to chat. At this point, you can choose to accept or deny the request.", "errorDisableIT": "Translation assistance is turned off.", diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 670765db1..15fa29b49 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -2290,7 +2290,6 @@ "report": "raporti", "signInWithPassword": "Ensaluti per pasvorto", "pleaseTryAgainLaterOrChooseDifferentServer": "Bonvolu provi denove poste aŭ elekti alian servilon.", - "signInWith": "Ensaluti kun {provider}", "profileNotFound": "La uzanto ne povis esti trovita en la servilo. Eble estas problemo kun la konekto aŭ la uzanto ne ekzistas.", "setTheme": "Agordi temon:", "setColorTheme": "Agordi kolortemon:", @@ -4628,14 +4627,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5046,9 +5037,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12154,4 +12143,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index f9f00d551..c7c555256 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -342,7 +342,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "La copia de respaldo del chat está protegida por una llave de seguridad. Procure no perderla.", + "chatBackupDescription": "Tus mensajes están protegidos por una llave de seguridad. Procura no perderla.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -2315,7 +2315,7 @@ "@enterSpace": {}, "pleaseEnterRecoveryKey": "Por favor, introduzca su clave de recuperación:", "@pleaseEnterRecoveryKey": {}, - "widgetNameError": "Por favor proporciona un nombre para mostrar.", + "widgetNameError": "Por favor, proporciona un nombre para mostrar.", "@widgetNameError": {}, "addWidget": "Añadir widget", "@addWidget": {}, @@ -2562,7 +2562,7 @@ "@hideRedactedMessages": {}, "appLockDescription": "Bloquear la aplicación cuando no se use con código pin", "@appLockDescription": {}, - "alwaysUse24HourFormat": "falso", + "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -2613,7 +2613,7 @@ "@disableEncryptionWarning": {}, "setColorTheme": "Poner tema de color:", "@setColorTheme": {}, - "inviteGroupChat": "📨 Invitar a grupo de chat", + "inviteGroupChat": "📨 Invitar a grupo", "@inviteGroupChat": {}, "invalidInput": "¡Entrada no válida!", "@invalidInput": {}, @@ -2656,7 +2656,7 @@ "@changelog": {}, "sendCanceled": "Envío cancelado", "@sendCanceled": {}, - "invite": "Invitación", + "invite": "Invitar", "@invite": {}, "searchIn": "Buscar en chat \"{chat}\"...", "@searchIn": { @@ -2707,15 +2707,6 @@ } } }, - "signInWith": "Entrar con {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "setTheme": "Poner tema:", "@setTheme": {}, "learnMore": "Aprender más", @@ -3279,7 +3270,7 @@ "@yourGlobalUserIdIs": {}, "noKeyForThisMessage": "Esto puede ocurrir si el mensaje se envió antes de que entraras en tu cuenta en este dispositivo.\n\nTambién puede que el remitente haya bloqueado tu dispositivo o haya fallado algo en la conexión a Internet.\n\n¿Puedes leer el mensaje en otra sesión? Entonces, ¡puedes transferir el mensaje desde allí! Ve a Ajustes > Dispositivos y asegúrate de que tus dispositivos se han verificado mutuamente. Cuando abras la sala la próxima vez y ambas sesiones estén en primer plano, las claves se transmitirán automáticamente.\n\n¿No quieres perder las claves al salir o al cambiar de dispositivo? Asegúrate de que has habilitado la copia de seguridad del chat en los ajustes.", "@noKeyForThisMessage": {}, - "invitePrivateChat": "📨 Invitar a chat privado", + "invitePrivateChat": "📨 Invitar a grupo privado", "@invitePrivateChat": {}, "banUserDescription": "Se expulsará al usuario del chat y no podrá volver a entrar hasta que se le permita.", "@banUserDescription": {}, @@ -3344,7 +3335,7 @@ "@enterNewChat": {}, "pleaseWaitUntilInvited": "Por favor espera, hasta que alguien del chat te invite.", "@pleaseWaitUntilInvited": {}, - "commandHint_roomupgrade": "Actualizar este chat a la versión de chat dada", + "commandHint_roomupgrade": "Actualizar este chat a la versión de chat asignada", "@commandHint_roomupgrade": {}, "checkList": "Lista de tareas", "@checkList": {}, @@ -3383,10 +3374,64 @@ "@commandHint_logoutall": {}, "displayNavigationRail": "Mostrar carril de navegación en móvil", "@displayNavigationRail": {}, - "youHaveKnocked": "Has sido golpeado", - "@youHaveKnocked": {}, "approve": "Aprobar", "@approve": {}, + "youHaveKnocked": "Has sido expulsado", + "@youHaveKnocked": {}, + "customReaction": "Reacción personalizada", + "@customReaction": {}, + "moreEvents": "Más eventos", + "@moreEvents": {}, + "declineInvitation": "Rechazar invitación", + "@declineInvitation": {}, + "changedTheChatDescription": "{username} ha cambiado la descripción del chat", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} ha cambiado el nombre del chat", + "@changedTheChatName": {}, + "noMessagesYet": "Sin mensajes", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Deja presionado para grabar un mensaje.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Pausa", + "@pause": {}, + "resume": "Continuar", + "@resume": {}, + "newSubSpace": "Nuevo sub espacio", + "@newSubSpace": {}, + "moveToDifferentSpace": "Mover a otro espacio", + "@moveToDifferentSpace": {}, + "moveUp": "Mover arriba", + "@moveUp": {}, + "moveDown": "Mover abajo", + "@moveDown": {}, + "removeFromSpaceDescription": "El chat sera removido del espacio pero continuara apareciendo en tu lista de chats.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} chats", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "startedAPoll": "{username} inició una encuesta.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Encuesta", + "@poll": {}, + "startPoll": "Iniciar encuesta", + "@startPoll": {}, + "endPoll": "Finalizar encuesta", + "@endPoll": {}, + "answersVisible": "Respuestas visibles", + "@answersVisible": {}, "archivedRoom": "", "askSSSSCache": "Ingrese su contraseña de almacenamiento segura (SSSS) o la clave de recuperación para almacenar en caché las claves.", "askSSSSVerify": "", @@ -3429,15 +3474,6 @@ "makeSureTheIdentifierIsValid": "Asegúrese de que el identificador es válido", "messageWillBeRemovedWarning": "El mensaje será eliminado para todos los participantes", "monday": "Lunes", - "moreEvents": "{count,plural, =1{1 evento más} other{{count} más eventos}}", - "@moreEvents": { - "type": "String", - "placeholders": { - "count": { - "type": "String" - } - } - }, "noCrossSignBootstrap": "Fluffychat actualmente no soporta la activación de Cross-Signing. Por favor, actívelo dentro de Riot.", "noMegolmBootstrap": "Fluffychat actualmente no soporta la activación de Online Key Backup. Por favor, actívelo dentro de Riot.", "noPublicRoomsFound": "Sin chats públicos…", @@ -5285,7 +5321,6 @@ "type": "String", "placeholders": {} }, - "customReaction": "Reacción personalizada", "chatCapacitySetTooLow": "La capacidad de chat debe ser al menos {count}.", "spaceCapacitySetTooLow": "La capacidad del espacio debe ser al menos {count}.", "grammarCopyVERBFORMshrt": "Corto", @@ -5352,10 +5387,6 @@ "errorFetchingActivity": "Error al obtener la actividad", "check": "Verificar", "unableToFindRoom": "No se encontró chat o espacio con ese código. Por favor, intenta de nuevo.", - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@chatCapacitySetTooLow": { "type": "int", "placeholders": { @@ -8307,4 +8338,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index 8d1fd2966..2b99b3818 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1361,7 +1361,7 @@ "type": "String", "placeholders": {} }, - "pin": "Klammerda", + "pin": "Tõsta esile", "@pin": { "type": "String", "placeholders": {} @@ -1874,7 +1874,7 @@ "type": "String", "placeholders": {} }, - "unpin": "Eemalda klammerdus", + "unpin": "Eemalda esiletõstmine", "@unpin": { "type": "String", "placeholders": {} @@ -2052,7 +2052,7 @@ "type": "String", "placeholders": {} }, - "writeAMessage": "Kirjuta üks sõnum…", + "writeAMessage": "Koosta sõnum…", "@writeAMessage": { "type": "String", "placeholders": {} @@ -2185,9 +2185,9 @@ "@unsupportedAndroidVersion": {}, "voiceCall": "Häälkõne", "@voiceCall": {}, - "confirmEventUnpin": "Kas sa oled kindel, et tahad klammerdatud sündmuse eemaldada?", + "confirmEventUnpin": "Kas sa oled kindel, et tahad esiletõstetud sündmuse jäädavalt eemaldada?", "@confirmEventUnpin": {}, - "pinMessage": "Klammerda sõnum jututuppa", + "pinMessage": "Tõsta sõnum jututoas esile", "@pinMessage": {}, "videoCallsBetaWarning": "Palun arvesta, et videokõned on veel beetajärgus. Nad ei pruugi veel toimida kõikidel platvormidel korrektselt.", "@videoCallsBetaWarning": {}, @@ -2510,15 +2510,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Palun proovi hiljem uuesti või muuda serveri nime.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Logi sisse kasutades teenusepakkujat {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "importFromZipFile": "Impordi zip-failist", "@importFromZipFile": {}, "exportEmotePack": "Ekspordi emotikonide pakk zip-failina", @@ -2592,7 +2583,7 @@ } } }, - "inviteGroupChat": "📨 Kutsu vestlusrühma", + "inviteGroupChat": "📨 Kutse vestlusrühma", "@inviteGroupChat": {}, "invitePrivateChat": "📨 Kutsu omavahelisele vestlusele", "@invitePrivateChat": {}, @@ -3378,8 +3369,155 @@ "@commandHint_logoutall": {}, "commandHint_logout": "Logi oma praegusest seadmest välja", "@commandHint_logout": {}, - "displayNavigationRail": "Näita navigeerimisliistu mobiilis", + "displayNavigationRail": "Näita mobiilis külgmist tööriistariba", + "@displayNavigationRail": {}, "customReaction": "Kohandatud reaktsioon", + "@customReaction": {}, + "moreEvents": "Veel sündmusi", + "@moreEvents": {}, + "declineInvitation": "Keeldu kutsest", + "@declineInvitation": {}, + "noMessagesYet": "Pole veel ühtegi sõnumit", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Pika vajutusega saad salvestada häälsõnumi.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Peata", + "@pause": {}, + "resume": "Jätka", + "@resume": {}, + "newSubSpace": "Uus alamkogukond", + "@newSubSpace": {}, + "moveToDifferentSpace": "Tõsta teise kogukonda", + "@moveToDifferentSpace": {}, + "moveUp": "Liiguta ülespoole", + "@moveUp": {}, + "moveDown": "Liiguta allapoole", + "@moveDown": {}, + "removeFromSpaceDescription": "See vestlus eemaldatakse nüüd kogukonnast, kuid on jätkuvalt nähtav sinu vestluste loendis.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} vestlust", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Kogukonna liige: {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "{spaces} kogukonna liige võib uksele koputada", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Toeta meid rahaliselt", + "@donate": {}, + "startedAPoll": "{username} koostas küsitluse.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Küsitlus", + "@poll": {}, + "startPoll": "Koosta küsitlus", + "@startPoll": {}, + "endPoll": "Lõpeta küsitlus", + "@endPoll": {}, + "answersVisible": "Vastused on näha", + "@answersVisible": {}, + "answersHidden": "Vastused on peidetud", + "@answersHidden": {}, + "pollQuestion": "Küsitluse küsimus", + "@pollQuestion": {}, + "answerOption": "Valikvastus", + "@answerOption": {}, + "addAnswerOption": "Lisa valikvastus", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Luba mitu vastusevalikut", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Küsitlus on lõppenud", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Üks hääl} other{{count} häält}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Vastused on näha küsitluse lõppedes", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Vasta jutulõngas", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Üks vastus} other{{count} vastust}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Jutulõng", + "@thread": {}, + "backToMainChat": "Tagasi põhivestlusesse", + "@backToMainChat": {}, + "saveChanges": "Salvesta muudatused", + "@saveChanges": {}, + "createSticker": "Loo kleeps või emoji", + "@createSticker": {}, + "useAsSticker": "Kasuta kleepsuna", + "@useAsSticker": {}, + "useAsEmoji": "Kasuta emojina", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Selline kleepsupaki nimi on juba olemas", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Uus kleepsupakk", + "@newStickerPack": {}, + "stickerPackName": "Kleepsupaki nimi", + "@stickerPackName": {}, + "attribution": "Autoriõigused", + "@attribution": {}, + "skipChatBackup": "Jäta vestluse varundamine vahele", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Kas oled kindel? Kui sa pole lülitanud sisse vestluste krüptovõtmete varundust, siis võid oma seadme vahetamisel kaotada ligipääsu oma senistele sõnumitele.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Laadin sõnumeid", + "@loadingMessages": {}, + "setupChatBackup": "Võta kasutusele vestluste varundus", + "@setupChatBackup": {}, + "changedTheChatDescription": "{username} muutis vestluse kirjeldust", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} muutis vestluse nime", + "@changedTheChatName": {}, + "noMoreResultsFound": "Rohkem tulemusi ei leidu", + "@noMoreResultsFound": {}, + "chatSearchedUntil": "Otsing vestlusest kuni {time}", + "@chatSearchedUntil": { + "type": "String", + "placeholders": { + "time": { + "type": "String" + } + } + }, "writeAMessageLangCodes": "Kirjuta {l1} või {l2}...", "requests": "Päringud", "holdForInfo": "Vajuta ja hoia sõna info saamiseks.", @@ -3422,7 +3560,6 @@ "updateLanguage": "Minu keeled", "whatLanguageYouWantToLearn": "Millist keelt soovite õppida?", "whatIsYourBaseLanguage": "Mis on teie põhikeel?", - "saveChanges": "Salvesta muudatused", "publicProfileTitle": "Luba minu profiili otsingus leida", "publicProfileDesc": "Lülitades sisse võimaldate teistel kasutajatel leida teie profiili globaalset otsinguriba kaudu ja saata vestlussoove. Sel hetkel saate otsustada, kas nõustute või keeldute soovist.", "errorDisableIT": "Tõlkeabi on välja lülitatud.", @@ -4427,14 +4564,6 @@ "errorFetchingActivity": "Tegevuse toomine ebaõnnestus", "check": "Kontrolli", "unableToFindRoom": "Selle koodiga vestlust või ruumi ei leitud. Palun proovi uuesti.", - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4614,10 +4743,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11336,4 +11461,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index bdec034ad..d2e9994a2 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1911,7 +1911,7 @@ "type": "String", "description": "Usage hint for the command /invite" }, - "commandHint_join": "Batu gelara", + "commandHint_join": "Batu adierazitako gelara", "@commandHint_join": { "type": "String", "description": "Usage hint for the command /join" @@ -2510,15 +2510,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Saiatu geroago edo aukeratu beste zerbitzari bat.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Hasi saioa {provider}(r)ekin", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Ez da irudi-fitxategia.", "@notAnImage": {}, "importNow": "Inportatu orain", @@ -2608,9 +2599,9 @@ "@invite": {}, "invalidInput": "Sartu duzunak ez du balio!", "@invalidInput": {}, - "inviteGroupChat": "📨 Gonbidatu taldeko txatera", + "inviteGroupChat": "📨 Taldeko txatera gonbidapena", "@inviteGroupChat": {}, - "invitePrivateChat": "📨 Gonbidatu txat pribatura", + "invitePrivateChat": "📨 Txat pribatura gonbidapena", "@invitePrivateChat": {}, "banUserDescription": "Erabiltzailea txatetik kanporatu eta berriro sartzeko debekua ezarriko zaio; ezingo da berriro sartu debekua kendu arte.", "@banUserDescription": {}, @@ -3362,7 +3353,7 @@ } } }, - "sentVoiceMessage": "🎙️ {duration} - {sender}", + "sentVoiceMessage": "🎙️ {duration} - {sender}(r)en ahots-mezua", "@sentVoiceMessage": { "type": "String", "placeholders": { @@ -3374,10 +3365,148 @@ } } }, - "commandHint_logout": "Saioa itxi zure gailu honetan", - "commandHint_logoutall": "Saioa itxi gailu aktibo guztietan", - "displayNavigationRail": "Erakutsi nabigazio errail txikian", + "commandHint_logout": "Amaitu saioa gailu honetan", + "@commandHint_logout": {}, + "commandHint_logoutall": "Amaitu saioa aktibo dauden gailu guztietan", + "@commandHint_logoutall": {}, + "moreEvents": "Gertaera gehiago", + "@moreEvents": {}, + "displayNavigationRail": "Erakutsi nabigazio-barra mugikorrean", + "@displayNavigationRail": {}, "customReaction": "Erreakzio pertsonalizatua", + "@customReaction": {}, + "declineInvitation": "Uko egin gonbidapenari", + "@declineInvitation": {}, + "noMessagesYet": "Mezurik ez oraingoz", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Sakatuta mantendu ahots-mezua grabatzeko.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Gelditu", + "@pause": {}, + "resume": "Jarraitu", + "@resume": {}, + "moveToDifferentSpace": "Beste gune batera mugitu", + "@moveToDifferentSpace": {}, + "moveUp": "Eraman gora", + "@moveUp": {}, + "moveDown": "Eraman behera", + "@moveDown": {}, + "removeFromSpaceDescription": "Txata gunetik kenduko da, baina txaten zerrendan mantenduko da.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} txat", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "donate": "Egin dohaintza", + "@donate": {}, + "newSubSpace": "Azpi-gune berria", + "@newSubSpace": {}, + "spaceMemberOf": "{spaces} guneko kidea", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "{spaces} guneko kideak sartzeko baimena eska dezake", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "startedAPoll": "{username}(e)k bozketa hasi du.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Bozketa", + "@poll": {}, + "startPoll": "Hasi bozketa", + "@startPoll": {}, + "endPoll": "Amaitu bozketa", + "@endPoll": {}, + "answersVisible": "Erakutsi emaitzak", + "@answersVisible": {}, + "answersHidden": "Ezkutatu emaitzak", + "@answersHidden": {}, + "pollQuestion": "Bozketako galdera", + "@pollQuestion": {}, + "answerOption": "Erantzun-aukera", + "@answerOption": {}, + "addAnswerOption": "Gehitu erantzun-aukera", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Baimendu erantzun bat baino gehiago", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Bozketa amaitu da", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Bozka bat} other{{count} bozka}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Emaitzak bozketak amaitu duenean egongo dira ikusgai", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Erantzun harian", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Erantzun bat} other{{count} erantzun}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Haria", + "@thread": {}, + "backToMainChat": "Joan txat nagusira", + "@backToMainChat": {}, + "changedTheChatDescription": "{username}(e)k txataren deskribapena aldatu du", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username}(e)k txataren izena aldatu du", + "@changedTheChatName": {}, + "saveChanges": "Gorde aldaketak", + "@saveChanges": {}, + "createSticker": "Sortu pegatina edo emojia", + "@createSticker": {}, + "attribution": "Sortzailea", + "@attribution": {}, + "skipChatBackup": "Ez egin txataren babeskopia", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Ziur? Txataren babeskopia gaitzen ez baduzu, gailuz aldatuz gero mezuen sarbidea gal zenezake.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Mezuak kargatzen", + "@loadingMessages": {}, + "setupChatBackup": "Ezarri txataren babeskopia", + "@setupChatBackup": {}, + "useAsSticker": "Erabili pegatina gisa", + "@useAsSticker": {}, + "stickerPackNameAlreadyExists": "Pegatina-sortaren izena badago lehendik ere", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Pegatina-sorta berria", + "@newStickerPack": {}, + "stickerPackName": "Pegatina-sortaren izena", + "@stickerPackName": {}, + "useAsEmoji": "Erabili emoji gisa", + "@useAsEmoji": {}, "writeAMessageLangCodes": "Idatzi {l1} edo {l2}...", "requests": "Eskariak", "holdForInfo": "Klikatu eta eutsi hitzaren informazioarentzat.", @@ -3419,7 +3548,6 @@ "updateLanguage": "Nire hizkuntzak", "whatLanguageYouWantToLearn": "Zein hizkuntza ikasi nahi duzu?", "whatIsYourBaseLanguage": "Zein da zure oinarrizko hizkuntza?", - "saveChanges": "Gorde aldaketak", "publicProfileTitle": "Onartu nire profila bilaketetan agertzeko", "publicProfileDesc": "Gaituta, beste erabiltzaileek zure profila bilaketa globalean aurkitu eta chat eskaerak bidali ahal izango dituzte. Orain, eskaera onartu edo ukatu dezakezu.", "errorDisableIT": "Itzulpen laguntza desgaituta dago.", @@ -4505,22 +4633,6 @@ "inviteYourFriends": "Gonbidatu zure lagunak", "playWithAI": "Jolastu AIrekin orain", "courseStartDesc": "Pangea Bot prest dago noiznahi joateko!\n\n...baina ikastea lagunekin hobe da!", - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4696,10 +4808,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11065,4 +11173,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index f83c4aad3..331585120 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,6 +1,6 @@ { "@@last_modified": "2026-02-09 15:32:19.749220", - "repeatPassword": "تکرار رمزعبور", + "repeatPassword": "تکرار گذرواژه", "@repeatPassword": {}, "about": "درباره", "@about": { @@ -26,7 +26,7 @@ "type": "String", "placeholders": {} }, - "addToSpace": "به فضا اضافه کنید", + "addToSpace": "به فضا افزودن", "@addToSpace": {}, "appLock": "قفل برنامه", "@appLock": { @@ -43,12 +43,12 @@ "type": "String", "placeholders": {} }, - "addEmail": "افزودن ایمیل", + "addEmail": "افزودن رایانامه", "@addEmail": { "type": "String", "placeholders": {} }, - "answeredTheCall": "{senderName} پاسخ تماس را داد", + "answeredTheCall": "{senderName} به تماس پاسخ داد", "@answeredTheCall": { "type": "String", "placeholders": { @@ -77,7 +77,7 @@ "type": "String", "placeholders": {} }, - "activatedEndToEndEncryption": "🔐 {username} رمزگذاری سرتاسر را فعال کرد", + "activatedEndToEndEncryption": "🔐 {username} رمزنگاری سراسری را فعال کرد", "@activatedEndToEndEncryption": { "type": "String", "placeholders": { @@ -86,12 +86,12 @@ } } }, - "admin": "ادمین", + "admin": "مدیر", "@admin": { "type": "String", "placeholders": {} }, - "supposedMxid": "این باید {mxid} باشد", + "supposedMxid": "گمان میرود {mxid} باشد", "@supposedMxid": { "type": "String", "placeholders": { @@ -100,12 +100,12 @@ } } }, - "botMessages": "پیام‌های روبات", + "botMessages": "پیام‌های ربات", "@botMessages": { "type": "String", "placeholders": {} }, - "changedTheDisplaynameTo": "{username} نام نمایشی خود را تغییر داد به: «{displayname}»", + "changedTheDisplaynameTo": "{username} نام نمایشی را به '{displayname}' تغییر داد", "@changedTheDisplaynameTo": { "type": "String", "placeholders": { @@ -117,9 +117,9 @@ } } }, - "confirmMatrixId": "برای حذف حسابتان، لطفا هویت ماتریکستان را تایید کنید.", + "confirmMatrixId": "برای پاک کردن حساب، لطفاً هویت ماتریکس خود را بپذیرید.", "@confirmMatrixId": {}, - "changeDeviceName": "نام دستگاه را تغییر دهید", + "changeDeviceName": "تغییر نام دستگاه", "@changeDeviceName": { "type": "String", "placeholders": {} @@ -129,12 +129,12 @@ "type": "String", "placeholders": {} }, - "banFromChat": "از گپ محروم کنید", + "banFromChat": "محروم کردن از گپ", "@banFromChat": { "type": "String", "placeholders": {} }, - "askVerificationRequest": "این درخواست تایید را از {username} می‌پذیرید؟", + "askVerificationRequest": "آیا درخواست بازبینی {username} را می‌پذیرید؟", "@askVerificationRequest": { "type": "String", "placeholders": { @@ -143,24 +143,24 @@ } } }, - "areGuestsAllowedToJoin": "آیا کاربران مهمان اجازه پیوستن دارند", + "areGuestsAllowedToJoin": "آیا مهمانان اجازه پیوستن دارند", "@areGuestsAllowedToJoin": { "type": "String", "placeholders": {} }, - "autoplayImages": "اموجی و برچسب‌های متحرک به طور خودکار پخش شوند", + "autoplayImages": "پخش خودکار شکلک‌ها و برچسب‌های متحرک", "@autoplayImages": { "type": "String", "placeholder": {} }, - "sendOnEnter": "ارسال با کلید تعويض سطر", + "sendOnEnter": "فرستادن با کلید Enter", "@sendOnEnter": {}, - "cancel": "لغو", + "cancel": "رد کردن", "@cancel": { "type": "String", "placeholders": {} }, - "changedTheChatDescriptionTo": "{username} توصیف گپ را تغییر داد به: «{description}»", + "changedTheChatDescriptionTo": "{username} توضیح گپ را به '{description}' تغییر داد", "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -172,7 +172,7 @@ } } }, - "changedTheChatPermissions": "{username} اجازه‌های گپ را تغییر داد", + "changedTheChatPermissions": "{username} دسترسی‌های گپ را تغییر داد", "@changedTheChatPermissions": { "type": "String", "placeholders": { @@ -181,7 +181,7 @@ } } }, - "changedTheChatNameTo": "{username} نام گپ را تغییر داد به: «{chatname}»", + "changedTheChatNameTo": "{username} نام گپ را به '{chatname}' تغییر داد", "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -193,7 +193,7 @@ } } }, - "cantOpenUri": "نمی‌توانیم این آدرس اینترنتی را باز کنیم: {uri}", + "cantOpenUri": "نمی‌توان پیوند {uri} را باز کرد", "@cantOpenUri": { "type": "String", "placeholders": { @@ -202,12 +202,12 @@ } } }, - "banned": "محروم شده", + "banned": "محروم‌شده", "@banned": { "type": "String", "placeholders": {} }, - "bannedUser": "{username} {targetName} را محروم کرد", + "bannedUser": "{username} کاربر {targetName} را محروم کرد", "@bannedUser": { "type": "String", "placeholders": { @@ -219,17 +219,17 @@ } } }, - "blockDevice": "دستگاه را مسدود کنید", + "blockDevice": "مسدود کردن دستگاه", "@blockDevice": { "type": "String", "placeholders": {} }, - "blocked": "مسدود شده", + "blocked": "مسدود‌شده", "@blocked": { "type": "String", "placeholders": {} }, - "badServerLoginTypesException": "سرور می‌تواند این گونه‌های ورود‮ را پشتیباتی کند:\n{serverVersions}\nولی این برنامه فقط می‌تواند این‌ها را پشتیبانی کند:\n{supportedVersions}", + "badServerLoginTypesException": "سرور از این نوع ورود پشتیبانی می‌کند:\n{serverVersions}\nاما برنامه تنها از این‌ها پشتیبانی می‌کند:\n{supportedVersions}", "@badServerLoginTypesException": { "type": "String", "placeholders": { @@ -241,7 +241,7 @@ } } }, - "badServerVersionsException": "سرور می‌تواند این نسخه‌های مشخصات را پشتیبانی کند:\n{serverVersions}\nولی این برنامه فقط می‌تواند این‌ها را پشتیبانی کند:\n{supportedVersions}", + "badServerVersionsException": "سرور از این نسخه‌های مشخصات پشتیبانی می‌کند:\n{serverVersions}\nاما برنامه تنها از این‌ها پشتیبانی می‌کند:\n{supportedVersions}", "@badServerVersionsException": { "type": "String", "placeholders": { @@ -253,7 +253,7 @@ } } }, - "changedTheChatAvatar": "{username} تصویر گپ را تغییر داد", + "changedTheChatAvatar": "{username} نماد گپ را تغییر داد", "@changedTheChatAvatar": { "type": "String", "placeholders": { @@ -271,7 +271,7 @@ } } }, - "changedTheProfileAvatar": "{username} عکس پروفایل خود را تغییر داد", + "changedTheProfileAvatar": "{username} نماد نمایه را تغییر داد", "@changedTheProfileAvatar": { "type": "String", "placeholders": { @@ -290,17 +290,17 @@ "type": "String", "placeholders": {} }, - "send": "ارسال", + "send": "فرستادن", "@send": { "type": "String", "placeholders": {} }, - "sendAudio": "ارسال صدا", + "sendAudio": "فرستادن صدا", "@sendAudio": { "type": "String", "placeholders": {} }, - "sendOriginal": "ارسال اصل", + "sendOriginal": "فرستادن نسخه اصلی", "@sendOriginal": { "type": "String", "placeholders": {} @@ -314,7 +314,7 @@ } } }, - "changedTheRoomInvitationLink": "{username} لینک دعوت را تغییر داد", + "changedTheRoomInvitationLink": "{username} پیوند دعوت را تغییر داد", "@changedTheRoomInvitationLink": { "type": "String", "placeholders": { @@ -328,17 +328,17 @@ "type": "String", "placeholders": {} }, - "sendImage": "ارسال تصویر", + "sendImage": "فرستادن تصویر", "@sendImage": { "type": "String", "placeholders": {} }, - "sendMessages": "ارسال پیام‌ها", + "sendMessages": "فرستادن پیام‌ها", "@sendMessages": { "type": "String", "placeholders": {} }, - "changedTheHistoryVisibility": "{username} قابليت‌ ديدن‌ تاریخچه را تغییر داد", + "changedTheHistoryVisibility": "{username} ویژگی دیدن تاریخچه را تغییر داد", "@changedTheHistoryVisibility": { "type": "String", "placeholders": { @@ -347,7 +347,7 @@ } } }, - "changedTheGuestAccessRulesTo": "{username} قوانین دسترسی مهمان را تغییر داد به: {rules}", + "changedTheGuestAccessRulesTo": "{username} قوانین دسترسی مهمان را به {rules} تغییر داد", "@changedTheGuestAccessRulesTo": { "type": "String", "placeholders": { @@ -359,7 +359,7 @@ } } }, - "changedTheJoinRulesTo": "{username} قوانین پیوستن را تغییر داد به: {joinRules}", + "changedTheJoinRulesTo": "{username} قوانین پیوستن را به {joinRules} تغییر داد", "@changedTheJoinRulesTo": { "type": "String", "placeholders": { @@ -371,7 +371,7 @@ } } }, - "changedTheHistoryVisibilityTo": "{username} قابليت‌ ديدن‌ تاریخچه را تغییر داد به: {rules}", + "changedTheHistoryVisibilityTo": "{username} ویژگی دیدن تاریخچه را به {rules} تغییر داد", "@changedTheHistoryVisibilityTo": { "type": "String", "placeholders": { @@ -383,51 +383,51 @@ } } }, - "chooseAStrongPassword": "رمز عبور قوی انتخاب کنید", + "chooseAStrongPassword": "یک گذرواژه قوی انتخاب کنید", "@chooseAStrongPassword": { "type": "String", "placeholders": {} }, - "yourChatBackupHasBeenSetUp": "پشتیبان گپ‌تان تنظیم شده است.", + "yourChatBackupHasBeenSetUp": "پشتیبان گپ شما تنظیم شد.", "@yourChatBackupHasBeenSetUp": {}, - "changeTheme": "سبک خود را تغییر دهید", + "changeTheme": "تغییر پوسته", "@changeTheme": { "type": "String", "placeholders": {} }, - "changeTheNameOfTheGroup": "نام گروه را تغییر دهید", + "changeTheNameOfTheGroup": "تغییر نام گروه", "@changeTheNameOfTheGroup": { "type": "String", "placeholders": {} }, - "changeYourAvatar": "عکس پروفایل خود را تغییر دهید", + "changeYourAvatar": "تغییر نماد نمایه", "@changeYourAvatar": { "type": "String", "placeholders": {} }, - "channelCorruptedDecryptError": "رمزگذاری مخدوش شده‌ است", + "channelCorruptedDecryptError": "رمزنگاری مخدوش شده است", "@channelCorruptedDecryptError": { "type": "String", "placeholders": {} }, - "clearArchive": "بایگانی را پاک کنید", + "clearArchive": "پاک کردن بایگانی", "@clearArchive": {}, - "commandHint_create": "یک گپ گروهی خالی بسازید\nاز «--no-encryption» برای غیرفعال کردن رمزگذاری استفاده کنید", + "commandHint_create": "ساختن یک گپ گروهی خالی\nاز «--no-encryption» برای غیرفعال کردن رمزنگاری استفاده کنید", "@commandHint_create": { "type": "String", "description": "Usage hint for the command /create" }, - "commandHint_dm": "یک گپ مستقیم شروع کنید\nاز «--no-encryption» برای غیرفعال کردن رمزگذاری استفاده کنید", + "commandHint_dm": "شروع یک گپ مستقیم\nاز «--no-encryption» برای غیرفعال کردن رمزنگاری استفاده کنید", "@commandHint_dm": { "type": "String", "description": "Usage hint for the command /dm" }, - "sendSticker": "ارسال برچسب", + "sendSticker": "فرستادن برچسب", "@sendSticker": { "type": "String", "placeholders": {} }, - "sendVideo": "ارسال ویدئو", + "sendVideo": "فرستادن ویدئو", "@sendVideo": { "type": "String", "placeholders": {} @@ -448,16 +448,16 @@ "type": "String", "placeholders": {} }, - "commandHint_markasdm": "برای دادن شناسه ماتریکس به عنوان اتاق پیام‌های مستقیم علامت بگذارید", + "commandHint_markasdm": "علامت‌گذاری به‌عنوان اتاق پیام مستقیم با شناسه ماتریکس", "@commandHint_markasdm": {}, - "commandHint_markasgroup": "به عنوان گروه علامت بگذارید", + "commandHint_markasgroup": "علامت‌گذاری به‌عنوان گروه", "@commandHint_markasgroup": {}, - "commandHint_clearcache": "حافظه پنھان را پاک کنید", + "commandHint_clearcache": "پاک کردن حافظه نهان", "@commandHint_clearcache": { "type": "String", "description": "Usage hint for the command /clearcache" }, - "commandHint_discardsession": "طرد نشست", + "commandHint_discardsession": "رد کردن نشست", "@commandHint_discardsession": { "type": "String", "description": "Usage hint for the command /discardsession" @@ -472,7 +472,7 @@ "type": "String", "placeholders": {} }, - "sendAMessage": "ارسال پیام", + "sendAMessage": "فرستادن پیام", "@sendAMessage": { "type": "String", "placeholders": {} @@ -486,16 +486,16 @@ } } }, - "sendAsText": "ارسال به عنوان متن", + "sendAsText": "فرستادن به‌عنوان متن", "@sendAsText": { "type": "String" }, - "sendFile": "ارسال فایل", + "sendFile": "فرستادن پرونده", "@sendFile": { "type": "String", "placeholders": {} }, - "startedACall": "{senderName} تماسی را شروع کرد", + "startedACall": "{senderName} تماس را آغاز کرد", "@startedACall": { "type": "String", "placeholders": { @@ -504,7 +504,7 @@ } } }, - "tryToSendAgain": "تلاش برای ارسال مجدد", + "tryToSendAgain": "تلاش دوباره برای فرستادن", "@tryToSendAgain": { "type": "String", "placeholders": {} @@ -521,12 +521,12 @@ } } }, - "askSSSSSign": "لطفا عبارت عبور یا کلید بازیابی حافظه امن خود را وارد کنید تا بتوانید شخص دیگر را امضا کنید.", + "askSSSSSign": "لطفاً عبارت عبور یا کلید بازیابی حافظه امن را وارد کنید تا شخص دیگری را امضا کنید.", "@askSSSSSign": { "type": "String", "placeholders": {} }, - "chatHasBeenAddedToThisSpace": "گپ به این فضا اضافه شده است", + "chatHasBeenAddedToThisSpace": "گپ به این فضا افزوده شد", "@chatHasBeenAddedToThisSpace": {}, "chat": "گپ", "@chat": { @@ -538,22 +538,22 @@ "type": "String", "placeholders": {} }, - "changePassword": "تغییر رمز عبور", + "changePassword": "تغییر گذرواژه", "@changePassword": { "type": "String", "placeholders": {} }, - "changeTheHomeserver": "تغییر سرور خانه", + "changeTheHomeserver": "تغییر سرور خانگی", "@changeTheHomeserver": { "type": "String", "placeholders": {} }, - "separateChatTypes": "گپ‌های مستقیم را از گروه‌ها جدا کنید", + "separateChatTypes": "جداسازی گپ‌های مستقیم از گروه‌ها", "@separateChatTypes": { "type": "String", "placeholders": {} }, - "sentAPicture": "🖼️ {username} یک عکس فرستاد", + "sentAPicture": "🖼️ {username} یک تصویر فرستاد", "@sentAPicture": { "type": "String", "placeholders": { @@ -585,12 +585,12 @@ }, "description": "State that {command} is not a valid /command." }, - "contactHasBeenInvitedToTheGroup": "مخاطب به گروه دعوت شده است", + "contactHasBeenInvitedToTheGroup": "مخاطب به گروه دعوت شد", "@contactHasBeenInvitedToTheGroup": { "type": "String", "placeholders": {} }, - "sentAFile": "📁 {username} یک فایل فرستاد", + "sentAFile": "📁 {username} یک پرونده فرستاد", "@sentAFile": { "type": "String", "placeholders": { @@ -599,7 +599,7 @@ } } }, - "sentAnAudio": "🎤 {username} یک صدای ضبط شده فرستاد", + "sentAnAudio": "🎤 {username} یک صدا فرستاد", "@sentAnAudio": { "type": "String", "placeholders": { @@ -620,12 +620,12 @@ } } }, - "weSentYouAnEmail": "یک ایمیل برایتان فرستادیم", + "weSentYouAnEmail": "یک رایانامه برای شما فرستادیم", "@weSentYouAnEmail": { "type": "String", "placeholders": {} }, - "loadCountMoreParticipants": "بارگیری {count} شرکت کنندۀ بیشتر", + "loadCountMoreParticipants": "بارگیری {count} شرکت‌کننده دیگر", "@loadCountMoreParticipants": { "type": "String", "placeholders": { @@ -648,39 +648,39 @@ "type": "String", "placeholders": {} }, - "confirm": "تایید", + "confirm": "پذیرفتن", "@confirm": { "type": "String", "placeholders": {} }, "allSpaces": "همه فضاها", "@allSpaces": {}, - "commandHint_ban": "کاربر مشخص شده را از این اتاق محروم کنید", + "commandHint_ban": "محروم کردن کاربر مشخص‌شده از این اتاق", "@commandHint_ban": { "type": "String", "description": "Usage hint for the command /ban" }, - "commandHint_kick": "کاربر مشخص شده را از این اتاق حذف کنید", + "commandHint_kick": "بیرون کردن کاربر مشخص‌شده از این اتاق", "@commandHint_kick": { "type": "String", "description": "Usage hint for the command /kick" }, - "commandHint_plain": "متن بی‌فرمت بفرستید", + "commandHint_plain": "فرستادن متن بدون قالب", "@commandHint_plain": { "type": "String", "description": "Usage hint for the command /plain" }, - "commandHint_unban": "محرومیت کاربر مشخص شده را از این اتاق لغو کنید", + "commandHint_unban": "رد محرومیت کاربر مشخص‌شده از این اتاق", "@commandHint_unban": { "type": "String", "description": "Usage hint for the command /unban" }, - "containsDisplayName": "شامل نام نمایشی است", + "containsDisplayName": "دارای نام نمایشی", "@containsDisplayName": { "type": "String", "placeholders": {} }, - "containsUserName": "شامل نام کاربری است", + "containsUserName": "دارای نام کاربری", "@containsUserName": { "type": "String", "placeholders": {} @@ -690,77 +690,77 @@ "type": "String", "placeholders": {} }, - "copy": "کپی", + "copy": "رونوشت", "@copy": { "type": "String", "placeholders": {} }, - "defaultPermissionLevel": "درجه اجازۀ پیشفرض", + "defaultPermissionLevel": "سطح دسترسی پیش‌فرض", "@defaultPermissionLevel": { "type": "String", "placeholders": {} }, - "delete": "حذف", + "delete": "پاک کردن", "@delete": { "type": "String", "placeholders": {} }, - "deleteAccount": "حساب را حذف کنید", + "deleteAccount": "پاک کردن حساب", "@deleteAccount": { "type": "String", "placeholders": {} }, - "deleteMessage": "پیام را حذف کنید", + "deleteMessage": "پاک کردن پیام", "@deleteMessage": { "type": "String", "placeholders": {} }, - "commandHint_html": "متن با فرمت HTML بفرستید", + "commandHint_html": "فرستادن متن با قالب HTML", "@commandHint_html": { "type": "String", "description": "Usage hint for the command /html" }, - "commandHint_join": "به اتاق مشخص شده بپیوندید", + "commandHint_join": "پیوستن به اتاق مشخص‌شده", "@commandHint_join": { "type": "String", "description": "Usage hint for the command /join" }, - "commandHint_leave": "این اتاق را ترک کنید", + "commandHint_leave": "ترک این اتاق", "@commandHint_leave": { "type": "String", "description": "Usage hint for the command /leave" }, - "commandHint_myroomnick": "نام نمایشی خود را برای این اتاق تنظیم کنید", + "commandHint_myroomnick": "تنظیم نام نمایشی برای این اتاق", "@commandHint_myroomnick": { "type": "String", "description": "Usage hint for the command /myroomnick" }, - "commandHint_myroomavatar": "عکس پروفایل خود را برای این اتاق تنظیم کنید (با mxc-uri)", + "commandHint_myroomavatar": "تنظیم نماد نمایه برای این اتاق (با mxc-uri)", "@commandHint_myroomavatar": { "type": "String", "description": "Usage hint for the command /myroomavatar" }, - "commandHint_op": "درجه اختیار کاربر مشخص شده را تنظیم کنید (پیشفرض: ۵۰)", + "commandHint_op": "تنظیم سطح دسترسی کاربر مشخص‌شده (پیش‌فرض: ۵۰)", "@commandHint_op": { "type": "String", "description": "Usage hint for the command /op" }, - "commandHint_react": "پاسخ را به عنوان یک واکنش بفرستید", + "commandHint_react": "فرستادن پاسخ به‌عنوان واکنش", "@commandHint_react": { "type": "String", "description": "Usage hint for the command /react" }, - "compareEmojiMatch": "لطفا ایموجی‌ها را مقایسه کنید", + "compareEmojiMatch": "لطفاً شکلک‌ها را مقایسه کنید", "@compareEmojiMatch": { "type": "String", "placeholders": {} }, - "copyToClipboard": "در حافظه کپی کنید", + "copyToClipboard": "رونوشت به بریده‌دان", "@copyToClipboard": { "type": "String", "placeholders": {} }, - "countParticipants": "{count} شرکت کننده", + "countParticipants": "{count} شرکت‌کننده", "@countParticipants": { "type": "String", "placeholders": { @@ -769,7 +769,7 @@ } } }, - "create": "ایجاد", + "create": "ساختن", "@create": { "type": "String", "placeholders": {} @@ -794,7 +794,7 @@ } } }, - "deviceId": "هویت دستگاه", + "deviceId": "شناسه دستگاه", "@deviceId": { "type": "String", "placeholders": {} @@ -804,12 +804,12 @@ "type": "String", "placeholders": {} }, - "displaynameHasBeenChanged": "نام نمایشی تغییر یافته است", + "displaynameHasBeenChanged": "نام نمایشی تغییر کرد", "@displaynameHasBeenChanged": { "type": "String", "placeholders": {} }, - "downloadFile": "بارگیری فایل", + "downloadFile": "بارگیری پرونده", "@downloadFile": { "type": "String", "placeholders": {} @@ -819,17 +819,17 @@ "type": "String", "placeholders": {} }, - "editBlockedServers": "سرور‌های مسدود را ویرایش کنید", + "editBlockedServers": "ویرایش سرورهای مسدود", "@editBlockedServers": { "type": "String", "placeholders": {} }, - "editRoomAliases": "نام‌های مستعار اتاق را ویرایش کنید", + "editRoomAliases": "ویرایش نام‌های مستعار اتاق", "@editRoomAliases": { "type": "String", "placeholders": {} }, - "editRoomAvatar": "عکس اتاق را ویرایش کنید", + "editRoomAvatar": "ویرایش نماد اتاق", "@editRoomAvatar": { "type": "String", "placeholders": {} @@ -844,7 +844,7 @@ "type": "String", "placeholders": {} }, - "emoteInvalid": "کد کوتاه شکلک نامعتبر!", + "emoteInvalid": "کد کوتاه شکلک نامعتبر است!", "@emoteInvalid": { "type": "String", "placeholders": {} @@ -854,12 +854,12 @@ "type": "String", "placeholders": {} }, - "copiedToClipboard": "در حافظه کپی شد", + "copiedToClipboard": "به بریده‌دان رونوشت شد", "@copiedToClipboard": { "type": "String", "placeholders": {} }, - "createdTheChat": "💬 {username} گپ را ایجاد کرد", + "createdTheChat": "💬 {username} گپ را ساخت", "@createdTheChat": { "type": "String", "placeholders": { @@ -868,7 +868,7 @@ } } }, - "darkTheme": "تاریک", + "darkTheme": "تیره", "@darkTheme": { "type": "String", "placeholders": {} @@ -883,7 +883,7 @@ "type": "String", "placeholders": {} }, - "emoteSettings": "‏تنظیمات شکلک", + "emoteSettings": "تنظیمات شکلک", "@emoteSettings": { "type": "String", "placeholders": {} @@ -893,17 +893,17 @@ "type": "String", "placeholders": {} }, - "commandHint_me": "خود را توصیف کنید", + "commandHint_me": "توصیف خود", "@commandHint_me": { "type": "String", "description": "Usage hint for the command /me" }, - "commandHint_send": "متن را بفرستید", + "commandHint_send": "فرستادن متن", "@commandHint_send": { "type": "String", "description": "Usage hint for the command /send" }, - "couldNotDecryptMessage": "نتوانستیم پیام را رمزگشایی کنیم: {error}", + "couldNotDecryptMessage": "نمی‌توان پیام را رمزگشایی کرد: {error}", "@couldNotDecryptMessage": { "type": "String", "placeholders": { @@ -912,27 +912,27 @@ } } }, - "chatBackupDescription": "پیام‌های قدیمی‌تان با یک کلید باز یابی، امن می‌شوند. لطفا مطمئن شوید که آن را گم نمی‌کنید.", + "chatBackupDescription": "پیام‌های قدیمی شما با یک کلید بازیابی امن می‌شوند. لطفاً مطمئن شوید آن را گم نمی‌کنید.", "@chatBackupDescription": { "type": "String", "placeholders": {} }, - "deactivateAccountWarning": "این کار حساب کاربری‌تان را غیرفعال خواهد کرد. این عمل قابل جبران و بازگشت نیست! آیا اطمینان دارید؟", + "deactivateAccountWarning": "این کار حساب شما را غیرفعال می‌کند. این کنش برگشت‌ناپذیر است! آیا مطمئن هستید؟", "@deactivateAccountWarning": { "type": "String", "placeholders": {} }, - "commandHint_invite": "کاربر مشخص شده را به این اتاق دعوت کنید", + "commandHint_invite": "دعوت از کاربر مشخص‌شده به این اتاق", "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" }, - "compareNumbersMatch": "لطفا اعداد را مقایسه کنید", + "compareNumbersMatch": "لطفاً اعداد را مقایسه کنید", "@compareNumbersMatch": { "type": "String", "placeholders": {} }, - "configureChat": "گپ را تنظیم کنید", + "configureChat": "پیکربندی گپ", "@configureChat": { "type": "String", "placeholders": {} @@ -971,12 +971,12 @@ "type": "String", "placeholders": {} }, - "id": "آی‌دی", + "id": "شناسه", "@id": { "type": "String", "placeholders": {} }, - "hasWithdrawnTheInvitationFor": "{username} دعوت‌نامه {targetName} را پس گرفته است", + "hasWithdrawnTheInvitationFor": "{username} دعوت {targetName} را پس گرفت", "@hasWithdrawnTheInvitationFor": { "type": "String", "placeholders": { @@ -988,12 +988,12 @@ } } }, - "guestsAreForbidden": "مهمان‌ها ممنوع شده‌اند", + "guestsAreForbidden": "مهمان‌ها ممنوع هستند", "@guestsAreForbidden": { "type": "String", "placeholders": {} }, - "hideRedactedEvents": "پنهان کردن رویدادهای ویرایش شده", + "hideRedactedEvents": "پنهان کردن رویدادهای ویرایش‌شده", "@hideRedactedEvents": { "type": "String", "placeholders": {} @@ -1003,7 +1003,7 @@ "type": "String", "placeholders": {} }, - "contentHasBeenReported": "محتوا به مدیران سرور گزارش شده است", + "contentHasBeenReported": "محتوا به مدیران سرور گزارش شد", "@contentHasBeenReported": { "type": "String", "placeholders": {} @@ -1018,7 +1018,7 @@ "type": "String", "placeholders": {} }, - "goToTheNewRoom": "به اتاق جدید بروید", + "goToTheNewRoom": "رفتن به اتاق جدید", "@goToTheNewRoom": { "type": "String", "placeholders": {} @@ -1042,42 +1042,42 @@ } } }, - "howOffensiveIsThisContent": "این محتوا چه مقدار توهین آمیز است؟", + "howOffensiveIsThisContent": "این محتوا چقدر توهین‌آمیز است؟", "@howOffensiveIsThisContent": { "type": "String", "placeholders": {} }, - "enableEmotesGlobally": "بسته شکلک را به طور سراسری فعال کنید", + "enableEmotesGlobally": "فعال کردن بسته شکلک به‌صورت سراسری", "@enableEmotesGlobally": { "type": "String", "placeholders": {} }, - "enableEncryption": "رمزگذاری را فعال کنید", + "enableEncryption": "فعال کردن رمزنگاری", "@enableEncryption": { "type": "String", "placeholders": {} }, - "enableEncryptionWarning": "شما دیگر قادر به غیرفعال کردن رمزگذاری نخواهید بود. آیا مطمئن هستید؟", + "enableEncryptionWarning": "نمی‌توانید رمزنگاری را غیرفعال کنید. آیا مطمئن هستید؟", "@enableEncryptionWarning": { "type": "String", "placeholders": {} }, - "encrypted": "رمزگذاری شده", + "encrypted": "رمزنگاری‌شده", "@encrypted": { "type": "String", "placeholders": {} }, - "encryption": "رمزگذاری", + "encryption": "رمزنگاری", "@encryption": { "type": "String", "placeholders": {} }, - "encryptionNotEnabled": "رمزگذاری فعال نیست", + "encryptionNotEnabled": "رمزنگاری فعال نیست", "@encryptionNotEnabled": { "type": "String", "placeholders": {} }, - "enterAnEmailAddress": "یک آدرس رایانامه(ایمیل) وارد کنید", + "enterAnEmailAddress": "یک نشانی رایانامه وارد کنید", "@enterAnEmailAddress": { "type": "String", "placeholders": {} @@ -1091,14 +1091,14 @@ } } }, - "homeserver": "سرور خانه", + "homeserver": "سرور خانگی", "@homeserver": {}, - "enterYourHomeserver": "سرور خانه خود را وارد کنید", + "enterYourHomeserver": "سرور خانگی خود را وارد کنید", "@enterYourHomeserver": { "type": "String", "placeholders": {} }, - "errorObtainingLocation": "خطا هنگام بدست آوردن مکان: {error}", + "errorObtainingLocation": "خطا در به‌دست آوردن مکان: {error}", "@errorObtainingLocation": { "type": "String", "placeholders": { @@ -1112,12 +1112,12 @@ "type": "String", "placeholders": {} }, - "extremeOffensive": "به شدت توهین آمیز", + "extremeOffensive": "بسیار توهین‌آمیز", "@extremeOffensive": { "type": "String", "placeholders": {} }, - "fileName": "نام فایل", + "fileName": "نام پرونده", "@fileName": { "type": "String", "placeholders": {} @@ -1132,7 +1132,7 @@ "type": "String", "placeholders": {} }, - "forward": "ارسال", + "forward": "هدایت", "@forward": { "type": "String", "placeholders": {} @@ -1152,9 +1152,9 @@ "type": "String", "placeholders": {} }, - "commandHint_googly": "ارسال چند چشم گوگولی", + "commandHint_googly": "فرستادن چند چشم گوگولی", "@commandHint_googly": {}, - "googlyEyesContent": "{senderName} به شما چشمان گوگولی می‌فرستد", + "googlyEyesContent": "{senderName} برای شما چشم‌های گوگولی می‌فرستد", "@googlyEyesContent": { "type": "String", "placeholders": { @@ -1191,18 +1191,18 @@ } } }, - "commandHint_cuddle": "ارسال آغوش", + "commandHint_cuddle": "فرستادن آغوش", "@commandHint_cuddle": {}, - "commandHint_hug": "ارسال بغل", + "commandHint_hug": "فرستادن بغل", "@commandHint_hug": {}, - "editBundlesForAccount": "بسته‌های این حساب را ویرایش کنید", + "editBundlesForAccount": "ویرایش بسته‌های این حساب", "@editBundlesForAccount": {}, - "logout": "خارج شدن", + "logout": "خروج", "@logout": { "type": "String", "placeholders": {} }, - "mention": "نام‌‌بردن‌", + "mention": "نام‌بردن", "@mention": { "type": "String", "placeholders": {} @@ -1222,22 +1222,22 @@ "type": "String", "placeholders": {} }, - "noConnectionToTheServer": "عدم اتصال به سرور", + "noConnectionToTheServer": "بدون اتصال به سرور", "@noConnectionToTheServer": { "type": "String", "placeholders": {} }, - "no": "نه", + "no": "خیر", "@no": { "type": "String", "placeholders": {} }, - "noPasswordRecoveryDescription": "شما هنوز راهی برای بازیابی رمز عبور خود اضافه نکرده‌اید.", + "noPasswordRecoveryDescription": "هنوز روشی برای بازیابی گذرواژه خود اضافه نکرده‌اید.", "@noPasswordRecoveryDescription": { "type": "String", "placeholders": {} }, - "notificationsEnabledForThisAccount": "اعلان‌ها برای این حساب فعال شد", + "notificationsEnabledForThisAccount": "آگاه‌سازها برای این حساب فعال شدند", "@notificationsEnabledForThisAccount": { "type": "String", "placeholders": {} @@ -1257,17 +1257,17 @@ "type": "String", "placeholders": {} }, - "password": "رمز عبور", + "password": "گذرواژه", "@password": { "type": "String", "placeholders": {} }, - "passwordHasBeenChanged": "رمز عبور تغییر کرد", + "passwordHasBeenChanged": "گذرواژه تغییر کرد", "@passwordHasBeenChanged": { "type": "String", "placeholders": {} }, - "passwordRecovery": "بازیابی رمز عبور", + "passwordRecovery": "بازیابی گذرواژه", "@passwordRecovery": { "type": "String", "placeholders": {} @@ -1286,22 +1286,22 @@ } } }, - "pleaseEnter4Digits": "لطفا ۴ رقم وارد کنید یا خالی بگذارید تا قفل برنامه غیرفعال شود.", + "pleaseEnter4Digits": "لطفاً ۴ رقم وارد کنید یا خالی بگذارید تا قفل برنامه غیرفعال شود.", "@pleaseEnter4Digits": { "type": "String", "placeholders": {} }, - "pleaseEnterYourPin": "لطفا کد خود را وارد کنید", + "pleaseEnterYourPin": "لطفاً رمز کوتاه خود را وارد کنید", "@pleaseEnterYourPin": { "type": "String", "placeholders": {} }, - "pleaseEnterYourPassword": "لطفا رمزعبور خود را وارد کنید", + "pleaseEnterYourPassword": "لطفاً گذرواژه خود را وارد کنید", "@pleaseEnterYourPassword": { "type": "String", "placeholders": {} }, - "pleaseFollowInstructionsOnWeb": "لطفا دستورالعمل‌های وب‌سایت را دنبال کنید و روی بعدی بزنید.", + "pleaseFollowInstructionsOnWeb": "لطفاً دستورالعمل‌های وبگاه را دنبال کنید و روی بعدی بزنید.", "@pleaseFollowInstructionsOnWeb": { "type": "String", "placeholders": {} @@ -1311,7 +1311,7 @@ "type": "String", "placeholders": {} }, - "obtainingLocation": "به دست آوردن مکان…", + "obtainingLocation": "در حال به‌دست آوردن مکان…", "@obtainingLocation": { "type": "String", "placeholders": {} @@ -1321,33 +1321,33 @@ "type": "String", "placeholders": {} }, - "addToBundle": "به بسته نرم‌افزاری اضافه کنید", + "addToBundle": "افزودن به بسته", "@addToBundle": {}, - "passwordForgotten": "رمز عبور را فراموش کرده‌ام", + "passwordForgotten": "فراموشی گذرواژه", "@passwordForgotten": { "type": "String", "placeholders": {} }, - "pleaseEnterRecoveryKey": "لطفا کلید بازیابی خود را وارد کنید:", + "pleaseEnterRecoveryKey": "لطفاً کلید بازیابی خود را وارد کنید:", "@pleaseEnterRecoveryKey": {}, "link": "پیوند", "@link": {}, - "iHaveClickedOnLink": "من روی پیوند کلیک کردم", + "iHaveClickedOnLink": "روی پیوند کلیک کردم", "@iHaveClickedOnLink": { "type": "String", "placeholders": {} }, - "incorrectPassphraseOrKey": "عبارت عبور یا کلید بازیابی اشتباه است", + "incorrectPassphraseOrKey": "عبارت عبور یا کلید بازیابی نادرست است", "@incorrectPassphraseOrKey": { "type": "String", "placeholders": {} }, - "inoffensive": "بی ضرر", + "inoffensive": "بی‌ضرر", "@inoffensive": { "type": "String", "placeholders": {} }, - "inviteContactToGroup": "دعوت از مخاطب به {groupName}", + "inviteContactToGroup": "دعوت مخاطب به {groupName}", "@inviteContactToGroup": { "type": "String", "placeholders": { @@ -1356,7 +1356,7 @@ } } }, - "invitedUsersOnly": "فقط کاربران دعوت شده", + "invitedUsersOnly": "فقط کاربران دعوت‌شده", "@invitedUsersOnly": { "type": "String", "placeholders": {} @@ -1376,7 +1376,7 @@ "type": "String", "placeholders": {} }, - "kicked": "👞 {username} {targetName} را بیرون کرد", + "kicked": "👞 {username} کاربر {targetName} را بیرون کرد", "@kicked": { "type": "String", "placeholders": { @@ -1388,7 +1388,7 @@ } } }, - "kickFromChat": "از گفتگو بیرون کردن", + "kickFromChat": "بیرون کردن از گپ", "@kickFromChat": { "type": "String", "placeholders": {} @@ -1398,26 +1398,26 @@ "type": "String", "placeholders": {} }, - "dehydrate": "صدور جلسه و پاک کردن دستگاه", + "dehydrate": "صدور نشست و پاک کردن دستگاه", "@dehydrate": {}, - "hydrateTorLong": "آیا آخرین بار جلسه خود را با تور (TOR) صادر کردید؟ به سرعت آن را وارد کنید و به گپ‌زنی ادامه دهید.", + "hydrateTorLong": "آیا آخرین بار نشست خود را با تور صادر کردید؟ آن را وارد کنید و گپ را ادامه دهید.", "@hydrateTorLong": {}, - "loadingPleaseWait": "در حال بارگیری... لطفا صبر کنید.", + "loadingPleaseWait": "در حال بارگذاری… لطفاً صبر کنید.", "@loadingPleaseWait": { "type": "String", "placeholders": {} }, - "loadMore": "بارگیری بیشتر…", + "loadMore": "بارگذاری بیشتر…", "@loadMore": { "type": "String", "placeholders": {} }, - "locationPermissionDeniedNotice": "مجوز مکان رد شد. برای به اشتراک گذاشتن موقعیت مکانی شما لطفا به آن اجازه دهید.", + "locationPermissionDeniedNotice": "دسترسی به مکان رد شد. برای هم‌رسانی مکان، لطفاً دسترسی بدهید.", "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} }, - "logInTo": "وارد شدن به {homeserver}", + "logInTo": "ورود به {homeserver}", "@logInTo": { "type": "String", "placeholders": { @@ -1431,17 +1431,17 @@ "type": "String", "placeholders": {} }, - "moderator": "مدیر", + "moderator": "ناظر", "@moderator": { "type": "String", "placeholders": {} }, - "newVerificationRequest": "درخواست تایید جدید!", + "newVerificationRequest": "درخواست بازبینی جدید!", "@newVerificationRequest": { "type": "String", "placeholders": {} }, - "noPermission": "بدون اجازه", + "noPermission": "بدون دسترسی", "@noPermission": { "type": "String", "placeholders": {} @@ -1461,7 +1461,7 @@ "type": "String", "placeholders": {} }, - "oopsPushError": "اوه! متاسفانه هنگام تنظیم اعلان‌ها خطایی روی داد.", + "oopsPushError": "اوه! خطایی در تنظیم آگاه‌سازها رخ داد.", "@oopsPushError": { "type": "String", "placeholders": {} @@ -1471,29 +1471,29 @@ "type": "String", "placeholders": {} }, - "addAccount": "اضافه کردن حساب کاربری", + "addAccount": "افزودن حساب", "@addAccount": {}, - "people": "مردم", + "people": "افراد", "@people": { "type": "String", "placeholders": {} }, - "pickImage": "یک عکس انتخاب کنید", + "pickImage": "انتخاب تصویر", "@pickImage": { "type": "String", "placeholders": {} }, - "pleaseChoose": "لطفا انتخاب کنید", + "pleaseChoose": "لطفاً انتخاب کنید", "@pleaseChoose": { "type": "String", "placeholders": {} }, - "pleaseChooseAPasscode": "لطفا یک کد عبور انتخاب کنید", + "pleaseChooseAPasscode": "لطفاً یک رمز کوتاه انتخاب کنید", "@pleaseChooseAPasscode": { "type": "String", "placeholders": {} }, - "pleaseEnterYourUsername": "لطفا نام‌کاربری خود را وارد کنید", + "pleaseEnterYourUsername": "لطفاً نام کاربری خود را وارد کنید", "@pleaseEnterYourUsername": { "type": "String", "placeholders": {} @@ -1503,7 +1503,7 @@ "type": "String", "placeholders": {} }, - "license": "پروانه", + "license": "گواهینامه", "@license": { "type": "String", "placeholders": {} @@ -1517,7 +1517,7 @@ } } }, - "invited": "دعوت شده", + "invited": "دعوت‌شده", "@invited": { "type": "String", "placeholders": {} @@ -1543,7 +1543,7 @@ } } }, - "kickedAndBanned": "🙅 {username} {targetName} را بیرون و محروم کرد", + "kickedAndBanned": "🙅 {username} کاربر {targetName} را بیرون و محروم کرد", "@kickedAndBanned": { "type": "String", "placeholders": { @@ -1555,16 +1555,16 @@ } } }, - "dehydrateWarning": "این عمل قابل لغو نیست. مطمئن شوید که فایل پشتیبان را به صورت امن ذخیره می کنید.", + "dehydrateWarning": "این کنش برگشت‌ناپذیر است. مطمئن شوید پرونده پشتیبان را به‌صورت امن ذخیره می‌کنید.", "@dehydrateWarning": {}, - "locationDisabledNotice": "خدمات مکان غیرفعال است. لطفا آن را فعال کنید تا بتوانید موقعیت مکانی خود را به اشتراک بگذارید.", + "locationDisabledNotice": "مکان‌یاب غیرفعال است. لطفاً آن را فعال کنید تا بتوانید مکان خود را هم‌رسانی کنید.", "@locationDisabledNotice": { "type": "String", "placeholders": {} }, - "dehydrateTorLong": "برای کاربران تور (TOR)، توصیه می شود قبل از بستن پنجره، جلسه را صادر کنند.", + "dehydrateTorLong": "برای کاربران تور، پیشنهاد می‌شود پیش از بستن پنجره، نشست را صادر کنند.", "@dehydrateTorLong": {}, - "needPantalaimonWarning": "لطفا توجه داشته باشید که در حال حاضر برای استفاده از رمزگذاری انتها به انتها به Pantalaimon نیاز دارید.", + "needPantalaimonWarning": "لطفاً توجه کنید که برای رمزنگاری سرتاسر به Pantalaimon نیاز دارید.", "@needPantalaimonWarning": { "type": "String", "placeholders": {} @@ -1574,43 +1574,43 @@ "type": "String", "placeholders": {} }, - "noEmotesFound": "هیچ شکلکی پیدا نشد. 😕", + "noEmotesFound": "شکلکی پیدا نشد. 😕", "@noEmotesFound": { "type": "String", "placeholders": {} }, - "noGoogleServicesWarning": "به نظر می رسد که شما سرویس‌های گوگل را در گوشی خود ندارید. این تصمیم خوبی برای حفظ حریم خصوصی شماست! برای دریافت اعلان‌ها در فلافی‌چت توصیه می‌کنیم ازhttps://ntfy.sh استفاده کنید. با ntfy یا یک ارائه دهنده UnifiedPush می توانید اعلان‌های فشار را به روش داده امن دریافت کنید. می توانید ntfy را از پلی استور یا از اف‌دروید بارگیری کنید.", + "noGoogleServicesWarning": "به نظر می‌رسد دستگاه شما سرویس‌های گوگل ندارد. این انتخاب خوبی برای حریم خصوصی است! برای دریافت آگاه‌سازها در فلافی‌چت، پیشنهاد می‌کنیم از https://ntfy.sh استفاده کنید. با ntfy یا یک فراهم‌کننده UnifiedPush می‌توانید آگاه‌سازهای امن دریافت کنید. می‌توانید ntfy را از Play Store یا F-Droid بارگیری کنید.", "@noGoogleServicesWarning": { "type": "String", "placeholders": {} }, - "noEncryptionForPublicRooms": "فقط زمانی می‌توانید رمزگذاری را فعال کنید که اتاق، دیگر در دسترس عموم نباشد.", + "noEncryptionForPublicRooms": "رمزنگاری را تنها زمانی می‌توانید فعال کنید که اتاق عمومی نباشد.", "@noEncryptionForPublicRooms": { "type": "String", "placeholders": {} }, - "onlineKeyBackupEnabled": "پشتیبان‌گیری آنلاین از کلید فعال است", + "onlineKeyBackupEnabled": "پشتیبان‌گیری آنلاین کلید فعال است", "@onlineKeyBackupEnabled": { "type": "String", "placeholders": {} }, - "enableMultiAccounts": "(آزمایشی) چند حساب را در این دستگاه فعال کنید", + "enableMultiAccounts": "(آزمایشی) فعال کردن چند حساب در این دستگاه", "@enableMultiAccounts": {}, - "pleaseClickOnLink": "لطفا روی لینک موجود در رایانامه(ایمیل) کلیک کنید و سپس ادامه دهید.", + "pleaseClickOnLink": "لطفاً روی پیوند در رایانامه کلیک کنید و ادامه دهید.", "@pleaseClickOnLink": { "type": "String", "placeholders": {} }, - "hydrateTor": "کاربران تور (TOR): صادرات جلسه را وارد کنید", + "hydrateTor": "کاربران تور: وارد کردن نشست صادر شده", "@hydrateTor": {}, - "hydrate": "بازیابی از فایل پشتیبان", + "hydrate": "بازیابی از پرونده پشتیبان", "@hydrate": {}, "inviteContact": "دعوت از مخاطب", "@inviteContact": { "type": "String", "placeholders": {} }, - "noMatrixServer": "{server1} سرور ماتریکس نیست، به جای آن از {server2} استفاده شود؟", + "noMatrixServer": "{server1} سرور ماتریکس نیست، از {server2} استفاده شود؟", "@noMatrixServer": { "type": "String", "placeholders": { @@ -1622,25 +1622,25 @@ } } }, - "openVideoCamera": "بازکردن دوربین برای فیلم‌برداری", + "openVideoCamera": "باز کردن دوربین برای فیلم‌برداری", "@openVideoCamera": { "type": "String", "placeholders": {} }, - "oneClientLoggedOut": "یکی از کلاینت(برنامه)های شما از سیستم خارج شده است", + "oneClientLoggedOut": "یکی از برنامه‌های شما از سیستم خارج شد", "@oneClientLoggedOut": {}, - "removeFromBundle": "از این بسته حذف کنید", + "removeFromBundle": "برداشتن از بسته", "@removeFromBundle": {}, - "bundleName": "اسم بسته", + "bundleName": "نام بسته", "@bundleName": {}, "openInMaps": "باز کردن در نقشه", "@openInMaps": { "type": "String", "placeholders": {} }, - "serverRequiresEmail": "برای ثبت‌نام، این سرور باید آدرس ایمیل شما را تایید کند.", + "serverRequiresEmail": "برای ثبت‌نام، این سرور باید نشانی رایانامه شما را تأیید کند.", "@serverRequiresEmail": {}, - "inviteText": "{username} شما را به فلافی‌چت دعوت کرد.\n۱. به fluffychat.im مراجعه کرده و کاره را نصب کنید\n۲. ثبت نام کنید یا وارد شوید.\n۳. لینک دعوت را باز کنید:\n {link}", + "inviteText": "{username} شما را به فلافی‌چت دعوت کرد.\n۱. به fluffychat.im بروید و برنامه را نصب کنید\n۲. ثبت‌نام کنید یا وارد شوید\n۳. پیوند دعوت را باز کنید:\n {link}", "@inviteText": { "type": "String", "placeholders": { @@ -1657,17 +1657,17 @@ "type": "String", "placeholders": {} }, - "none": "هیچ‌کدام", + "none": "هیچ", "@none": { "type": "String", "placeholders": {} }, - "offensive": "توهین آمیز", + "offensive": "توهین‌آمیز", "@offensive": { "type": "String", "placeholders": {} }, - "scanQrCode": "کد QR را اسکن کنید", + "scanQrCode": "پویش کد QR", "@scanQrCode": {}, "lastActiveAgo": "آخرین فعالیت: {localizedTimeShort}", "@lastActiveAgo": { @@ -1683,29 +1683,29 @@ "type": "String", "placeholders": {} }, - "dehydrateTor": "کاربران تور (TOR): صدور جلسه", + "dehydrateTor": "کاربران تور: صدور نشست", "@dehydrateTor": {}, - "login": "وارد شدن", + "login": "ورود", "@login": { "type": "String", "placeholders": {} }, - "notifications": "اعلان‌ها", + "notifications": "آگاه‌سازها", "@notifications": { "type": "String", "placeholders": {} }, - "ok": "تایید", + "ok": "خوب", "@ok": { "type": "String", "placeholders": {} }, - "roomHasBeenUpgraded": "اتاق ارتقا پیدا کرد", + "roomHasBeenUpgraded": "اتاق ارتقا یافت", "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} }, - "register": "ثبت نام", + "register": "ثبت‌نام", "@register": { "type": "String", "placeholders": {} @@ -1724,7 +1724,7 @@ } } }, - "rejoin": "دوباره پیوستن", + "rejoin": "پیوستن دوباره", "@rejoin": { "type": "String", "placeholders": {} @@ -1739,12 +1739,12 @@ "type": "String", "placeholders": {} }, - "requestPermission": "درخواست اجازه", + "requestPermission": "درخواست دسترسی", "@requestPermission": { "type": "String", "placeholders": {} }, - "roomVersion": "نسخه اتاق", + "roomVersion": "نگارش اتاق", "@roomVersion": { "type": "String", "placeholders": {} @@ -1759,7 +1759,7 @@ "type": "String", "placeholders": {} }, - "seenByUser": "دیده شده توسط {username}", + "seenByUser": "دیده‌شده توسط {username}", "@seenByUser": { "type": "String", "placeholders": { @@ -1768,12 +1768,12 @@ } } }, - "setAsCanonicalAlias": "به عنوان نام مستعار اصلی تنظیم کنید", + "setAsCanonicalAlias": "تنظیم به‌عنوان نام مستعار اصلی", "@setAsCanonicalAlias": { "type": "String", "placeholders": {} }, - "removeYourAvatar": "آواتار(عکس حساب) خود را حذف کنید", + "removeYourAvatar": "برداشتن نماد نمایه", "@removeYourAvatar": { "type": "String", "placeholders": {} @@ -1792,17 +1792,17 @@ } } }, - "remove": "حذف کردن", + "remove": "برداشتن", "@remove": { "type": "String", "placeholders": {} }, - "removeAllOtherDevices": "حذف تمام دستگاه‌های دیگر", + "removeAllOtherDevices": "پاک کردن همه دستگاه‌های دیگر", "@removeAllOtherDevices": { "type": "String", "placeholders": {} }, - "removedBy": "حذف شده توسط {username}", + "removedBy": "پاک‌شده توسط {username}", "@removedBy": { "type": "String", "placeholders": { @@ -1811,17 +1811,17 @@ } } }, - "removeDevice": "حذف دستگاه", + "removeDevice": "پاک کردن دستگاه", "@removeDevice": { "type": "String", "placeholders": {} }, - "reportMessage": "گزارش دادن پیام", + "reportMessage": "گزارش پیام", "@reportMessage": { "type": "String", "placeholders": {} }, - "saveFile": "ذخیره فایل", + "saveFile": "ذخیره پرونده", "@saveFile": { "type": "String", "placeholders": {} @@ -1833,7 +1833,7 @@ "type": "String", "placeholders": {} }, - "pushRules": "قواعد دریافت اعلان", + "pushRules": "قوانین آگاه‌ساز", "@pushRules": { "type": "String", "placeholders": {} @@ -1843,7 +1843,7 @@ "type": "String", "placeholders": {} }, - "setCustomEmotes": "شکلک سفارشی را تنظیم کنید", + "setCustomEmotes": "تنظیم شکلک‌های سفارشی", "@setCustomEmotes": { "type": "String", "placeholders": {} @@ -1853,14 +1853,14 @@ "type": "String", "placeholders": {} }, - "replaceRoomWithNewerVersion": "اتاق را با نسخه جدیدتر جایگزین کنید", + "replaceRoomWithNewerVersion": "جایگزینی اتاق با نگارش جدیدتر", "@replaceRoomWithNewerVersion": { "type": "String", "placeholders": {} }, - "recoveryKeyLost": "کلید بازیابی را گم کردید؟", + "recoveryKeyLost": "کلید بازیابی گم شد؟", "@recoveryKeyLost": {}, - "fileHasBeenSavedAt": "فایل در {path} ذخیره شده است", + "fileHasBeenSavedAt": "پرونده در {path} ذخیره شد", "@fileHasBeenSavedAt": { "type": "String", "placeholders": { @@ -1871,7 +1871,7 @@ }, "enterSpace": "ورود به فضا", "@enterSpace": {}, - "wasDirectChatDisplayName": "گپ خالی ({oldDisplayName} بود)", + "wasDirectChatDisplayName": "گپ خالی (پیش‌تر {oldDisplayName} بود)", "@wasDirectChatDisplayName": { "type": "String", "placeholders": { @@ -1880,29 +1880,29 @@ } } }, - "newSpaceDescription": "فضاها به شما امکان می‌دهند گپ‌های خود را یکپارچه کنید و جوامع خصوصی یا عمومی بسازید.", + "newSpaceDescription": "فضاها امکان یکپارچه‌سازی گپ‌ها و ساخت جوامع خصوصی یا عمومی را فراهم می‌کنند.", "@newSpaceDescription": {}, - "encryptThisChat": "این گپ را رمزگذاری کنید", + "encryptThisChat": "رمزنگاری این گپ", "@encryptThisChat": {}, - "sorryThatsNotPossible": "متاسفم... این امکان‌پذیر نیست", + "sorryThatsNotPossible": "متأسفیم... این ممکن نیست", "@sorryThatsNotPossible": {}, "deviceKeys": "کلیدهای دستگاه:", "@deviceKeys": {}, - "fileIsTooBigForServer": "سرور گزارش می‌دهد که فایل برای ارسال بسیار بزرگ است.", + "fileIsTooBigForServer": "نمیتوان فرستاد! سرور تنها از پیوست های تا {max} پشتیبانی میکند.", "@fileIsTooBigForServer": {}, - "jumpToLastReadMessage": "پرش به آخرین پیام خوانده شده", + "jumpToLastReadMessage": "پرش به آخرین پیام خوانده‌شده", "@jumpToLastReadMessage": {}, - "hideUnimportantStateEvents": "رویدادهای غیر مهم مربوط به وضعیت را پنهان کنید", + "hideUnimportantStateEvents": "پنهان کردن رویدادهای غیرمهم وضعیت", "@hideUnimportantStateEvents": {}, - "doNotShowAgain": "دوباره نشان نده", + "doNotShowAgain": "دوباره نمایش نده", "@doNotShowAgain": {}, - "readUpToHere": "تا اینجا خوانده شده", + "readUpToHere": "خوانده‌شده تا اینجا", "@readUpToHere": {}, - "noBackupWarning": "هشدار! بدون فعال کردن پشتیبان گپ، دسترسی به پیام های رمزگذاری شده خود را از دست خواهید داد. قویا توصیه می‌شود قبل از خروج از سیستم، ابتدا پشتیبان‌گیری گپ را فعال کنید.", + "noBackupWarning": "هشدار! بدون فعال کردن پشتیبان گپ، دسترسی به پیام‌های رمزنگاری‌شده خود را از دست خواهید داد. پیشنهاد می‌شود پیش از خروج، پشتیبان گپ را فعال کنید.", "@noBackupWarning": {}, "noOtherDevicesFound": "دستگاه دیگری پیدا نشد", "@noOtherDevicesFound": {}, - "countFiles": "{count} فایل", + "countFiles": "{count} پرونده", "@countFiles": { "placeholders": { "count": { @@ -1910,9 +1910,9 @@ } } }, - "callingAccountDetails": "به فلافی‌چت اجازه می‌دهد تا از برنامه شماره‌گیر بومی اندروید استفاده کند.", + "callingAccountDetails": "اجازه به فلافی‌چت برای استفاده از شماره‌گیر بومی اندروید.", "@callingAccountDetails": {}, - "noKeyForThisMessage": "اگر پیام قبل از ورود به حسابتان در این دستگاه ارسال شده باشد، ممکن است این اتفاق بیفتد.\n\nهمچنین ممکن است فرستنده، دستگاه شما را مسدود کرده باشد یا مشکلی در اتصال اینترنت رخ داده باشد.\n\nآیا می توانید پیام را در نشست دیگری بخوانید؟ بنابراین می توانید پیام را از آن منتقل کنید! به تنظیمات > دستگاه‌ها بروید و مطمئن شوید که دستگاه های شما یکدیگر را تایید کرده‌اند. هنگامی که دفعه بعد اتاق را باز می‌کنید و هر دو جلسه در پیش‌زمینه هستند، کلیدها به طور خودکار منتقل می‌شوند.\n\nآیا نمی‌خواهید هنگام خروج از سیستم یا تعویض دستگاه، کلیدها را گم کنید؟ مطمئن شوید که پشتیبان گپ را در تنظیمات فعال کرده‌اید.", + "noKeyForThisMessage": "اگر پیام پیش از ورود به حساب در این دستگاه فرستاده شده باشد، این مشکل ممکن است رخ دهد.\n\nهمچنین ممکن است فرستنده دستگاه شما را مسدود کرده باشد یا مشکلی در اتصال اینترنت وجود داشته باشد.\n\nآیا می‌توانید پیام را در نشست دیگری بخوانید؟ در این صورت، می‌توانید آن را منتقل کنید! به تنظیمات > دستگاه‌ها بروید و مطمئن شوید دستگاه‌هایتان یکدیگر را بازبینی کرده‌اند. هنگام باز کردن دوباره اتاق و فعال بودن هر دو نشست، کلیدها به‌صورت خودکار منتقل می‌شوند.\n\nآیا نمی‌خواهید هنگام خروج یا تغییر دستگاه کلیدها را گم کنید؟ مطمئن شوید پشتیبان گپ را در تنظیمات فعال کرده‌اید.", "@noKeyForThisMessage": {}, "numChats": "{number} گپ", "@numChats": { @@ -1923,111 +1923,111 @@ } } }, - "disableEncryptionWarning": "به دلایل امنیتی نمی‌توانید رمزگذاری را در گپ غیرفعال کنید، در حالی که از قبل فعال شده است.", + "disableEncryptionWarning": "به دلایل امنیتی نمی‌توانید رمزنگاری را در گپی که فعال شده غیرفعال کنید.", "@disableEncryptionWarning": {}, "enterRoom": "ورود به اتاق", "@enterRoom": {}, "newGroup": "گروه جدید", "@newGroup": {}, - "foregroundServiceRunning": "این اعلان زمانی وقتی ظاهر می شود که سرویس پیش‌زمینه در حال اجرا است.", + "foregroundServiceRunning": "این آگاه‌ساز زمانی ظاهر می‌شود که خدمت پیش‌زمینه فعال است.", "@foregroundServiceRunning": {}, - "appearOnTopDetails": "به برنامه اجازه می‌دهد در بالا ظاهر شود (اگر قبلا فلافی‌‌چت را به عنوان حساب تماس تنظیم کرده‌اید، لازم نیست)", + "appearOnTopDetails": "اجازه به برنامه برای نمایش در بالا (اگر فلافی‌چت را به‌عنوان حساب تماس تنظیم کرده‌اید، لازم نیست)", "@appearOnTopDetails": {}, - "storeSecurlyOnThisDevice": "به طور ایمن در دستگاه ذخیره کنید", + "storeSecurlyOnThisDevice": "ذخیره امن در این دستگاه", "@storeSecurlyOnThisDevice": {}, - "screenSharingDetail": "شما در حال به اشتراک‌گذاری صفحه‌نمایش خود در فلافی‌چت هستید", + "screenSharingDetail": "شما در حال هم‌رسانی صفحه‌نمایش خود در فلافی‌چت هستید", "@screenSharingDetail": {}, "newSpace": "فضای جدید", "@newSpace": {}, - "saveKeyManuallyDescription": "این کلید را به صورت دستی با فعال کردن گفتگوی اشتراک گذاری سیستم یا کلیپ بورد ذخیره کنید.", + "saveKeyManuallyDescription": "این کلید را با استفاده از هم‌رسانی یا بریده‌دان به‌طور دستی ذخیره کنید.", "@saveKeyManuallyDescription": {}, - "storeInAndroidKeystore": "در Android KeyStore ذخیره کنید", + "storeInAndroidKeystore": "ذخیره در Android KeyStore", "@storeInAndroidKeystore": {}, - "storeInAppleKeyChain": "در Apple KeyChain ذخیره کنید", + "storeInAppleKeyChain": "ذخیره در Apple KeyChain", "@storeInAppleKeyChain": {}, "user": "کاربر", "@user": {}, "custom": "سفارشی", "@custom": {}, - "screenSharingTitle": "اشتراک گذاری صفحه نمایش", + "screenSharingTitle": "هم‌رسانی صفحه‌نمایش", "@screenSharingTitle": {}, - "callingPermissions": "اجازه‌های تماس", + "callingPermissions": "دسترسی‌های تماس", "@callingPermissions": {}, "callingAccount": "حساب تماس", "@callingAccount": {}, - "appearOnTop": "در بالا ظاهر شود", + "appearOnTop": "نمایش در بالا", "@appearOnTop": {}, - "otherCallingPermissions": "میکروفون، دوربین و سایر مجوزهای فلافی‌چت", + "otherCallingPermissions": "میکروفون، دوربین و دیگر دسترسی‌های فلافی‌چت", "@otherCallingPermissions": {}, - "whyIsThisMessageEncrypted": "چرا این پیام قابل خواندن نیست؟", + "whyIsThisMessageEncrypted": "چرا این پیام خوانا نیست؟", "@whyIsThisMessageEncrypted": {}, - "reopenChat": "گپ را دوباره باز کنید", + "reopenChat": "باز کردن دوباره گپ", "@reopenChat": {}, - "unmuteChat": "بازکردن صدای گپ", + "unmuteChat": "فعال کردن صدای گپ", "@unmuteChat": { "type": "String", "placeholders": {} }, "nextAccount": "حساب بعدی", "@nextAccount": {}, - "unlockOldMessages": "گشودن قفل پیام‌های قدیمی", + "unlockOldMessages": "گشودن پیام‌های قدیمی", "@unlockOldMessages": {}, - "share": "اشتراک‌گذاری", + "share": "هم‌رسانی", "@share": { "type": "String", "placeholders": {} }, - "skip": "رد شدن", + "skip": "رد کردن", "@skip": { "type": "String", "placeholders": {} }, - "systemTheme": "سیستم", + "systemTheme": "سامانه", "@systemTheme": { "type": "String", "placeholders": {} }, - "theyDontMatch": "با هم منطبق نیستند", + "theyDontMatch": "هم‌خوانی ندارند", "@theyDontMatch": { "type": "String", "placeholders": {} }, - "toggleFavorite": "تغییر حالت محبوبیت", + "toggleFavorite": "تغییر وضعیت برگزیده", "@toggleFavorite": { "type": "String", "placeholders": {} }, - "toggleMuted": "تغییر حالت بی‌صدا", + "toggleMuted": "تغییر وضعیت بی‌صدا", "@toggleMuted": { "type": "String", "placeholders": {} }, - "toggleUnread": "علامت‌گذاشتن به عنوان خوانده‌شده/خوانده‌نشده", + "toggleUnread": "تغییر وضعیت خوانده‌شده/خوانده‌نشده", "@toggleUnread": { "type": "String", "placeholders": {} }, - "unavailable": "خارج از دسترس", + "unavailable": "در دسترس نیست", "@unavailable": { "type": "String", "placeholders": {} }, - "unblockDevice": "برداشتن مسدود بودن دستگاه", + "unblockDevice": "باز کردن دستگاه", "@unblockDevice": { "type": "String", "placeholders": {} }, - "verifyStart": "شروع بازبینی و تایید", + "verifyStart": "آغاز بازبینی", "@verifyStart": { "type": "String", "placeholders": {} }, - "verifySuccess": "بازبینی و تایید با موفقیت انجام شد!", + "verifySuccess": "بازبینی با موفقیت انجام شد!", "@verifySuccess": { "type": "String", "placeholders": {} }, - "waitingPartnerEmoji": "در انتظار پذیرفتن شکلک از جانب فرد دیگر…", + "waitingPartnerEmoji": "در انتظار پذیرش شکلک توسط دیگری…", "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -2037,7 +2037,7 @@ "type": "String", "placeholders": {} }, - "whoCanPerformWhichAction": "چه کسی توان انجام کدام عمل را داراست", + "whoCanPerformWhichAction": "چه کسی می‌تواند چه کاری انجام دهد", "@whoCanPerformWhichAction": { "type": "String", "placeholders": {} @@ -2054,7 +2054,7 @@ "type": "String", "placeholders": {} }, - "setPermissionsLevel": "تنظیم درجه اجازه‌ها", + "setPermissionsLevel": "تنظیم سطح دسترسی", "@setPermissionsLevel": { "type": "String", "placeholders": {} @@ -2069,12 +2069,12 @@ "type": "String", "placeholders": {} }, - "showPassword": "نمایش رمز عبور", + "showPassword": "نمایش گذرواژه", "@showPassword": { "type": "String", "placeholders": {} }, - "singlesignon": "شناسایی یگانه(Single Sign on)", + "singlesignon": "ورود یکپارچه", "@singlesignon": { "type": "String", "placeholders": {} @@ -2084,9 +2084,9 @@ "type": "String", "placeholders": {} }, - "startFirstChat": "اولین گپ خود را شروع کنید", + "startFirstChat": "شروع اولین گپ", "@startFirstChat": {}, - "theyMatch": "با هم منطبق هستند", + "theyMatch": "هم‌خوانی دارند", "@theyMatch": { "type": "String", "placeholders": {} @@ -2097,7 +2097,7 @@ "type": "String", "placeholders": {} }, - "tooManyRequestsWarning": "تعداد درخواست‌های بیش از حد. لطفا بعدا دوباره امتحان کنید!", + "tooManyRequestsWarning": "درخواست‌های بیش از حد. لطفاً بعداً دوباره تلاش کنید!", "@tooManyRequestsWarning": { "type": "String", "placeholders": {} @@ -2114,7 +2114,7 @@ } } }, - "unknownEncryptionAlgorithm": "الگوریتم رمزگذاری ناشناخته", + "unknownEncryptionAlgorithm": "الگوریتم رمزنگاری ناشناخته", "@unknownEncryptionAlgorithm": { "type": "String", "placeholders": {} @@ -2124,7 +2124,7 @@ "type": "String", "placeholders": {} }, - "userAndUserAreTyping": "{username} و {username2} در حال تایپ کردن…", + "userAndUserAreTyping": "{username} و {username2} در حال نوشتن…", "@userAndUserAreTyping": { "type": "String", "placeholders": { @@ -2141,12 +2141,12 @@ "type": "String", "placeholders": {} }, - "visibleForAllParticipants": "قابل رویت برای تمام شرکت‌کنندگان", + "visibleForAllParticipants": "قابل‌دید برای همه شرکت‌کنندگان", "@visibleForAllParticipants": { "type": "String", "placeholders": {} }, - "visibleForEveryone": "قابل رویت برای همه", + "visibleForEveryone": "قابل‌دید برای همه", "@visibleForEveryone": { "type": "String", "placeholders": {} @@ -2156,7 +2156,7 @@ "type": "String", "placeholders": {} }, - "waitingPartnerAcceptRequest": "در انتظار پذیرفتن درخواست از جانب فرد دیگر…", + "waitingPartnerAcceptRequest": "در انتظار پذیرش درخواست توسط دیگری…", "@waitingPartnerAcceptRequest": { "type": "String", "placeholders": {} @@ -2177,9 +2177,9 @@ "@time": {}, "messageType": "نوع پیام", "@messageType": {}, - "addToSpaceDescription": "فضایی برای افزودن این گپ به آن انتخاب کنید.", + "addToSpaceDescription": "فضایی را برای افزودن این گپ انتخاب کنید.", "@addToSpaceDescription": {}, - "start": "شروع", + "start": "آغاز", "@start": {}, "videoWithSize": "ویدئو ({size})", "@videoWithSize": { @@ -2190,7 +2190,7 @@ } } }, - "userIsTyping": "{username} در حال تایپ کردن…", + "userIsTyping": "{username} در حال نوشتن…", "@userIsTyping": { "type": "String", "placeholders": { @@ -2199,7 +2199,7 @@ } } }, - "username": "نام‌کاربری", + "username": "نام کاربری", "@username": { "type": "String", "placeholders": {} @@ -2216,7 +2216,7 @@ }, "publish": "انتشار", "@publish": {}, - "shareLocation": "اشتراک‌گذاری وضعیت مکانی", + "shareLocation": "هم‌رسانی مکان", "@shareLocation": { "type": "String", "placeholders": {} @@ -2229,18 +2229,18 @@ } } }, - "unverified": "تاییدنشده", + "unverified": "تأییدنشده", "@unverified": {}, - "verify": "بازبینی و تایید", + "verify": "بازبینی", "@verify": { "type": "String", "placeholders": {} }, - "openChat": "بازکردن گپ", + "openChat": "باز کردن گپ", "@openChat": {}, - "dismiss": "رد كردن‌", + "dismiss": "رد کردن", "@dismiss": {}, - "unsupportedAndroidVersion": "نسخه اندروید پشتیبانی نشده", + "unsupportedAndroidVersion": "نسخه اندروید پشتیبانی‌نشده", "@unsupportedAndroidVersion": {}, "youUnbannedUser": "شما محرومیت {user} را برداشتید", "@youUnbannedUser": { @@ -2282,7 +2282,7 @@ "type": "String", "placeholders": {} }, - "unknownEvent": "رویداد ناشناخته «{type}»", + "unknownEvent": "رویداد ناشناخته '{type}'", "@unknownEvent": { "type": "String", "placeholders": { @@ -2303,7 +2303,7 @@ } } }, - "userLeftTheChat": "🚪{username} گپ را ترک کرد", + "userLeftTheChat": "👋 {username} گپ را ترک کرد", "@userLeftTheChat": { "type": "String", "placeholders": { @@ -2327,7 +2327,7 @@ "type": "String", "placeholders": {} }, - "waitingPartnerNumbers": "در انتظار پذیرفتن اعداد از جانب فرد دیگر…", + "waitingPartnerNumbers": "در انتظار پذیرش اعداد توسط دیگری…", "@waitingPartnerNumbers": { "type": "String", "placeholders": {} @@ -2511,293 +2511,869 @@ "@pleaseTryAgainLaterOrChooseDifferentServer": {}, "notAnImage": "یک فایل تصویری نیست.", "@notAnImage": {}, - "alwaysUse24HourFormat": "در حالت عادی، از فرمت 24 ساعته استفاده کن", - "setCustomPermissionLevel": "سطح مجوز سفارشی را تنظیم کن", - "setPermissionsLevelDescription": "لطفاً یک نقش از پیش تعریف شده در زیر انتخاب کنید یا سطح مجوز سفارشی بین 0 تا 100 وارد کنید.", - "ignoreUser": "نادیده گرفتن کاربر", + "alwaysUse24HourFormat": "false", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "setCustomPermissionLevel": "تنظیم سطح دسترسی سفارشی", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "لطفاً نقش از پیش تعریف‌شده‌ای را انتخاب کنید یا سطح دسترسی سفارشی بین ۰ تا ۱۰۰ وارد کنید.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "چشم‌پوشی از کاربر", + "@ignoreUser": {}, "normalUser": "کاربر عادی", - "importNow": "هم‌اکنون وارد کردن", - "importEmojis": "وارد کردن ایموجی‌ها", - "importFromZipFile": "وارد کردن از فایل .zip", - "exportEmotePack": "صادر کردن بسته ایموجی به عنوان فایل .zip", + "@normalUser": {}, + "importNow": "اکنون وارد کنید", + "@importNow": {}, + "importEmojis": "وارد کردن شکلک‌ها", + "@importEmojis": {}, + "importFromZipFile": "وارد کردن از پرونده زیپ", + "@importFromZipFile": {}, + "exportEmotePack": "صادر کردن بسته شکلک به‌صورت زیپ", + "@exportEmotePack": {}, "replace": "جایگزین کردن", + "@replace": {}, "aboutHomeserver": "درباره {homeserver}", - "addChatDescription": "افزودن توضیحات چت...", - "commandHint_roomupgrade": "ارتقاء این اتاق به نسخه اتاق داده شده", - "appLockDescription": "قفل کردن برنامه هنگام عدم استفاده با کد پین", - "sendTypingNotifications": "ارسال اعلان‌های تایپ کردن", - "swipeRightToLeftToReply": "برای پاسخ، از راست به چپ سوایپ کنید", - "countChatsAndCountParticipants": "{chats} چت و {participants} شرکت‌کننده", - "noMoreChatsFound": "چت بیشتری پیدا نشد...", - "noChatsFoundHere": "هنوز چتی پیدا نشد. برای شروع چت جدید با کسی، از دکمه زیر استفاده کنید. ⚵", - "joinedChats": "چت‌های پیوسته", - "unread": "خوانده نشده", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "addChatDescription": "افزودن توضیح گپ...", + "@addChatDescription": {}, + "commandHint_roomupgrade": "ارتقای این اتاق به نگارش مشخص‌شده", + "@commandHint_roomupgrade": {}, + "appLockDescription": "قفل کردن برنامه با رمز کوتاه هنگام عدم استفاده", + "@appLockDescription": {}, + "sendTypingNotifications": "فرستادن آگاه‌سازهای نوشتن", + "@sendTypingNotifications": {}, + "swipeRightToLeftToReply": "کشیدن از راست به چپ برای پاسخ", + "@swipeRightToLeftToReply": {}, + "countChatsAndCountParticipants": "{chats} گپ و {participants} شرکت‌کننده", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noMoreChatsFound": "گپ دیگری پیدا نشد...", + "@noMoreChatsFound": {}, + "noChatsFoundHere": "اینجا هنوز گپی پیدا نشد. با استفاده از دکمه زیر گپ جدیدی با کسی شروع کنید. ⤵️", + "@noChatsFoundHere": {}, + "joinedChats": "گپ‌های پیوسته", + "@joinedChats": {}, + "unread": "خوانده‌نشده", + "@unread": {}, "space": "فضا", + "@space": {}, "spaces": "فضاها", - "checkList": "لیست بررسی", - "countInvited": "{count} دعوت شده", - "createGroup": "ایجاد گروه", - "chatPermissions": "مجوزهای چت", - "emoteKeyboardNoRecents": "احساسات استفاده‌شده اخیراً در اینجا ظاهر می‌شوند...", - "globalChatId": "شناسه چت جهانی", - "accessAndVisibility": "دسترسی و دید", - "accessAndVisibilityDescription": "چه کسی مجاز به پیوستن به این چت است و چگونه می‌توان آن را کشف کرد.", + "@spaces": {}, + "checkList": "فهرست بررسی", + "@checkList": {}, + "countInvited": "{count} دعوت‌شده", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "createGroup": "ساختن گروه", + "@createGroup": {}, + "chatPermissions": "دسترسی‌های گپ", + "@chatPermissions": {}, + "emoteKeyboardNoRecents": "شکلک‌هایی که به تازگی استفاده‌شده اینجا نمایش داده میشوند...", + "@emoteKeyboardNoRecents": { + "type": "String", + "placeholders": {} + }, + "globalChatId": "شناسه گپ سراسری", + "@globalChatId": {}, + "accessAndVisibility": "دسترسی و قابلیت دید", + "@accessAndVisibility": {}, + "accessAndVisibilityDescription": "چه کسی اجازه پیوستن به این گپ را دارد و گپ چگونه قابل کشف است.", + "@accessAndVisibilityDescription": {}, "calls": "تماس‌ها", - "customEmojisAndStickers": "ایموجی‌ها و استیکرهای سفارشی", - "customEmojisAndStickersBody": "ایموجی‌ها یا استیکرهای سفارشی را اضافه یا به اشتراک بگذارید که در هر چتی قابل استفاده هستند.", - "chatDescription": "توضیحات چت", - "chatDescriptionHasBeenChanged": "توضیحات چت تغییر یافته است", + "@calls": {}, + "customEmojisAndStickers": "شکلک‌ها و برچسب‌های سفارشی", + "@customEmojisAndStickers": {}, + "customEmojisAndStickersBody": "افزودن یا هم‌رسانی شکلک‌ها یا برچسب‌های سفارشی که در هر گپ قابل استفاده‌اند.", + "@customEmojisAndStickersBody": {}, + "chatDescription": "توضیح گپ", + "@chatDescription": {}, + "chatDescriptionHasBeenChanged": "توضیح گپ تغییر کرد", + "@chatDescriptionHasBeenChanged": {}, "hideRedactedMessages": "پنهان کردن پیام‌های ویرایش‌شده", - "hideRedactedMessagesBody": "اگر کسی پیامی را ویرایش کند، این پیام دیگر در چت قابل مشاهده نخواهد بود.", + "@hideRedactedMessages": {}, + "hideRedactedMessagesBody": "اگر کسی پیامی را ویرایش کند، دیگر نمیتوان آن پیام را در گپ دید.", + "@hideRedactedMessagesBody": {}, "hideInvalidOrUnknownMessageFormats": "پنهان کردن قالب‌های پیام نامعتبر یا ناشناخته", + "@hideInvalidOrUnknownMessageFormats": {}, "block": "مسدود کردن", - "blockedUsers": "کاربران مسدود شده", - "blockListDescription": "می‌توانید کاربرانی را که مزاحمتان می‌کنند مسدود کنید. نمی‌توانید پیام‌ها یا دعوت‌نامه‌های اتاق از کاربران در لیست مسدود شده شخصی خود دریافت کنید.", - "blockUsername": "نادیده گرفتن نام کاربری", - "inviteContactToGroupQuestion": "آیا می‌خواهید {contact} را به چت \"{groupName}\" دعوت کنید؟", - "noChatDescriptionYet": "تاکنون توضیحی برای چت ایجاد نشده است.", - "tryAgain": "دوباره تلاش کنید", - "invalidServerName": "نام سرور نامعتبر است", - "redactMessageDescription": "پیام برای تمام شرکت‌کنندگان در این گفتگو حذف خواهد شد. این عملیات قابل بازگشت نیست.", - "optionalRedactReason": "(اختیاری) دلیل حذف این پیام...", + "@block": {}, + "blockedUsers": "کاربران مسدود‌شده", + "@blockedUsers": {}, + "blockListDescription": "میتوانید کاربرانی که مزاحم شما هستند را مسدود کنید. از کاربران موجود در فهرست مسدود شخصی، پیام یا دعوت به اتاق دریافت نخواهید کرد.", + "@blockListDescription": {}, + "blockUsername": "چشم‌پوشی از نام کاربری", + "@blockUsername": {}, + "inviteContactToGroupQuestion": "آیا میخواهید {contact} را به گپ «{groupName}» دعوت کنید؟", + "@inviteContactToGroupQuestion": {}, + "noChatDescriptionYet": "هنوز توضیح گپی ساخته نشده است.", + "@noChatDescriptionYet": {}, + "tryAgain": "تلاش دوباره", + "@tryAgain": {}, + "invalidServerName": "نام سرور نامعتبر", + "@invalidServerName": {}, + "redactMessageDescription": "پیام برای همه شرکت‌کنندگان در این گفتگو ویرایش خواهد شد. این کار برگشت‌ناپذیر است.", + "@redactMessageDescription": {}, + "optionalRedactReason": "(اختیاری) دلیل ویرایش این پیام...", + "@optionalRedactReason": {}, "messagesStyle": "پیام‌ها:", - "shareInviteLink": "اشتراک‌گذاری لینک دعوت", - "hideMemberChangesInPublicChats": "پنهان کردن تغییرات اعضا در چت‌های عمومی", - "hideMemberChangesInPublicChatsBody": "برای بهبود خوانایی، اگر کسی به چت عمومی ملحق یا از آن خارج شد، در خط زمانی چت نشان داده نشود.", - "overview": "مرور کلی", - "notifyMeFor": "به من اطلاع بده برای", - "passwordRecoverySettings": "تنظیمات بازیابی رمز عبور", - "redactedBy": "حذف شده توسط {username}", - "directChat": "چت مستقیم", - "redactedByBecause": "حذف شده توسط {username} به دلیل: \"{reason}\"", - "sendImages": "ارسال {count} تصویر", - "setChatDescription": "تنظیم توضیحات چت", + "@messagesStyle": {}, + "shareInviteLink": "هم‌رسانی پیوند دعوت", + "@shareInviteLink": {}, + "hideMemberChangesInPublicChats": "پنهان کردن تغییرات اعضا در گپ‌های عمومی", + "@hideMemberChangesInPublicChats": {}, + "hideMemberChangesInPublicChatsBody": "برای بهبود خوانایی، اینکه کسی به گپ عمومی پیوست یا آن را ترک کرد در خط زمانی گپ نمایش داده نشود.", + "@hideMemberChangesInPublicChatsBody": {}, + "overview": "دید کلی", + "@overview": {}, + "notifyMeFor": "برایم آگاه‌ساز بفرست برای", + "@notifyMeFor": {}, + "passwordRecoverySettings": "تنظیمات بازیابی گذرواژه", + "@passwordRecoverySettings": {}, + "redactedBy": "ویرایش‌شده به‌دست {username}", + "@redactedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "directChat": "گپ مستقیم", + "@directChat": {}, + "redactedByBecause": "ویرایش‌شده به‌دست {username} زیرا: «{reason}»", + "@redactedByBecause": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "reason": { + "type": "String" + } + } + }, + "sendImages": "فرستادن {count} تصویر", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "setChatDescription": "تنظیم توضیح گپ", + "@setChatDescription": {}, "presenceStyle": "حضور:", - "presencesToggle": "نمایش پیام‌های وضعیت از کاربران دیگر", - "synchronizingPleaseWaitCounter": "در حال همگام‌سازی… ({percentage}%)", - "youInvitedToBy": "📩 شما از طریق لینک دعوت شده‌اید به:\n{alias}", - "invitedBy": "📩 دعوت شده توسط {user}", - "hasKnocked": "🚪 {user} زده است", - "usersMustKnock": "کاربران باید زنگ بزنند", - "noOneCanJoin": "هیچ‌کس نمی‌تواند وارد شود", - "userWouldLikeToChangeTheChat": "{user} مایل است به چت بپیوندد.", - "noPublicLinkHasBeenCreatedYet": "هنوز لینک عمومی ایجاد نشده است", - "knock": "زنگ زدن", - "hidePresences": "پنهان کردن لیست وضعیت؟", - "signInWith": "ورود با {provider}", - "profileNotFound": "کاربر در سرور پیدا نشد. ممکن است مشکل اتصال باشد یا کاربر وجود نداشته باشد.", - "setTheme": "انتخاب تم:", - "setColorTheme": "انتخاب تم رنگ:", + "@presenceStyle": { + "type": "String", + "placeholders": {} + }, + "presencesToggle": "نمایش پیام‌های وضعیت از دیگر کاربران", + "@presencesToggle": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWaitCounter": " در حال همگام‌سازی... ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "youInvitedToBy": "📩 شما با پیوند دعوت شده‌اید به:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": { + "type": "String" + } + } + }, + "invitedBy": "📩 دعوت‌شده توسط {user}", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "hasKnocked": "🚪 {user} در زده است", + "@hasKnocked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "usersMustKnock": "کاربران باید در بزنند", + "@usersMustKnock": {}, + "noOneCanJoin": "هیچ‌کس نمیتواند بپیوندد", + "@noOneCanJoin": {}, + "userWouldLikeToChangeTheChat": "{user} مایل به پیوستن به گپ است.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "noPublicLinkHasBeenCreatedYet": "هنوز پیوند عمومی ساخته نشده است", + "@noPublicLinkHasBeenCreatedYet": {}, + "knock": "در زدن", + "@knock": {}, + "hidePresences": "پنهان کردن فهرست وضعیت؟", + "@hidePresences": {}, + "profileNotFound": "کاربر در سرور پیدا نشد. شاید مشکل اتصال وجود دارد یا کاربر وجود ندارد.", + "@profileNotFound": {}, + "setTheme": "تنظیم پوسته:", + "@setTheme": {}, + "setColorTheme": "تنظیم پوسته رنگی:", + "@setColorTheme": {}, "invite": "دعوت", - "inviteGroupChat": "📨 دعوت به گروه چت", - "invitePrivateChat": "📨 دعوت به چت خصوصی", + "@invite": {}, + "inviteGroupChat": "📨 دعوت به گپ گروهی", + "@inviteGroupChat": {}, + "invitePrivateChat": "📨 دعوت به گپ خصوصی", + "@invitePrivateChat": {}, "invalidInput": "ورودی نامعتبر!", - "wrongPinEntered": "کد پین اشتباه وارد شد! دوباره تلاش کنید در {seconds} ثانیه...", - "pleaseEnterANumber": "لطفاً عددی بزرگتر از 0 وارد کنید", - "archiveRoomDescription": "گفتگو به آرشیو منتقل خواهد شد. سایر کاربران قادر خواهند بود ببینند که شما از گفتگو خارج شده‌اید.", - "roomUpgradeDescription": "سپس گفتگو با نسخه جدید اتاق بازسازی می‌شود. همه شرکت‌کنندگان اطلاع داده خواهند شد که باید به گفتگو جدید منتقل شوند. می‌توانید اطلاعات بیشتر درباره نسخه‌های اتاق را در https://spec.matrix.org/latest/rooms/ بیابید.", - "removeDevicesDescription": "از این دستگاه خارج خواهید شد و دیگر قادر به دریافت پیام‌ها نخواهید بود.", - "banUserDescription": "کاربر از گفتگو مسدود می‌شود و نمی‌تواند دوباره وارد شود تا زمانی که رفع مسدود شود.", - "unbanUserDescription": "کاربر می‌تواند دوباره وارد گفتگو شود اگر تلاش کند.", - "kickUserDescription": "کاربر از گفتگو اخراج می‌شود اما مسدود نمی‌شود. در گفتگوهای عمومی، کاربر هر زمان می‌تواند دوباره وارد شود.", - "makeAdminDescription": "وقتی این کاربر را مدیر می‌کنید، ممکن است نتوانید این تغییر را برگردانید زیرا همان مجوزهای شما را خواهد داشت.", - "pushNotificationsNotAvailable": "اعلان‌های فشار قابل دسترسی نیستند", + "@invalidInput": {}, + "wrongPinEntered": "رمز کوتاه نادرست وارد شد! {seconds} ثانیه دیگر دوباره تلاش کنید...", + "@wrongPinEntered": { + "type": "String", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "pleaseEnterANumber": "لطفاً عددی بزرگ‌تر از ۰ وارد کنید", + "@pleaseEnterANumber": {}, + "archiveRoomDescription": "گپ به بایگانی خواهد رفت. کاربران دیگر میتوانند ببینند که شما گپ را ترک کرده‌اید.", + "@archiveRoomDescription": {}, + "roomUpgradeDescription": "گپ با نگارش جدید اتاق بازسازی خواهد شد. به همه شرکت‌کنندگان آگاهی‌رسانی میشود که باید به گپ جدید بروند. داده‌های بیشتر درباره نگارش‌های اتاق در https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "removeDevicesDescription": "از این دستگاه خارج خواهید شد و دیگر نمیتوانید پیام دریافت کنید.", + "@removeDevicesDescription": {}, + "banUserDescription": "کاربر از گپ محروم خواهد شد و تا زمانی که محرومیت برداشته نشود، نمیتواند دوباره وارد گپ شود.", + "@banUserDescription": {}, + "unbanUserDescription": "کاربر در صورت تلاش دوباره میتواند وارد گپ شود.", + "@unbanUserDescription": {}, + "kickUserDescription": "کاربر از گپ بیرون میشود اما محروم نمیشود. در گپ‌های عمومی، کاربر میتواند هر زمان دوباره بپیوندد.", + "@kickUserDescription": {}, + "makeAdminDescription": "پس از مدیر کردن این کاربر، ممکن است نتوانید این کار را لغو کنید، زیرا آن‌ها همان دسترسی‌های شما را خواهند داشت.", + "@makeAdminDescription": {}, + "pushNotificationsNotAvailable": "آگاه‌سازهای فشاری در دسترس نیستند", + "@pushNotificationsNotAvailable": {}, "learnMore": "بیشتر بدانید", - "yourGlobalUserIdIs": "شناسه کاربری جهانی شما: ", - "noUsersFoundWithQuery": "متأسفانه کاربری با \"{query}\" پیدا نشد. لطفاً بررسی کنید که اشتباهی تایپ نکرده باشید.", - "knocking": "در حال کوبیدن", - "chatCanBeDiscoveredViaSearchOnServer": "گفتگو از طریق جستجو در {server} قابل کشف است", - "searchChatsRooms": "جستجو برای #گفتگوها، @کاربران...", + "@learnMore": {}, + "yourGlobalUserIdIs": "شناسه کاربری سراسری شما: ", + "@yourGlobalUserIdIs": {}, + "noUsersFoundWithQuery": "متأسفانه کاربری با «{query}» پیدا نشد. لطفاً بررسی کنید که آیا اشتباه نوشتاری دارید.", + "@noUsersFoundWithQuery": { + "type": "String", + "placeholders": { + "query": { + "type": "String" + } + } + }, + "knocking": "در زدن", + "@knocking": {}, + "chatCanBeDiscoveredViaSearchOnServer": "گپ با جستجو در {server} قابل کشف است", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "searchChatsRooms": "جستجو برای #گپ‌ها، @کاربران...", + "@searchChatsRooms": {}, "nothingFound": "چیزی پیدا نشد...", + "@nothingFound": {}, "groupName": "نام گروه", - "createGroupAndInviteUsers": "ایجاد گروه و دعوت از کاربران", - "groupCanBeFoundViaSearch": "گروه از طریق جستجو قابل یافتن است", - "wrongRecoveryKey": "متأسفیم... این به نظر نمی‌رسد کلید بازیابی صحیح باشد.", - "startConversation": "شروع گفتگو", - "commandHint_sendraw": "ارسال JSON خام", - "databaseMigrationTitle": "پایگاه داده بهینه‌سازی شده است", - "databaseMigrationBody": "لطفاً صبر کنید. این ممکن است کمی طول بکشد.", - "leaveEmptyToClearStatus": "برای پاک کردن وضعیت خود، خالی بگذارید", + "@groupName": {}, + "createGroupAndInviteUsers": "ساختن گروه و دعوت کاربران", + "@createGroupAndInviteUsers": {}, + "groupCanBeFoundViaSearch": "گروه با جستجو قابل یافتن است", + "@groupCanBeFoundViaSearch": {}, + "wrongRecoveryKey": "متأسفیم... به نظر میرسد این کلید بازیابی درست نباشد.", + "@wrongRecoveryKey": {}, + "startConversation": "آغاز گفتگو", + "@startConversation": {}, + "commandHint_sendraw": "فرستادن JSON خام", + "@commandHint_sendraw": {}, + "databaseMigrationTitle": "پایگاه داده بهینه‌سازی شد", + "@databaseMigrationTitle": {}, + "databaseMigrationBody": "لطفاً صبر کنید. این ممکن است لحظه‌ای طول بکشد.", + "@databaseMigrationBody": {}, + "leaveEmptyToClearStatus": "برای پاک کردن وضعیت، خالی بگذارید.", + "@leaveEmptyToClearStatus": {}, "select": "انتخاب", + "@select": {}, "searchForUsers": "جستجو برای @کاربران...", - "pleaseEnterYourCurrentPassword": "لطفاً رمز عبور فعلی خود را وارد کنید", - "newPassword": "رمز عبور جدید", - "pleaseChooseAStrongPassword": "لطفاً یک رمز عبور قوی انتخاب کنید", - "passwordsDoNotMatch": "رمزهای عبور مطابقت ندارند", - "passwordIsWrong": "رمز عبور وارد شده اشتباه است", - "publicLink": "لینک عمومی", - "publicChatAddresses": "آدرس‌های چت عمومی", - "createNewAddress": "ایجاد آدرس جدید", + "@searchForUsers": {}, + "pleaseEnterYourCurrentPassword": "لطفاً گذرواژه کنونی خود را وارد کنید", + "@pleaseEnterYourCurrentPassword": {}, + "newPassword": "گذرواژه جدید", + "@newPassword": {}, + "pleaseChooseAStrongPassword": "لطفاً یک گذرواژه قوی انتخاب کنید", + "@pleaseChooseAStrongPassword": {}, + "passwordsDoNotMatch": "گذرواژه‌ها هم‌خوانی ندارند", + "@passwordsDoNotMatch": {}, + "passwordIsWrong": "گذرواژه واردشده نادرست است", + "@passwordIsWrong": {}, + "publicLink": "پیوند عمومی", + "@publicLink": {}, + "publicChatAddresses": "نشانی‌های گپ عمومی", + "@publicChatAddresses": {}, + "createNewAddress": "ساختن نشانی جدید", + "@createNewAddress": {}, "joinSpace": "پیوستن به فضا", + "@joinSpace": {}, "publicSpaces": "فضاهای عمومی", - "addChatOrSubSpace": "افزودن چت یا زیر فضا", + "@publicSpaces": {}, + "addChatOrSubSpace": "افزودن گپ یا زیرفضا", + "@addChatOrSubSpace": {}, "subspace": "زیرفضا", - "decline": "رد کردن", + "@subspace": {}, + "decline": "نپذیرفتن", + "@decline": {}, "thisDevice": "این دستگاه:", - "initAppError": "در حین راه‌اندازی برنامه خطایی رخ داد", + "@thisDevice": {}, "userRole": "نقش کاربر", - "minimumPowerLevel": "{level} کمترین سطح قدرت است.", - "searchIn": "جستجو در چت \"{chat}\"...", + "@userRole": {}, + "minimumPowerLevel": "{level} کمینه سطح دسترسی است.", + "@minimumPowerLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "String" + } + } + }, + "searchIn": "جستجو در گپ «{chat}»...", + "@searchIn": { + "type": "String", + "placeholders": { + "chat": { + "type": "String" + } + } + }, "searchMore": "جستجوی بیشتر...", - "gallery": "گالری", - "files": "فایل‌ها", - "databaseBuildErrorBody": "نمی‌توان پایگاه داده SQlite را ساخت. برنامه در حال حاضر سعی می‌کند از پایگاه داده قدیمی استفاده کند. لطفاً این خطا را به توسعه‌دهندگان گزارش دهید در {url}. پیام خطا: {error}", - "sessionLostBody": "جلسه شما از دست رفته است. لطفاً این خطا را به توسعه‌دهندگان گزارش دهید در {url}. پیام خطا: {error}", - "restoreSessionBody": "برنامه اکنون سعی می‌کند جلسه شما را از پشتیبان بازیابی کند. لطفاً این خطا را به توسعه‌دهندگان گزارش دهید در {url}. پیام خطا: {error}", - "forwardMessageTo": "آیا می‌خواهید پیام را به {roomName} فوروارد کنید؟", - "sendReadReceipts": "ارسال تأیید خوانده شدن", - "sendTypingNotificationsDescription": "سایر شرکت‌کنندگان در چت می‌توانند ببینند که شما در حال نوشتن پیام جدید هستید.", - "sendReadReceiptsDescription": "سایر شرکت‌کنندگان در چت می‌توانند ببینند که شما پیام را خوانده‌اید.", - "formattedMessages": "پیام‌های قالب‌بندی شده", - "formattedMessagesDescription": "نمایش محتوای غنی پیام مانند متن بولد با استفاده از مارک‌دان.", - "verifyOtherUser": "🔐 تأیید کاربر دیگر", - "verifyOtherUserDescription": "اگر شما کاربر دیگری را تأیید کنید، می‌توانید مطمئن باشید که واقعاً با چه کسی در حال نوشتن هستید. 💪\n\nوقتی فرآیند تأیید را شروع می‌کنید، شما و کاربر دیگر در برنامه یک پنجره پاپ‌آپ مشاهده خواهید کرد. در آنجا، مجموعه‌ای از ایموجی‌ها یا اعداد را مشاهده می‌کنید که باید با هم مقایسه کنید.\n\nبهترین راه برای انجام این کار ملاقات حضوری یا شروع تماس ویدیویی است. 👩‍💻", - "verifyOtherDevice": "🔐 تأیید دستگاه دیگر", - "verifyOtherDeviceDescription": "وقتی دستگاه دیگری را تأیید می‌کنید، این دستگاه‌ها می‌توانند کلیدهای رمزگذاری را رد و بدل کنند، که امنیت کلی شما را افزایش می‌دهد. 💪 وقتی فرآیند تأیید را شروع می‌کنید، یک پنجره پاپ‌آپ در برنامه در هر دو دستگاه ظاهر می‌شود. در آنجا، مجموعه‌ای از ایموجی‌ها یا اعداد را مشاهده می‌کنید که باید با هم مقایسه کنید. بهتر است قبل از شروع فرآیند تأیید، هر دو دستگاه در دسترس باشند. 🤳", - "acceptedKeyVerification": "{sender} تأیید کلید را پذیرفت", - "canceledKeyVerification": "{sender} تأیید کلید را لغو کرد", - "completedKeyVerification": "{sender} تأیید کلید را کامل کرد", - "isReadyForKeyVerification": "{sender} آماده برای تأیید کلید است", - "requestedKeyVerification": "{sender} درخواست تأیید کلید داد", - "startedKeyVerification": "{sender} فرآیند تأیید کلید را شروع کرد", + "@searchMore": {}, + "gallery": "نگارخانه", + "@gallery": {}, + "files": "پرونده‌ها", + "@files": {}, + "databaseBuildErrorBody": "ناتوانی در ساخت پایگاه داده SQLite. برنامه اکنون سعی میکند از پایگاه داده قدیمی استفاده کند. لطفاً این خطا را به توسعه‌دهندگان در {url} گزارش دهید. پیام خطا: {error}", + "@databaseBuildErrorBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "sessionLostBody": "نشست شما گم شده است. لطفاً این خطا را به توسعه‌دهندگان در {url} گزارش دهید. پیام خطا: {error}", + "@sessionLostBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "restoreSessionBody": "برنامه اکنون سعی میکند نشست شما را از پشتیبان بازیابی کند. لطفاً این خطا را به توسعه‌دهندگان در {url} گزارش دهید. پیام خطا: {error}", + "@restoreSessionBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "forwardMessageTo": "هدایت پیام به {roomName}؟", + "@forwardMessageTo": { + "type": "String", + "placeholders": { + "roomName": { + "type": "String" + } + } + }, + "sendReadReceipts": "فرستادن رسیدهای خواندن", + "@sendReadReceipts": {}, + "sendTypingNotificationsDescription": "دیگر شرکت‌کنندگان در گپ میتوانند ببینند که شما در حال تایپ پیام جدید هستید.", + "@sendTypingNotificationsDescription": {}, + "sendReadReceiptsDescription": "دیگر شرکت‌کنندگان در گپ میتوانند ببینند که شما پیام را خوانده‌اید.", + "@sendReadReceiptsDescription": {}, + "formattedMessages": "پیام‌های قالب‌بندی‌شده", + "@formattedMessages": {}, + "formattedMessagesDescription": "نمایش محتوای پیام غنی مانند متن پررنگ با استفاده از مارک‌داون.", + "@formattedMessagesDescription": {}, + "verifyOtherUser": "🔐 بازبینی کاربر دیگر", + "@verifyOtherUser": {}, + "verifyOtherUserDescription": "اگر کاربر دیگری را بازبینی کنید، میتوانید مطمئن شوید که واقعاً با چه کسی در حال نوشتن هستید. 💪\n\nهنگام شروع بازبینی، شما و کاربر دیگر پنجره‌ای در برنامه خواهید دید. در آنجا مجموعه‌ای از شکلک‌ها یا اعداد را مشاهده میکنید که باید با یکدیگر مقایسه کنید.\n\nبهترین راه برای این کار دیدار حضوری یا شروع تماس تصویری است. 👭", + "@verifyOtherUserDescription": {}, + "verifyOtherDevice": "🔐 بازبینی دستگاه دیگر", + "@verifyOtherDevice": {}, + "verifyOtherDeviceDescription": "هنگام بازبینی دستگاه دیگر، آن دستگاه‌ها میتوانند کلیدها را تبادل کنند و امنیت کلی شما را افزایش دهند. 💪 هنگام شروع بازبینی، پنجره‌ای در برنامه روی هر دو دستگاه ظاهر میشود. در آنجا مجموعه‌ای از شکلک‌ها یا اعداد را مشاهده میکنید که باید با یکدیگر مقایسه کنید. بهتر است پیش از شروع بازبینی، هر دو دستگاه در دسترس باشند. 🤳", + "@verifyOtherDeviceDescription": {}, + "acceptedKeyVerification": "{sender} بازبینی کلید را پذیرفت", + "@acceptedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "canceledKeyVerification": "{sender} بازبینی کلید را رد کرد", + "@canceledKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "completedKeyVerification": "{sender} بازبینی کلید را کامل کرد", + "@completedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "isReadyForKeyVerification": "{sender} برای بازبینی کلید آماده است", + "@isReadyForKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "requestedKeyVerification": "{sender} درخواست بازبینی کلید کرد", + "@requestedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "startedKeyVerification": "{sender} بازبینی کلید را آغاز کرد", + "@startedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, "transparent": "شفاف", - "incomingMessages": "پیام‌های ورودی", - "stickers": "استیکرها", - "discover": "کشف کردن", - "commandHint_ignore": "رد کردن شناسه ماتریس داده شده", - "commandHint_unignore": "لغو رد کردن شناسه ماتریس داده شده", - "unreadChatsInApp": "{appname}: {unread} چت خوانده نشده", - "noDatabaseEncryption": "رمزگذاری پایگاه داده در این پلتفرم پشتیبانی نمی‌شود", - "thereAreCountUsersBlocked": "در حال حاضر {count} کاربر مسدود شده است.", - "restricted": "محدود شده", - "knockRestricted": "درخواست درب محدود شده", - "goToSpace": "برو به فضا: {space}", - "markAsUnread": "علامت‌گذاری به عنوان خوانده نشده", + "@transparent": {}, + "incomingMessages": "پیام‌های دریافتی", + "@incomingMessages": {}, + "stickers": "برچسب‌ها", + "@stickers": {}, + "discover": "کشف", + "@discover": {}, + "commandHint_ignore": "چشم‌پوشی از شناسه ماتریکس داده‌شده", + "@commandHint_ignore": {}, + "commandHint_unignore": "لغو چشم‌پوشی از شناسه ماتریکس داده‌شده", + "@commandHint_unignore": {}, + "unreadChatsInApp": "{appname}: {unread} گپ خوانده‌نشده", + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "noDatabaseEncryption": "رمزنگاری پایگاه داده در این سکو پشتیبانی نمیشود", + "@noDatabaseEncryption": {}, + "thereAreCountUsersBlocked": "اکنون {count} کاربر مسدود شده‌اند.", + "@thereAreCountUsersBlocked": { + "type": "String", + "count": {} + }, + "restricted": "محدودشده", + "@restricted": {}, + "knockRestricted": "در زدن محدود", + "@knockRestricted": {}, + "goToSpace": "رفتن به فضا: {space}", + "@goToSpace": { + "type": "String", + "space": {} + }, + "markAsUnread": "علامت‌گذاری به‌عنوان خوانده‌نشده", + "@markAsUnread": {}, "userLevel": "{level} - کاربر", - "moderatorLevel": "{level} - مدیر", - "adminLevel": "{level} - مدیر کل", - "changeGeneralChatSettings": "تغییر تنظیمات چت عمومی", - "inviteOtherUsers": "دعوت از کاربران دیگر به این چت", - "changeTheChatPermissions": "تغییر مجوزهای چت", - "changeTheVisibilityOfChatHistory": "تغییر دیداری تاریخچه چت", - "changeTheCanonicalRoomAlias": "تغییر آدرس اصلی چت عمومی", - "sendRoomNotifications": "ارسال اعلان @room", - "changeTheDescriptionOfTheGroup": "تغییر توضیحات چت", - "chatPermissionsDescription": "تعریف سطح قدرت لازم برای انجام اقدامات خاص در این چت. سطوح قدرت 0، 50 و 100 معمولاً نشان‌دهنده کاربران، مدیران و مدیران کل هستند، اما هر درجه‌ای ممکن است.", - "updateInstalled": "🎉 نسخه {version} به‌روزرسانی شد!", - "changelog": "تغییرات نسخه", - "sendCanceled": "ارسال لغو شد", - "loginWithMatrixId": "ورود با Matrix-ID", + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "moderatorLevel": "{level} - ناظر", + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "adminLevel": "{level} - مدیر", + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "changeGeneralChatSettings": "تغییر تنظیمات عمومی گپ", + "@changeGeneralChatSettings": {}, + "inviteOtherUsers": "دعوت کاربران دیگر به این گپ", + "@inviteOtherUsers": {}, + "changeTheChatPermissions": "تغییر دسترسی‌های گپ", + "@changeTheChatPermissions": {}, + "changeTheVisibilityOfChatHistory": "تغییر قابلیت دید تاریخچه گپ", + "@changeTheVisibilityOfChatHistory": {}, + "changeTheCanonicalRoomAlias": "تغییر نشانی اصلی گپ عمومی", + "@changeTheCanonicalRoomAlias": {}, + "sendRoomNotifications": "فرستادن آگاه‌سازهای @room", + "@sendRoomNotifications": {}, + "changeTheDescriptionOfTheGroup": "تغییر توضیح گپ", + "@changeTheDescriptionOfTheGroup": {}, + "chatPermissionsDescription": "مشخص کنید کدام سطح دسترسی برای اقدامات خاصی در این گپ لازم است. سطح‌های دسترسی ۰، ۵۰ و ۱۰۰ معمولاً نشان‌دهنده کاربران، ناظران و مدیران هستند، اما هر درجه‌بندی ممکن است.", + "@chatPermissionsDescription": {}, + "updateInstalled": "🎉 به‌روزرسانی {version} نصب شد!", + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "changelog": "فهرست تغییرات", + "@changelog": {}, + "sendCanceled": "فرستادن رد شد", + "@sendCanceled": {}, + "loginWithMatrixId": "ورود با شناسه ماتریکس", + "@loginWithMatrixId": {}, "discoverHomeservers": "کشف سرورهای خانگی", + "@discoverHomeservers": {}, "whatIsAHomeserver": "سرور خانگی چیست؟", - "homeserverDescription": "تمام داده‌های شما در سرور خانگی ذخیره می‌شود، همانند یک ارائه‌دهنده ایمیل. شما می‌توانید سرور خانگی مورد نظر خود را انتخاب کنید، در حالی که هنوز می‌توانید با همه ارتباط برقرار کنید. بیشتر بدانید در https://matrix.org.", - "doesNotSeemToBeAValidHomeserver": "به نظر نمی‌رسد سرور خانگی سازگار باشد. آدرس URL نادرست است؟", - "calculatingFileSize": "در حال محاسبه حجم فایل...", - "prepareSendingAttachment": "آماده‌سازی برای ارسال پیوست...", - "sendingAttachment": "در حال ارسال پیوست...", - "generatingVideoThumbnail": "در حال تولید تصویر بند انگشتی ویدیو...", - "compressVideo": "در حال فشرده‌سازی ویدیو...", - "sendingAttachmentCountOfCount": "در حال ارسال پیوست {index} از {length}...", - "serverLimitReached": "محدودیت سرور رسیده است! در حال انتظار {seconds} ثانیه...", - "oneOfYourDevicesIsNotVerified": "یکی از دستگاه‌های شما تأیید نشده است", - "noticeChatBackupDeviceVerification": "توجه: هنگامی که تمام دستگاه‌های خود را به پشتیبان چت متصل می‌کنید، به طور خودکار تأیید می‌شوند.", - "continueText": "ادامه دهید", - "welcomeText": "سلام سلام 👋 این فلفلی‌چت است. می‌توانید وارد هر سرور خانگی سازگار با https://matrix.org شوید. و سپس با هر کسی چت کنید. این یک شبکه پیام‌رسان غیرمتمرکز بزرگ است!", + "@whatIsAHomeserver": {}, + "homeserverDescription": "همه داده‌های شما روی سرور خانگی نگه‌داری میشوند، مانند یک فراهم‌کننده رایانامه. میتوانید سرور خانگی مورد نظر خود را انتخاب کنید، در حالی که همچنان میتوانید با هر کسی گفتگو کنید. اطلاعات بیشتر در https://matrix.org.", + "@homeserverDescription": {}, + "doesNotSeemToBeAValidHomeserver": "به نظر نمیرسد سرور خانگی سازگاری داشته باشد. نشانی اشتباه است؟", + "@doesNotSeemToBeAValidHomeserver": {}, + "calculatingFileSize": "در حال محاسبه اندازه پرونده...", + "@calculatingFileSize": {}, + "prepareSendingAttachment": "آماده‌سازی برای فرستادن پیوست...", + "@prepareSendingAttachment": {}, + "sendingAttachment": "در حال فرستادن پیوست...", + "@sendingAttachment": {}, + "generatingVideoThumbnail": "در حال تولید تصویر کوچک ویدئو...", + "@generatingVideoThumbnail": {}, + "compressVideo": "در حال فشرده‌سازی ویدئو...", + "@compressVideo": {}, + "sendingAttachmentCountOfCount": "در حال فرستادن پیوست {index} از {length}...", + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } + } + }, + "serverLimitReached": "محدودیت سرور رسیده است! {seconds} ثانیه صبر کنید...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "oneOfYourDevicesIsNotVerified": "یکی از دستگاه‌های شما بازبینی نشده است", + "@oneOfYourDevicesIsNotVerified": {}, + "noticeChatBackupDeviceVerification": "توجه: وقتی همه دستگاه‌های خود را به پشتیبان گپ متصل کنید، به‌صورت خودکار بازبینی میشوند.", + "@noticeChatBackupDeviceVerification": {}, + "continueText": "ادامه", + "@continueText": {}, + "welcomeText": "درود درود 👋 این فلافی‌چت است. میتوانید به هر سرور خانگی سازگار با https://matrix.org وارد شوید و با هر کسی گپ بزنید. این یک شبکه پیام‌رسانی غیرمتمرکز بزرگ است!", + "@welcomeText": {}, "blur": "محو کردن:", + "@blur": {}, "opacity": "شفافیت:", - "setWallpaper": "تنظیم پس‌زمینه", + "@opacity": {}, + "setWallpaper": "تنظیم کاغذدیواری", + "@setWallpaper": {}, "manageAccount": "مدیریت حساب", - "noContactInformationProvided": "سرور هیچ اطلاعات تماس معتبر ارائه نمی‌دهد", + "@manageAccount": {}, + "noContactInformationProvided": "سرور هیچ اطلاعات تماس معتبری نمیدهد", + "@noContactInformationProvided": {}, "contactServerAdmin": "تماس با مدیر سرور", + "@contactServerAdmin": {}, "contactServerSecurity": "تماس با امنیت سرور", + "@contactServerSecurity": {}, "supportPage": "صفحه پشتیبانی", - "serverInformation": "اطلاعات سرور:", + "@supportPage": {}, + "serverInformation": "درباره سرور:", + "@serverInformation": {}, "name": "نام", - "version": "نسخه", - "website": "وب‌سایت", + "@name": {}, + "version": "نگارش", + "@version": {}, + "website": "وبگاه", + "@website": {}, "compress": "فشرده‌سازی", - "boldText": "متن پررنگ", - "italicText": "متن مورب", - "strikeThrough": "خط خورده", + "@compress": {}, + "boldText": "متن درشت", + "@boldText": {}, + "italicText": "متن کج", + "@italicText": {}, + "strikeThrough": "خط‌خورده", + "@strikeThrough": {}, "pleaseFillOut": "لطفاً پر کنید", - "invalidUrl": "آدرس URL نامعتبر", - "addLink": "افزودن لینک", - "unableToJoinChat": "امکان پیوستن به چت وجود ندارد. شاید طرف مقابل قبلاً گفتگو را بسته است.", - "previous": "قبلی", - "otherPartyNotLoggedIn": "طرف مقابل در حال حاضر وارد نشده است و بنابراین نمی‌تواند پیام‌ها را دریافت کند!", - "appWantsToUseForLogin": "استفاده از '{server}' برای ورود", - "appWantsToUseForLoginDescription": "با این مجوز، برنامه و وب‌سایت اجازه دارند اطلاعاتی درباره شما به اشتراک بگذارند.", + "@pleaseFillOut": {}, + "invalidUrl": "نشانی نامعتبر", + "@invalidUrl": {}, + "addLink": "افزودن پیوند", + "@addLink": {}, + "unableToJoinChat": "ناتوانی در پیوستن به گپ. شاید طرف مقابل گفتگو را بسته است.", + "@unableToJoinChat": {}, + "previous": "پیشین", + "@previous": {}, + "otherPartyNotLoggedIn": "طرف مقابل اکنون وارد نشده است و بنابراین نمیتواند پیام دریافت کند!", + "@otherPartyNotLoggedIn": {}, + "appWantsToUseForLogin": "برای ورود از '{server}' استفاده کنید", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appWantsToUseForLoginDescription": "شما بدین‌وسیله به برنامه و وبگاه اجازه میدهید اطلاعات شما را هم‌رسانی کنند.", + "@appWantsToUseForLoginDescription": {}, "open": "باز کردن", + "@open": {}, "waitingForServer": "در انتظار سرور...", - "appIntroduction": "FluffyChat به شما امکان می‌دهد با دوستانتان در پیام‌رسان‌های مختلف چت کنید. برای اطلاعات بیشتر به https://matrix.org مراجعه کنید یا فقط روی *ادامه* ضربه بزنید.", - "newChatRequest": "درخواست چت جدید 📩", - "contentNotificationSettings": "تنظیمات اعلان محتوا", - "generalNotificationSettings": "تنظیمات اعلان عمومی", - "roomNotificationSettings": "تنظیمات اعلان اتاق", - "userSpecificNotificationSettings": "تنظیمات اعلان خاص کاربر", - "otherNotificationSettings": "سایر تنظیمات اعلان", - "notificationRuleContainsUserName": "شامل نام کاربری", - "notificationRuleContainsUserNameDescription": "کاربر را زمانی که پیام حاوی نام کاربری او است مطلع می‌کند.", - "notificationRuleMaster": "بی‌صدا کردن همه اعلان‌ها", - "notificationRuleMasterDescription": "تمام قوانین دیگر را نادیده می‌گیرد و همه اعلان‌ها را غیرفعال می‌کند.", + "@waitingForServer": {}, + "appIntroduction": "فلافی‌چت به شما امکان گپ با دوستانتان در پیام‌رسان‌های مختلف را میدهد. اطلاعات بیشتر در https://matrix.org یا فقط روی *ادامه* بزنید.", + "@appIntroduction": {}, + "newChatRequest": "📩 درخواست گپ جدید", + "@newChatRequest": {}, + "contentNotificationSettings": "تنظیمات آگاه‌ساز محتوا", + "@contentNotificationSettings": {}, + "generalNotificationSettings": "تنظیمات آگاه‌ساز عمومی", + "@generalNotificationSettings": {}, + "roomNotificationSettings": "تنظیمات آگاه‌ساز اتاق", + "@roomNotificationSettings": {}, + "userSpecificNotificationSettings": "تنظیمات آگاه‌ساز خاص کاربر", + "@userSpecificNotificationSettings": {}, + "otherNotificationSettings": "سایر تنظیمات آگاه‌ساز", + "@otherNotificationSettings": {}, + "notificationRuleContainsUserName": "دارای نام کاربری", + "@notificationRuleContainsUserName": {}, + "notificationRuleContainsUserNameDescription": "وقتی پیامی حاوی نام کاربری باشد، کاربر را آگاه میکند.", + "@notificationRuleContainsUserNameDescription": {}, + "notificationRuleMaster": "بی‌صدا کردن همه آگاه‌سازها", + "@notificationRuleMaster": {}, + "notificationRuleMasterDescription": "از قوانین دیگر چشم‌پوشی میکند و همه آگاه‌سازها را غیرفعال میکند.", + "@notificationRuleMasterDescription": {}, "notificationRuleSuppressNotices": "سرکوب پیام‌های خودکار", - "notificationRuleSuppressNoticesDescription": "اعلان‌ها را از مشتریان خودکار مانند ربات‌ها سرکوب می‌کند.", + "@notificationRuleSuppressNotices": {}, + "notificationRuleSuppressNoticesDescription": "آگاه‌سازهای کارخواه‌های خودکار مانند ربات‌ها را سرکوب میکند.", + "@notificationRuleSuppressNoticesDescription": {}, "notificationRuleInviteForMe": "دعوت برای من", - "notificationRuleInviteForMeDescription": "کاربر را زمانی که به یک اتاق دعوت می‌شود مطلع می‌کند.", - "notificationRuleMemberEvent": "رویداد عضو", - "notificationRuleMemberEventDescription": "اعلان‌ها را برای رویدادهای عضویت سرکوب می‌کند.", - "notificationRuleIsUserMention": "اشاره کاربر", - "notificationRuleIsUserMentionDescription": "کاربر را زمانی که مستقیماً در پیام اشاره شده است مطلع می‌کند.", - "notificationRuleContainsDisplayName": "شامل نام نمایشی", - "notificationRuleContainsDisplayNameDescription": "کاربر را زمانی که پیام حاوی نام نمایشی او است مطلع می‌کند.", - "notificationRuleIsRoomMention": "اشاره به اتاق", - "notificationRuleIsRoomMentionDescription": "کاربر را زمانی که در یک اتاق ذکر شده است، مطلع می‌کند.", - "notificationRuleRoomnotif": "اعلان اتاق", - "notificationRuleRoomnotifDescription": "کاربر را زمانی که پیام شامل '@room' است، مطلع می‌کند.", + "@notificationRuleInviteForMe": {}, + "notificationRuleInviteForMeDescription": "وقتی کاربر به اتاقی دعوت میشود، او را آگاه میکند.", + "@notificationRuleInviteForMeDescription": {}, + "notificationRuleMemberEvent": "رویداد عضویت", + "@notificationRuleMemberEvent": {}, + "notificationRuleMemberEventDescription": "آگاه‌سازهای رویدادهای عضویت را سرکوب میکند.", + "@notificationRuleMemberEventDescription": {}, + "notificationRuleIsUserMention": "نام‌بردن از کاربر", + "@notificationRuleIsUserMention": {}, + "notificationRuleIsUserMentionDescription": "وقتی در پیامی مستقیماً از کاربر نام برده میشود، او را آگاه میکند.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleContainsDisplayName": "دارای نام نمایشی", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleContainsDisplayNameDescription": "وقتی پیامی حاوی نام نمایشی کاربر باشد، کاربر را آگاه میکند.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsRoomMention": "نام‌بردن از اتاق", + "@notificationRuleIsRoomMention": {}, + "notificationRuleIsRoomMentionDescription": "وقتی نام اتاق ذکر میشود، کاربر را آگاه میکند.", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomnotif": "آگاه‌ساز اتاق", + "@notificationRuleRoomnotif": {}, + "notificationRuleRoomnotifDescription": "وقتی پیامی حاوی '@room' باشد، کاربر را آگاه میکند.", + "@notificationRuleRoomnotifDescription": {}, "notificationRuleTombstone": "سنگ قبر", - "notificationRuleTombstoneDescription": "کاربر را درباره پیام‌های غیرفعال‌سازی اتاق مطلع می‌کند.", + "@notificationRuleTombstone": {}, + "notificationRuleTombstoneDescription": "کاربر را از پیام‌های غیرفعال‌سازی اتاق آگاه میکند.", + "@notificationRuleTombstoneDescription": {}, "notificationRuleReaction": "واکنش", - "notificationRuleReactionDescription": "اعلان‌ها برای واکنش‌ها را خاموش می‌کند.", - "notificationRuleRoomServerAcl": "لیست کنترل دسترسی سرور اتاق", - "notificationRuleRoomServerAclDescription": "اعلان‌ها برای لیست‌های کنترل دسترسی سرور اتاق (ACL) را خاموش می‌کند.", - "notificationRuleSuppressEdits": "مخفی کردن ویرایش‌ها", - "notificationRuleSuppressEditsDescription": "اعلان‌ها برای پیام‌های ویرایش‌شده را خاموش می‌کند.", + "@notificationRuleReaction": {}, + "notificationRuleReactionDescription": "آگاه‌سازهای واکنش‌ها را سرکوب میکند.", + "@notificationRuleReactionDescription": {}, + "notificationRuleRoomServerAcl": "ACL سرور اتاق", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomServerAclDescription": "آگاه‌سازهای فهرست‌های کنترل دسترسی سرور اتاق (ACL) را سرکوب میکند.", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleSuppressEdits": "سرکوب ویرایش‌ها", + "@notificationRuleSuppressEdits": {}, + "notificationRuleSuppressEditsDescription": "آگاه‌سازهای پیام‌های ویرایش‌شده را سرکوب میکند.", + "@notificationRuleSuppressEditsDescription": {}, "notificationRuleCall": "تماس", - "notificationRuleCallDescription": "کاربر را درباره تماس‌ها مطلع می‌کند.", - "notificationRuleEncryptedRoomOneToOne": "اتاق رمزگذاری‌شده یک‌به‌یک", - "notificationRuleEncryptedRoomOneToOneDescription": "کاربر را درباره پیام‌های در اتاق‌های رمزگذاری‌شده یک‌به‌یک مطلع می‌کند.", + "@notificationRuleCall": {}, + "notificationRuleCallDescription": "درباره تماس‌ها کاربر را آگاه میکند.", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "اتاق رمزنگاری‌شده یک‌به‌یک", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "کاربر را از پیام‌ها در اتاق‌های رمزنگاری‌شده یک‌به‌یک آگاه میکند.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, "notificationRuleRoomOneToOne": "اتاق یک‌به‌یک", - "notificationRuleRoomOneToOneDescription": "کاربر را درباره پیام‌های در اتاق‌های یک‌به‌یک مطلع می‌کند.", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleRoomOneToOneDescription": "کاربر را از پیام‌ها در اتاق‌های یک‌به‌یک آگاه میکند.", + "@notificationRuleRoomOneToOneDescription": {}, "notificationRuleMessage": "پیام", - "notificationRuleMessageDescription": "کاربر را درباره پیام‌های عمومی مطلع می‌کند.", - "notificationRuleEncrypted": "رمزگذاری‌شده", - "notificationRuleEncryptedDescription": "کاربر را در مورد پیام‌ها در اتاق‌های رمزگذاری‌شده مطلع می‌کند.", - "notificationRuleJitsi": "Jitsi", - "notificationRuleJitsiDescription": "کاربر را در مورد رویدادهای ویجت Jitsi مطلع می‌کند.", + "@notificationRuleMessage": {}, + "notificationRuleMessageDescription": "کاربر را از پیام‌های عمومی آگاه میکند.", + "@notificationRuleMessageDescription": {}, + "notificationRuleEncrypted": "رمزنگاری‌شده", + "@notificationRuleEncrypted": {}, + "notificationRuleEncryptedDescription": "کاربر را از پیام‌ها در اتاق‌های رمزنگاری‌شده آگاه میکند.", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsi": "جیتسی", + "@notificationRuleJitsi": {}, + "notificationRuleJitsiDescription": "کاربر را از رویدادهای ابزارک جیتسی آگاه میکند.", + "@notificationRuleJitsiDescription": {}, "notificationRuleServerAcl": "سرکوب رویدادهای ACL سرور", - "notificationRuleServerAclDescription": "اعلان‌ها را برای رویدادهای ACL سرور سرکوب می‌کند.", - "unknownPushRule": "قانون فشار ناشناخته '{rule}'", + "@notificationRuleServerAcl": {}, + "notificationRuleServerAclDescription": "آگاه‌سازهای رویدادهای ACL سرور را سرکوب میکند.", + "@notificationRuleServerAclDescription": {}, + "unknownPushRule": "قانون ناشناخته آگاه‌ساز '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, "sentVoiceMessage": "🎙️ {duration} - پیام صوتی از {sender}", - "deletePushRuleCanNotBeUndone": "اگر این تنظیم اعلان را حذف کنید، قابل بازگشت نخواهد بود.", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "اگر این تنظیم آگاه‌ساز را پاک کنید، این کار برگشت‌ناپذیر است.", + "@deletePushRuleCanNotBeUndone": {}, "more": "بیشتر", - "shareKeysWith": "اشتراک‌گذاری کلیدها با...", - "shareKeysWithDescription": "کدام دستگاه‌ها باید مورد اعتماد قرار گیرند تا بتوانند پیام‌های شما در چت‌های رمزگذاری‌شده را بخوانند؟", + "@more": {}, + "shareKeysWith": "هم‌رسانی کلیدها با...", + "@shareKeysWith": {}, + "shareKeysWithDescription": "کدام دستگاه‌ها باید مورد اعتماد باشند تا بتوانند پیام‌های شما را در گپ‌های رمزنگاری‌شده بخوانند؟", + "@shareKeysWithDescription": {}, "allDevices": "همه دستگاه‌ها", - "crossVerifiedDevicesIfEnabled": "دستگاه‌های تاییدشده متقاطع در صورت فعال بودن", - "crossVerifiedDevices": "دستگاه‌های تاییدشده متقاطع", - "verifiedDevicesOnly": "فقط دستگاه‌های تاییدشده", + "@allDevices": {}, + "crossVerifiedDevicesIfEnabled": "دستگاه‌های بازبینی‌شده متقابل اگر فعال باشد", + "@crossVerifiedDevicesIfEnabled": {}, + "crossVerifiedDevices": "دستگاه‌های بازبینی‌شده متقابل", + "@crossVerifiedDevices": {}, + "verifiedDevicesOnly": "فقط دستگاه‌های بازبینی‌شده", + "@verifiedDevicesOnly": {}, "takeAPhoto": "گرفتن عکس", + "@takeAPhoto": {}, "recordAVideo": "ضبط ویدئو", + "@recordAVideo": {}, "optionalMessage": "(اختیاری) پیام...", - "notSupportedOnThisDevice": "در این دستگاه پشتیبانی نمی‌شود", - "enterNewChat": "وارد کردن چت جدید", - "approve": "تایید", - "youHaveKnocked": "شما زده‌اید", - "pleaseWaitUntilInvited": "لطفاً اکنون منتظر بمانید تا کسی از اتاق شما را دعوت کند.", - "commandHint_logout": "خروج از حساب کاربری فعلی خود", - "commandHint_logoutall": "خروج از تمام دستگاه‌های فعال", - "displayNavigationRail": "نمایش نوار ناوبری در موبایل", + "@optionalMessage": {}, + "notSupportedOnThisDevice": "در این دستگاه پشتیبانی نمیشود", + "@notSupportedOnThisDevice": {}, + "enterNewChat": "ورود به گپ جدید", + "@enterNewChat": {}, + "approve": "پذیرفتن", + "@approve": {}, + "youHaveKnocked": "شما در زده‌اید", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "لطفاً اکنون صبر کنید تا کسی از اتاق شما را دعوت کند.", + "@pleaseWaitUntilInvited": {}, + "commandHint_logout": "خروج از دستگاه کنونی", + "@commandHint_logout": {}, + "commandHint_logoutall": "خروج از همه دستگاه‌های فعال", + "@commandHint_logoutall": {}, + "displayNavigationRail": "نمایش نوار ناوبری در تلفن همراه", + "@displayNavigationRail": {}, "customReaction": "واکنش سفارشی", + "@customReaction": {}, + "moreEvents": "رویدادهای بیشتر", + "@moreEvents": {}, + "initAppError": "خطایی هنگام آغاز برنامه رخ داد", + "@initAppError": {}, "writeAMessageLangCodes": "در {l1} یا {l2} تایپ کنید...", "requests": "درخواست‌ها", "holdForInfo": "برای اطلاعات کلمه نگه دارید.", @@ -3926,1333 +4502,6 @@ "playWithAI": "فعلاً با هوش مصنوعی بازی کنید", "courseStartDesc": "ربات پنگئا هر زمان آماده است!\n\n... اما یادگیری با دوستان بهتر است!", "@@locale": "fa", - "@alwaysUse24HourFormat": { - "type": "String", - "placeholders": {} - }, - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@importNow": { - "type": "String", - "placeholders": {} - }, - "@importEmojis": { - "type": "String", - "placeholders": {} - }, - "@importFromZipFile": { - "type": "String", - "placeholders": {} - }, - "@exportEmotePack": { - "type": "String", - "placeholders": {} - }, - "@replace": { - "type": "String", - "placeholders": {} - }, - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@appLockDescription": { - "type": "String", - "placeholders": {} - }, - "@sendTypingNotifications": { - "type": "String", - "placeholders": {} - }, - "@swipeRightToLeftToReply": { - "type": "String", - "placeholders": {} - }, - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" - }, - "participants": { - "type": "int" - } - } - }, - "@noMoreChatsFound": { - "type": "String", - "placeholders": {} - }, - "@noChatsFoundHere": { - "type": "String", - "placeholders": {} - }, - "@joinedChats": { - "type": "String", - "placeholders": {} - }, - "@unread": { - "type": "String", - "placeholders": {} - }, - "@space": { - "type": "String", - "placeholders": {} - }, - "@spaces": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, - "@emoteKeyboardNoRecents": { - "type": "String", - "placeholders": {} - }, - "@globalChatId": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibility": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibilityDescription": { - "type": "String", - "placeholders": {} - }, - "@calls": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickers": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickersBody": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessages": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessagesBody": { - "type": "String", - "placeholders": {} - }, - "@hideInvalidOrUnknownMessageFormats": { - "type": "String", - "placeholders": {} - }, - "@block": { - "type": "String", - "placeholders": {} - }, - "@blockedUsers": { - "type": "String", - "placeholders": {} - }, - "@blockListDescription": { - "type": "String", - "placeholders": {} - }, - "@blockUsername": { - "type": "String", - "placeholders": {} - }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChats": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChatsBody": { - "type": "String", - "placeholders": {} - }, - "@overview": { - "type": "String", - "placeholders": {} - }, - "@notifyMeFor": { - "type": "String", - "placeholders": {} - }, - "@passwordRecoverySettings": { - "type": "String", - "placeholders": {} - }, - "@redactedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, - "@redactedByBecause": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "reason": { - "type": "String" - } - } - }, - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, - "@presenceStyle": { - "type": "String", - "placeholders": {} - }, - "@presencesToggle": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, - "@youInvitedToBy": { - "type": "String", - "placeholders": { - "alias": { - "type": "String" - } - } - }, - "@invitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@hasKnocked": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@usersMustKnock": { - "type": "String", - "placeholders": {} - }, - "@noOneCanJoin": { - "type": "String", - "placeholders": {} - }, - "@userWouldLikeToChangeTheChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@noPublicLinkHasBeenCreatedYet": { - "type": "String", - "placeholders": {} - }, - "@knock": { - "type": "String", - "placeholders": {} - }, - "@hidePresences": { - "type": "String", - "placeholders": {} - }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, - "@wrongPinEntered": { - "type": "String", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, - "@yourGlobalUserIdIs": { - "type": "String", - "placeholders": {} - }, - "@noUsersFoundWithQuery": { - "type": "String", - "placeholders": { - "query": { - "type": "String" - } - } - }, - "@knocking": { - "type": "String", - "placeholders": {} - }, - "@chatCanBeDiscoveredViaSearchOnServer": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@searchChatsRooms": { - "type": "String", - "placeholders": {} - }, - "@nothingFound": { - "type": "String", - "placeholders": {} - }, - "@groupName": { - "type": "String", - "placeholders": {} - }, - "@createGroupAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@groupCanBeFoundViaSearch": { - "type": "String", - "placeholders": {} - }, - "@wrongRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@startConversation": { - "type": "String", - "placeholders": {} - }, - "@commandHint_sendraw": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationTitle": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationBody": { - "type": "String", - "placeholders": {} - }, - "@leaveEmptyToClearStatus": { - "type": "String", - "placeholders": {} - }, - "@select": { - "type": "String", - "placeholders": {} - }, - "@searchForUsers": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourCurrentPassword": { - "type": "String", - "placeholders": {} - }, - "@newPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@passwordsDoNotMatch": { - "type": "String", - "placeholders": {} - }, - "@passwordIsWrong": { - "type": "String", - "placeholders": {} - }, - "@publicLink": { - "type": "String", - "placeholders": {} - }, - "@publicChatAddresses": { - "type": "String", - "placeholders": {} - }, - "@createNewAddress": { - "type": "String", - "placeholders": {} - }, - "@joinSpace": { - "type": "String", - "placeholders": {} - }, - "@publicSpaces": { - "type": "String", - "placeholders": {} - }, - "@addChatOrSubSpace": { - "type": "String", - "placeholders": {} - }, - "@subspace": { - "type": "String", - "placeholders": {} - }, - "@decline": { - "type": "String", - "placeholders": {} - }, - "@thisDevice": { - "type": "String", - "placeholders": {} - }, - "@initAppError": { - "type": "String", - "placeholders": {} - }, - "@userRole": { - "type": "String", - "placeholders": {} - }, - "@minimumPowerLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "String" - } - } - }, - "@searchIn": { - "type": "String", - "placeholders": { - "chat": { - "type": "String" - } - } - }, - "@searchMore": { - "type": "String", - "placeholders": {} - }, - "@gallery": { - "type": "String", - "placeholders": {} - }, - "@files": { - "type": "String", - "placeholders": {} - }, - "@databaseBuildErrorBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@sessionLostBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@restoreSessionBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@forwardMessageTo": { - "type": "String", - "placeholders": { - "roomName": { - "type": "String" - } - } - }, - "@sendReadReceipts": { - "type": "String", - "placeholders": {} - }, - "@sendTypingNotificationsDescription": { - "type": "String", - "placeholders": {} - }, - "@sendReadReceiptsDescription": { - "type": "String", - "placeholders": {} - }, - "@formattedMessages": { - "type": "String", - "placeholders": {} - }, - "@formattedMessagesDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUser": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUserDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDevice": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDeviceDescription": { - "type": "String", - "placeholders": {} - }, - "@acceptedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@canceledKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@completedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@isReadyForKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@requestedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@startedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@transparent": { - "type": "String", - "placeholders": {} - }, - "@incomingMessages": { - "type": "String", - "placeholders": {} - }, - "@stickers": { - "type": "String", - "placeholders": {} - }, - "@discover": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ignore": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unignore": { - "type": "String", - "placeholders": {} - }, - "@unreadChatsInApp": { - "type": "String", - "placeholders": { - "appname": { - "type": "String" - }, - "unread": { - "type": "String" - } - } - }, - "@noDatabaseEncryption": { - "type": "String", - "placeholders": {} - }, - "@thereAreCountUsersBlocked": { - "type": "String", - "placeholders": { - "count": {} - } - }, - "@restricted": { - "type": "String", - "placeholders": {} - }, - "@knockRestricted": { - "type": "String", - "placeholders": {} - }, - "@goToSpace": { - "type": "String", - "placeholders": { - "space": {} - } - }, - "@markAsUnread": { - "type": "String", - "placeholders": {} - }, - "@userLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@moderatorLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@adminLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@changeGeneralChatSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsers": { - "type": "String", - "placeholders": {} - }, - "@changeTheChatPermissions": { - "type": "String", - "placeholders": {} - }, - "@changeTheVisibilityOfChatHistory": { - "type": "String", - "placeholders": {} - }, - "@changeTheCanonicalRoomAlias": { - "type": "String", - "placeholders": {} - }, - "@sendRoomNotifications": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescriptionOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "@chatPermissionsDescription": { - "type": "String", - "placeholders": {} - }, - "@updateInstalled": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - } - } - }, - "@changelog": { - "type": "String", - "placeholders": {} - }, - "@sendCanceled": { - "type": "String", - "placeholders": {} - }, - "@loginWithMatrixId": { - "type": "String", - "placeholders": {} - }, - "@discoverHomeservers": { - "type": "String", - "placeholders": {} - }, - "@whatIsAHomeserver": { - "type": "String", - "placeholders": {} - }, - "@homeserverDescription": { - "type": "String", - "placeholders": {} - }, - "@doesNotSeemToBeAValidHomeserver": { - "type": "String", - "placeholders": {} - }, - "@calculatingFileSize": { - "type": "String", - "placeholders": {} - }, - "@prepareSendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@generatingVideoThumbnail": { - "type": "String", - "placeholders": {} - }, - "@compressVideo": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachmentCountOfCount": { - "type": "integer", - "placeholders": { - "index": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@oneOfYourDevicesIsNotVerified": { - "type": "String", - "placeholders": {} - }, - "@noticeChatBackupDeviceVerification": { - "type": "String", - "placeholders": {} - }, - "@continueText": { - "type": "String", - "placeholders": {} - }, - "@welcomeText": { - "type": "String", - "placeholders": {} - }, - "@blur": { - "type": "String", - "placeholders": {} - }, - "@opacity": { - "type": "String", - "placeholders": {} - }, - "@setWallpaper": { - "type": "String", - "placeholders": {} - }, - "@manageAccount": { - "type": "String", - "placeholders": {} - }, - "@noContactInformationProvided": { - "type": "String", - "placeholders": {} - }, - "@contactServerAdmin": { - "type": "String", - "placeholders": {} - }, - "@contactServerSecurity": { - "type": "String", - "placeholders": {} - }, - "@supportPage": { - "type": "String", - "placeholders": {} - }, - "@serverInformation": { - "type": "String", - "placeholders": {} - }, - "@name": { - "type": "String", - "placeholders": {} - }, - "@version": { - "type": "String", - "placeholders": {} - }, - "@website": { - "type": "String", - "placeholders": {} - }, - "@compress": { - "type": "String", - "placeholders": {} - }, - "@boldText": { - "type": "String", - "placeholders": {} - }, - "@italicText": { - "type": "String", - "placeholders": {} - }, - "@strikeThrough": { - "type": "String", - "placeholders": {} - }, - "@pleaseFillOut": { - "type": "String", - "placeholders": {} - }, - "@invalidUrl": { - "type": "String", - "placeholders": {} - }, - "@addLink": { - "type": "String", - "placeholders": {} - }, - "@unableToJoinChat": { - "type": "String", - "placeholders": {} - }, - "@previous": { - "type": "String", - "placeholders": {} - }, - "@otherPartyNotLoggedIn": { - "type": "String", - "placeholders": {} - }, - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@appWantsToUseForLoginDescription": { - "type": "String", - "placeholders": {} - }, - "@open": { - "type": "String", - "placeholders": {} - }, - "@waitingForServer": { - "type": "String", - "placeholders": {} - }, - "@appIntroduction": { - "type": "String", - "placeholders": {} - }, - "@newChatRequest": { - "type": "String", - "placeholders": {} - }, - "@contentNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@generalNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@roomNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@userSpecificNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@otherNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMaster": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMasterDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNotices": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNoticesDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMe": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEvent": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEventDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReaction": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReactionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEditsDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCall": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCallDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessage": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncrypted": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsi": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@deletePushRuleCanNotBeUndone": { - "type": "String", - "placeholders": {} - }, - "@more": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWith": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@allDevices": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@takeAPhoto": { - "type": "String", - "placeholders": {} - }, - "@recordAVideo": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@enterNewChat": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, - "@youHaveKnocked": { - "type": "String", - "placeholders": {} - }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -11797,4 +11046,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 18002cb55..3cd73f725 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -582,7 +582,7 @@ "type": "String", "placeholders": {} }, - "emoteInvalid": "Epäkelpo emote-lyhytkoodi", + "emoteInvalid": "Epäkelpo emote-lyhytkoodi!", "@emoteInvalid": { "type": "String", "placeholders": {} @@ -2172,7 +2172,7 @@ "@dismiss": {}, "emojis": "Hymiöt", "@emojis": {}, - "unsupportedAndroidVersionLong": "Tämä ominaisuus vaatii uudemman Android-version. Tarkista päivitykset tai LineageOS-tuki.", + "unsupportedAndroidVersionLong": "Tämä ominaisuus vaatii uudemman Android-version. Tarkistathan päivitykset tai Lineage OS :n tuki.", "@unsupportedAndroidVersionLong": {}, "unsupportedAndroidVersion": "Ei tuettu Android-versio", "@unsupportedAndroidVersion": {}, @@ -2322,7 +2322,7 @@ "@hydrate": {}, "dehydrate": "Vie istunto ja tyhjennä laite", "@dehydrate": {}, - "dehydrateWarning": "Tätä toimenpidettä ei voi kumota.\nVarmista varmuuskopiotiedoston turvallinen tallennus.", + "dehydrateWarning": "Tätä toimenpidettä ei voi kumota. Varmista varmuuskopiotiedoston turvallinen tallennus.", "@dehydrateWarning": {}, "users": "Käyttäjät", "@users": {}, @@ -2533,15 +2533,6 @@ } } }, - "signInWith": "Kirjaudu sisään palvelulla {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "optionalRedactReason": "(Vapaaehtoinen) Syy tämän viestin poistamiselle...", "@optionalRedactReason": {}, "archiveRoomDescription": "Keskustelu siirretään arkistoon. Muut käyttäjät näkevät sinun poistuneen keskustelusta.", @@ -2587,11 +2578,11 @@ } } }, - "sendTypingNotifications": "acLähetä kirjoitusilmoituksia", + "sendTypingNotifications": "Lähetä kirjoitusilmoituksia", "@sendTypingNotifications": {}, - "inviteGroupChat": "Kutsu ryhmäkeskusteluun", + "inviteGroupChat": "📨 Kutsu ryhmäkeskusteluun", "@inviteGroupChat": {}, - "invitePrivateChat": "Kutsu yksityiskeskusteluun", + "invitePrivateChat": "📨 Kutsu yksityiskeskusteluun", "@invitePrivateChat": {}, "importEmojis": "Tuo emojit", "@importEmojis": {}, @@ -2632,252 +2623,859 @@ "@unread": {}, "noMoreChatsFound": "Lisää keskusteluja ei löytynyt...", "@noMoreChatsFound": {}, - "alwaysUse24HourFormat": "epätosi", - "setCustomPermissionLevel": "Aseta mukautettu käyttöoikeustaso", - "setPermissionsLevelDescription": "Valitse alla ennalta määritetty rooli tai syötä mukautettu käyttöoikeustaso väliltä 0–100.", - "ignoreUser": "Ohita käyttäjä", - "normalUser": "Normaali käyttäjä", - "aboutHomeserver": "Tietoja {homeserver}", - "commandHint_roomupgrade": "Päivitä tämä huone annettuun huonetyyppiin", - "appLockDescription": "Lukitse sovellus, kun sitä ei käytetä, PIN-koodilla", - "countChatsAndCountParticipants": "{chats} keskustelua ja {participants} osallistujaa", - "noChatsFoundHere": "Täällä ei ole vielä keskusteluja. Aloita uusi keskustelu jonkun kanssa käyttämällä alla olevaa painiketta. ⤵️", - "joinedChats": "Liitetyt keskustelut", - "space": "Tila", - "spaces": "Tilat", - "checkList": "Tarkistuslista", - "countInvited": "{count} kutsuttu", - "globalChatId": "Globaali keskustelutunnus", - "accessAndVisibilityDescription": "Kuka saa liittyä tähän keskusteluun ja kuinka keskustelu voidaan löytää.", - "calls": "Puhelut", - "customEmojisAndStickers": "Mukautetut emotikonit ja tarrat", - "customEmojisAndStickersBody": "Lisää tai jaa mukautettuja emotikoneja tai tarroja, joita voi käyttää missä tahansa keskustelussa.", - "hideRedactedMessages": "Piilota muokatut viestit", - "hideRedactedMessagesBody": "Jos joku muokkaa viestin, tämä viesti ei ole enää näkyvissä keskustelussa.", - "hideInvalidOrUnknownMessageFormats": "Piilota virheelliset tai tuntemattomat viestimuodot", - "block": "Estä", - "blockedUsers": "Estetyt käyttäjät", - "blockListDescription": "Voit estää häiritseviä käyttäjiä. Et voi vastaanottaa viestejä tai huoneen kutsuja käyttäjiltä, jotka ovat estolistallasi.", - "blockUsername": "Ohita käyttäjänimi", - "hideMemberChangesInPublicChats": "Piilota jäsenvaihdokset julkisissa keskusteluissa", - "hideMemberChangesInPublicChatsBody": "Älä näytä keskustelun aikajanalla, jos joku liittyy tai poistuu julkisesta keskustelusta, parantaen luettavuutta.", - "overview": "Yleiskatsaus", - "notifyMeFor": "Ilmoita minulle", - "passwordRecoverySettings": "Salasanan palautusasetukset", - "sendImages": "Lähetä {count} kuva", - "presenceStyle": "Läsnäolo:", - "presencesToggle": "Näytä muiden käyttäjien tilaviestit", - "synchronizingPleaseWaitCounter": " Synkronoidaan… ({percentage}%)", - "youInvitedToBy": "📩 Sinut on kutsuttu linkin kautta:\n{alias}", - "invitedBy": "📩 Kutsunut {user}", - "usersMustKnock": "Käyttäjien täytyy koputtaa", - "noOneCanJoin": "Kukaan ei voi liittyä", - "userWouldLikeToChangeTheChat": "{user} haluaisi liittyä keskusteluun.", - "noPublicLinkHasBeenCreatedYet": "Yhtään julkista linkkiä ei ole vielä luotu", - "knock": " Koputa", - "hidePresences": "Piilota tila-lista?", - "removeDevicesDescription": "Sinut kirjataan ulos tästä laitteesta etkä enää voi vastaanottaa viestejä.", - "banUserDescription": "Käyttäjä estetään keskustelusta eikä hän voi enää liittyä keskusteluun ennen kuin hänet vapautetaan estosta.", - "unbanUserDescription": "Käyttäjä voi liittyä keskusteluun uudelleen, jos hän yrittää.", - "kickUserDescription": "Käyttäjä poistetaan keskustelusta, mutta häntä ei estetä. Julkisissa keskusteluissa käyttäjä voi liittyä uudelleen milloin tahansa.", - "makeAdminDescription": "Kun teet tämän käyttäjän ylläpitäjäksi, et ehkä voi peruuttaa tätä, koska hänellä on samat oikeudet kuin sinulla.", - "pushNotificationsNotAvailable": "Ponnahdusilmoitukset eivät ole saatavilla", - "learnMore": "Lisätietoja", - "yourGlobalUserIdIs": "Kansainvälinen käyttäjätunnuksesi on: ", - "noUsersFoundWithQuery": "Valitettavasti käyttäjää ei löytynyt haulla \"{query}\". Tarkista, onko kirjoitusvirhe.", - "knocking": " Koputtaa", - "chatCanBeDiscoveredViaSearchOnServer": "Keskustelu voidaan löytää haun avulla palvelimella {server}", - "searchChatsRooms": "Etsi #keskusteluja, @käyttäjiä...", - "nothingFound": "Ei mitään löytynyt...", - "groupName": "Ryhmän nimi", - "createGroupAndInviteUsers": "Luo ryhmä ja kutsu käyttäjiä", - "groupCanBeFoundViaSearch": "Ryhmän voi löytää haun avulla", - "wrongRecoveryKey": "Valitettavasti tämä ei näytä oikealta palautusavaimelta.", - "startConversation": "Aloita keskustelu", - "commandHint_sendraw": "Lähetä raakaa jsonia", - "databaseMigrationTitle": "Tietokanta on optimoitu", - "databaseMigrationBody": "Odota hetki. Tämä saattaa kestää hetken.", - "leaveEmptyToClearStatus": "Jätä tyhjäksi poistaaksesi tilasi.", + "alwaysUse24HourFormat": "väärä", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, "select": "Valitse", - "searchForUsers": "Etsi käyttäjiä @...", - "pleaseEnterYourCurrentPassword": "Syötä nykyinen salasanasi", + "@select": {}, + "searchForUsers": "Etsi @users...", + "@searchForUsers": {}, + "pleaseEnterYourCurrentPassword": "Anna nykyinen salasanasi", + "@pleaseEnterYourCurrentPassword": {}, "newPassword": "Uusi salasana", + "@newPassword": {}, "pleaseChooseAStrongPassword": "Valitse vahva salasana", + "@pleaseChooseAStrongPassword": {}, "passwordsDoNotMatch": "Salasanat eivät täsmää", - "passwordIsWrong": "Syöttämäsi salasana on väärä", - "publicLink": "Julkinen linkki", - "publicChatAddresses": "Julkiset keskustelunumerot", - "createNewAddress": "Luo uusi osoite", - "joinSpace": "Liity tilaan", - "publicSpaces": "Julkiset tilat", - "addChatOrSubSpace": "Lisää keskustelu tai alitila", - "subspace": "Alitila", - "decline": "Hylkää", + "@passwordsDoNotMatch": {}, + "passwordIsWrong": "Salasanasi on väärä", + "@passwordIsWrong": {}, "thisDevice": "Tämä laite:", - "initAppError": "Sovelluksen alustuksessa tapahtui virhe", - "userRole": "Käyttäjän rooli", - "minimumPowerLevel": "{level} on minimitehotaso.", - "searchIn": "Etsi keskustelusta \"{chat}\"...", - "searchMore": "Etsi lisää...", + "@thisDevice": {}, "gallery": "Galleria", + "@gallery": {}, "files": "Tiedostot", - "databaseBuildErrorBody": "Tietokannan SQlite:n rakentaminen epäonnistui. Sovellus yrittää käyttää vanhaa tietokantaa toistaiseksi. Ilmoita tästä virheestä kehittäjille osoitteessa {url}. Virheilmoitus on: {error}", - "sessionLostBody": "Istuntosi on kadonnut. Ilmoita tästä virheestä kehittäjille osoitteessa {url}. Virheilmoitus on: {error}", - "restoreSessionBody": "Sovellus yrittää nyt palauttaa istuntosi varmuuskopiosta. Ilmoita tästä virheestä kehittäjille osoitteessa {url}. Virheilmoitus on: {error}", - "forwardMessageTo": "Lähetä viesti eteenpäin {roomName}?", - "sendReadReceipts": "Lähetä lukutodistukset", - "sendTypingNotificationsDescription": "Muut osallistujat keskustelussa näkevät, kun kirjoitat uutta viestiä.", - "sendReadReceiptsDescription": "Muut osallistujat keskustelussa näkevät, kun olet lukenut viestin.", - "formattedMessages": "Muotoillut viestit", - "formattedMessagesDescription": "Näytä rikkaan viestisisällön kuten lihavoitu teksti markdownin avulla.", - "verifyOtherUser": "🔐 Vahvista toinen käyttäjä", - "verifyOtherUserDescription": "Jos vahvistat toisen käyttäjän, voit olla varma, että tiedät, kenelle oikeasti kirjoitat. 💪\n\nKun aloitat vahvistuksen, sinä ja toinen käyttäjä näette sovelluksessa ponnahdusikkunan. Siellä näette sitten sarjan emojeja tai numeroita, jotka teidän tulee verrata toisiinne.\n\nParas tapa tehdä tämä on tavata kasvotusten tai aloittaa videopuhelu. 👭", - "verifyOtherDevice": "🔄 Vahvista toinen laite", - "verifyOtherDeviceDescription": "Kun vahvistat toisen laitteen, nämä laitteet voivat vaihtaa avaimia, mikä lisää kokonaisturvallisuuttasi. 💪 Kun aloitat vahvistuksen, sovelluksessa molemmissa laitteissa ilmestyy ponnahdusikkuna. Siinä näet sarjan emojeja tai numeroita, jotka sinun tulee verrata toisiinsa. On parasta olla molemmat laitteet käsillä ennen vahvistuksen aloittamista. 🤓", - "acceptedKeyVerification": "{sender} hyväksyi avaintarkistuksen", - "canceledKeyVerification": "{sender} peruutti avaintarkistuksen", - "completedKeyVerification": "{sender} suoritti avaintarkistuksen", - "isReadyForKeyVerification": "{sender} on valmis avaintarkistukseen", - "requestedKeyVerification": "{sender} pyysi avaintarkistusta", - "startedKeyVerification": "{sender} aloitti avaintarkistuksen", - "transparent": "Läpinäkyvä", - "incomingMessages": "Saapuvat viestit", - "stickers": "Tarrat", - "discover": "Löydä", - "commandHint_ignore": "Ohita annettu matriisikoodi", - "commandHint_unignore": "Poista ohitus annettua matriisikoodia", - "unreadChatsInApp": "{appname}: {unread} lukematonta keskustelua", - "noDatabaseEncryption": "Tietokannan salausta ei tueta tällä alustalla", - "thereAreCountUsersBlocked": "Tällä hetkellä on {count} estettyä käyttäjää.", - "restricted": "Rajoitettu", - "knockRestricted": "Koputus rajoitettu", - "goToSpace": "Siirry avaruuteen: {space}", - "markAsUnread": "Merkitse lukemattomaksi", - "userLevel": "{level} - Käyttäjä", - "moderatorLevel": "{level} - Moderaattori", - "adminLevel": "{level} - Ylläpitäjä", - "changeGeneralChatSettings": "Vaihda yleiset keskusteluasetukset", - "inviteOtherUsers": "Kutsu muita käyttäjiä tähän keskusteluun", - "changeTheChatPermissions": "Vaihda keskustelun oikeudet", - "changeTheVisibilityOfChatHistory": "Vaihda keskusteluhistorian näkyvyyttä", - "changeTheCanonicalRoomAlias": "Vaihda pääasiallinen julkinen keskustelun osoite", - "sendRoomNotifications": "Lähetä @huone ilmoituksia", - "changeTheDescriptionOfTheGroup": "Vaihda keskustelun kuvaus", - "chatPermissionsDescription": "Määritä, mikä valtuustaso tarvitaan tiettyihin toimintoihin tässä keskustelussa. Valtuustasot 0, 50 ja 100 yleensä edustavat käyttäjiä, moderaattoreita ja ylläpitäjiä, mutta kaikki asteet ovat mahdollisia.", - "updateInstalled": "🎉 Päivitys {version} asennettu!", + "@files": {}, "changelog": "Muutosloki", - "sendCanceled": "Lähetys peruutettu", - "loginWithMatrixId": "Kirjaudu Matrix-ID:llä", - "discoverHomeservers": "Löydä kotipalvelimet", - "whatIsAHomeserver": "Mikä on kotipalvelin?", - "homeserverDescription": "Kaikki tietosi tallennetaan kotipalvelimelle, aivan kuten sähköpostipalvelimella. Voit valita, mitä kotipalvelinta käytät, mutta voit silti kommunikoida kaikkien kanssa. Lisätietoja osoitteessa https://matrix.org.", - "doesNotSeemToBeAValidHomeserver": "Ei vaikuta olevan yhteensopiva kotipalvelin. Väärä URL?", - "calculatingFileSize": "Lasketaan tiedoston kokoa...", - "prepareSendingAttachment": "Valmistellaan liitteen lähettämistä...", - "sendingAttachment": "Lähetetään liitettä...", - "generatingVideoThumbnail": "Luodaan videokuvaketta...", - "compressVideo": "pakataan videota...", - "sendingAttachmentCountOfCount": "Lähetetään liitettä {index} / {length}...", - "serverLimitReached": "Palvelimen raja saavutettu! Odotetaan {seconds} sekuntia...", - "oneOfYourDevicesIsNotVerified": "Yksi laitteistasi ei ole vahvistettu", - "noticeChatBackupDeviceVerification": "Huomautus: Kun yhdistät kaikki laitteesi chat-varmistukseen, ne vahvistetaan automaattisesti.", + "@changelog": {}, "continueText": "Jatka", - "welcomeText": "Hei Hei 👋 Tämä on FluffyChat. Voit kirjautua sisään mihin tahansa palvelimeen, joka on yhteensopiva https://matrix.org:n kanssa. Ja sitten keskustella kenen tahansa kanssa. Se on suuri hajautettu viestintäverkosto!", - "blur": "Sumenna:", - "opacity": "Läpinäkyvyys:", - "setWallpaper": "Aseta taustakuva", - "manageAccount": "Hallitse tiliä", - "noContactInformationProvided": "Palvelin ei tarjoa mitään kelvollista yhteystietoa", - "contactServerAdmin": "Ota yhteyttä palvelimen ylläpitäjään", - "contactServerSecurity": "Ota yhteyttä palvelimen turvallisuuteen", - "supportPage": "Tukisivu", + "@continueText": {}, + "welcomeText": "Hei 👋, Tämä on FluffyChat. Voit kirjautua sisään mihin tahansa kotipalvelimeen, joka on yhteensopiva https:/matrix.org:in kanssa. Sitten jutellaan kenen kanssa tahansa. Se on hajautettu viestiverkosto!", + "@welcomeText": {}, "serverInformation": "Palvelimen tiedot:", + "@serverInformation": {}, "name": "Nimi", + "@name": {}, "version": "Versio", - "website": "Verkkosivusto", - "compress": "Pakkaa", - "boldText": "Lihavoitu teksti", - "italicText": "Kursivoitu teksti", - "strikeThrough": "Yliviivaus", - "pleaseFillOut": "Täytäthän", - "invalidUrl": "Virheellinen URL", - "addLink": "Lisää linkki", - "unableToJoinChat": "Ei voida liittyä keskusteluun. Ehkä toinen osapuoli on jo sulkenut keskustelun.", + "@version": {}, + "website": "Verkkosivu", + "@website": {}, "previous": "Edellinen", - "otherPartyNotLoggedIn": "Toinen osapuoli ei ole tällä hetkellä kirjautuneena sisään, joten hän ei voi vastaanottaa viestejä!", - "appWantsToUseForLogin": "Käytä '{server}' kirjautumiseen", - "appWantsToUseForLoginDescription": "Sallitte tässä, että sovellus ja verkkosivusto jakavat tietoja teistä.", + "@previous": {}, "open": "Avaa", - "waitingForServer": "Odotetaan palvelinta...", - "appIntroduction": "FluffyChat antaa sinun keskustella ystäviesi kanssa eri viestintäsovelluksilla. Lisätietoja osoitteessa https://matrix.org tai napauta vain *Jatka*.", - "newChatRequest": "📩 Uusi keskustelupyyntö", - "contentNotificationSettings": "Sisältöilmoitusasetukset", - "generalNotificationSettings": "Yleiset ilmoitusasetukset", - "roomNotificationSettings": "Huoneen ilmoitusasetukset", - "userSpecificNotificationSettings": "Käyttäjäkohtaiset ilmoitusasetukset", - "otherNotificationSettings": "Muut ilmoitusasetukset", - "notificationRuleContainsUserName": "Sisältää käyttäjänimen", - "notificationRuleContainsUserNameDescription": "Ilmoittaa käyttäjälle, kun viesti sisältää heidän käyttäjänimensä.", - "notificationRuleMaster": "Mykistä kaikki ilmoitukset", - "notificationRuleMasterDescription": "Ohittaa kaikki muut säännöt ja poistaa kaikki ilmoitukset käytöstä.", - "notificationRuleSuppressNotices": "Piilota automaattiset viestit", - "notificationRuleSuppressNoticesDescription": "Piilottaa ilmoitukset automatisoiduilta asiakkailta kuten boteilta.", - "notificationRuleInviteForMe": "Kutsu minulle", - "notificationRuleInviteForMeDescription": "Ilmoittaa käyttäjälle, kun hänelle lähetetään kutsu huoneeseen.", - "notificationRuleMemberEvent": "Jäsenen tapahtuma", - "notificationRuleMemberEventDescription": "Piilottaa ilmoitukset jäsenyyteen liittyvistä tapahtumista.", - "notificationRuleIsUserMention": "Käyttäjän maininta", - "notificationRuleIsUserMentionDescription": "Ilmoittaa käyttäjälle, kun hänet mainitaan suoraan viestissä.", - "notificationRuleContainsDisplayName": "Sisältää näyttönimen", - "notificationRuleContainsDisplayNameDescription": "Ilmoittaa käyttäjälle, kun viesti sisältää hänen näyttönimensä.", - "notificationRuleIsRoomMention": "Huoneen maininta", - "notificationRuleIsRoomMentionDescription": "Ilmoittaa käyttäjälle, kun huone mainitaan.", - "notificationRuleRoomnotif": "Huoneen ilmoitus", - "notificationRuleRoomnotifDescription": "Ilmoittaa käyttäjälle, kun viestissä on '@room'.", - "notificationRuleTombstone": "Kivijalka", - "notificationRuleTombstoneDescription": "Ilmoittaa käyttäjälle huoneen deaktivaatioviesteistä.", - "notificationRuleReaction": "Reaktio", - "notificationRuleReactionDescription": "Poistaa ilmoitukset reaktioista.", - "notificationRuleRoomServerAcl": "Huoneen palvelimen ACL", - "notificationRuleRoomServerAclDescription": "Poistaa ilmoitukset huoneen palvelimen käyttöoikeuslistoista (ACL).", - "notificationRuleSuppressEdits": "Poista muokkaukset", - "notificationRuleSuppressEditsDescription": "Poistaa ilmoitukset muokatuista viesteistä.", - "notificationRuleCall": "Puhelu", - "notificationRuleCallDescription": "Ilmoittaa käyttäjälle puheluista.", - "notificationRuleEncryptedRoomOneToOne": "Salattu huone yksi vastaan yksi", - "notificationRuleEncryptedRoomOneToOneDescription": "Ilmoittaa käyttäjälle salatuista yksi vastaan yksi -huoneiden viesteistä.", - "notificationRuleRoomOneToOne": "Huone yksi vastaan yksi", - "notificationRuleRoomOneToOneDescription": "Ilmoittaa käyttäjälle viesteistä yksi vastaan yksi -huoneissa.", - "notificationRuleMessage": "Viestit", - "notificationRuleMessageDescription": "Ilmoittaa käyttäjälle yleisistä viesteistä.", - "notificationRuleEncrypted": "Salattu", - "notificationRuleEncryptedDescription": "Ilmoittaa käyttäjälle viesteistä salatuissa huoneissa.", - "notificationRuleJitsi": "Jitsi", - "notificationRuleJitsiDescription": "Ilmoittaa käyttäjälle Jitsi-widget-tapahtumista.", - "notificationRuleServerAcl": "Poista palvelimen ACL-tapahtumat käytöstä", - "notificationRuleServerAclDescription": "Poistaa ilmoitukset palvelimen ACL-tapahtumista.", - "unknownPushRule": "Tuntematon push-sääntö '{rule}'", - "sentVoiceMessage": "🎙️ {duration} - Ääniviesti lähettäjältä {sender}", - "deletePushRuleCanNotBeUndone": "Jos poistat tämän ilmoitusasetuksen, sitä ei voi peruuttaa.", + "@open": {}, "more": "Lisää", - "shareKeysWith": "Jaa avaimet...", - "shareKeysWithDescription": "Luotetaanko laitteisiin, jotta ne voivat lukea viestisi salatuissa keskusteluissa?", + "@more": {}, "allDevices": "Kaikki laitteet", - "crossVerifiedDevicesIfEnabled": "Ristiinvarmistetut laitteet, jos käytössä", - "crossVerifiedDevices": "Ristiinvarmistetut laitteet", - "verifiedDevicesOnly": "Vain varmennetut laitteet", - "takeAPhoto": "Ota valokuva", - "recordAVideo": "Tallenna video", - "optionalMessage": "(Valinnainen) viesti...", - "notSupportedOnThisDevice": "Ei tuettu tällä laitteella", - "enterNewChat": "Aloita uusi keskustelu", + "@allDevices": {}, + "recordAVideo": "Nauhoita video", + "@recordAVideo": {}, "approve": "Hyväksy", + "@approve": {}, + "pause": "Keskeytä", + "@pause": {}, + "resume": "Jatka", + "@resume": {}, + "moveUp": "Siirrä ylös", + "@moveUp": {}, + "moveDown": "Siirrä alas", + "@moveDown": {}, + "poll": "Kysely", + "@poll": {}, + "setCustomPermissionLevel": "Aseta mukautettu lupataso", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Valitse alla oleva ennalta määritetty rooli tai anna mukautettu lupataso väliltä 0–100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Jätä huomiotta", + "@ignoreUser": {}, + "normalUser": "Peruskäyttäjä", + "@normalUser": {}, + "aboutHomeserver": "Tietoja {homeserver}:sta", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "commandHint_roomupgrade": "Päivitä tämä huone annettuun huoneversioon", + "@commandHint_roomupgrade": {}, + "appLockDescription": "Lukitse sovellus kun sitä ei käytetä PIN-koodin kanssa", + "@appLockDescription": {}, + "countChatsAndCountParticipants": "{chats} pikakeskustelut ja {participants} osallistujat", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noChatsFoundHere": "Täältä ei löytynyt vielä pikakeskusteluja. Aloita uusi pikakeskustelu jonkun kanssa alla olevalla painikkeella. ⤵️", + "@noChatsFoundHere": {}, + "joinedChats": "Liittyneet pikakeskustelut", + "@joinedChats": {}, + "space": "Tila", + "@space": {}, + "spaces": "Tilat", + "@spaces": {}, + "checkList": "Tarkistuslista", + "@checkList": {}, + "countInvited": "{count} kutsuttu", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "globalChatId": "Yleisesti pätevä keskustelutunnus", + "@globalChatId": {}, + "accessAndVisibilityDescription": "Kuka voi liittyä tähän pikakeskusteluun ja miten pikakeskustelun voi löytää.", + "@accessAndVisibilityDescription": {}, + "calls": "Puhelut", + "@calls": {}, + "customEmojisAndStickers": "Mukautetut emojit ja tarrat", + "@customEmojisAndStickers": {}, + "customEmojisAndStickersBody": "Lisää tai jaa mukautettuja emojeja tai tarroja, joita voidaan käyttää missä tahansa pikakeskustelussa.", + "@customEmojisAndStickersBody": {}, + "hideInvalidOrUnknownMessageFormats": "Piilota virheelliset tai tuntemattomat viestimuodot", + "@hideInvalidOrUnknownMessageFormats": {}, + "block": "Estä", + "@block": {}, + "blockedUsers": "Estetyt käyttäjät", + "@blockedUsers": {}, + "blockListDescription": "Voit estää sinua häiritsevät käyttäjät. Et voi vastaanottaa viestejä tai huonekutsuja henkilökohtaisella estolistallasi olevilta käyttäjiltä.", + "@blockListDescription": {}, + "hideMemberChangesInPublicChats": "Piilota jäsenten muutokset julkisissa pikakeskusteluissa", + "@hideMemberChangesInPublicChats": {}, + "hideMemberChangesInPublicChatsBody": "Älä näytä pikakeskustelun aikajanalla, jos joku liittyy julkiseen pikakeskusteluun tai poistuu siitä luettavuuden parantamiseksi.", + "@hideMemberChangesInPublicChatsBody": {}, + "blockUsername": "Jätä käyttäjänimi huomiotta", + "@blockUsername": {}, + "overview": "Yleiskatsaus", + "@overview": {}, + "notifyMeFor": "Ilmoita minulle", + "@notifyMeFor": {}, + "passwordRecoverySettings": "Salasanan palautusasetukset", + "@passwordRecoverySettings": {}, + "sendImages": "Lähetä {count} kuva", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "presenceStyle": "Läsnäolo:", + "@presenceStyle": { + "type": "String", + "placeholders": {} + }, + "presencesToggle": "Näytä muiden käyttäjien tilaviestit", + "@presencesToggle": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWaitCounter": " Synkronoidaan… ({percentage} %)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "youInvitedToBy": "📩 Sinut on kutsuttu linkin kautta kohteeseen:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": { + "type": "String" + } + } + }, + "invitedBy": "📩 Kutsujana {user}", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "usersMustKnock": "Käyttäjien on koputettava", + "@usersMustKnock": {}, + "noOneCanJoin": "Kukaan ei voi liittyä", + "@noOneCanJoin": {}, + "userWouldLikeToChangeTheChat": "{user} haluaa liittyä pikakeskusteluun.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "noPublicLinkHasBeenCreatedYet": "Julkista linkkiä ei ole vielä luotu", + "@noPublicLinkHasBeenCreatedYet": {}, + "knock": "Koputa", + "@knock": {}, + "hidePresences": "Piilotetaanko tilaluettelo?", + "@hidePresences": {}, + "removeDevicesDescription": "Sinut kirjataan ulos tästä laitteesta, etkä voi enää vastaanottaa viestejä.", + "@removeDevicesDescription": {}, + "banUserDescription": "Käyttäjä kielletään pikakeskustelusta, eikä hän voi liittyä pikakeskusteluun uudelleen ennen kuin kielto kumotetaan.", + "@banUserDescription": {}, + "unbanUserDescription": "Käyttäjä voi liittyä pikakeskusteluun uudelleen, jos hän yrittää.", + "@unbanUserDescription": {}, + "kickUserDescription": "Käyttäjä potkitaan ulos pikakeskustelusta, mutta häntä ei porttikieltoa saada. Julkisissa pikakeskusteluissa käyttäjä voi liittyä takaisin milloin tahansa.", + "@kickUserDescription": {}, + "makeAdminDescription": "Kun olet tehnyt tästä käyttäjästä järjestelmänvalvojan, et ehkä voi perua tätä, koska hänellä on siitä hetkestä lähtien samat oikeudet kuin sinulla.", + "@makeAdminDescription": {}, + "pushNotificationsNotAvailable": "Työntöilmoitukset ei saatavilla", + "@pushNotificationsNotAvailable": {}, + "learnMore": "Opi lisää", + "@learnMore": {}, + "yourGlobalUserIdIs": "Yleisesti pätevä käyttäjätunnuksesi on: ", + "@yourGlobalUserIdIs": {}, + "noUsersFoundWithQuery": "Valitettavasti käyttäjää ei löytynyt haulla \"{query}\". Tarkistathan, onko kirjoitusvirhe.", + "@noUsersFoundWithQuery": { + "type": "String", + "placeholders": { + "query": { + "type": "String" + } + } + }, + "knocking": "Koputetaan", + "@knocking": {}, + "chatCanBeDiscoveredViaSearchOnServer": "Pikakeskustelu löytyy haulla {server}:lta", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "searchChatsRooms": "Hae #pikakeskustelut, @käyttäjät...", + "@searchChatsRooms": {}, + "nothingFound": "Mitään ei löytynyt...", + "@nothingFound": {}, + "groupName": "Ryhmän nimi", + "@groupName": {}, + "createGroupAndInviteUsers": "Luo ryhmä ja kutsu käyttäjiä", + "@createGroupAndInviteUsers": {}, + "groupCanBeFoundViaSearch": "Ryhmä löytyy haun kautta", + "@groupCanBeFoundViaSearch": {}, + "wrongRecoveryKey": "Pahoittelut... tämä ei vaikuta olevan oikea palautusavain.", + "@wrongRecoveryKey": {}, + "startConversation": "Aloita keskustelu", + "@startConversation": {}, + "commandHint_sendraw": "Lähetä raaka JSON", + "@commandHint_sendraw": {}, + "databaseMigrationTitle": "Tietokanta on optimoitu", + "@databaseMigrationTitle": {}, + "databaseMigrationBody": "Odotathan hetki. Tämä voi kestää hetken.", + "@databaseMigrationBody": {}, + "leaveEmptyToClearStatus": "Jätä tyhjäksi tyhjentääksesi tilasi.", + "@leaveEmptyToClearStatus": {}, + "publicLink": "Julkinen linkki", + "@publicLink": {}, + "publicChatAddresses": "Julkiset keskusteluosoitteet", + "@publicChatAddresses": {}, + "createNewAddress": "Luo uusi osoite", + "@createNewAddress": {}, + "joinSpace": "Liity tilaan", + "@joinSpace": {}, + "publicSpaces": "Julkiset tilat", + "@publicSpaces": {}, + "addChatOrSubSpace": "Lisää pikakeskustelu tai alitila", + "@addChatOrSubSpace": {}, + "subspace": "Alitila", + "@subspace": {}, + "decline": "Hylkää", + "@decline": {}, + "initAppError": "Sovelluksen alustamisessa tapahtui virhe", + "@initAppError": {}, + "userRole": "Käyttäjärooli", + "@userRole": {}, + "minimumPowerLevel": "{level} on pienin tehotaso.", + "@minimumPowerLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "String" + } + } + }, + "searchIn": "Hae keskustelusta \"{chat}\"...", + "@searchIn": { + "type": "String", + "placeholders": { + "chat": { + "type": "String" + } + } + }, + "searchMore": "Hae lisää...", + "@searchMore": {}, + "databaseBuildErrorBody": "SQlite-tietokantaa ei voida rakentaa. Sovellus yrittää toistaiseksi käyttää vanhaa tietokantaa. Ilmoita tästä virheestä kehittäjille osoitteessa {url}. Virheviesti on: {error}", + "@databaseBuildErrorBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "sessionLostBody": "Istuntosi on menetetty. Ilmoita tästä virheestä kehittäjille osoitteessa {url}. Virheviesti on: {error}", + "@sessionLostBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "restoreSessionBody": "Sovellus yrittää nyt palauttaa istuntosi varmuuskopiosta. Ilmoita tästä virheestä kehittäjille osoitteessa {url}. Virheviesti on: {error}", + "@restoreSessionBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "forwardMessageTo": "Lähetätkö viestin edelleen kohteeseen {roomName}?", + "@forwardMessageTo": { + "type": "String", + "placeholders": { + "roomName": { + "type": "String" + } + } + }, + "sendReadReceipts": "Lähetä lukukuittaukset", + "@sendReadReceipts": {}, + "sendTypingNotificationsDescription": "Muut keskustelun osallistujat näkevät, milloin olet kirjoittamassa uutta viestiä.", + "@sendTypingNotificationsDescription": {}, + "sendReadReceiptsDescription": "Muut keskustelun osallistujat näkevät, milloin olet lukenut viestin.", + "@sendReadReceiptsDescription": {}, + "formattedMessages": "Muotoillut viestit", + "@formattedMessages": {}, + "formattedMessagesDescription": "Näytä rikasta viestisisältöä, kuten lihavoitua tekstiä, käyttämällä Markdownia.", + "@formattedMessagesDescription": {}, + "verifyOtherUser": "🔐 Vahvista toinen käyttäjä", + "@verifyOtherUser": {}, + "verifyOtherUserDescription": "Jos vahvistat toisen käyttäjän, voit olla varma, että tiedät kenelle todella kirjoitat. 💪\n\nKun aloitat vahvistuksen, sinä ja toinen käyttäjä näette sovelluksessa ponnahdusikkunan. Siellä näette sitten sarjan emojeja tai numeroita, joita teidän on verrattava toisiinsa.\n\nParas tapa tehdä tämä on tavata heidät tai aloittaa videopuhelu. 👭", + "@verifyOtherUserDescription": {}, + "verifyOtherDevice": "🔐 Vahvista toinen laite", + "@verifyOtherDevice": {}, + "verifyOtherDeviceDescription": "Kun vahvistat toisen laitteen, kyseiset laitteet voivat vaihtaa avaimia, mikä lisää yleistä turvallisuuttasi. 💪 Kun aloitat vahvistuksen, molempien laitteiden sovellukseen ilmestyy ponnahdusikkuna. Siellä näet sitten sarjan emojeja tai numeroita, joita sinun on verrattava toisiinsa. On parasta pitää molemmat laitteet käsillä ennen vahvistuksen aloittamista. 🤳", + "@verifyOtherDeviceDescription": {}, + "acceptedKeyVerification": "{sender} hyväksyi avaimen vahvistuksen", + "@acceptedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "canceledKeyVerification": "{sender} peruutti avaimen vahvistuksen", + "@canceledKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "completedKeyVerification": "{sender} suoritti avaimen vahvistuksen", + "@completedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "isReadyForKeyVerification": "{sender} on valmis avaimen vahvistukseen", + "@isReadyForKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "requestedKeyVerification": "{sender} pyysi avaimen vahvistusta", + "@requestedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "startedKeyVerification": "{sender} aloitti avaimen vahvistuksen", + "@startedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "transparent": "Läpinäkyvä", + "@transparent": {}, + "incomingMessages": "Saapuvat viestit", + "@incomingMessages": {}, + "stickers": "Tarrat", + "@stickers": {}, + "discover": "Tutustu", + "@discover": {}, + "commandHint_ignore": "Jätä huomiotta annettu matrix-tunnus", + "@commandHint_ignore": {}, + "commandHint_unignore": "Kumoa annetun matrix-tunnuksen huomiottajätäminen", + "@commandHint_unignore": {}, + "unreadChatsInApp": "{appname}: {unread} lukematonta keskustelua", + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "noDatabaseEncryption": "Tietokannan salausta ei tueta tällä alustalla", + "@noDatabaseEncryption": {}, + "thereAreCountUsersBlocked": "Tällä hetkellä {count} käyttäjää on estetty.", + "@thereAreCountUsersBlocked": { + "type": "String", + "count": {} + }, + "restricted": "Rajoitettu", + "@restricted": {}, + "knockRestricted": "Koputus rajoitettu", + "@knockRestricted": {}, + "goToSpace": "Siirry tilaan: {space}", + "@goToSpace": { + "type": "String", + "space": {} + }, + "markAsUnread": "Merkitse lukemattomaksi", + "@markAsUnread": {}, + "userLevel": "{level} - Käyttäjä", + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "moderatorLevel": "{level} - Valvoja", + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "adminLevel": "{level} - Järjestelmänvalvoja", + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "changeGeneralChatSettings": "Muuta yleisiä keskusteluasetuksia", + "@changeGeneralChatSettings": {}, + "inviteOtherUsers": "Kutsu muita käyttäjiä tähän pikakeskusteluun", + "@inviteOtherUsers": {}, + "changeTheChatPermissions": "Muuta keskustelulupia", + "@changeTheChatPermissions": {}, + "changeTheVisibilityOfChatHistory": "Muuta pikakeskusteluhistorian näkyvyyttä", + "@changeTheVisibilityOfChatHistory": {}, + "changeTheCanonicalRoomAlias": "Vaihda julkisen pikakeskustelun pääosoite", + "@changeTheCanonicalRoomAlias": {}, + "sendRoomNotifications": "Lähetä @room-ilmoituksia", + "@sendRoomNotifications": {}, + "changeTheDescriptionOfTheGroup": "Muuta keskustelun kuvausta", + "@changeTheDescriptionOfTheGroup": {}, + "chatPermissionsDescription": "Määritä tarvittava tehotaso tietyille toiminnoille tässä pikakeskustelussa. Tehotasot 0, 50 ja 100 edustavat yleensä käyttäjiä, valvoja ja ylläpitäjiä, mutta mikä tahansa porrastus on mahdollinen.", + "@chatPermissionsDescription": {}, + "updateInstalled": "🎉 Päivitys {version} asennettu!", + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "sendCanceled": "Lähetys peruttu", + "@sendCanceled": {}, + "loginWithMatrixId": "Kirjaudu sisään Matrix-tunnuksella", + "@loginWithMatrixId": {}, + "discoverHomeservers": "Tutustu kotipalvelimiin", + "@discoverHomeservers": {}, + "whatIsAHomeserver": "Mikä on kotipalvelin?", + "@whatIsAHomeserver": {}, + "homeserverDescription": "Kaikki tietosi tallennetaan kotipalvelimelle, aivan kuten sähköpostipalveluntarjoaja. Voit valita, mitä kotipalvelinta haluat käyttää, ja silti kommunikoida kaikkien kanssa. Lisätietoja osoitteessa https://matrix.org.", + "@homeserverDescription": {}, + "doesNotSeemToBeAValidHomeserver": "Ei näytä olevan yhteensopiva kotipalvelin. Väärä URL-osoite?", + "@doesNotSeemToBeAValidHomeserver": {}, + "calculatingFileSize": "Lasketaan tiedoston kokoa...", + "@calculatingFileSize": {}, + "prepareSendingAttachment": "Valmistele lähetettävä liite...", + "@prepareSendingAttachment": {}, + "sendingAttachment": "Lähetetään liitettä...", + "@sendingAttachment": {}, + "generatingVideoThumbnail": "Videon pikkukuvan luominen...", + "@generatingVideoThumbnail": {}, + "compressVideo": "Pakataan videota...", + "@compressVideo": {}, + "sendingAttachmentCountOfCount": "Lähetetään {length} pituista liitettä {index}...", + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } + } + }, + "serverLimitReached": "Palvelinraja saavutettu! Odotetaan {seconds} sekuntia...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "oneOfYourDevicesIsNotVerified": "Yhtä laitteistasi ei ole vahvistettu", + "@oneOfYourDevicesIsNotVerified": {}, + "noticeChatBackupDeviceVerification": "Huomautus: Kun yhdistät kaikki laitteesi keskustelun varmuuskopiointiin, ne vahvistetaan automaattisesti.", + "@noticeChatBackupDeviceVerification": {}, + "blur": "Sumeus:", + "@blur": {}, + "opacity": "Läpinäkymättömyys:", + "@opacity": {}, + "setWallpaper": "Aseta taustakuva", + "@setWallpaper": {}, + "manageAccount": "Hallinnoi tiliä", + "@manageAccount": {}, + "noContactInformationProvided": "Palvelin ei ilmoittaa mitään kelvollisia yhteystietoja", + "@noContactInformationProvided": {}, + "contactServerAdmin": "Ota yhteyttä palvelimen ylläpitäjään", + "@contactServerAdmin": {}, + "contactServerSecurity": "Ota yhteyttä palvelimen tietoturvaosastoon", + "@contactServerSecurity": {}, + "supportPage": "Tukisivu", + "@supportPage": {}, + "compress": "Pakkaa", + "@compress": {}, + "boldText": "Lihavoitu teksti", + "@boldText": {}, + "italicText": "Kursivoitu teksti", + "@italicText": {}, + "strikeThrough": "Yliviivaus", + "@strikeThrough": {}, + "pleaseFillOut": "Ole hyvä ja täytä", + "@pleaseFillOut": {}, + "invalidUrl": "Virheellinen URL-osoite", + "@invalidUrl": {}, + "addLink": "Lisää linkki", + "@addLink": {}, + "unableToJoinChat": "Pikakeskusteluun liittyminen ei onnistu. Toinen osapuoli on ehkä jo sulkenut keskustelun.", + "@unableToJoinChat": {}, + "otherPartyNotLoggedIn": "Toinen osapuoli ei ole tällä hetkellä kirjautuneena sisään, joten ei voi vastaanottaa viestejä!", + "@otherPartyNotLoggedIn": {}, + "appWantsToUseForLogin": "Kirjaudu sisään käyttämällä '{server}':ta", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appWantsToUseForLoginDescription": "Annat täten sovellukselle ja verkkosivustolle luvan jakaa tietoja sinusta.", + "@appWantsToUseForLoginDescription": {}, + "waitingForServer": "Odotetaan palvelinta...", + "@waitingForServer": {}, + "appIntroduction": "FluffyChatin avulla voit keskustella ystäviesi kanssa eri pikaviestimien kautta. Lue lisää osoitteesta https://matrix.org tai napauta *Jatka*.", + "@appIntroduction": {}, + "newChatRequest": "📩 Uusi pikakeskustelupyyntö", + "@newChatRequest": {}, + "contentNotificationSettings": "Sisältöilmoitusten asetukset", + "@contentNotificationSettings": {}, + "generalNotificationSettings": "Yleiset ilmoitusasetukset", + "@generalNotificationSettings": {}, + "roomNotificationSettings": "Huoneen ilmoitusten asetukset", + "@roomNotificationSettings": {}, + "userSpecificNotificationSettings": "Käyttäjäkohtaiset ilmoitusten asetukset", + "@userSpecificNotificationSettings": {}, + "otherNotificationSettings": "Muut ilmoitusten asetukset", + "@otherNotificationSettings": {}, + "notificationRuleContainsUserName": "Sisältää käyttäjän nimen", + "@notificationRuleContainsUserName": {}, + "notificationRuleContainsUserNameDescription": "Ilmoittaa käyttäjälle, kun viesti sisältää hänen käyttäjän nimensä.", + "@notificationRuleContainsUserNameDescription": {}, + "notificationRuleMaster": "Mykistä kaikki ilmoitukset", + "@notificationRuleMaster": {}, + "notificationRuleMasterDescription": "Ohittaa kaikki muut säännöt ja poistaa kaikki ilmoitukset käytöstä.", + "@notificationRuleMasterDescription": {}, + "notificationRuleSuppressNotices": "Poista kaikki automatisoidut viestit", + "@notificationRuleSuppressNotices": {}, + "notificationRuleSuppressNoticesDescription": "Poistaa ilmoitukset automatisoiduilta asiakkailta, kuten boteilta.", + "@notificationRuleSuppressNoticesDescription": {}, + "notificationRuleInviteForMe": "Kutsu minulle", + "@notificationRuleInviteForMe": {}, + "notificationRuleInviteForMeDescription": "Ilmoittaa käyttäjälle, kun hänet on kutsuttu huoneeseen.", + "@notificationRuleInviteForMeDescription": {}, + "notificationRuleMemberEvent": "Jäsentapahtuma", + "@notificationRuleMemberEvent": {}, + "notificationRuleMemberEventDescription": "Poistaa jäsenyystapahtumien ilmoitukset.", + "@notificationRuleMemberEventDescription": {}, + "notificationRuleIsUserMention": "Käyttäjän maininta", + "@notificationRuleIsUserMention": {}, + "notificationRuleIsUserMentionDescription": "Ilmoittaa käyttäjälle, kun hänet mainitaan suoraan viestissä.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleContainsDisplayName": "Sisältää näyttönimen", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleContainsDisplayNameDescription": "Ilmoittaa käyttäjälle, kun viesti sisältää hänen näyttönimensä.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsRoomMention": "Huoneen maininta", + "@notificationRuleIsRoomMention": {}, + "notificationRuleIsRoomMentionDescription": "Ilmoittaa käyttäjälle, kun huoneesta on maininta.", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomnotif": "Huoneilmoitus", + "@notificationRuleRoomnotif": {}, + "notificationRuleRoomnotifDescription": "Ilmoittaa käyttäjälle, kun viesti sisältää '@room'.", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleTombstone": "Hautakivi", + "@notificationRuleTombstone": {}, + "notificationRuleTombstoneDescription": "Ilmoittaa käyttäjälle huoneen deaktivointiviesteistä.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleReaction": "Reagointi", + "@notificationRuleReaction": {}, + "notificationRuleReactionDescription": "Poistaa ilmoitukset reaktioista.", + "@notificationRuleReactionDescription": {}, + "notificationRuleRoomServerAcl": "Huonepalvelimen pääsyluettelo", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomServerAclDescription": "Poistaa huonepalvelimen pääsyluetteloiden (ACL) ilmoitukset.", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleSuppressEdits": "Poista muokkaukset", + "@notificationRuleSuppressEdits": {}, + "notificationRuleSuppressEditsDescription": "Poistaa ilmoitukset muokatuista viesteistä.", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleCall": "Soita", + "@notificationRuleCall": {}, + "notificationRuleCallDescription": "Ilmoittaa käyttäjälle soitoista.", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "Kahdenkeskinen salattu huone", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Ilmoittaa käyttäjälle kahdenkeskisissä salatuissa huoneissa olevista viesteistä.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "Kahdenkeskinen huone", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleRoomOneToOneDescription": "Ilmoittaa käyttäjälle kahdenkeskisissä huoneissa olevista viesteistä.", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleMessage": "Viesti", + "@notificationRuleMessage": {}, + "notificationRuleMessageDescription": "Ilmoittaa käyttäjälle yleisistä viesteistä.", + "@notificationRuleMessageDescription": {}, + "notificationRuleEncrypted": "Salattu", + "@notificationRuleEncrypted": {}, + "notificationRuleEncryptedDescription": "Ilmoittaa käyttäjälle salatuissa huoneissa olevista viesteistä.", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsiDescription": "Ilmoittaa käyttäjälle tapahtumista Jitsi-vimpaimesta.", + "@notificationRuleJitsiDescription": {}, + "notificationRuleServerAcl": "Poista tapahtumat palvelimen pääsyluettelosta", + "@notificationRuleServerAcl": {}, + "notificationRuleServerAclDescription": "Poistaa ilmoitukset palvelimen pääsyluettelosta.", + "@notificationRuleServerAclDescription": {}, + "unknownPushRule": "Tuntematon työntösääntö '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "sentVoiceMessage": "🎙️ {duration} - Ääniviesti {sender}:lta", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "Jos poistat tämän ilmoitusasetuksen, sitä ei voi kumota.", + "@deletePushRuleCanNotBeUndone": {}, + "shareKeysWith": "Jaa avaimet...", + "@shareKeysWith": {}, + "shareKeysWithDescription": "Mihin laitteisiin tulisi luottaa, jotta ne voivat lukea viestejäsi salatuissa keskusteluissa?", + "@shareKeysWithDescription": {}, + "crossVerifiedDevicesIfEnabled": "Ristiinvahvistetut laitteet, jos otettu käyttöön", + "@crossVerifiedDevicesIfEnabled": {}, + "crossVerifiedDevices": "Ristiinvahvistetut laitteet", + "@crossVerifiedDevices": {}, + "verifiedDevicesOnly": "Vain vahvistetut laitteet", + "@verifiedDevicesOnly": {}, + "takeAPhoto": "Ota valokuva", + "@takeAPhoto": {}, + "optionalMessage": "(Valinnainen) viesti...", + "@optionalMessage": {}, + "notSupportedOnThisDevice": "Ei tuettu tällä laitteella", + "@notSupportedOnThisDevice": {}, + "hideRedactedMessages": "Piilota valvojan toimesta poistetut viestit", + "@hideRedactedMessages": {}, + "hideRedactedMessagesBody": "Jos viesti jonkun toimesta poistetaan, se ei enää näy pikakeskustelussa.", + "@hideRedactedMessagesBody": {}, + "enterNewChat": "Aloita uusi pikakeskustelu", + "@enterNewChat": {}, "youHaveKnocked": "Olet koputtanut", - "pleaseWaitUntilInvited": "Odota nyt, kunnes joku huoneesta kutsuu sinut.", - "commandHint_logout": "Kirjaudu ulos nykyisestä laitteestasi", - "commandHint_logoutall": "Kirjaudu ulos kaikista aktiivisista laitteista", - "displayNavigationRail": "Näytä navigointiraita mobiilissa", - "customReaction": "Mukautettu reaktio", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Odotathan nyt, kunnes joku huoneesta kutsuu sinut.", + "@pleaseWaitUntilInvited": {}, + "commandHint_logout": "Kirjaudu ulos nykyinen laitteesi", + "@commandHint_logout": {}, + "commandHint_logoutall": "Kirjaudu ulos kaikki aktiiviset laitteet", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Näytä navigointipalkki mobiililaitteella", + "@displayNavigationRail": {}, + "customReaction": "Mukautettu reagointi", + "@customReaction": {}, + "moreEvents": "Lisää tapahtumia", + "@moreEvents": {}, + "declineInvitation": "Hylkää kutsu", + "@declineInvitation": {}, + "noMessagesYet": "Ei vielä viestejä", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Pitkä painallus ääniviestin tallentamiseksi.", + "@longPressToRecordVoiceMessage": {}, + "newSubSpace": "Uusi alitila", + "@newSubSpace": {}, + "moveToDifferentSpace": "Siirry eri tilaan", + "@moveToDifferentSpace": {}, + "removeFromSpaceDescription": "Pikakeskustelu poistetaan tilasta, mutta se näkyy edelleen pikakeskusteluluettelossasi.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} pikakeskustelua", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "{spaces}:jen tilanjäsen", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "{spaces}:jen tilanjäsen saa koputtaa", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Lahjoita", + "@donate": {}, + "startedAPoll": "{username} aloitti kyselyn.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "startPoll": "Aloita kysely", + "@startPoll": {}, + "endPoll": "Lopeta kysely", + "@endPoll": {}, + "answersVisible": "Vastaukset näkyvissä", + "@answersVisible": {}, + "answersHidden": "Vastaukset piilotettu", + "@answersHidden": {}, + "pollQuestion": "Kyselykysymys", + "@pollQuestion": {}, + "answerOption": "Vastausvaihtoehto", + "@answerOption": {}, + "addAnswerOption": "Lisää vastausvaihtoehto", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Salli useita vastauksia", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Kysely on päättynyt", + "@pollHasBeenEnded": {}, + "answersWillBeVisibleWhenPollHasEnded": "Vastaukset näkyvät, kun kysely on päättynyt", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Vastaa ketjussa", + "@replyInThread": {}, + "countVotes": "{count, plural, =1{One vote} other{{count} ääntä}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "countReplies": "{count, plural, =1{One reply} other{{count} vastausta}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Ketju", + "@thread": {}, + "backToMainChat": "Takaisin pääpikakeskusteluun", + "@backToMainChat": {}, + "notificationRuleJitsi": "Jitsi", "writeAMessageLangCodes": "Kirjoita {l1} tai {l2}...", "requests": "Pyyntöjä", "holdForInfo": "Pidä painettuna sanan tiedoista.", @@ -4006,1144 +4604,11 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-02-09 15:30:38.959801", - "@alwaysUse24HourFormat": { - "type": "String", - "placeholders": {} - }, - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@appLockDescription": { - "type": "String", - "placeholders": {} - }, - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" - }, - "participants": { - "type": "int" - } - } - }, - "@noChatsFoundHere": { - "type": "String", - "placeholders": {} - }, - "@joinedChats": { - "type": "String", - "placeholders": {} - }, - "@space": { - "type": "String", - "placeholders": {} - }, - "@spaces": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@globalChatId": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibilityDescription": { - "type": "String", - "placeholders": {} - }, - "@calls": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickers": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickersBody": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessages": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessagesBody": { - "type": "String", - "placeholders": {} - }, - "@hideInvalidOrUnknownMessageFormats": { - "type": "String", - "placeholders": {} - }, - "@block": { - "type": "String", - "placeholders": {} - }, - "@blockedUsers": { - "type": "String", - "placeholders": {} - }, - "@blockListDescription": { - "type": "String", - "placeholders": {} - }, - "@blockUsername": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChats": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChatsBody": { - "type": "String", - "placeholders": {} - }, - "@overview": { - "type": "String", - "placeholders": {} - }, - "@notifyMeFor": { - "type": "String", - "placeholders": {} - }, - "@passwordRecoverySettings": { - "type": "String", - "placeholders": {} - }, - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@presenceStyle": { - "type": "String", - "placeholders": {} - }, - "@presencesToggle": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, - "@youInvitedToBy": { - "type": "String", - "placeholders": { - "alias": { - "type": "String" - } - } - }, - "@invitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@usersMustKnock": { - "type": "String", - "placeholders": {} - }, - "@noOneCanJoin": { - "type": "String", - "placeholders": {} - }, - "@userWouldLikeToChangeTheChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@noPublicLinkHasBeenCreatedYet": { - "type": "String", - "placeholders": {} - }, - "@knock": { - "type": "String", - "placeholders": {} - }, - "@hidePresences": { - "type": "String", - "placeholders": {} - }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, - "@yourGlobalUserIdIs": { - "type": "String", - "placeholders": {} - }, - "@noUsersFoundWithQuery": { - "type": "String", - "placeholders": { - "query": { - "type": "String" - } - } - }, - "@knocking": { - "type": "String", - "placeholders": {} - }, - "@chatCanBeDiscoveredViaSearchOnServer": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@searchChatsRooms": { - "type": "String", - "placeholders": {} - }, - "@nothingFound": { - "type": "String", - "placeholders": {} - }, - "@groupName": { - "type": "String", - "placeholders": {} - }, - "@createGroupAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@groupCanBeFoundViaSearch": { - "type": "String", - "placeholders": {} - }, - "@wrongRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@startConversation": { - "type": "String", - "placeholders": {} - }, - "@commandHint_sendraw": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationTitle": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationBody": { - "type": "String", - "placeholders": {} - }, - "@leaveEmptyToClearStatus": { - "type": "String", - "placeholders": {} - }, - "@select": { - "type": "String", - "placeholders": {} - }, - "@searchForUsers": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourCurrentPassword": { - "type": "String", - "placeholders": {} - }, - "@newPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@passwordsDoNotMatch": { - "type": "String", - "placeholders": {} - }, - "@passwordIsWrong": { - "type": "String", - "placeholders": {} - }, - "@publicLink": { - "type": "String", - "placeholders": {} - }, - "@publicChatAddresses": { - "type": "String", - "placeholders": {} - }, - "@createNewAddress": { - "type": "String", - "placeholders": {} - }, - "@joinSpace": { - "type": "String", - "placeholders": {} - }, - "@publicSpaces": { - "type": "String", - "placeholders": {} - }, - "@addChatOrSubSpace": { - "type": "String", - "placeholders": {} - }, - "@subspace": { - "type": "String", - "placeholders": {} - }, - "@decline": { - "type": "String", - "placeholders": {} - }, - "@thisDevice": { - "type": "String", - "placeholders": {} - }, - "@initAppError": { - "type": "String", - "placeholders": {} - }, - "@userRole": { - "type": "String", - "placeholders": {} - }, - "@minimumPowerLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "String" - } - } - }, - "@searchIn": { - "type": "String", - "placeholders": { - "chat": { - "type": "String" - } - } - }, - "@searchMore": { - "type": "String", - "placeholders": {} - }, - "@gallery": { - "type": "String", - "placeholders": {} - }, - "@files": { - "type": "String", - "placeholders": {} - }, - "@databaseBuildErrorBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@sessionLostBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@restoreSessionBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@forwardMessageTo": { - "type": "String", - "placeholders": { - "roomName": { - "type": "String" - } - } - }, - "@sendReadReceipts": { - "type": "String", - "placeholders": {} - }, - "@sendTypingNotificationsDescription": { - "type": "String", - "placeholders": {} - }, - "@sendReadReceiptsDescription": { - "type": "String", - "placeholders": {} - }, - "@formattedMessages": { - "type": "String", - "placeholders": {} - }, - "@formattedMessagesDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUser": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUserDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDevice": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherDeviceDescription": { - "type": "String", - "placeholders": {} - }, - "@acceptedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@canceledKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@completedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@isReadyForKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@requestedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@startedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@transparent": { - "type": "String", - "placeholders": {} - }, - "@incomingMessages": { - "type": "String", - "placeholders": {} - }, - "@stickers": { - "type": "String", - "placeholders": {} - }, - "@discover": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ignore": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unignore": { - "type": "String", - "placeholders": {} - }, - "@unreadChatsInApp": { - "type": "String", - "placeholders": { - "appname": { - "type": "String" - }, - "unread": { - "type": "String" - } - } - }, - "@noDatabaseEncryption": { - "type": "String", - "placeholders": {} - }, - "@thereAreCountUsersBlocked": { - "type": "String", - "placeholders": { - "count": {} - } - }, - "@restricted": { - "type": "String", - "placeholders": {} - }, - "@knockRestricted": { - "type": "String", - "placeholders": {} - }, - "@goToSpace": { - "type": "String", - "placeholders": { - "space": {} - } - }, - "@markAsUnread": { - "type": "String", - "placeholders": {} - }, - "@userLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@moderatorLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@adminLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@changeGeneralChatSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsers": { - "type": "String", - "placeholders": {} - }, - "@changeTheChatPermissions": { - "type": "String", - "placeholders": {} - }, - "@changeTheVisibilityOfChatHistory": { - "type": "String", - "placeholders": {} - }, - "@changeTheCanonicalRoomAlias": { - "type": "String", - "placeholders": {} - }, - "@sendRoomNotifications": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescriptionOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "@chatPermissionsDescription": { - "type": "String", - "placeholders": {} - }, - "@updateInstalled": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - } - } - }, - "@changelog": { - "type": "String", - "placeholders": {} - }, - "@sendCanceled": { - "type": "String", - "placeholders": {} - }, - "@loginWithMatrixId": { - "type": "String", - "placeholders": {} - }, - "@discoverHomeservers": { - "type": "String", - "placeholders": {} - }, - "@whatIsAHomeserver": { - "type": "String", - "placeholders": {} - }, - "@homeserverDescription": { - "type": "String", - "placeholders": {} - }, - "@doesNotSeemToBeAValidHomeserver": { - "type": "String", - "placeholders": {} - }, - "@calculatingFileSize": { - "type": "String", - "placeholders": {} - }, - "@prepareSendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@generatingVideoThumbnail": { - "type": "String", - "placeholders": {} - }, - "@compressVideo": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachmentCountOfCount": { - "type": "integer", - "placeholders": { - "index": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@oneOfYourDevicesIsNotVerified": { - "type": "String", - "placeholders": {} - }, - "@noticeChatBackupDeviceVerification": { - "type": "String", - "placeholders": {} - }, - "@continueText": { - "type": "String", - "placeholders": {} - }, - "@welcomeText": { - "type": "String", - "placeholders": {} - }, - "@blur": { - "type": "String", - "placeholders": {} - }, - "@opacity": { - "type": "String", - "placeholders": {} - }, - "@setWallpaper": { - "type": "String", - "placeholders": {} - }, - "@manageAccount": { - "type": "String", - "placeholders": {} - }, - "@noContactInformationProvided": { - "type": "String", - "placeholders": {} - }, - "@contactServerAdmin": { - "type": "String", - "placeholders": {} - }, - "@contactServerSecurity": { - "type": "String", - "placeholders": {} - }, - "@supportPage": { - "type": "String", - "placeholders": {} - }, - "@serverInformation": { - "type": "String", - "placeholders": {} - }, - "@name": { - "type": "String", - "placeholders": {} - }, - "@version": { - "type": "String", - "placeholders": {} - }, - "@website": { - "type": "String", - "placeholders": {} - }, - "@compress": { - "type": "String", - "placeholders": {} - }, - "@boldText": { - "type": "String", - "placeholders": {} - }, - "@italicText": { - "type": "String", - "placeholders": {} - }, - "@strikeThrough": { - "type": "String", - "placeholders": {} - }, - "@pleaseFillOut": { - "type": "String", - "placeholders": {} - }, - "@invalidUrl": { - "type": "String", - "placeholders": {} - }, - "@addLink": { - "type": "String", - "placeholders": {} - }, - "@unableToJoinChat": { - "type": "String", - "placeholders": {} - }, - "@previous": { - "type": "String", - "placeholders": {} - }, - "@otherPartyNotLoggedIn": { - "type": "String", - "placeholders": {} - }, - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@appWantsToUseForLoginDescription": { - "type": "String", - "placeholders": {} - }, - "@open": { - "type": "String", - "placeholders": {} - }, - "@waitingForServer": { - "type": "String", - "placeholders": {} - }, - "@appIntroduction": { - "type": "String", - "placeholders": {} - }, - "@newChatRequest": { - "type": "String", - "placeholders": {} - }, - "@contentNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@generalNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@roomNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@userSpecificNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@otherNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMaster": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMasterDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNotices": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNoticesDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMe": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEvent": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEventDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReaction": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReactionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEditsDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCall": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCallDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessage": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncrypted": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, + "@@last_modified": "2026-02-05 10:09:16.239112", "@notificationRuleJitsi": { "type": "String", "placeholders": {} }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@deletePushRuleCanNotBeUndone": { - "type": "String", - "placeholders": {} - }, - "@more": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWith": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@allDevices": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@takeAPhoto": { - "type": "String", - "placeholders": {} - }, - "@recordAVideo": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@enterNewChat": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, - "@youHaveKnocked": { - "type": "String", - "placeholders": {} - }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -11360,6 +10825,8 @@ "congratulations": "Onnittelut!", "anotherRound": "Toinen kierros", "noActivityRequest": "Ei nykyistä aktiviteettipyyntöä.", + "quit": "Poistu", + "congratulationsYouveCompletedPractice": "Onnittelut! Olet suorittanut harjoitussession.", "mustHave10Words": "Sinulla on oltava vähintään 10 sanastoa harjoiteltavaksi. Yritä puhua ystävän tai Pangea Botin kanssa löytääksesi lisää!", "botSettings": "Bottiasetukset", "activitySettingsOverrideWarning": "Kieli ja kielitaso määräytyvät aktiviteettisuunnitelman mukaan", @@ -11408,6 +10875,14 @@ "type": "String", "placeholders": {} }, + "@quit": { + "type": "String", + "placeholders": {} + }, + "@congratulationsYouveCompletedPractice": { + "type": "String", + "placeholders": {} + }, "@mustHave10Words": { "type": "String", "placeholders": {} @@ -11583,11 +11058,6 @@ "type": "String", "placeholders": {} }, - "aboutMeHint": "Tietoa minusta", - "@aboutMeHint": { - "type": "String", - "placeholders": {} - }, "grammarCopyPOSidiom": "Idioomi", "grammarCopyPOSphrasalv": "Fraasiverbi", "grammarCopyPOScompn": "Yhdistelmä", @@ -11602,90 +11072,5 @@ "@grammarCopyPOScompn": { "type": "String", "placeholders": {} - }, - "perfectPractice": "Täydellinen harjoittelu!", - "greatPractice": "Loistava harjoittelu!", - "usedNoHints": "Hyvä työ, ettet käyttänyt mitään vihjeitä!", - "youveCompletedPractice": "Olet suorittanut harjoituksen, jatka samaan malliin parantuaksesi!", - "@perfectPractice": { - "type": "String", - "placeholders": {} - }, - "@greatPractice": { - "type": "String", - "placeholders": {} - }, - "@usedNoHints": { - "type": "String", - "placeholders": {} - }, - "@youveCompletedPractice": { - "type": "String", - "placeholders": {} - }, - "changeEmail": "Vaihda sähköpostiosoite", - "withTheseAddressesDescription": "Näiden sähköpostiosoitteiden avulla voit kirjautua sisään, palauttaa salasanasi ja hallita tilauksia.", - "noAddressDescription": "Et ole vielä lisännyt mitään sähköpostiosoitteita.", - "@changeEmail": { - "type": "String", - "placeholders": {} - }, - "@withTheseAddressesDescription": { - "type": "String", - "placeholders": {} - }, - "@noAddressDescription": { - "type": "String", - "placeholders": {} - }, - "spanTypeGrammar": "Kielioppi", - "spanTypeWordChoice": "Sananvalinta", - "spanTypeSpelling": "Oikeinkirjoitus", - "spanTypePunctuation": "Välihuomautukset", - "spanTypeStyle": "Tyyli", - "spanTypeFluency": "Sujuvuus", - "spanTypeAccents": "Aksentit", - "spanTypeCapitalization": "Isot kirjaimet", - "spanTypeCorrection": "Korjaus", - "spanFeedbackTitle": "Ilmoita korjausongelmasta", - "@spanTypeGrammar": { - "type": "String", - "placeholders": {} - }, - "@spanTypeWordChoice": { - "type": "String", - "placeholders": {} - }, - "@spanTypeSpelling": { - "type": "String", - "placeholders": {} - }, - "@spanTypePunctuation": { - "type": "String", - "placeholders": {} - }, - "@spanTypeStyle": { - "type": "String", - "placeholders": {} - }, - "@spanTypeFluency": { - "type": "String", - "placeholders": {} - }, - "@spanTypeAccents": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCapitalization": { - "type": "String", - "placeholders": {} - }, - "@spanTypeCorrection": { - "type": "String", - "placeholders": {} - }, - "@spanFeedbackTitle": { - "type": "String", - "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index 2a6022855..dc6dc3774 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -1428,7 +1428,6 @@ "report": "iulat", "signInWithPassword": "Mag-sign in gamit ang password", "pleaseTryAgainLaterOrChooseDifferentServer": "Mangyaring subukan muli mamaya o pumili ng ibang server.", - "signInWith": "Mag-sign in gamit ang {provider}", "profileNotFound": "Hindi mahanap ang user sa server. Maaaring may problema sa koneksyon o hindi umiiral ang user.", "setTheme": "Itakda ang tema:", "setColorTheme": "Itakda ang kulay na tema:", @@ -4524,14 +4523,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -4942,9 +4933,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12041,4 +12030,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 953cb7541..adce6e7c9 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -2518,9 +2518,9 @@ "@tryAgain": {}, "blockedUsers": "Utilisateurs/trices bloqués", "@blockedUsers": {}, - "redactMessageDescription": "Le message sera modifié pour tous les participants de cette conversation. Il n'est pas possible de revenir en arrière.", + "redactMessageDescription": "Le message sera supprimé pour tous les participants de cette conversation. Il n'est pas possible de revenir en arrière.", "@redactMessageDescription": {}, - "redactedBy": "Modifié par {username}", + "redactedBy": "Supprimé par {username}", "@redactedBy": { "type": "String", "placeholders": { @@ -2531,7 +2531,7 @@ }, "directChat": "Discussion directe", "@directChat": {}, - "optionalRedactReason": "(Facultatif) Raison de la modification de ce message...", + "optionalRedactReason": "(Facultatif) Raison de la suppression de ce message...", "@optionalRedactReason": {}, "subspace": "Sous-espace", "@subspace": {}, @@ -2700,7 +2700,7 @@ "@calls": {}, "customEmojisAndStickers": "Émoticônes et autocollants personnalisés", "@customEmojisAndStickers": {}, - "hideRedactedMessages": "Cacher les messages édités", + "hideRedactedMessages": "Cacher les messages supprimés", "@hideRedactedMessages": {}, "pleaseEnterYourCurrentPassword": "Veuillez saisir votre mot de passe actuel", "@pleaseEnterYourCurrentPassword": {}, @@ -2710,7 +2710,7 @@ "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, - "hideRedactedMessagesBody": "Si quelqu'un modifie un message, celui-ci ne sera plus visible dans la discussion.", + "hideRedactedMessagesBody": "Si quelqu'un supprime un message, celui-ci ne sera plus visible dans la discussion.", "@hideRedactedMessagesBody": {}, "customEmojisAndStickersBody": "Ajoutez ou partagez des émoticônes ou autocollants personnalisés qui peuvent être utilisés dans n'importe quelle discussion.", "@customEmojisAndStickersBody": {}, @@ -2722,7 +2722,7 @@ "@hideInvalidOrUnknownMessageFormats": {}, "messagesStyle": "Messages :", "@messagesStyle": {}, - "redactedByBecause": "Modifié par {username} car : \"{reason}\"", + "redactedByBecause": "Supprimé par {username} car : \"{reason}\"", "@redactedByBecause": { "type": "String", "placeholders": { @@ -2847,15 +2847,6 @@ "@restricted": {}, "knockRestricted": "Frapper à la porte limité", "@knockRestricted": {}, - "signInWith": "Se connecter avec {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "groupCanBeFoundViaSearch": "Le groupe peut être trouvé via la recherche", "@groupCanBeFoundViaSearch": {}, "groupName": "Nom du groupe", @@ -3089,27 +3080,86 @@ }, "loginWithMatrixId": "Connexion avec l'identifiant Matrix", "@loginWithMatrixId": {}, - "setCustomPermissionLevel": "Définir le niveau de permission personnalisé", - "setPermissionsLevelDescription": "Veuillez choisir un rôle prédéfini ci-dessous ou entrer un niveau de permission personnalisé entre 0 et 100.", + "setCustomPermissionLevel": "Définir un niveau d’autorisation", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Veuillez choisir un rôle prédéfini ci-dessous ou saisir un niveau d’autorisation entre 0 et 100.", + "@setPermissionsLevelDescription": {}, "ignoreUser": "Ignorer l'utilisateur", - "normalUser": "Utilisateur normal", - "commandHint_roomupgrade": "Mettre à niveau cette salle vers la version de salle donnée", - "checkList": "Liste de vérification", - "countInvited": "{count} invité(s)", + "@ignoreUser": {}, + "normalUser": "Utilisateur standard", + "@normalUser": {}, + "checkList": "Check-list", + "@checkList": {}, + "countInvited": "{count} invité(e/s)", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, "sendImages": "Envoyer {count} image(s)", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "homeserverDescription": "Toutes vos données sont stockées sur le serveur de votre fournisseur matrix, comme chez un fournisseur d'e-mails. Vous pouvez choisir le serveur que vous souhaitez utiliser, tout en pouvant communiquer avec tout le monde. Pour en savoir plus, rendez-vous sur https://matrix.org.", + "@homeserverDescription": {}, + "calculatingFileSize": "Calcul en cours...", + "@calculatingFileSize": {}, + "prepareSendingAttachment": "Préparation à l'envoi...", + "@prepareSendingAttachment": {}, + "sendingAttachment": "Envoi en cours...", + "@sendingAttachment": {}, + "generatingVideoThumbnail": "Génération de la vignette...", + "@generatingVideoThumbnail": {}, + "compressVideo": "Compression de la vidéo...", + "@compressVideo": {}, + "sendingAttachmentCountOfCount": "Envoi {index} sur {length}...", + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } + } + }, + "serverLimitReached": "Nombre de tentatives dépassé! Patientez {seconds} secondes...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "oneOfYourDevicesIsNotVerified": "Un de vos appareils n'est pas vérifié", + "@oneOfYourDevicesIsNotVerified": {}, + "customReaction": "Réaction", + "@customReaction": {}, + "declineInvitation": "Refuser l'invitation", + "@declineInvitation": {}, + "invalidUrl": "Adresse incorrecte", + "@invalidUrl": {}, + "addAnswerOption": "Ajouter une réponse", + "@addAnswerOption": {}, + "addLink": "Ajouter un lien", + "@addLink": {}, + "allowMultipleAnswers": "Autoriser plusieurs réponses", + "@allowMultipleAnswers": {}, + "commandHint_roomupgrade": "Mettre à niveau cette salle vers la version de salle donnée", "sendRoomNotifications": "Envoyer une notification @room", "discoverHomeservers": "Découvrir les serveurs principaux", "whatIsAHomeserver": "Qu'est-ce qu'un serveur principal ?", - "homeserverDescription": "Toutes vos données sont stockées sur le serveur principal, tout comme un fournisseur de messagerie. Vous pouvez choisir le serveur principal que vous souhaitez utiliser, tout en pouvant communiquer avec tout le monde. En savoir plus sur https://matrix.org.", "doesNotSeemToBeAValidHomeserver": "Il ne semble pas s'agir d'un serveur principal compatible. Mauvaise URL ?", - "calculatingFileSize": "Calcul de la taille du fichier...", - "prepareSendingAttachment": "Préparer l'envoi de la pièce jointe...", - "sendingAttachment": "Envoi de la pièce jointe...", - "generatingVideoThumbnail": "Génération de la miniature vidéo...", - "compressVideo": "Compression de la vidéo...", - "sendingAttachmentCountOfCount": "Envoi de la pièce jointe {index} sur {length}...", - "serverLimitReached": "Limite du serveur atteinte ! Attente de {seconds} secondes...", - "oneOfYourDevicesIsNotVerified": "L'un de vos appareils n'est pas vérifié", "noticeChatBackupDeviceVerification": "Remarque : lorsque vous connectez tous vos appareils à la sauvegarde de chat, ils sont automatiquement vérifiés.", "continueText": "Continuer", "welcomeText": "Hé Hé 👋 C'est FluffyChat. Vous pouvez vous connecter à n'importe quel serveur domestique, compatible avec https://matrix.org. Et ensuite discuter avec n'importe qui. C'est un réseau de messagerie décentralisé énorme !", @@ -3130,8 +3180,6 @@ "italicText": "Texte en italique", "strikeThrough": "Barré", "pleaseFillOut": "Veuillez remplir", - "invalidUrl": "URL invalide", - "addLink": "Ajouter un lien", "unableToJoinChat": "Impossible de rejoindre la conversation. Peut-être que l'autre partie a déjà fermé la discussion.", "previous": "Précédent", "otherPartyNotLoggedIn": "L'autre partie n'est pas actuellement connectée et ne peut donc pas recevoir de messages !", @@ -3207,7 +3255,6 @@ "commandHint_logout": "Déconnectez votre appareil actuel", "commandHint_logoutall": "Déconnectez tous les appareils actifs", "displayNavigationRail": "Afficher le rail de navigation sur mobile", - "customReaction": "Réaction personnalisée", "writeAMessageLangCodes": "Tapez en {l1} ou {l2}...", "requests": "Demandes", "holdForInfo": "Cliquez et maintenez pour obtenir des infos sur le mot.", @@ -4335,46 +4382,10 @@ "inviteYourFriends": "Invitez vos amis", "playWithAI": "Jouez avec l'IA pour l'instant", "courseStartDesc": "Pangea Bot est prêt à partir à tout moment !\n\n...mais apprendre est meilleur avec des amis !", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, "@commandHint_roomupgrade": { "type": "String", "placeholders": {} }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, "@sendRoomNotifications": { "type": "String", "placeholders": {} @@ -4387,57 +4398,10 @@ "type": "String", "placeholders": {} }, - "@homeserverDescription": { - "type": "String", - "placeholders": {} - }, "@doesNotSeemToBeAValidHomeserver": { "type": "String", "placeholders": {} }, - "@calculatingFileSize": { - "type": "String", - "placeholders": {} - }, - "@prepareSendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@generatingVideoThumbnail": { - "type": "String", - "placeholders": {} - }, - "@compressVideo": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachmentCountOfCount": { - "type": "integer", - "placeholders": { - "index": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@oneOfYourDevicesIsNotVerified": { - "type": "String", - "placeholders": {} - }, "@noticeChatBackupDeviceVerification": { "type": "String", "placeholders": {} @@ -4518,14 +4482,6 @@ "type": "String", "placeholders": {} }, - "@invalidUrl": { - "type": "String", - "placeholders": {} - }, - "@addLink": { - "type": "String", - "placeholders": {} - }, "@unableToJoinChat": { "type": "String", "placeholders": {} @@ -4841,10 +4797,6 @@ "type": "String", "placeholders": {} }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -11389,4 +11341,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index 0f52fbca2..7de1b2a89 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -769,7 +769,7 @@ }, "chatHasBeenAddedToThisSpace": "Cuireadh comhrá leis an spás seo", "@chatHasBeenAddedToThisSpace": {}, - "chatBackupDescription": "Tá do sheanteachtaireachtaí slán le eochair athshlánaithe. Le do thoil déan cinnte nach gcaillfidh tú é.", + "chatBackupDescription": "Tá do theachtaireachtaí slán le heochair aisghabhála. Déan cinnte nach gcaillfidh tú í.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -2146,7 +2146,7 @@ "@noOtherDevicesFound": {}, "profileNotFound": "Níorbh fhéidir an t-úsáideoir a aimsiú ar an bhfreastalaí. B'fhéidir go bhfuil fadhb nasctha ann nó nach bhfuil an t-úsáideoir ann.", "@profileNotFound": {}, - "inviteGroupChat": "📨 Tabhair cuireadh comhrá grúpa", + "inviteGroupChat": "📨 Cuireadh chuig comhrá grúpa", "@inviteGroupChat": {}, "knocking": "Cnagadh", "@knocking": {}, @@ -2482,16 +2482,7 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Bain triail eile as níos déanaí nó roghnaigh freastalaí eile.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Sínigh isteach le {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, - "invitePrivateChat": "📨 Tabhair cuireadh comhrá príobháideach", + "invitePrivateChat": "📨 Cuireadh chuig comhrá príobháideach", "@invitePrivateChat": {}, "wrongPinEntered": "Tháinig biorán mícheart isteach! Bain triail eile as i {seconds} soicind...", "@wrongPinEntered": { @@ -3385,7 +3376,142 @@ "@commandHint_logout": {}, "commandHint_logoutall": "Logáil amach gach gléas gníomhach", "@commandHint_logoutall": {}, - "customReaction": "Freagairt shaincheaptha", + "customReaction": "Imoibriú saincheaptha", + "@customReaction": {}, + "moreEvents": "Tuilleadh imeachtaí", + "@moreEvents": {}, + "declineInvitation": "Diúltaigh don chuireadh", + "@declineInvitation": {}, + "noMessagesYet": "Gan aon teachtaireachtaí fós", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Brúigh go fada chun teachtaireacht gutha a thaifeadadh.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Sos", + "@pause": {}, + "resume": "Atosú", + "@resume": {}, + "newSubSpace": "Fo-spás nua", + "@newSubSpace": {}, + "moveToDifferentSpace": "Bog go spás difriúil", + "@moveToDifferentSpace": {}, + "moveUp": "Bog suas", + "@moveUp": {}, + "moveDown": "Bog síos", + "@moveDown": {}, + "removeFromSpaceDescription": "Bainfear an comhrá as an spás ach beidh sé fós le feiceáil i do liosta comhrá.", + "@removeFromSpaceDescription": {}, + "countChats": "comhráite {chats}", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Ball spáis de {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Is féidir le ball spáis de {spaces} cnagadh", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Tabhair Síntiús", + "@donate": {}, + "startedAPoll": "Chuir {username} tús le pobalbhreith.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Polaitíocht", + "@poll": {}, + "startPoll": "Tosaigh pobalbhreith", + "@startPoll": {}, + "endPoll": "Deireadh leis an pobalbhreith", + "@endPoll": {}, + "answersVisible": "Freagraí le feiceáil", + "@answersVisible": {}, + "answersHidden": "Freagraí i bhfolach", + "@answersHidden": {}, + "pollQuestion": "Ceist pobalbhreithe", + "@pollQuestion": {}, + "answerOption": "Rogha freagartha", + "@answerOption": {}, + "addAnswerOption": "Cuir rogha freagra leis", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Ceadaigh freagraí iolracha", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Tá deireadh leis an pobalbhreith", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Vóta amháin} other{{count} vótaí}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Beidh freagraí le feiceáil nuair a bheidh an pobalbhreith thart", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Freagra sa snáithe", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Freagra amháin} other{{count} freagraí}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Snáithe", + "@thread": {}, + "backToMainChat": "Ar ais chuig an bpríomhchomhrá", + "@backToMainChat": {}, + "saveChanges": "Sábháil athruithe", + "@saveChanges": {}, + "createSticker": "Cruthaigh greamán nó emoji", + "@createSticker": {}, + "useAsSticker": "Úsáid mar ghreamán", + "@useAsSticker": {}, + "useAsEmoji": "Úsáid mar emoji", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Tá ainm an phacáiste greamán ann cheana féin", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Pacáiste greamán nua", + "@newStickerPack": {}, + "stickerPackName": "Ainm an phacáiste greamán", + "@stickerPackName": {}, + "attribution": "Atribution", + "@attribution": {}, + "skipChatBackup": "Seachain cúltaca comhrá", + "@skipChatBackup": {}, + "skipChatBackupWarning": "An bhfuil tú cinnte? Mura gcumasaíonn tú an cúltaca comhrá, d’fhéadfá rochtain ar do theachtaireachtaí a chailleadh má athraíonn tú do ghléas.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Ag lódáil teachtaireachtaí", + "@loadingMessages": {}, + "setupChatBackup": "Socraigh cúltaca comhrá", + "@setupChatBackup": {}, + "changedTheChatDescription": "D'athraigh {username} cur síos an chomhrá", + "@changedTheChatDescription": {}, + "changedTheChatName": "D'athraigh {username} ainm an chomhrá", + "@changedTheChatName": {}, "writeAMessageLangCodes": "Clóscríobh i {l1} nó {l2}...", "requests": "Iarratais", "holdForInfo": "Bain triail as agus coinnigh síos le haghaidh eolas faoin bhfocal.", @@ -3427,7 +3553,6 @@ "updateLanguage": "Mo chuid teangacha", "whatLanguageYouWantToLearn": "Cén teanga ar mhaith leat foghlaim?", "whatIsYourBaseLanguage": "Cén teanga bhunúsach atá agat?", - "saveChanges": "Sábháil na hathruithe", "publicProfileTitle": "Ceadaigh go bhféadfaidh daoine mo phróifíl a aimsiú sa chuardach", "publicProfileDesc": "Trí chumasú, ligfidh tú do dhaoine eile do phróifíl a fháil sa bharra cuardaigh domhanda agus iarratais a sheoladh chun comhrá. Sa phointe seo, is féidir leat glacadh nó diúltú leis an iarratas.", "errorDisableIT": "Tá cabhair aistriúcháin dícheangailte.", @@ -4515,10 +4640,6 @@ "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", "@@last_modified": "2026-02-09 15:32:44.231605", - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4694,10 +4815,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11063,4 +11180,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index 791b54790..ae12b0870 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -381,7 +381,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "As mensaxes antigas están protexidas cunha chave de recuperación. Pon coidado e non a perdas.", + "chatBackupDescription": "As mensaxes están protexidas cunha clave de recuperación. Pon coidado e non a perdas.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -2042,7 +2042,7 @@ "type": "String", "placeholders": {} }, - "wipeChatBackup": "Queres eliminar a copia de apoio da conversa e crear unha nova chave de recuperación?", + "wipeChatBackup": "Queres eliminar a copia de apoio e crear unha nova chave de recuperación?", "@wipeChatBackup": { "type": "String", "placeholders": {} @@ -2510,15 +2510,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Inténtao máis tarde ou elixe un servidor diferente.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Accede con {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Non é un ficheiro de imaxe.", "@notAnImage": {}, "importNow": "Importar agora", @@ -3069,7 +3060,7 @@ }, "sendCanceled": "Cancelouse o envío", "@sendCanceled": {}, - "noChatsFoundHere": "Sen charlas por aquí. Comeza unha nova conversa con alguén premendo no botón de abaixo. ⤵️", + "noChatsFoundHere": "Aínda non hai conversas. Comeza a conversar con alguén premendo no botón de abaixo. ⤵️", "@noChatsFoundHere": {}, "discoverHomeservers": "Atopar servidores", "@discoverHomeservers": {}, @@ -3351,7 +3342,7 @@ "@pleaseWaitUntilInvited": {}, "youHaveKnocked": "Petaches á porta", "@youHaveKnocked": {}, - "sentVoiceMessage": "🎙️ {duration} - Mensaxe de voz de {sender}", + "sentVoiceMessage": "🎙️ {duration} - Mensaxe de voz de {sender}", "@sentVoiceMessage": { "type": "String", "placeholders": { @@ -3376,9 +3367,146 @@ "@commandHint_logoutall": {}, "commandHint_logout": "Fechar a sesión no dispositivo actual", "@commandHint_logout": {}, - "checkList": "Lista de verificación", - "displayNavigationRail": "Mostrar rail de navegación en móbil", + "moreEvents": "Máis eventos", + "@moreEvents": {}, "customReaction": "Reacción personalizada", + "@customReaction": {}, + "declineInvitation": "Rexeitar o convite", + "@declineInvitation": {}, + "noMessagesYet": "Aínda non hai mensaxes", + "@noMessagesYet": {}, + "checkList": "Comprobar lista", + "@checkList": {}, + "displayNavigationRail": "Mostrar carril de navegación nos móbiles", + "@displayNavigationRail": {}, + "longPressToRecordVoiceMessage": "Pulsación longa para gravar mensaxe de voz.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Deter", + "@pause": {}, + "resume": "Continuar", + "@resume": {}, + "newSubSpace": "Novo sub espazo", + "@newSubSpace": {}, + "moveToDifferentSpace": "Mover a outro espazo", + "@moveToDifferentSpace": {}, + "moveUp": "Mover arriba", + "@moveUp": {}, + "moveDown": "Mover abaixo", + "@moveDown": {}, + "removeFromSpaceDescription": "Vaise quitar a conversa do espazo pero seguirá aparecendo na túa lista de conversas.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} conversas", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "donate": "Doar", + "@donate": {}, + "spaceMemberOf": "Participa no espazo {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Os membros de {spaces} poden petar á porta", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "startedAPoll": "{username} publicou unha enquisa.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Enquisa", + "@poll": {}, + "startPoll": "Publicar enquisa", + "@startPoll": {}, + "endPoll": "Finalizar enquisa", + "@endPoll": {}, + "answersVisible": "Resultados visibles", + "@answersVisible": {}, + "answersHidden": "Non se ven os resultados", + "@answersHidden": {}, + "pollQuestion": "Pregunta da enquisa", + "@pollQuestion": {}, + "answerOption": "Opción de resposta", + "@answerOption": {}, + "addAnswerOption": "Engadir opción", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Permitir varias respostas", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Rematou a enquisa", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Un voto} other{{count} votos}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "As respostas son visibles ao finalizar a enquisa", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Responder ao fío", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Unha resposta} other{{count} respostas}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Fío", + "@thread": {}, + "backToMainChat": "Volver á conversa principal", + "@backToMainChat": {}, + "attribution": "Atribución", + "@attribution": {}, + "saveChanges": "Gardar cambios", + "@saveChanges": {}, + "createSticker": "Crear adhesivo ou emoji", + "@createSticker": {}, + "useAsSticker": "Usar como adhesivo", + "@useAsSticker": {}, + "useAsEmoji": "Usar como emoji", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Xa existe ese nome de paquete de adhesivos", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Novo paquete de adhesivos", + "@newStickerPack": {}, + "stickerPackName": "Nome do paquete de adhesivos", + "@stickerPackName": {}, + "skipChatBackup": "Omitir copia da conversa", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Tes certeza? A non activar a copia de apoio da conversa poderías perder o acceso ás mensaxes se cambias de dispositivo.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Cargando mensaxes", + "@loadingMessages": {}, + "setupChatBackup": "Configurar copia de apoio", + "@setupChatBackup": {}, + "changedTheChatDescription": "{username} cambiou a descrición da sala", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} cambiou o nome da sala", + "@changedTheChatName": {}, "writeAMessageLangCodes": "Escribe en {l1} ou {l2}...", "requests": "Solicitudes", "holdForInfo": "Fai clic e mantén para obter información sobre a palabra.", @@ -3420,7 +3548,6 @@ "updateLanguage": "As miñas linguas", "whatLanguageYouWantToLearn": "Que linguaxe queres aprender?", "whatIsYourBaseLanguage": "Cal é a tua linguaxe base?", - "saveChanges": "Gardar cambios", "publicProfileTitle": "Permitir que meu perfil se atope na busca", "publicProfileDesc": "Ao activalo, permites que outros usuarios atopen o teu perfil na barra de busca global e envien solicitudes para chatear. Neste momento, podes escoller aceptar ou denegar a solicitude.", "errorDisableIT": "A axuda de tradución está desactivada.", @@ -4506,18 +4633,6 @@ "inviteYourFriends": "Invita aos teus amigos", "playWithAI": "Xoga con IA por agora", "courseStartDesc": "O Pangea Bot está listo para comezar en calquera momento!\n\n...pero aprender é mellor con amigos!", - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4693,10 +4808,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11062,4 +11173,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index 20c6d0953..5264fb2cb 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1428,7 +1428,7 @@ }, "notAnImage": "הקובץ אינו תמונה.", "@notAnImage": {}, - "alwaysUse24HourFormat": "שגוי", + "alwaysUse24HourFormat": "", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -1750,7 +1750,6 @@ "report": "דווח", "signInWithPassword": "התחבר עם סיסמה", "pleaseTryAgainLaterOrChooseDifferentServer": "אנא נסה שוב מאוחר יותר או בחר שרת אחר.", - "signInWith": "התחבר עם {provider}", "profileNotFound": "לא נמצא משתמש בשרת. ייתכן שיש בעיית חיבור או שהמשתמש לא קיים.", "setTheme": "הגדר נושא:", "setColorTheme": "הגדר נושא צבע:", @@ -4588,14 +4587,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5006,9 +4997,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12114,4 +12103,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index 2c51d432c..32e26d17c 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -31,10 +31,7 @@ "type": "String", "placeholders": {} }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, + "@jumpToLastReadMessage": {}, "@allRooms": { "type": "String", "placeholders": {} @@ -43,22 +40,13 @@ "type": "String", "placeholders": {} }, - "@commandHint_cuddle": { - "type": "String", - "placeholders": {} - }, + "@commandHint_cuddle": {}, "@chats": { "type": "String", "placeholders": {} }, - "@widgetVideo": { - "type": "String", - "placeholders": {} - }, - "@dismiss": { - "type": "String", - "placeholders": {} - }, + "@widgetVideo": {}, + "@dismiss": {}, "@unknownDevice": { "type": "String", "placeholders": {} @@ -75,10 +63,7 @@ "type": "String", "placeholders": {} }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, + "@reportErrorDescription": {}, "@directChats": { "type": "String", "placeholders": {} @@ -95,10 +80,7 @@ } } }, - "@addAccount": { - "type": "String", - "placeholders": {} - }, + "@addAccount": {}, "@close": { "type": "String", "placeholders": {} @@ -107,10 +89,7 @@ "type": "String", "placeholders": {} }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, + "@chatHasBeenAddedToThisSpace": {}, "@reply": { "type": "String", "placeholders": {} @@ -123,10 +102,7 @@ "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersion": {}, "@device": { "type": "String", "placeholders": {} @@ -139,10 +115,7 @@ "type": "String", "description": "Usage hint for the command /html" }, - "@widgetJitsi": { - "type": "String", - "placeholders": {} - }, + "@widgetJitsi": {}, "@youAreNoLongerParticipatingInThisChat": { "type": "String", "placeholders": {} @@ -151,26 +124,14 @@ "type": "String", "placeholders": {} }, - "@messageType": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorLong": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, + "@messageType": {}, + "@indexedDbErrorLong": {}, + "@oneClientLoggedOut": {}, "@toggleMuted": { "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersionLong": {}, "@kicked": { "type": "String", "placeholders": { @@ -211,14 +172,8 @@ "type": "String", "placeholders": {} }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, - "@callingAccount": { - "type": "String", - "placeholders": {} - }, + "@startFirstChat": {}, + "@callingAccount": {}, "@requestPermission": { "type": "String", "placeholders": {} @@ -246,14 +201,8 @@ } } }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@nextAccount": { - "type": "String", - "placeholders": {} - }, + "@setColorTheme": {}, + "@nextAccount": {}, "@commandHint_create": { "type": "String", "description": "Usage hint for the command /create" @@ -270,10 +219,7 @@ "type": "String", "placeholders": {} }, - "@allSpaces": { - "type": "String", - "placeholders": {} - }, + "@allSpaces": {}, "@supposedMxid": { "type": "String", "placeholders": { @@ -286,10 +232,7 @@ "type": "String", "placeholders": {} }, - "@user": { - "type": "String", - "placeholders": {} - }, + "@user": {}, "@roomVersion": { "type": "String", "placeholders": {} @@ -306,10 +249,7 @@ "type": "String", "placeholders": {} }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, + "@youAcceptedTheInvitation": {}, "@banFromChat": { "type": "String", "placeholders": {} @@ -341,8 +281,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@userIsTyping": { "type": "String", @@ -364,10 +303,7 @@ } } }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, + "@banUserDescription": {}, "@inviteContact": { "type": "String", "placeholders": {} @@ -376,10 +312,7 @@ "type": "String", "placeholders": {} }, - "@widgetEtherpad": { - "type": "String", - "placeholders": {} - }, + "@widgetEtherpad": {}, "@waitingPartnerAcceptRequest": { "type": "String", "placeholders": {} @@ -401,10 +334,7 @@ "type": "String", "placeholders": {} }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, + "@removeDevicesDescription": {}, "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -428,10 +358,7 @@ "type": "String", "placeholders": {} }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, + "@tryAgain": {}, "@areGuestsAllowedToJoin": { "type": "String", "placeholders": {} @@ -445,8 +372,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@dateWithoutYear": { "type": "String", @@ -463,10 +389,7 @@ "type": "String", "placeholders": {} }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, + "@unbanUserDescription": {}, "@userAndUserAreTyping": { "type": "String", "placeholders": { @@ -486,10 +409,7 @@ "type": "String", "placeholders": {} }, - "@sendOnEnter": { - "type": "String", - "placeholders": {} - }, + "@sendOnEnter": {}, "@pickImage": { "type": "String", "placeholders": {} @@ -502,18 +422,9 @@ } } }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, + "@youRejectedTheInvitation": {}, + "@otherCallingPermissions": {}, + "@messagesStyle": {}, "@couldNotDecryptMessage": { "type": "String", "placeholders": { @@ -526,30 +437,12 @@ "type": "String", "placeholders": {} }, - "@link": { - "type": "String", - "placeholders": {} - }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, - "@newSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, + "@link": {}, + "@widgetUrlError": {}, + "@emailOrUsername": {}, + "@newSpaceDescription": {}, + "@chatDescription": {}, + "@callingAccountDetails": {}, "@next": { "type": "String", "placeholders": {} @@ -584,14 +477,8 @@ "type": "String", "placeholders": {} }, - "@enterSpace": { - "type": "String", - "placeholders": {} - }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, + "@enterSpace": {}, + "@encryptThisChat": {}, "@fileName": { "type": "String", "placeholders": {} @@ -600,10 +487,7 @@ "type": "String", "placeholders": {} }, - "@previousAccount": { - "type": "String", - "placeholders": {} - }, + "@previousAccount": {}, "@publicRooms": { "type": "String", "placeholders": {} @@ -624,14 +508,8 @@ "type": "String", "placeholders": {} }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, + "@reopenChat": {}, + "@pleaseEnterRecoveryKey": {}, "@create": { "type": "String", "placeholders": {} @@ -648,10 +526,7 @@ "type": "String", "placeholders": {} }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, + "@widgetNameError": {}, "@inoffensive": { "type": "String", "placeholders": {} @@ -660,10 +535,7 @@ "type": "String", "placeholders": {} }, - "@addToBundle": { - "type": "String", - "placeholders": {} - }, + "@addToBundle": {}, "@reportMessage": { "type": "String", "placeholders": {} @@ -672,10 +544,7 @@ "type": "String", "placeholders": {} }, - "@addWidget": { - "type": "String", - "placeholders": {} - }, + "@addWidget": {}, "@all": { "type": "String", "placeholders": {} @@ -693,13 +562,9 @@ "count": { "type": "int" } - }, - "type": "String" - }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} + } }, + "@noKeyForThisMessage": {}, "@enableEncryptionWarning": { "type": "String", "placeholders": {} @@ -723,10 +588,7 @@ "type": "String", "placeholders": {} }, - "@commandHint_markasgroup": { - "type": "String", - "placeholders": {} - }, + "@commandHint_markasgroup": {}, "@errorObtainingLocation": { "type": "String", "placeholders": { @@ -735,38 +597,20 @@ } } }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, + "@hydrateTor": {}, + "@pushNotificationsNotAvailable": {}, "@passwordRecovery": { "type": "String", "placeholders": {} }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, + "@storeInAppleKeyChain": {}, "@replaceRoomWithNewerVersion": { "type": "String", "placeholders": {} }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, + "@hydrate": {}, + "@invalidServerName": {}, + "@chatPermissions": {}, "@voiceMessage": { "type": "String", "placeholders": {} @@ -794,14 +638,8 @@ } } }, - "@sender": { - "type": "String", - "placeholders": {} - }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, + "@sender": {}, + "@storeInAndroidKeystore": {}, "@hideRedactedEvents": { "type": "String", "placeholders": {} @@ -810,10 +648,7 @@ "type": "String", "placeholders": {} }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, + "@signInWithPassword": {}, "@ignoredUsers": { "type": "String", "placeholders": {} @@ -849,10 +684,7 @@ "type": "String", "placeholders": {} }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, + "@makeAdminDescription": {}, "@edit": { "type": "String", "placeholders": {} @@ -897,26 +729,17 @@ "type": "String", "placeholders": {} }, - "@saveKeyManuallyDescription": { - "type": "String", - "placeholders": {} - }, + "@saveKeyManuallyDescription": {}, "@none": { "type": "String", "placeholders": {} }, - "@editBundlesForAccount": { - "type": "String", - "placeholders": {} - }, + "@editBundlesForAccount": {}, "@enableEncryption": { "type": "String", "placeholders": {} }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, + "@whyIsThisMessageEncrypted": {}, "@unreadChats": { "type": "String", "placeholders": { @@ -933,10 +756,7 @@ } } }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, + "@setChatDescription": {}, "@userLeftTheChat": { "type": "String", "placeholders": { @@ -959,18 +779,12 @@ "type": "String", "placeholders": {} }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, + "@dehydrateWarning": {}, "@sendOriginal": { "type": "String", "placeholders": {} }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, + "@noOtherDevicesFound": {}, "@whoIsAllowedToJoinThisGroup": { "type": "String", "placeholders": {} @@ -987,14 +801,8 @@ } } }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, + "@storeSecurlyOnThisDevice": {}, + "@yourChatBackupHasBeenSetUp": {}, "@chatBackup": { "type": "String", "placeholders": {} @@ -1011,10 +819,7 @@ "type": "String", "placeholders": {} }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, + "@videoCallsBetaWarning": {}, "@unmuteChat": { "type": "String", "placeholders": {} @@ -1063,14 +868,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@username": { "type": "String", "placeholders": {} @@ -1083,18 +880,8 @@ } } }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "@homeserver": { - "type": "String", - "placeholders": {} - }, + "@fileIsTooBigForServer": {}, + "@homeserver": {}, "@help": { "type": "String", "placeholders": {} @@ -1140,10 +927,7 @@ } } }, - "@callingPermissions": { - "type": "String", - "placeholders": {} - }, + "@callingPermissions": {}, "@delete": { "type": "String", "placeholders": {} @@ -1152,14 +936,8 @@ "type": "String", "placeholders": {} }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@start": { - "type": "String", - "placeholders": {} - }, + "@readUpToHere": {}, + "@start": {}, "@downloadFile": { "type": "String", "placeholders": {} @@ -1172,10 +950,7 @@ "type": "String", "placeholders": {} }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, + "@unlockOldMessages": {}, "@identity": { "type": "String", "placeholders": {} @@ -1219,10 +994,7 @@ "type": "String", "placeholders": {} }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, + "@optionalRedactReason": {}, "@acceptedTheInvitation": { "type": "String", "placeholders": { @@ -1255,10 +1027,7 @@ "type": "String", "placeholders": {} }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, + "@dehydrate": {}, "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} @@ -1297,10 +1066,7 @@ "type": "String", "placeholders": {} }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, + "@archiveRoomDescription": {}, "exportEmotePack": "इमोट पैक को .zip के रूप में निर्यात करें", "@exportEmotePack": {}, "@changedTheChatNameTo": { @@ -1345,10 +1111,7 @@ "type": "String", "placeholders": {} }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, + "@placeCall": {}, "@removedBy": { "type": "String", "placeholders": { @@ -1381,18 +1144,12 @@ "type": "String", "placeholders": {} }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, + "@experimentalVideoCalls": {}, "@openCamera": { "type": "String", "placeholders": {} }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterRecoveryKeyDescription": {}, "@guestsAreForbidden": { "type": "String", "placeholders": {} @@ -1409,13 +1166,7 @@ "type": "String", "placeholders": {} }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, + "@inviteContactToGroupQuestion": {}, "@emoteExists": { "type": "String", "placeholders": {} @@ -1440,8 +1191,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@chat": { "type": "String", @@ -1459,18 +1209,12 @@ "type": "String", "placeholders": {} }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, + "@appearOnTopDetails": {}, "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} }, - "@enterRoom": { - "type": "String", - "placeholders": {} - }, + "@enterRoom": {}, "@enableEmotesGlobally": { "type": "String", "placeholders": {} @@ -1499,10 +1243,7 @@ "type": "String", "placeholders": {} }, - "@reportUser": { - "type": "String", - "placeholders": {} - }, + "@reportUser": {}, "@sharedTheLocation": { "type": "String", "placeholders": { @@ -1530,10 +1271,7 @@ } } }, - "@confirmEventUnpin": { - "type": "String", - "placeholders": {} - }, + "@confirmEventUnpin": {}, "@badServerVersionsException": { "type": "String", "placeholders": { @@ -1550,8 +1288,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@kickedAndBanned": { "type": "String", @@ -1580,10 +1317,7 @@ "type": "String", "placeholders": {} }, - "@addToSpace": { - "type": "String", - "placeholders": {} - }, + "@addToSpace": {}, "@unbanFromChat": { "type": "String", "placeholders": {} @@ -1597,18 +1331,12 @@ }, "description": "State that {command} is not a valid /command." }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, + "@redactMessageDescription": {}, "@rejoin": { "type": "String", "placeholders": {} }, - "@recoveryKey": { - "type": "String", - "placeholders": {} - }, + "@recoveryKey": {}, "@redactMessage": { "type": "String", "placeholders": {} @@ -1621,10 +1349,7 @@ "type": "String", "description": "Usage hint for the command /discardsession" }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, + "@invalidInput": {}, "@about": { "type": "String", "placeholders": {} @@ -1637,10 +1362,7 @@ "type": "String", "placeholders": {} }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, + "@dehydrateTorLong": {}, "@yourPublicKey": { "type": "String", "placeholders": {} @@ -1676,10 +1398,7 @@ "type": "String", "placeholders": {} }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, + "@doNotShowAgain": {}, "@activatedEndToEndEncryption": { "type": "String", "placeholders": { @@ -1688,10 +1407,7 @@ } } }, - "@report": { - "type": "String", - "placeholders": {} - }, + "@report": {}, "@status": { "type": "String", "placeholders": {} @@ -1716,10 +1432,7 @@ "type": "String", "placeholders": {} }, - "@unverified": { - "type": "String", - "placeholders": {} - }, + "@unverified": {}, "@fluffychat": { "type": "String", "placeholders": {} @@ -1728,22 +1441,10 @@ "type": "String", "placeholders": {} }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@screenSharingTitle": { - "type": "String", - "placeholders": {} - }, - "@widgetCustom": { - "type": "String", - "placeholders": {} - }, + "@serverRequiresEmail": {}, + "@hideUnimportantStateEvents": {}, + "@screenSharingTitle": {}, + "@widgetCustom": {}, "@sentCallInformations": { "type": "String", "placeholders": { @@ -1752,10 +1453,7 @@ } } }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, + "@addToSpaceDescription": {}, "@googlyEyesContent": { "type": "String", "placeholders": { @@ -1769,8 +1467,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@theyDontMatch": { "type": "String", @@ -1784,10 +1481,7 @@ "type": "String", "placeholders": {} }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, + "@addChatDescription": {}, "@sentAnAudio": { "type": "String", "placeholders": { @@ -1821,21 +1515,11 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@publish": { - "type": "String", - "placeholders": {} - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} - }, - "@clearArchive": { - "type": "String", - "placeholders": {} + } }, + "@publish": {}, + "@openLinkInBrowser": {}, + "@clearArchive": {}, "@appLock": { "type": "String", "placeholders": {} @@ -1860,18 +1544,9 @@ "type": "String", "placeholders": {} }, - "@messageInfo": { - "type": "String", - "placeholders": {} - }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, + "@messageInfo": {}, + "@disableEncryptionWarning": {}, + "@directChat": {}, "@encryptionNotEnabled": { "type": "String", "placeholders": {} @@ -1884,42 +1559,24 @@ } } }, - "@sendTypingNotifications": { - "type": "String", - "placeholders": {} - }, + "@sendTypingNotifications": {}, "@lightTheme": { "type": "String", "placeholders": {} }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, + "@inviteGroupChat": {}, + "@appearOnTop": {}, + "@invitePrivateChat": {}, "@verifyTitle": { "type": "String", "placeholders": {} }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, + "@foregroundServiceRunning": {}, "@enterAnEmailAddress": { "type": "String", "placeholders": {} }, - "@voiceCall": { - "type": "String", - "placeholders": {} - }, + "@voiceCall": {}, "@commandHint_kick": { "type": "String", "description": "Usage hint for the command /kick" @@ -1958,18 +1615,12 @@ } } }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, + "@noChatDescriptionYet": {}, "@defaultPermissionLevel": { "type": "String", "placeholders": {} }, - "@removeFromBundle": { - "type": "String", - "placeholders": {} - }, + "@removeFromBundle": {}, "@numUsersTyping": { "type": "String", "placeholders": { @@ -1986,14 +1637,8 @@ "type": "String", "placeholders": {} }, - "@confirmMatrixId": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, + "@confirmMatrixId": {}, + "@learnMore": {}, "@iHaveClickedOnLink": { "type": "String", "placeholders": {} @@ -2004,38 +1649,17 @@ }, "notAnImage": "कोई छवि फ़ाइल नहीं।", "@notAnImage": {}, - "@users": { - "type": "String", - "placeholders": {} - }, - "@openGallery": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, + "@users": {}, + "@openGallery": {}, + "@chatDescriptionHasBeenChanged": {}, "@search": { "type": "String", "placeholders": {} }, - "@newGroup": { - "type": "String", - "placeholders": {} - }, - "@bundleName": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, + "@newGroup": {}, + "@bundleName": {}, + "@dehydrateTor": {}, + "@removeFromSpace": {}, "@dateAndTimeOfDay": { "type": "String", "placeholders": { @@ -2059,10 +1683,7 @@ "type": "String", "placeholders": {} }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, + "@roomUpgradeDescription": {}, "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" @@ -2078,18 +1699,12 @@ } } }, - "@scanQrCode": { - "type": "String", - "placeholders": {} - }, + "@scanQrCode": {}, "@logout": { "type": "String", "placeholders": {} }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterANumber": {}, "@contactHasBeenInvitedToTheGroup": { "type": "String", "placeholders": {} @@ -2099,8 +1714,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@areYouSureYouWantToLogout": { "type": "String", @@ -2114,14 +1728,8 @@ } } }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, + "@profileNotFound": {}, + "@jump": {}, "@groups": { "type": "String", "placeholders": {} @@ -2148,10 +1756,7 @@ } } }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, + "@sorryThatsNotPossible": {}, "@videoWithSize": { "type": "String", "placeholders": { @@ -2172,18 +1777,9 @@ } } }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasdm": { - "type": "String", - "placeholders": {} - }, - "@recoveryKeyLost": { - "type": "String", - "placeholders": {} - }, + "@shareInviteLink": {}, + "@commandHint_markasdm": {}, + "@recoveryKeyLost": {}, "@cuddleContent": { "type": "String", "placeholders": { @@ -2212,10 +1808,7 @@ "type": "String", "placeholders": {} }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, + "@deviceKeys": {}, "@waitingPartnerNumbers": { "type": "String", "placeholders": {} @@ -2272,18 +1865,12 @@ "type": "String", "placeholders": {} }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, + "@setTheme": {}, "@changeTheHomeserver": { "type": "String", "placeholders": {} }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, + "@youJoinedTheChat": {}, "@wallpaper": { "type": "String", "placeholders": {} @@ -2320,18 +1907,12 @@ "type": "String", "placeholders": {} }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, + "@markAsRead": {}, "@sendAudio": { "type": "String", "placeholders": {} }, - "@widgetName": { - "type": "String", - "placeholders": {} - }, + "@widgetName": {}, "@sentASticker": { "type": "String", "placeholders": { @@ -2340,22 +1921,13 @@ } } }, - "@errorAddingWidget": { - "type": "String", - "placeholders": {} - }, + "@errorAddingWidget": {}, "@commandHint_dm": { "type": "String", "description": "Usage hint for the command /dm" }, - "@commandHint_hug": { - "type": "String", - "placeholders": {} - }, - "@replace": { - "type": "String", - "placeholders": {} - }, + "@commandHint_hug": {}, + "@replace": {}, "@reject": { "type": "String", "placeholders": {} @@ -2377,8 +1949,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@deactivateAccountWarning": { "type": "String", @@ -2404,10 +1975,7 @@ "type": "String", "placeholders": {} }, - "@newSpace": { - "type": "String", - "placeholders": {} - }, + "@newSpace": {}, "@changePassword": { "type": "String", "placeholders": {} @@ -2428,10 +1996,7 @@ } } }, - "@emojis": { - "type": "String", - "placeholders": {} - }, + "@emojis": {}, "@pleaseEnterYourPin": { "type": "String", "placeholders": {} @@ -2444,18 +2009,9 @@ "type": "String", "placeholders": {} }, - "@commandHint_googly": { - "type": "String", - "placeholders": {} - }, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, + "@commandHint_googly": {}, + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "@createGroup": {}, "@privacy": { "type": "String", "placeholders": {} @@ -2468,14 +2024,8 @@ "type": "String", "placeholders": {} }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@time": { - "type": "String", - "placeholders": {} - }, + "@hydrateTorLong": {}, + "@time": {}, "@enterYourHomeserver": { "type": "String", "placeholders": {} @@ -2488,14 +2038,8 @@ "type": "String", "placeholders": {} }, - "@custom": { - "type": "String", - "placeholders": {} - }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, + "@custom": {}, + "@noBackupWarning": {}, "@fromJoining": { "type": "String", "placeholders": {} @@ -2508,18 +2052,9 @@ "type": "String", "placeholders": {} }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, - "@openChat": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, + "@storeInSecureStorageDescription": {}, + "@openChat": {}, + "@kickUserDescription": {}, "@sendAMessage": { "type": "String", "placeholders": {} @@ -2538,26 +2073,14 @@ "type": "String", "placeholders": {} }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, + "@pinMessage": {}, + "@screenSharingDetail": {}, "@muteChat": { "type": "String", "placeholders": {} }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, + "@invite": {}, + "@enableMultiAccounts": {}, "@anyoneCanJoin": { "type": "String", "placeholders": {} @@ -2566,10 +2089,7 @@ "type": "String", "placeholders": {} }, - "@indexedDbErrorTitle": { - "type": "String", - "placeholders": {} - }, + "@indexedDbErrorTitle": {}, "@endedTheCall": { "type": "String", "placeholders": { @@ -3136,7 +2656,6 @@ "report": "रिपोर्ट करें", "signInWithPassword": "पासवर्ड के साथ साइन इन करें", "pleaseTryAgainLaterOrChooseDifferentServer": "कृपया बाद में पुनः प्रयास करें या अलग सर्वर चुनें।", - "signInWith": "{provider} के साथ साइन इन करें", "profileNotFound": "सर्वर पर उपयोगकर्ता नहीं मिल सका। हो सकता है कि कनेक्शन में समस्या हो या उपयोगकर्ता मौजूद न हो।", "setTheme": "थीम सेट करें:", "setColorTheme": "रंग थीम सेट करें:", @@ -5042,9 +4561,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12150,4 +11667,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index 3bba7943d..f8ad07e6e 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -196,7 +196,7 @@ } } }, - "changedTheChatDescriptionTo": "{username} je promijenio/la opis razgovora u: „{description}”", + "changedTheChatDescriptionTo": "{username} je promijenio/la opis razgovora u: '{description}'", "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -208,7 +208,7 @@ } } }, - "changedTheChatNameTo": "{username} je promijenio/la ime razgovora u: „{chatname}”", + "changedTheChatNameTo": "{username} je promijenio/la ime razgovora u: '{chatname}'", "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -229,7 +229,7 @@ } } }, - "changedTheDisplaynameTo": "{username} je promijenio/la ime u: „{displayname}”", + "changedTheDisplaynameTo": "{username} je promijenio/la svoje prikazno ime u: '{displayname}'", "@changedTheDisplaynameTo": { "type": "String", "placeholders": { @@ -371,7 +371,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "Tvoji su stari razgovori osigurani s ključem za obnavljanje. Pazi da ga ne izgubiš.", + "chatBackupDescription": "Tvoje poruke su osigurane s ključem za obnavljanje. Pazi da ga ne izgubiš.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -633,7 +633,7 @@ "type": "String", "placeholders": {} }, - "defaultPermissionLevel": "Standardna razina dozvole", + "defaultPermissionLevel": "Standardna razina dozvole za nove korisnike", "@defaultPermissionLevel": { "type": "String", "placeholders": {} @@ -1799,7 +1799,7 @@ "type": "String", "placeholders": {} }, - "unknownEvent": "Nepoznat događaj „{type}”", + "unknownEvent": "Nepoznat događaj '{type}'", "@unknownEvent": { "type": "String", "placeholders": { @@ -2505,15 +2505,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Pokušaj ponovo kasnije ili odaberi jedan drugi poslužitelj.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Prijavi se pomoću {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Nije slikovna datoteka.", "@notAnImage": {}, "importNow": "Uvezi sada", @@ -2573,9 +2564,9 @@ "@addChatDescription": {}, "directChat": "Izravni razgovor", "@directChat": {}, - "inviteGroupChat": "📨 Pozovi u grupni razgovor", + "inviteGroupChat": "📨 Pozivnica u grupni razgovor", "@inviteGroupChat": {}, - "invitePrivateChat": "📨 Pozovi u privatni razgovor", + "invitePrivateChat": "📨 Pozivnica u privatni razgovor", "@invitePrivateChat": {}, "noChatDescriptionYet": "Opis razgovora još nije stvoren.", "@noChatDescriptionYet": {}, @@ -2885,7 +2876,7 @@ "@restricted": {}, "roomUpgradeDescription": "Razgovor će se tada ponovo stvoriti s novom verzijom sobe. Svi sudionici će biti obaviješteni da se moraju prebaciti na novi razgovor. Više o verzijama soba možeš saznati na https://spec.matrix.org/latest/rooms/", "@roomUpgradeDescription": {}, - "noGoogleServicesWarning": "Čini se da Firebase Cloud Messaging nije dostupan na tvom uređaju. Za daljnje primanje push obavijesti, preporučujemo da instaliraš ntfy. S ntfy ili drugim pružateljem usluge Unified Push možeš primati push obavijesti na podatkovno siguran način. Ntfy možeš preuzeti s PlayStorea ili s F-Droida.", + "noGoogleServicesWarning": "Čini se da Firebase Cloud Messaging nije dostupan na tvom uređaju. Za daljnje primanje push obavijesti, preporučujemo da instaliraš ntfy. S ntfy ili drugim pružateljem Unified Push usluge možeš primati push obavijesti na podatkovno siguran način. Ntfy možeš preuzeti s PlayStorea ili s F-Droida.", "@noGoogleServicesWarning": { "type": "String", "placeholders": {} @@ -2992,12 +2983,25 @@ } } }, - "setCustomPermissionLevel": "Postavi prilagođenu razinu dopuštenja", - "setPermissionsLevelDescription": "Molimo odaberite unaprijed definiranu ulogu ispod ili unesite prilagođenu razinu dopuštenja između 0 i 100.", - "ignoreUser": "Ignoriraj korisnika", - "normalUser": "Običan korisnik", - "aboutHomeserver": "O {homeserver}", + "setCustomPermissionLevel": "Postavi razinu dozvole", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Odaberi unaprijed definiranu ulogu u nastavku ili upiši prilagođenu razinu dozvole između 0 i 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Zanemari korisnika", + "@ignoreUser": {}, + "normalUser": "Normalni korisnik", + "@normalUser": {}, + "aboutHomeserver": "Informacije o {homeserver}", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, "commandHint_roomupgrade": "Nadogradi ovu sobu na zadanu verziju sobe", + "@commandHint_roomupgrade": {}, "countChatsAndCountParticipants": "{chats} razgovora i {participants} sudionika", "noMoreChatsFound": "Nema više pronađenih razgovora...", "noChatsFoundHere": "Ovdje još nisu pronađeni razgovori. Započnite novi razgovor s nekim pomoću gumba ispod. ⤵️", @@ -4261,34 +4265,6 @@ "inviteYourFriends": "Pozovite svoje prijatelje", "playWithAI": "Igraj s AI-jem za sada", "courseStartDesc": "Pangea Bot je spreman za rad u bilo koje vrijeme!\n\n...ali je bolje učiti s prijateljima!", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, "@countChatsAndCountParticipants": { "type": "String", "placeholders": { @@ -11437,4 +11413,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index e9ada78b8..1af2c3966 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -2285,15 +2285,6 @@ }, "videoCallsBetaWarning": "Kérem vegye figyelembe, hogy a videó hívások jelenleg béta fázisban vannak. Nem biztos, hogy megfelelően fognak működni, vagy egyáltalán elindulnak egyes platformokon.", "@videoCallsBetaWarning": {}, - "signInWith": "Bejelentkezés a {provider} kiszolgálóval", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "fileIsTooBigForServer": "Nem küldhető el! A szerver csak {max} határig enged csatolmányokat.", "@fileIsTooBigForServer": {}, "verified": "Hitelesített", @@ -3055,7 +3046,7 @@ "@changeTheDescriptionOfTheGroup": {}, "swipeRightToLeftToReply": "Húzza balra a válaszoláshoz", "@swipeRightToLeftToReply": {}, - "alwaysUse24HourFormat": "hamis", + "alwaysUse24HourFormat": "", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -11066,4 +11057,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 614a00661..cc34e5549 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -611,7 +611,6 @@ "report": "reportar", "signInWithPassword": "Signar in con parola", "pleaseTryAgainLaterOrChooseDifferentServer": "Per favor, tenta ancora plus tarde o selige un altere servitore.", - "signInWith": "Signar in con {provider}", "profileNotFound": "Le usator non poteva esser trovate in le servitore. Forse il ha un problema de connection o le usator non existe.", "setTheme": "Configurar thema:", "setColorTheme": "Configurar thema de colore:", @@ -4609,14 +4608,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5027,9 +5018,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12143,4 +12132,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index 49d95edb0..8da69060b 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -2509,15 +2509,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Silakan coba lagi nanti atau pilih server yang lain.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "Masuk dengan {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Bukan berkas gambar.", "@notAnImage": {}, "importNow": "Impor sekarang", @@ -2591,9 +2582,9 @@ "@setColorTheme": {}, "invite": "Undang", "@invite": {}, - "inviteGroupChat": "📨 Undang percakapan grup", + "inviteGroupChat": "📨 Undangan percakapan grup", "@inviteGroupChat": {}, - "invitePrivateChat": "📨 Undang percakapan privat", + "invitePrivateChat": "📨 Undangan percakapan privat", "@invitePrivateChat": {}, "emoteKeyboardNoRecents": "Emote yang telah digunakan akan muncul di sini...", "@emoteKeyboardNoRecents": { @@ -3379,7 +3370,20 @@ "@commandHint_logoutall": {}, "displayNavigationRail": "Tampilkan jalur navigasi pada ponsel", "@displayNavigationRail": {}, - "customReaction": "Reaksi Kustom", + "customReaction": "Reaksi khusus", + "@customReaction": {}, + "moreEvents": "Peristiwa tambahan", + "@moreEvents": {}, + "declineInvitation": "Tolak undangan", + "@declineInvitation": {}, + "noMessagesYet": "Belum ada pesan", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Tekan lama untuk merekam pesan suara.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Jeda", + "@pause": {}, + "resume": "Lanjut", + "@resume": {}, "writeAMessageLangCodes": "Ketik dalam {l1} atau {l2}...", "requests": "Permintaan", "holdForInfo": "Klik dan tahan untuk info kata.", @@ -4508,10 +4512,6 @@ "playWithAI": "Main dengan AI untuk saat ini", "courseStartDesc": "Pangea Bot siap digunakan kapan saja!\n\n...tapi belajar lebih baik dengan teman!", "@@locale": "id", - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -11056,4 +11056,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index 64e46a6fd..b36e2ba3c 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -1201,26 +1201,17 @@ "type": "String", "placeholders": {} }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, + "@jumpToLastReadMessage": {}, "@allRooms": { "type": "String", "placeholders": {} }, - "@commandHint_cuddle": { - "type": "String", - "placeholders": {} - }, + "@commandHint_cuddle": {}, "@noEncryptionForPublicRooms": { "type": "String", "placeholders": {} }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, + "@reportErrorDescription": {}, "@setPermissionsLevel": { "type": "String", "placeholders": {} @@ -1233,38 +1224,23 @@ } } }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, + "@chatHasBeenAddedToThisSpace": {}, "@removeYourAvatar": { "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersion": {}, "@youAreNoLongerParticipatingInThisChat": { "type": "String", "placeholders": {} }, - "@indexedDbErrorLong": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, + "@indexedDbErrorLong": {}, + "@oneClientLoggedOut": {}, "@toggleMuted": { "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersionLong": {}, "@kicked": { "type": "String", "placeholders": { @@ -1292,10 +1268,7 @@ "type": "String", "placeholders": {} }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, + "@startFirstChat": {}, "@sentAPicture": { "type": "String", "placeholders": { @@ -1315,10 +1288,7 @@ } } }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, + "@setColorTheme": {}, "@commandHint_create": { "type": "String", "description": "Usage hint for the command /create" @@ -1335,10 +1305,7 @@ } } }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, + "@youAcceptedTheInvitation": {}, "@noMatrixServer": { "type": "String", "placeholders": { @@ -1366,8 +1333,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@userIsTyping": { "type": "String", @@ -1389,10 +1355,7 @@ } } }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, + "@banUserDescription": {}, "@askSSSSSign": { "type": "String", "placeholders": {} @@ -1405,10 +1368,7 @@ "type": "String", "placeholders": {} }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, + "@removeDevicesDescription": {}, "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -1424,10 +1384,7 @@ "type": "String", "placeholders": {} }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, + "@tryAgain": {}, "@areGuestsAllowedToJoin": { "type": "String", "placeholders": {} @@ -1437,13 +1394,9 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} + } }, + "@unbanUserDescription": {}, "@userAndUserAreTyping": { "type": "String", "placeholders": { @@ -1471,18 +1424,9 @@ } } }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, + "@youRejectedTheInvitation": {}, + "@otherCallingPermissions": {}, + "@messagesStyle": {}, "@couldNotDecryptMessage": { "type": "String", "placeholders": { @@ -1491,26 +1435,11 @@ } } }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, - "@newSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, + "@widgetUrlError": {}, + "@emailOrUsername": {}, + "@newSpaceDescription": {}, + "@chatDescription": {}, + "@callingAccountDetails": {}, "@pleaseFollowInstructionsOnWeb": { "type": "String", "placeholders": {} @@ -1523,10 +1452,7 @@ } } }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, + "@encryptThisChat": {}, "@incorrectPassphraseOrKey": { "type": "String", "placeholders": {} @@ -1535,26 +1461,14 @@ "type": "String", "placeholders": {} }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, + "@reopenChat": {}, + "@pleaseEnterRecoveryKey": {}, "@toggleFavorite": { "type": "String", "placeholders": {} }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, - "@addToBundle": { - "type": "String", - "placeholders": {} - }, + "@widgetNameError": {}, + "@addToBundle": {}, "@spaceIsPublic": { "type": "String", "placeholders": {} @@ -1563,10 +1477,7 @@ "type": "String", "placeholders": {} }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} - }, + "@noKeyForThisMessage": {}, "@enableEncryptionWarning": { "type": "String", "placeholders": {} @@ -1590,50 +1501,26 @@ } } }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, + "@hydrateTor": {}, + "@pushNotificationsNotAvailable": {}, + "@storeInAppleKeyChain": {}, "@replaceRoomWithNewerVersion": { "type": "String", "placeholders": {} }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, + "@hydrate": {}, + "@invalidServerName": {}, + "@chatPermissions": {}, "@wipeChatBackup": { "type": "String", "placeholders": {} }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, + "@storeInAndroidKeystore": {}, "@hideRedactedEvents": { "type": "String", "placeholders": {} }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, + "@signInWithPassword": {}, "@changedTheGuestAccessRulesTo": { "type": "String", "placeholders": { @@ -1653,10 +1540,7 @@ "type": "String", "placeholders": {} }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, + "@makeAdminDescription": {}, "@noEmotesFound": { "type": "String", "placeholders": {} @@ -1677,18 +1561,9 @@ "type": "String", "placeholders": {} }, - "@saveKeyManuallyDescription": { - "type": "String", - "placeholders": {} - }, - "@editBundlesForAccount": { - "type": "String", - "placeholders": {} - }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, + "@saveKeyManuallyDescription": {}, + "@editBundlesForAccount": {}, + "@whyIsThisMessageEncrypted": {}, "@unreadChats": { "type": "String", "placeholders": { @@ -1705,10 +1580,7 @@ } } }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, + "@setChatDescription": {}, "@userLeftTheChat": { "type": "String", "placeholders": { @@ -1717,18 +1589,9 @@ } } }, - "@importFromZipFile": { - "type": "String", - "placeholders": {} - }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, + "@importFromZipFile": {}, + "@dehydrateWarning": {}, + "@noOtherDevicesFound": {}, "@whoIsAllowedToJoinThisGroup": { "type": "String", "placeholders": {} @@ -1741,14 +1604,8 @@ } } }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, + "@storeSecurlyOnThisDevice": {}, + "@yourChatBackupHasBeenSetUp": {}, "@redactedBy": { "type": "String", "placeholders": { @@ -1757,10 +1614,7 @@ } } }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, + "@videoCallsBetaWarning": {}, "@unmuteChat": { "type": "String", "placeholders": {} @@ -1793,14 +1647,6 @@ } } }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@changedTheRoomAliases": { "type": "String", "placeholders": { @@ -1809,14 +1655,7 @@ } } }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, + "@fileIsTooBigForServer": {}, "@changedTheHistoryVisibilityTo": { "type": "String", "placeholders": { @@ -1832,14 +1671,8 @@ "type": "String", "placeholders": {} }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, + "@readUpToHere": {}, + "@unlockOldMessages": {}, "@changedTheJoinRulesTo": { "type": "String", "placeholders": { @@ -1859,10 +1692,7 @@ } } }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, + "@optionalRedactReason": {}, "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -1879,10 +1709,7 @@ "type": "String", "placeholders": {} }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, + "@dehydrate": {}, "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} @@ -1905,14 +1732,8 @@ "@sendAsText": { "type": "String" }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@exportEmotePack": { - "type": "String", - "placeholders": {} - }, + "@archiveRoomDescription": {}, + "@exportEmotePack": {}, "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -1944,10 +1765,7 @@ "type": "String", "placeholders": {} }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, + "@placeCall": {}, "@removedBy": { "type": "String", "placeholders": { @@ -1964,14 +1782,8 @@ } } }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, + "@experimentalVideoCalls": {}, + "@pleaseEnterRecoveryKeyDescription": {}, "@guestsAreForbidden": { "type": "String", "placeholders": {} @@ -1980,13 +1792,7 @@ "type": "String", "placeholders": {} }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, + "@inviteContactToGroupQuestion": {}, "@redactedByBecause": { "type": "String", "placeholders": { @@ -2003,13 +1809,9 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} + } }, + "@appearOnTopDetails": {}, "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} @@ -2057,17 +1859,13 @@ } } }, - "@confirmEventUnpin": { - "type": "String", - "placeholders": {} - }, + "@confirmEventUnpin": {}, "@youInvitedUser": { "placeholders": { "user": { "type": "String" } - }, - "type": "String" + } }, "@kickedAndBanned": { "type": "String", @@ -2105,26 +1903,17 @@ }, "description": "State that {command} is not a valid /command." }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, + "@redactMessageDescription": {}, "@commandHint_discardsession": { "type": "String", "description": "Usage hint for the command /discardsession" }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, + "@invalidInput": {}, "@chooseAStrongPassword": { "type": "String", "placeholders": {} }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, + "@dehydrateTorLong": {}, "@yourPublicKey": { "type": "String", "placeholders": {} @@ -2141,10 +1930,7 @@ "type": "String", "description": "Usage hint for the command /myroomnick" }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, + "@doNotShowAgain": {}, "@activatedEndToEndEncryption": { "type": "String", "placeholders": { @@ -2153,10 +1939,7 @@ } } }, - "@report": { - "type": "String", - "placeholders": {} - }, + "@report": {}, "@compareNumbersMatch": { "type": "String", "placeholders": {} @@ -2165,14 +1948,8 @@ "type": "String", "placeholders": {} }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, + "@serverRequiresEmail": {}, + "@hideUnimportantStateEvents": {}, "@sentCallInformations": { "type": "String", "placeholders": { @@ -2181,10 +1958,7 @@ } } }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, + "@addToSpaceDescription": {}, "@googlyEyesContent": { "type": "String", "placeholders": { @@ -2198,8 +1972,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@theyDontMatch": { "type": "String", @@ -2213,10 +1986,7 @@ "type": "String", "placeholders": {} }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, + "@addChatDescription": {}, "@sentAnAudio": { "type": "String", "placeholders": { @@ -2234,13 +2004,9 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} + } }, + "@openLinkInBrowser": {}, "@appLock": { "type": "String", "placeholders": {} @@ -2261,14 +2027,8 @@ "type": "String", "placeholders": {} }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, + "@disableEncryptionWarning": {}, + "@directChat": {}, "@encryptionNotEnabled": { "type": "String", "placeholders": {} @@ -2281,30 +2041,15 @@ } } }, - "@sendTypingNotifications": { - "type": "String", - "placeholders": {} - }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, + "@sendTypingNotifications": {}, + "@inviteGroupChat": {}, + "@appearOnTop": {}, + "@invitePrivateChat": {}, "@verifyTitle": { "type": "String", "placeholders": {} }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, + "@foregroundServiceRunning": {}, "@enterAnEmailAddress": { "type": "String", "placeholders": {} @@ -2325,10 +2070,7 @@ "type": "String", "description": "Usage hint for the command /ban" }, - "@importEmojis": { - "type": "String", - "placeholders": {} - }, + "@importEmojis": {}, "@wasDirectChatDisplayName": { "type": "String", "placeholders": { @@ -2337,18 +2079,12 @@ } } }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, + "@noChatDescriptionYet": {}, "@defaultPermissionLevel": { "type": "String", "placeholders": {} }, - "@removeFromBundle": { - "type": "String", - "placeholders": {} - }, + "@removeFromBundle": {}, "@numUsersTyping": { "type": "String", "placeholders": { @@ -2361,34 +2097,16 @@ "type": "String", "placeholders": {} }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, + "@learnMore": {}, "@iHaveClickedOnLink": { "type": "String", "placeholders": {} }, - "@notAnImage": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@bundleName": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, + "@notAnImage": {}, + "@chatDescriptionHasBeenChanged": {}, + "@bundleName": {}, + "@dehydrateTor": {}, + "@removeFromSpace": {}, "@commandHint_op": { "type": "String", "description": "Usage hint for the command /op" @@ -2397,10 +2115,7 @@ "type": "String", "description": "Usage hint for the command /join" }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, + "@roomUpgradeDescription": {}, "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" @@ -2416,10 +2131,7 @@ } } }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterANumber": {}, "@contactHasBeenInvitedToTheGroup": { "type": "String", "placeholders": {} @@ -2429,8 +2141,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@changedTheJoinRules": { "type": "String", @@ -2440,14 +2151,8 @@ } } }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, + "@profileNotFound": {}, + "@jump": {}, "@reactedWith": { "type": "String", "placeholders": { @@ -2459,10 +2164,7 @@ } } }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, + "@sorryThatsNotPossible": {}, "@oopsSomethingWentWrong": { "type": "String", "placeholders": {} @@ -2475,18 +2177,9 @@ } } }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasdm": { - "type": "String", - "placeholders": {} - }, - "@recoveryKeyLost": { - "type": "String", - "placeholders": {} - }, + "@shareInviteLink": {}, + "@commandHint_markasdm": {}, + "@recoveryKeyLost": {}, "@cuddleContent": { "type": "String", "placeholders": { @@ -2495,10 +2188,7 @@ } } }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, + "@deviceKeys": {}, "@waitingPartnerNumbers": { "type": "String", "placeholders": {} @@ -2535,14 +2225,8 @@ "type": "String", "placeholders": {} }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, + "@setTheme": {}, + "@youJoinedTheChat": {}, "@openVideoCamera": { "type": "String", "placeholders": {} @@ -2555,10 +2239,7 @@ "type": "String", "placeholders": {} }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, + "@markAsRead": {}, "@sentASticker": { "type": "String", "placeholders": { @@ -2567,22 +2248,13 @@ } } }, - "@errorAddingWidget": { - "type": "String", - "placeholders": {} - }, + "@errorAddingWidget": {}, "@commandHint_dm": { "type": "String", "description": "Usage hint for the command /dm" }, - "@commandHint_hug": { - "type": "String", - "placeholders": {} - }, - "@replace": { - "type": "String", - "placeholders": {} - }, + "@commandHint_hug": {}, + "@replace": {}, "@oopsPushError": { "type": "String", "placeholders": {} @@ -2592,8 +2264,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@deactivateAccountWarning": { "type": "String", @@ -2627,74 +2298,35 @@ "type": "String", "placeholders": {} }, - "@commandHint_googly": { - "type": "String", - "placeholders": {} - }, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, + "@commandHint_googly": {}, + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "@createGroup": {}, + "@hydrateTorLong": {}, "@contentHasBeenReported": { "type": "String", "placeholders": {} }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, + "@noBackupWarning": {}, + "@storeInSecureStorageDescription": {}, + "@kickUserDescription": {}, "@sendAMessage": { "type": "String", "placeholders": {} }, - "@importNow": { - "type": "String", - "placeholders": {} - }, + "@importNow": {}, "@setInvitationLink": { "type": "String", "placeholders": {} }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, + "@pinMessage": {}, + "@screenSharingDetail": {}, + "@invite": {}, + "@enableMultiAccounts": {}, "@emotePacks": { "type": "String", "placeholders": {} }, - "@indexedDbErrorTitle": { - "type": "String", - "placeholders": {} - }, + "@indexedDbErrorTitle": {}, "@endedTheCall": { "type": "String", "placeholders": { @@ -3027,7 +2659,6 @@ "report": "reportar", "signInWithPassword": "Signar con passphrase", "pleaseTryAgainLaterOrChooseDifferentServer": "Per favor, tenta ancora plus tarde o selige un altere servitor.", - "signInWith": "Signar con {provider}", "profileNotFound": "La usator ne poteva esser trovate in su servitor. Probabilmente es un problema de conexion o la usator no existe.", "setTheme": "Stabilir tema:", "setColorTheme": "Stabilir tema de color:", @@ -4931,9 +4562,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12039,4 +11668,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 3e3779fd4..387c4a353 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -2223,15 +2223,6 @@ }, "videoCallsBetaWarning": "Nota che le video chiamate sono attualmente in beta. Potrebbero non funzionare come previsto o non funzionare del tutto su alcune piattaforme.", "@videoCallsBetaWarning": {}, - "signInWith": "Accedi con {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "fileIsTooBigForServer": "Impossibile inviare! Il server supporta solo allegati fino a {max}.", "@fileIsTooBigForServer": {}, "homeserver": "Homeserver", @@ -3350,13 +3341,57 @@ "@youHaveKnocked": {}, "pleaseWaitUntilInvited": "Ora attendi, finché qualcuno dalla stanza non ti invita.", "@pleaseWaitUntilInvited": {}, - "checkList": "Elenco di controllo", + "checkList": "Checklist", + "@checkList": {}, "countInvited": "{count} invitati", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, "sentVoiceMessage": "🎙️ {duration} - Messaggio vocale da {sender}", - "commandHint_logout": "Disconnetti il dispositivo corrente", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "commandHint_logout": "Disconnetti questo dispositivo", + "@commandHint_logout": {}, "commandHint_logoutall": "Disconnetti tutti i dispositivi attivi", - "displayNavigationRail": "Mostra la barra di navigazione su mobile", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Mostra barra di navigazione su mobile", + "@displayNavigationRail": {}, "customReaction": "Reazione personalizzata", + "@customReaction": {}, + "moreEvents": "Altri eventi", + "@moreEvents": {}, + "declineInvitation": "Rifiuta invito", + "@declineInvitation": {}, + "noMessagesYet": "Ancora nessun messaggio", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Premi a lungo per registrare un messaggio vocale.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Pausa", + "@pause": {}, + "resume": "Riprendi", + "@resume": {}, + "newSubSpace": "Nuovo sotto sapzio", + "@newSubSpace": {}, + "moveToDifferentSpace": "Cambia spazio", + "@moveToDifferentSpace": {}, + "moveUp": "Muoviti sopra", + "@moveUp": {}, + "moveDown": "Muoviti sotto", + "@moveDown": {}, "writeAMessageLangCodes": "Scrivi in {l1} o {l2}...", "requests": "Richieste", "holdForInfo": "Tieni premuto per informazioni sulla parola.", @@ -4485,45 +4520,6 @@ "playWithAI": "Gioca con l'IA per ora", "courseStartDesc": "Pangea Bot è pronto in qualsiasi momento!\n\n...ma imparare è meglio con gli amici!", "@@locale": "it", - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -11068,4 +11064,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index a3e3a81c8..6c60ae247 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1779,12 +1779,12 @@ "type": "String", "placeholders": {} }, - "waitingPartnerAcceptRequest": "パートナーのリクエスト承諾待ちです...", + "waitingPartnerAcceptRequest": "パートナーのリクエスト承諾待ちです…", "@waitingPartnerAcceptRequest": { "type": "String", "placeholders": {} }, - "waitingPartnerEmoji": "パートナーの絵文字承諾待ちです...", + "waitingPartnerEmoji": "パートナーの絵文字承諾待ちです…", "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -2394,15 +2394,6 @@ "@newSpace": {}, "reopenChat": "チャットを再開する", "@reopenChat": {}, - "signInWith": "{provider}でログイン", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "signInWithPassword": "パスワードでログイン", "@signInWithPassword": {}, "alwaysUse24HourFormat": "常に24時間表示を使用", @@ -4747,9 +4738,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -11855,4 +11844,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index 3f6998afb..5b6e36f17 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -140,7 +140,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "თქვენი ძველი შეტყობინებები დაცულია აღდგების გასაღებით. არ დაკარგოთ ის.", + "chatBackupDescription": "თქვენი შეტყობინებები დაცულია აღდგენის გასაღებით. არ დაკარგოთ ის.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -1247,7 +1247,6 @@ "report": "შეტყობინება", "signInWithPassword": "შესვლა პაროლით", "pleaseTryAgainLaterOrChooseDifferentServer": "გთხოვთ, სცადეთ მოგვიანებით ან აირჩიეთ სხვა სერვერი.", - "signInWith": "შესვლა {provider}-ით", "profileNotFound": "მომხმარებელი ვერ მოიძებნა სერვერზე. შესაძლოა, კავშირი იყოს პრობლემური ან მომხმარებელი არ არსებობს.", "setTheme": "პარამეტრის დაყენება:", "setColorTheme": "ფერის თემის დაყენება:", @@ -4561,14 +4560,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -4979,9 +4970,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12095,4 +12084,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index d59230507..3859105e8 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -2381,15 +2381,6 @@ } } }, - "signInWith": "{provider}로 로그인", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "fileIsTooBigForServer": "전송에 실패했습니다. 서버는 {max}가 넘는 파일을 지원하지 않습니다.", "@fileIsTooBigForServer": {}, "callingPermissions": "통화 권한", @@ -3272,12 +3263,26 @@ "notificationRuleCall": "전화", "@notificationRuleCall": {}, "setCustomPermissionLevel": "사용자 지정 권한 수준 설정", - "setPermissionsLevelDescription": "아래에서 미리 정의된 역할을 선택하거나 0에서 100 사이의 사용자 지정 권한 수준을 입력하세요.", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "아래에서 미리 정의된 역할을 선택하거나 0부터 100 사이의 사용자 지정 권한 수준을 입력해 주세요.", + "@setPermissionsLevelDescription": {}, "ignoreUser": "사용자 무시", + "@ignoreUser": {}, "normalUser": "일반 사용자", - "commandHint_roomupgrade": "이 방을 지정된 방 버전으로 업그레이드", + "@normalUser": {}, + "commandHint_roomupgrade": "이 방을 주어진 방 버전으로 업그레이드합니다", + "@commandHint_roomupgrade": {}, "checkList": "체크리스트", - "countInvited": "{count}명 초대됨", + "@checkList": {}, + "countInvited": "{count}초대받은", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, "userSpecificNotificationSettings": "사용자별 알림 설정", "notificationRuleCallDescription": "통화에 대해 사용자에게 알림을 보냅니다.", "notificationRuleEncryptedRoomOneToOne": "암호화된 1:1 방", @@ -4442,38 +4447,6 @@ "playWithAI": "일단 AI와 놀아보세요", "courseStartDesc": "파니아 봇은 언제든 준비되어 있습니다!\n\n...하지만 친구와 함께 배우는 것이 더 좋아요!", "@@locale": "ko", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, "@userSpecificNotificationSettings": { "type": "String", "placeholders": {} @@ -11173,4 +11146,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index ab65c2762..8adb45ef5 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -2516,7 +2516,6 @@ "report": "Pranešti", "signInWithPassword": "Prisijungti naudojant slaptažodį", "pleaseTryAgainLaterOrChooseDifferentServer": "Prašome pabandyti vėliau arba pasirinkti kitą serverį.", - "signInWith": "Prisijungti su {provider}", "profileNotFound": "Vartotojo nerasta serveryje. Galbūt yra ryšio problema arba vartotojas neegzistuoja.", "setTheme": "Nustatyti temą:", "setColorTheme": "Nustatyti spalvų temą:", @@ -4344,14 +4343,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -4762,9 +4753,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -11870,4 +11859,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index 4c5d77857..a5262fcf5 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -1001,7 +1001,7 @@ }, "storeSecurlyOnThisDevice": "Droši uzglabāt šajā ierīcē", "@storeSecurlyOnThisDevice": {}, - "yourChatBackupHasBeenSetUp": "Tērzēšanu rezerves kopēšana tika iestatīta.", + "yourChatBackupHasBeenSetUp": "Tērzēšanu rezerves kopēšana iestatīta.", "@yourChatBackupHasBeenSetUp": {}, "chatBackup": "Tērzēšanu rezerves kopēšana", "@chatBackup": { @@ -1081,15 +1081,6 @@ "type": "String", "placeholders": {} }, - "signInWith": "Pieteikties ar {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "username": "Lietotājvārds", "@username": { "type": "String", @@ -1250,7 +1241,7 @@ } } }, - "waitingPartnerEmoji": "Gaida, līdz biedrs apstiprinās emocijzīmi…", + "waitingPartnerEmoji": "Gaida, līdz biedrs apstiprinās emocijzīmes…", "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -1692,7 +1683,7 @@ "type": "String", "placeholders": {} }, - "tooManyRequestsWarning": "Pārāk daudz pieprasījumu. Lūgums vēlāk mēģināt vēlreiz.", + "tooManyRequestsWarning": "Pārāk daudz pieprasījumu. Lūdzu, vēlāk mēģini vēlreiz!", "@tooManyRequestsWarning": { "type": "String", "placeholders": {} @@ -1941,11 +1932,11 @@ "type": "String", "placeholders": {} }, - "inviteGroupChat": "📨 Uzaicināt kopas tērzēšanu", + "inviteGroupChat": "📨 Uzaicinājums uz kopas tērzēšanu", "@inviteGroupChat": {}, "appearOnTop": "Parādīt virspusē", "@appearOnTop": {}, - "invitePrivateChat": "📨 Uzaicināt privātu tērzēšanu", + "invitePrivateChat": "📨 Uzaicinājums uz privātu tērzēšanu", "@invitePrivateChat": {}, "verifyTitle": "Apliecina citu kontu", "@verifyTitle": { @@ -2256,7 +2247,7 @@ "type": "String", "placeholders": {} }, - "noGoogleServicesWarning": "Izskatās, ka Firebase mākoņziņojumapmaiņa nav pieejama šajā ierīcē. Lai joprojām saņemtu pašpiegādes paziņojumus, mēs iesakām uzstādīt ntfy. Ar ntfy vai citu Vienotās pašpiegādes nodrošinātāju ir iespējams saņemt pašpiegādes paziņojumus drošā veidā. ntfy var lejupielādēt no PlayStore vai F-Droid.", + "noGoogleServicesWarning": "Izskatās, ka Firebase mākoņziņojumapmaiņa nav pieejama šajā ierīcē. Lai joprojām saņemtu pašpiegādes paziņojumus, mēs iesakām uzstādīt ntfy. Ar ntfy vai citu UnifiedPush nodrošinātāju ir iespējams saņemt pašpiegādes paziņojumus drošā veidā. ntfy var lejupielādēt no Play Store vai F-Droid.", "@noGoogleServicesWarning": { "type": "String", "placeholders": {} @@ -2348,7 +2339,7 @@ } } }, - "chatBackupDescription": "Iepriekšējās ziņas ir aizsargātas ar atkopes atslēgu. Lūgums nodrošināt, ka tā netiek pazaudēta.", + "chatBackupDescription": "Ziņas ir aizsargātas ar atkopes atslēgu. Lūgums nodrošināt, ka tā netiek pazaudēta.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -3364,6 +3355,141 @@ "displayNavigationRail": "Rādīt pārvietošanās sliedi viedierīcēs", "@displayNavigationRail": {}, "customReaction": "Pielāgota reakcija", + "@customReaction": {}, + "moreEvents": "Vairāk notikumu", + "@moreEvents": {}, + "declineInvitation": "Noraidīt uzaicinājumu", + "@declineInvitation": {}, + "noMessagesYet": "Vēl nav ziņu", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Ilga piespiešana, lai ierakstītu balss ziņu.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Apturēt", + "@pause": {}, + "resume": "Atsākt", + "@resume": {}, + "newSubSpace": "Jauna apakšvieta", + "@newSubSpace": {}, + "moveToDifferentSpace": "Pārvietot uz citu vietu", + "@moveToDifferentSpace": {}, + "moveUp": "Pārvietot augšup", + "@moveUp": {}, + "moveDown": "Pārvietot lejup", + "@moveDown": {}, + "removeFromSpaceDescription": "Tērzēšana tiks noņemta no vietas, bet tā joprojām būs redzama tērzēšanu sarakstā.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} tērzēšanas", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "{spaces} dalībnieks", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "{spaces} dalībnieks var pieklauvēt", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Ziedot", + "@donate": {}, + "startedAPoll": "{username} uzsāka aptauju.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Aptauja", + "@poll": {}, + "startPoll": "Sākt aptauju", + "@startPoll": {}, + "endPoll": "Noslēgt aptauju", + "@endPoll": {}, + "answersVisible": "Atbildes ir redzamas", + "@answersVisible": {}, + "answersHidden": "Atbildes ir paslēptas", + "@answersHidden": {}, + "pollQuestion": "Aptaujas jautājums", + "@pollQuestion": {}, + "answerOption": "Atbildes iespēja", + "@answerOption": {}, + "addAnswerOption": "Pievienot atbildes iespēju", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Atļaut vairākas atbildes", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Aptauja ir noslēgusies", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =0{{count} balsu} =1{{count} balss} other{{count} balsis}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Atbildes būs redzams, kad aptauja noslēgsies", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Atbildēt pavedienā", + "@replyInThread": {}, + "countReplies": "{count, plural, =0{{count} atbilžu} =1{{count} atbilde} other{{count} atbildes}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Pavediens", + "@thread": {}, + "backToMainChat": "Atgriezties galvenajā tērzēšanā", + "@backToMainChat": {}, + "saveChanges": "Saglabāt izmaiņas", + "@saveChanges": {}, + "createSticker": "Izveidot uzlīmi vai emocijzīmi", + "@createSticker": {}, + "useAsSticker": "Izmantot kā uzlīmi", + "@useAsSticker": {}, + "useAsEmoji": "Izmantot kā emocijzīmi", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Uzlīmju pakas nosaukums jau pastāv", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Jauna uzlīmju paka", + "@newStickerPack": {}, + "stickerPackName": "Uzlīmju pakas nosaukums", + "@stickerPackName": {}, + "attribution": "Piedēvējums", + "@attribution": {}, + "skipChatBackup": "Izlaist tērzēšanu rezerves kopēšanu", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Vai tiešām? Bez tērzēšanu rezerves kopēšanas var tikt zaudēta piekļuve savām ziņām, kad tiks mainīta ierīce.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Ielādē ziņas", + "@loadingMessages": {}, + "setupChatBackup": "Iestatīt tērzēšanu rezerves kopēšanu", + "@setupChatBackup": {}, + "changedTheChatDescription": "{username} nomainīja tērzēšanas aprakstu", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} nomainīja tērzēšanas nosaukumu", + "@changedTheChatName": {}, "ignore": "Bloķēt", "ignoredUsers": "Bloķētie lietotāji", "writeAMessageLangCodes": "Rakstiet {l1} vai {l2}...", @@ -3407,7 +3533,6 @@ "updateLanguage": "Manas valodas", "whatLanguageYouWantToLearn": "Kuru valodu vēlaties iemācīties?", "whatIsYourBaseLanguage": "Kāda ir jūsu pamata valoda?", - "saveChanges": "Saglabāt izmaiņas", "publicProfileTitle": "Atļaut, lai manu profilu var atrast meklēšanā", "publicProfileDesc": "Ieslēdzot, jūs ļaujat citiem lietotājiem atrast jūsu profilu globālajā meklēšanas joslā un sūtīt pieprasījumus tērzēšanai. Šajā brīdī jūs varat izvēlēties pieņemt vai noraidīt pieprasījumu.", "errorDisableIT": "Tulkošanas palīdzība ir izslēgta.", @@ -4495,10 +4620,6 @@ "editCourseLater": "Jūs varat vēlāk rediģēt šablona nosaukumu, aprakstus un kursa attēlu.", "createCourse": "Izveidot kursu", "stats": "Statistika", - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@ignore": { "type": "String", "placeholders": {} @@ -4682,10 +4803,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11051,4 +11168,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index d5977516d..2f7929093 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -10,7 +10,7 @@ "type": "String", "placeholders": {} }, - "acceptedTheInvitation": "{username} godtok invitasjonen", + "acceptedTheInvitation": "👍 {username} godtok invitasjonen", "@acceptedTheInvitation": { "type": "String", "placeholders": { @@ -152,7 +152,7 @@ } } }, - "changedTheChatDescriptionTo": "{username} endret sludrebeskrivelse til: «{description}»", + "changedTheChatDescriptionTo": "{username} endret chatbeskrivelsen til: '{description}'", "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -164,7 +164,7 @@ } } }, - "changedTheChatNameTo": "{username} endret sludringsnavn til: «{chatname}»", + "changedTheChatNameTo": "{username} endret chatnavnet til: '{chatname}'", "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -185,7 +185,7 @@ } } }, - "changedTheDisplaynameTo": "{username} endret visningsnavn til: {displayname}", + "changedTheDisplaynameTo": "{username} endret visningsnavn til: '{displayname}'", "@changedTheDisplaynameTo": { "type": "String", "placeholders": { @@ -1813,486 +1813,1538 @@ }, "notAnImage": "Ikke en bildefil.", "@notAnImage": {}, - "alwaysUse24HourFormat": "falsk", - "setCustomPermissionLevel": "Sett egendefinert tillatelsesnivå", - "setPermissionsLevelDescription": "Vennligst velg en forhåndsdefinert rolle nedenfor eller skriv inn et egendefinert tillatelsesnivå mellom 0 og 100.", - "ignoreUser": "Ignorer bruker", - "normalUser": "Normal bruker", "importNow": "Importer nå", - "importEmojis": "Importer Emojis", - "importFromZipFile": "Importer fra .zip-fil", - "exportEmotePack": "Eksporter Emote-pakke som .zip", - "replace": "Erstatt", - "aboutHomeserver": "Om {homeserver}", - "confirmMatrixId": "Vennligst bekreft din Matrix-ID for å slette kontoen din.", - "supposedMxid": "Dette skal være {mxid}", - "addChatDescription": "Legg til en chatbeskrivelse...", - "commandHint_roomupgrade": "Oppgrader dette rommet til den gitte romversjonen", - "commandHint_googly": "Send noen googly-øyne", - "commandHint_cuddle": "Send en kose", - "commandHint_hug": "Send en klem", - "googlyEyesContent": "{senderName} sender deg googly-øyne", - "cuddleContent": "{senderName} koser deg", + "@importNow": {}, "hugContent": "{senderName} klemmer deg", - "appLockDescription": "Lås appen når den ikke er i bruk med en pinkode", + "@hugContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "space": "Område", + "@space": {}, + "spaces": "Områder", + "@spaces": {}, + "cuddleContent": "{senderName} koser med deg", + "@cuddleContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "appLockDescription": "Lås appen med en PIN-kode når den ikke er i bruk", + "@appLockDescription": {}, + "ignoreUser": "Ignorer bruker", + "@ignoreUser": {}, + "setCustomPermissionLevel": "Angi egendefinert tillatelsesnivå", + "@setCustomPermissionLevel": {}, + "normalUser": "Vanlig bruker", + "@normalUser": {}, + "replace": "Erstatt", + "@replace": {}, + "noMoreChatsFound": "Ingen flere chatter funnet ...", + "@noMoreChatsFound": {}, + "confirmMatrixId": "Vennligst bekreft din Matrix-ID for å slette kontoen din.", + "@confirmMatrixId": {}, + "unread": "Ulest", + "@unread": {}, + "aboutHomeserver": "Om {homeserver}", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "commandHint_hug": "Send en klem", + "@commandHint_hug": {}, + "markAsRead": "Marker som lest", + "@markAsRead": {}, + "openLinkInBrowser": "Åpne lenke i nettleser", + "@openLinkInBrowser": {}, + "start": "Start", + "@start": {}, + "passwordsDoNotMatch": "Passordene stemmer ikke overens", + "@passwordsDoNotMatch": {}, + "decline": "Avslå", + "@decline": {}, + "emailOrUsername": "E-post eller brukernavn", + "@emailOrUsername": {}, + "encryptThisChat": "Krypter denne chatten", + "@encryptThisChat": {}, + "nextAccount": "Neste konto", + "@nextAccount": {}, + "doNotShowAgain": "Ikke vis igjen", + "@doNotShowAgain": {}, + "notificationRuleContainsUserName": "Inneholder brukernavn", + "@notificationRuleContainsUserName": {}, + "notificationRuleMaster": "Demp alle varslinger", + "@notificationRuleMaster": {}, + "presenceStyle": "Tilstedeværelse:", + "@presenceStyle": { + "type": "String", + "placeholders": {} + }, + "verified": "Verifisert", + "@verified": { + "type": "String", + "placeholders": {} + }, + "saveFile": "Lagre fil", + "@saveFile": { + "type": "String", + "placeholders": {} + }, + "recoveryKey": "Gjenopprettingsnøkkel", + "@recoveryKey": {}, + "sendSticker": "Send sticker", + "@sendSticker": { + "type": "String", + "placeholders": {} + }, + "shareLocation": "Del lokasjon", + "@shareLocation": { + "type": "String", + "placeholders": {} + }, + "showPassword": "Vis passord", + "@showPassword": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWait": "Synkroniserer … Vent litt.", + "@synchronizingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "dismiss": "Avvis", + "@dismiss": {}, + "openChat": "Åpne chat", + "@openChat": {}, + "addWidget": "Legg til widget", + "@addWidget": {}, + "reopenChat": "Gjenåpne chat", + "@reopenChat": {}, + "changeTheDescriptionOfTheGroup": "Endre beskrivelsen til chatten", + "@changeTheDescriptionOfTheGroup": {}, + "inviteOtherUsers": "Inviter andre brukere til denne chatten", + "@inviteOtherUsers": {}, + "open": "Åpne", + "@open": {}, + "waitingForServer": "Venter på server...", + "@waitingForServer": {}, + "notificationRuleJitsi": "Jitsi", + "@notificationRuleJitsi": {}, + "takeAPhoto": "Ta et bilde", + "@takeAPhoto": {}, + "setChatDescription": "Sett chat beskrivelse", + "@setChatDescription": {}, + "singlesignon": "Single Sign on (SSO)", + "@singlesignon": { + "type": "String", + "placeholders": {} + }, + "openGallery": "Åpne galleri", + "@openGallery": {}, + "widgetCustom": "Egendefinert", + "@widgetCustom": {}, + "widgetVideo": "Video", + "@widgetVideo": {}, + "pushNotificationsNotAvailable": "Push-varsler er ikke tilgjengelige", + "@pushNotificationsNotAvailable": {}, + "gallery": "Galleri", + "@gallery": {}, + "moderatorLevel": "{level} - Moderator", + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "adminLevel": "{level} - Admin", + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "restricted": "Begrenset", + "@restricted": {}, + "updateInstalled": "🎉 Oppdatering {version} installert!", + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "calculatingFileSize": "Beregner filstørrelse...", + "@calculatingFileSize": {}, + "continueText": "Fortsett", + "@continueText": {}, + "serverInformation": "Serverinformasjon:", + "@serverInformation": {}, + "previous": "Forrige", + "@previous": {}, + "name": "Navn", + "@name": {}, + "version": "Versjon", + "@version": {}, + "invalidUrl": "Ugyldig url", + "@invalidUrl": {}, + "notificationRuleReaction": "Reaksjon", + "@notificationRuleReaction": {}, + "notificationRuleMessage": "Melding", + "@notificationRuleMessage": {}, + "notificationRuleEncrypted": "Kkryptert", + "@notificationRuleEncrypted": {}, + "more": "Mer", + "@more": {}, + "passwordIsWrong": "Det inntastede passordet ditt er feil", + "@passwordIsWrong": {}, + "startConversation": "Start samtale", + "@startConversation": {}, + "manageAccount": "Administrer konto", + "@manageAccount": {}, + "nothingFound": "Ingenting funnet...", + "@nothingFound": {}, + "incomingMessages": "Innkommende meldinger", + "@incomingMessages": {}, + "changelog": "Endringslogg", + "@changelog": {}, + "contactServerAdmin": "Kontakt serveradministrator", + "@contactServerAdmin": {}, + "setWallpaper": "Sett bakgrunnsbilde", + "@setWallpaper": {}, + "unsupportedAndroidVersion": "Usupportert Android-versjon", + "@unsupportedAndroidVersion": {}, + "widgetName": "Navn", + "@widgetName": {}, + "youJoinedTheChat": "Du har blitt med i chatten", + "@youJoinedTheChat": {}, + "widgetJitsi": "Jitsi Meet", + "@widgetJitsi": {}, + "widgetNameError": "Vennligst oppgi et visningsnavn.", + "@widgetNameError": {}, + "youRejectedTheInvitation": "Du har avvist invitasjonen", + "@youRejectedTheInvitation": {}, + "formattedMessages": "Formaterte meldinger", + "@formattedMessages": {}, + "allDevices": "Alle enheter", + "@allDevices": {}, + "learnMore": "Lær mer", + "@learnMore": {}, + "sorryThatsNotPossible": "Beklager... det er ikke mulig", + "@sorryThatsNotPossible": {}, + "markAsUnread": "Marker som ulest", + "@markAsUnread": {}, + "newGroup": "Ny gruppe", + "@newGroup": {}, + "userRole": "Brukerrolle", + "@userRole": {}, + "addLink": "Legg til lenke", + "@addLink": {}, + "synchronizingPleaseWaitCounter": " Synkroniserer… ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "unverified": "Ikke verifisert", + "@unverified": {}, + "thisDevice": "Denne enheten:", + "@thisDevice": {}, + "sendingAttachment": "Sender vedlegg...", + "@sendingAttachment": {}, + "removeYourAvatar": "Fjern din avatar", + "@removeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "importFromZipFile": "Importer fra .zip-fil", + "@importFromZipFile": {}, + "select": "Velg", + "@select": {}, + "databaseMigrationTitle": "Databasen er optimalisert", + "@databaseMigrationTitle": {}, + "newPassword": "Nytt passord", + "@newPassword": {}, + "files": "Filer", + "@files": {}, + "searchIn": "Søk i chatten «{chat}»...", + "@searchIn": { + "type": "String", + "placeholders": { + "chat": { + "type": "String" + } + } + }, + "approve": "Godkjenn", + "@approve": {}, + "goToSpace": "Gå til område: {space}", + "@goToSpace": { + "type": "String", + "space": {} + }, + "userLevel": "{level} - Bruker", + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "oneOfYourDevicesIsNotVerified": "En av dine enheter er ikke verifisert", + "@oneOfYourDevicesIsNotVerified": {}, + "website": "Nettside", + "@website": {}, + "boldText": "Fet skrift", + "@boldText": {}, + "pleaseFillOut": "Vennligst fyll ut", + "@pleaseFillOut": {}, + "importEmojis": "Importer emojier", + "@importEmojis": {}, + "supposedMxid": "Denne bør være {mxid}", + "@supposedMxid": { + "type": "String", + "placeholders": { + "mxid": { + "type": "String" + } + } + }, + "addChatDescription": "Legg til chat beskrivelse...", + "@addChatDescription": {}, + "commandHint_roomupgrade": "Oppgrader dette rommet til den gitte romversjonen", + "@commandHint_roomupgrade": {}, + "hideMemberChangesInPublicChatsBody": "Ikke vis i chattens tidslinje hvis noen blir med i eller forlater en offentlig chat for økt lesbarhet.", + "@hideMemberChangesInPublicChatsBody": {}, + "overview": "Oversikt", + "@overview": {}, + "notifyMeFor": "Varsle meg om", + "@notifyMeFor": {}, + "passwordRecoverySettings": "Innstillinger for gjenoppretting av passord", + "@passwordRecoverySettings": {}, + "people": "Folk", + "@people": { + "type": "String", + "placeholders": {} + }, + "pleaseChoose": "Vennligst velg", + "@pleaseChoose": { + "type": "String", + "placeholders": {} + }, + "pleaseChooseAPasscode": "Vennligst velg en passordkode", + "@pleaseChooseAPasscode": { + "type": "String", + "placeholders": {} + }, + "pleaseEnter4Digits": "Skriv inn fire sifre eller la feltet stå tomt for å deaktivere applåsen.", + "@pleaseEnter4Digits": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterRecoveryKey": "Vennligst skriv inn gjenopprettingsnøkkelen din:", + "@pleaseEnterRecoveryKey": {}, + "pleaseEnterYourPin": "Vennligst skriv inn PIN-koden din", + "@pleaseEnterYourPin": { + "type": "String", + "placeholders": {} + }, + "globalChatId": "Global chat-ID", + "@globalChatId": {}, + "allRooms": "Alle gruppechatter", + "@allRooms": { + "type": "String", + "placeholders": {} + }, + "chatPermissions": "Chat tillatelser", + "@chatPermissions": {}, + "setPermissionsLevelDescription": "Vennligst velg en forhåndsdefinert rolle nedenfor eller skriv inn et tilpasset tillatelsesnivå mellom 0 og 100.", + "@setPermissionsLevelDescription": {}, + "commandHint_invite": "Inviter den gitte brukeren til dette rommet", + "@commandHint_invite": { + "type": "String", + "description": "Usage hint for the command /invite" + }, + "commandHint_join": "Bli med i det gitte rommet", + "@commandHint_join": { + "type": "String", + "description": "Usage hint for the command /join" + }, + "chats": "Chatter", + "@chats": { + "type": "String", + "placeholders": {} + }, + "yourChatBackupHasBeenSetUp": "Sikkerhetskopien av chatten din er konfigurert.", + "@yourChatBackupHasBeenSetUp": {}, + "discover": "Oppdag", + "@discover": {}, + "thereAreCountUsersBlocked": "Akkurat nå er det {count} blokkerte brukere.", + "@thereAreCountUsersBlocked": { + "type": "String", + "count": {} + }, + "sendCanceled": "Sending avbrutt", + "@sendCanceled": {}, + "loginWithMatrixId": "Logg på med Matrix ID", + "@loginWithMatrixId": {}, + "discoverHomeservers": "Oppdag hjemmeservere", + "@discoverHomeservers": {}, + "shareInviteLink": "Del invitasjonslenke", + "@shareInviteLink": {}, + "scanQrCode": "Skann QR-kode", + "@scanQrCode": {}, + "messagesStyle": "Meldinger:", + "@messagesStyle": {}, + "hydrate": "Gjenopprett fra sikkerhetskopifil", + "@hydrate": {}, + "oneClientLoggedOut": "En av klientene dine har blitt logget ut", + "@oneClientLoggedOut": {}, + "addAccount": "Legg til konto", + "@addAccount": {}, + "enableMultiAccounts": "(BETA) Aktiver flere kontoer på denne enheten", + "@enableMultiAccounts": {}, + "openInMaps": "Åpne i kart", + "@openInMaps": { + "type": "String", + "placeholders": {} + }, + "link": "Lenke", + "@link": {}, + "serverRequiresEmail": "Denne serveren må validere e-postadressen din for registrering.", + "@serverRequiresEmail": {}, + "or": "Eller", + "@or": { + "type": "String", + "placeholders": {} + }, + "previousAccount": "Forrige konto", + "@previousAccount": {}, + "widgetUrlError": "Dette er ikke en gyldig URL.", + "@widgetUrlError": {}, + "custom": "Egendefinert", + "@custom": {}, + "user": "Bruker", + "@user": {}, + "users": "Brukere", + "@users": {}, + "countInvited": "{count} inviterte", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "createGroup": "Opprett gruppe", + "@createGroup": {}, + "editRoomAliases": "Rediger rom aliaser", + "@editRoomAliases": { + "type": "String", + "placeholders": {} + }, + "accessAndVisibility": "Tilgang og synlighet", + "@accessAndVisibility": {}, + "hideInvalidOrUnknownMessageFormats": "Skjul ugyldige eller ukjente meldingsformater", + "@hideInvalidOrUnknownMessageFormats": {}, + "block": "Blokkér", + "@block": {}, + "blockedUsers": "Blokkerte brukere", + "@blockedUsers": {}, + "tryAgain": "Prøv igjen", + "@tryAgain": {}, + "invalidServerName": "Ugyldig servernavn", + "@invalidServerName": {}, + "messageInfo": "Meldingsinformasjon", + "@messageInfo": {}, + "time": "Tid", + "@time": {}, + "messageType": "Meldingstype", + "@messageType": {}, + "sender": "Avsender", + "@sender": {}, + "publish": "Publiser", + "@publish": {}, + "videoWithSize": "Video ({size})", + "@videoWithSize": { + "type": "String", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "reportUser": "Rapporter bruker", + "@reportUser": {}, + "youHaveWithdrawnTheInvitationFor": "Du har trukket tilbake invitasjonen for {user}", + "@youHaveWithdrawnTheInvitationFor": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youAcceptedTheInvitation": "👍 Du har akseptert invitasjonen", + "@youAcceptedTheInvitation": {}, + "youBannedUser": "Du stengte ute {user}", + "@youBannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "chatDescriptionHasBeenChanged": "Chatbeskrivelsen er endret", + "@chatDescriptionHasBeenChanged": {}, + "goToTheNewRoom": "Gå til det nye rommet", + "@goToTheNewRoom": { + "type": "String", + "placeholders": {} + }, + "screenSharingTitle": "skjermdeling", + "@screenSharingTitle": {}, + "screenSharingDetail": "Du deler skjermen din i FuffyChat", + "@screenSharingDetail": {}, + "callingPermissions": "Anropstillatelser", + "@callingPermissions": {}, + "callingAccountDetails": "Lar FluffyChat bruke den innebygde Android-oppringingsappen.", + "@callingAccountDetails": {}, + "appearOnTop": "Vis øverst", + "@appearOnTop": {}, + "otherCallingPermissions": "Mikrofon, kamera og andre FluffyChat-tillatelser", + "@otherCallingPermissions": {}, + "whyIsThisMessageEncrypted": "Hvorfor er denne meldingen uleselig?", + "@whyIsThisMessageEncrypted": {}, + "yourGlobalUserIdIs": "Din globale bruker-ID er: ", + "@yourGlobalUserIdIs": {}, + "searchChatsRooms": "Søk etter #chatter, @brukere...", + "@searchChatsRooms": {}, + "groupName": "Gruppenavn", + "@groupName": {}, + "createGroupAndInviteUsers": "Opprett en gruppe og inviter brukere", + "@createGroupAndInviteUsers": {}, + "invite": "Inviter", + "@invite": {}, + "wrongPinEntered": "Feil PIN-kode tastet inn! Prøv igjen om {seconds} sekunder...", + "@wrongPinEntered": { + "type": "String", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "sendAsText": "Send som tekst", + "@sendAsText": { + "type": "String" + }, + "sendImages": "Send {count} bilde", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "presencesToggle": "Vis statusmeldinger fra andre brukere", + "@presencesToggle": { + "type": "String", + "placeholders": {} + }, + "voiceCall": "Taleanrop", + "@voiceCall": {}, + "accessAndVisibilityDescription": "Hvem som har lov til å bli med i denne chatten og hvordan chatten kan oppdages.", + "@accessAndVisibilityDescription": {}, + "calls": "Anrop", + "@calls": {}, + "customEmojisAndStickers": "Egendefinerte emojier og klistremerker", + "@customEmojisAndStickers": {}, + "oopsPushError": "Oops! Dessverre oppsto det en feil under oppsettet av push-varsler.", + "@oopsPushError": { + "type": "String", + "placeholders": {} + }, + "openVideoCamera": "Åpne kameraet for en video", + "@openVideoCamera": { + "type": "String", + "placeholders": {} + }, + "obtainingLocation": "Henter sted …", + "@obtainingLocation": { + "type": "String", + "placeholders": {} + }, + "noDatabaseEncryption": "Databasekryptering støttes ikke på denne plattformen", + "@noDatabaseEncryption": {}, + "changeGeneralChatSettings": "Endre generelle chatinnstillinger", + "@changeGeneralChatSettings": {}, + "changeTheChatPermissions": "Endre chattillatelsene", + "@changeTheChatPermissions": {}, + "youInvitedToBy": "📩 Du har blitt invitert via lenke til:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": { + "type": "String" + } + } + }, + "youInvitedBy": "📩 Du har blitt invitert av {user}", + "@youInvitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "invitedBy": "📩 Invitert av {user}", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youInvitedUser": "📩 Du inviterte {user}", + "@youInvitedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youKicked": "👞 You sparket ut {user}", + "@youKicked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youKickedAndBanned": "🙅 Du sparket og stengte ute {user}", + "@youKickedAndBanned": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "countFiles": "{count} filer", + "@countFiles": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "noOtherDevicesFound": "Ingen andre enheter funnet", + "@noOtherDevicesFound": {}, + "fileIsTooBigForServer": "Kan ikke sende! Serveren støtter bare vedlegg opptil {max}.", + "@fileIsTooBigForServer": { + "type": "String", + "placeholders": { + "max": { + "type": "String" + } + } + }, + "fileHasBeenSavedAt": "Filen er lagret på {path}", + "@fileHasBeenSavedAt": { + "type": "String", + "placeholders": { + "path": { + "type": "String" + } + } + }, + "jumpToLastReadMessage": "Hopp til sist leste melding", + "@jumpToLastReadMessage": {}, + "serverLimitReached": "Grensen for server er nådd! Venter i {seconds} sekunder...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "blur": "Uskarphet:", + "@blur": {}, + "opacity": "Ugjennomsiktighet:", + "@opacity": {}, + "noContactInformationProvided": "Serveren oppgir ingen gyldig kontaktinformasjon", + "@noContactInformationProvided": {}, + "supportPage": "Supportside", + "@supportPage": {}, + "compress": "Komprimer", + "@compress": {}, + "italicText": "Kursiv tekst", + "@italicText": {}, + "strikeThrough": "Gjennomstreking", + "@strikeThrough": {}, + "youUnbannedUser": "Du opphevet utestengelsen av {user}", + "@youUnbannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "disableEncryptionWarning": "Av sikkerhetshensyn kan du ikke deaktivere kryptering i en chat der det har vært aktivert tidligere.", + "@disableEncryptionWarning": {}, + "deviceKeys": "Enhetsnøkler:", + "@deviceKeys": {}, + "readUpToHere": "Lest frem til her", + "@readUpToHere": {}, + "jump": "Hopp", + "@jump": {}, + "reportErrorDescription": "😭 Å nei. Noe gikk galt. Hvis du ønsker det, kan du rapportere denne feilen til utviklerne.", + "@reportErrorDescription": {}, + "invalidInput": "Ugyldig inndata!", + "@invalidInput": {}, + "pleaseEnterANumber": "Vennligst skriv inn et tall større enn 0", + "@pleaseEnterANumber": {}, + "archiveRoomDescription": "Chatten vil bli flyttet til arkivet. Andre brukere vil kunne se at du har forlatt chatten.", + "@archiveRoomDescription": {}, + "removeDevicesDescription": "Du vil bli logget ut av denne enheten og vil ikke lenger kunne motta meldinger.", + "@removeDevicesDescription": {}, + "profileNotFound": "Brukeren ble ikke funnet på serveren. Kanskje det er et tilkoblingsproblem, eller brukeren finnes ikke.", + "@profileNotFound": {}, + "setTheme": "Angi tema:", + "@setTheme": {}, + "setColorTheme": "Angi fargetema:", + "@setColorTheme": {}, + "inviteGroupChat": "📨 Invitasjon til gruppechat", + "@inviteGroupChat": {}, + "invitePrivateChat": "📨 Invitasjon til privat chat", + "@invitePrivateChat": {}, + "pleaseChooseAStrongPassword": "Vennligst velg et sterkt passord", + "@pleaseChooseAStrongPassword": {}, + "publicLink": "Offentlig lenke", + "@publicLink": {}, + "publicChatAddresses": "Offentlige chatadresser", + "@publicChatAddresses": {}, + "createNewAddress": "Opprett ny adresse", + "@createNewAddress": {}, + "initAppError": "Det oppsto en feil under oppstart av appen", + "@initAppError": {}, + "minimumPowerLevel": "{level} er det laveste strømnivået.", + "@minimumPowerLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "String" + } + } + }, + "sendReadReceipts": "Send lesebekreftelser", + "@sendReadReceipts": {}, + "searchMore": "Søk mer...", + "@searchMore": {}, + "shareKeysWithDescription": "Hvilke enheter bør man stole på, slik at de kan lese meldingene dine i krypterte chatter?", + "@shareKeysWithDescription": {}, + "crossVerifiedDevicesIfEnabled": "Kryssbekreftede enheter hvis dette er aktivert", + "@crossVerifiedDevicesIfEnabled": {}, + "crossVerifiedDevices": "Kryssverifiserte enheter", + "@crossVerifiedDevices": {}, + "verifiedDevicesOnly": "Bare verifiserte enheter", + "@verifiedDevicesOnly": {}, + "recordAVideo": "Ta opp en video", + "@recordAVideo": {}, + "optionalMessage": "(Valgfritt) melding...", + "@optionalMessage": {}, + "notSupportedOnThisDevice": "Ikke støttet på denne enheten", + "@notSupportedOnThisDevice": {}, + "enterNewChat": "Bli med i ny chat", + "@enterNewChat": {}, + "pleaseWaitUntilInvited": "Vennligst vent nå, til noen fra rommet inviterer deg.", + "@pleaseWaitUntilInvited": {}, + "notificationRuleSuppressNoticesDescription": "Demper varsler fra automatiserte klienter som roboter.", + "@notificationRuleSuppressNoticesDescription": {}, + "notificationRuleInviteForMe": "Inviter for meg", + "@notificationRuleInviteForMe": {}, + "notificationRuleInviteForMeDescription": "Varsler bruker når en blir invitert til et rom.", + "@notificationRuleInviteForMeDescription": {}, + "newChatRequest": "📩 Ny chatforespørsel", + "@newChatRequest": {}, + "appWantsToUseForLogin": "Bruk '{server}' for å logge inn", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appWantsToUseForLoginDescription": "Du gir herved tillatelse til at appen og nettstedet deler informasjon om deg.", + "@appWantsToUseForLoginDescription": {}, + "exportEmotePack": "Eksporter smilefjes som .zip-fil", + "@exportEmotePack": {}, + "noUsersFoundWithQuery": "Dessverre ble ingen bruker funnet med \"{query}\". Sjekk om du har skrevet feil.", + "@noUsersFoundWithQuery": { + "type": "String", + "placeholders": { + "query": { + "type": "String" + } + } + }, + "chatCanBeDiscoveredViaSearchOnServer": "Chatten kan oppdages via søket på {server}", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "leaveEmptyToClearStatus": "La stå tomt for å slette statusen din.", + "@leaveEmptyToClearStatus": {}, + "searchForUsers": "Søk etter @brukere...", + "@searchForUsers": {}, + "pleaseEnterYourCurrentPassword": "Skriv inn ditt nåværende passord", + "@pleaseEnterYourCurrentPassword": {}, + "databaseMigrationBody": "Vent litt. Dette kan ta et øyeblikk.", + "@databaseMigrationBody": {}, + "wrongRecoveryKey": "Beklager ... dette ser ikke ut til å være riktig gjenopprettingsnøkkel.", + "@wrongRecoveryKey": {}, + "declineInvitation": "Avslå invitasjon", + "@declineInvitation": {}, + "restoreSessionBody": "Appen prøver nå å gjenopprette økten din fra sikkerhetskopien. Rapporter denne feilen til utviklerne på {url}. Feilmeldingen er: {error}", + "@restoreSessionBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "forwardMessageTo": "Videresende melding til {roomName}?", + "@forwardMessageTo": { + "type": "String", + "placeholders": { + "roomName": { + "type": "String" + } + } + }, + "hideUnimportantStateEvents": "Skjul uviktige tilstandshendelser", + "@hideUnimportantStateEvents": {}, + "hidePresences": "Skjul statuslisten?", + "@hidePresences": {}, + "signInWithPassword": "Logg inn med passord", + "@signInWithPassword": {}, + "pleaseTryAgainLaterOrChooseDifferentServer": "Prøv igjen senere eller velg en annen server.", + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "noMessagesYet": "Ingen meldinger enda", + "@noMessagesYet": {}, + "notificationRuleMasterDescription": "Overstyrer alle andre regler og deaktiverer alle varsler.", + "@notificationRuleMasterDescription": {}, + "notificationRuleSuppressNotices": "Undertrykk automatiserte meldinger", + "@notificationRuleSuppressNotices": {}, + "startedKeyVerification": "{sender} startet nøkkelverifisering", + "@startedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "transparent": "Gjennomsiktig", + "@transparent": {}, + "stickers": "Stickers", + "@stickers": {}, + "commandHint_ignore": "Ignorer den oppgitte matrix IDen", + "@commandHint_ignore": {}, + "commandHint_unignore": "Opphev ignorering av den gitte matrix IDen", + "@commandHint_unignore": {}, + "unreadChatsInApp": "{appname}: {unread} uleste chatter", + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "changeTheVisibilityOfChatHistory": "Endre synligheten til chatloggen", + "@changeTheVisibilityOfChatHistory": {}, + "changeTheCanonicalRoomAlias": "Endre hovedadressen til den offentlige chatten", + "@changeTheCanonicalRoomAlias": {}, + "sendRoomNotifications": "Send en @room varsling", + "@sendRoomNotifications": {}, + "formattedMessagesDescription": "Vis rikt meldingsinnhold som fet skrift ved hjelp av markdown.", + "@formattedMessagesDescription": {}, + "verifyOtherUser": "🔐 Verifiser annen bruker", + "@verifyOtherUser": {}, + "verifyOtherDevice": "🔐 Verifiser annen enhet", + "@verifyOtherDevice": {}, + "sendTypingNotificationsDescription": "Andre deltakere i en chat kan se når du skriver en ny melding.", + "@sendTypingNotificationsDescription": {}, + "sendReadReceiptsDescription": "Andre deltakere i en chat kan se når du har lest en melding.", + "@sendReadReceiptsDescription": {}, + "commandHint_logout": "Logg av din nåværende enhet", + "@commandHint_logout": {}, + "commandHint_logoutall": "Logg ut alle aktive enheter", + "@commandHint_logoutall": {}, + "resume": "Gjenoppta", + "@resume": {}, + "unknownPushRule": "Ukjent push-regel '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "sentVoiceMessage": "🎙️ {duration} – Talemelding fra {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "Hvis du sletter denne varslingsinnstillingen, kan du ikke angre dette.", + "@deletePushRuleCanNotBeUndone": {}, + "shareKeysWith": "Del nøkler med...", + "@shareKeysWith": {}, + "canceledKeyVerification": "{sender} avbrøt nøkkelverifisering", + "@canceledKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "completedKeyVerification": "{sender} fullførte nøkkelverifisering", + "@completedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "isReadyForKeyVerification": "{sender} er klar for nøkkelverifisering", + "@isReadyForKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "requestedKeyVerification": "{sender} har bedt om nøkkelverifisering", + "@requestedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "notificationRuleEncryptedRoomOneToOne": "Kryptert rom én-til-én", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Varsler brukeren om meldinger i krypterte en-til-en-rom.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "Rom én-til-én", + "@notificationRuleRoomOneToOne": {}, + "customEmojisAndStickersBody": "Legg til eller del tilpassede emojier eller klistremerker som kan brukes i hvilken som helst chat.", + "@customEmojisAndStickersBody": {}, + "homeserver": "Hjemmeserver", + "@homeserver": {}, + "errorObtainingLocation": "Feil ved henting av posisjon: {error}", + "@errorObtainingLocation": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "chatDescription": "Chat beskrivelse", + "@chatDescription": {}, + "hideRedactedMessages": "Skjul redigerte meldinger", + "@hideRedactedMessages": {}, + "hideRedactedMessagesBody": "Hvis noen redigerer en melding, vil ikke denne meldingen lenger være synlig i chatten.", + "@hideRedactedMessagesBody": {}, + "blockListDescription": "Du kan blokkere brukere som forstyrrer deg. Du vil ikke kunne motta meldinger eller rominvitasjoner fra brukerne på din personlige blokkeringsliste.", + "@blockListDescription": {}, + "blockUsername": "Ignorer brukernavn", + "@blockUsername": {}, + "inviteContactToGroupQuestion": "Vil du invitere {contact} til chatten «{groupName}»?", + "@inviteContactToGroupQuestion": {}, + "noChatDescriptionYet": "Ingen chatbeskrivelse er opprettet ennå.", + "@noChatDescriptionYet": {}, + "redactMessageDescription": "Meldingen vil bli redigert for alle deltakerne i denne samtalen. Dette kan ikke angres.", + "@redactMessageDescription": {}, + "optionalRedactReason": "(Valgfritt) Årsak til redigering av denne meldingen...", + "@optionalRedactReason": {}, + "locationDisabledNotice": "Lokasjonstjenester er deaktivert. Vennligst aktiver dem for at du skal kunne dele posisjonen din.", + "@locationDisabledNotice": { + "type": "String", + "placeholders": {} + }, + "locationPermissionDeniedNotice": "Lokasjonstillatelse nektet. Gi dem tillatelse til å dele din lokasjon.", + "@locationPermissionDeniedNotice": { + "type": "String", + "placeholders": {} + }, + "dehydrate": "Eksporter økten og slett enheten", + "@dehydrate": {}, + "dehydrateWarning": "Denne handlingen kan ikke angres. Sørg for at du lagrer sikkerhetskopifilen på en trygg måte.", + "@dehydrateWarning": {}, + "dehydrateTor": "TOR-brukere: Eksporter økt", + "@dehydrateTor": {}, + "dehydrateTorLong": "For TOR-brukere anbefales det å eksportere økten før vinduet lukkes.", + "@dehydrateTorLong": {}, + "hydrateTor": "TOR-brukere: Importer eksportert økt", + "@hydrateTor": {}, + "hydrateTorLong": "Eksporterte du økten din sist gang på TOR? Importer den raskt og fortsett å chatte.", + "@hydrateTorLong": {}, + "noEncryptionForPublicRooms": "Du kan bare aktivere kryptering på rom som ikke er offentlig tilgjengelig.", + "@noEncryptionForPublicRooms": { + "type": "String", + "placeholders": {} + }, + "noMatrixServer": "{server1} er ingen matrix-server, bruk {server2} i stedet?", + "@noMatrixServer": { + "type": "String", + "placeholders": { + "server1": { + "type": "String" + }, + "server2": { + "type": "String" + } + } + }, + "roomNotificationSettings": "Innstillinger for romvarsler", + "@roomNotificationSettings": {}, + "userSpecificNotificationSettings": "Brukerspesifikke varslingsinnstillinger", + "@userSpecificNotificationSettings": {}, + "otherNotificationSettings": "Andre varslingsinnstillinger", + "@otherNotificationSettings": {}, + "contentNotificationSettings": "Innstillinger for innholdsvarslinger", + "@contentNotificationSettings": {}, + "generalNotificationSettings": "Generelle varslingsinnstillinger", + "@generalNotificationSettings": {}, + "appIntroduction": "Med FluffyChat kan du chatte med vennene dine på tvers av forskjellige meldingstjenester. Finn ut mer på https://matrix.org eller trykk bare på *Fortsett*.", + "@appIntroduction": {}, + "notificationRuleContainsUserNameDescription": "Varsler bruker når en melding inneholder ens brukernavn.", + "@notificationRuleContainsUserNameDescription": {}, + "hideMemberChangesInPublicChats": "Skjul medlemsendringer i offentlige chatter", + "@hideMemberChangesInPublicChats": {}, + "removeFromSpace": "Fjern fra området", + "@removeFromSpace": {}, + "addToSpaceDescription": "Velg områder hvor denne chatten legges til.", + "@addToSpaceDescription": {}, + "pleaseEnterRecoveryKeyDescription": "For å låse opp gamle meldinger, vennligst skriv inn gjenopprettingsnøkkelen som ble generert i en tidligere økt. Gjenopprettingsnøkkelen er IKKE passordet ditt.", + "@pleaseEnterRecoveryKeyDescription": {}, + "reactedWith": "{sender} reagerte med {reaction}", + "@reactedWith": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "reaction": { + "type": "String" + } + } + }, + "pinMessage": "Fest til rommet", + "@pinMessage": {}, + "emojis": "Emojier", + "@emojis": {}, + "placeCall": "Ringe opp", + "@placeCall": {}, + "unsupportedAndroidVersionLong": "Denne funksjonen krever en nyere Android-versjon. Se etter oppdateringer eller støtte for Lineage OS.", + "@unsupportedAndroidVersionLong": {}, + "videoCallsBetaWarning": "Vær oppmerksom på at videosamtaler for øyeblikket er i betaversjon. Det fungerer kanskje ikke som forventet eller i det hele tatt.", + "@videoCallsBetaWarning": {}, + "experimentalVideoCalls": "Eksperimentelle videoanrop", + "@experimentalVideoCalls": {}, + "notificationRuleRoomnotifDescription": "Varsler brukeren når en melding inneholder ‘@room’.", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleTombstoneDescription": "Varsler brukeren om meldinger om deaktivering av rom.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleReactionDescription": "Demper varsler for reaksjoner.", + "@notificationRuleReactionDescription": {}, + "notificationRuleSuppressEdits": "Demp redigeringer", + "@notificationRuleSuppressEdits": {}, + "chatHasBeenAddedToThisSpace": "Chatten er lagt til i dette området", + "@chatHasBeenAddedToThisSpace": {}, + "clearArchive": "Tøm arkivet", + "@clearArchive": {}, + "commandHint_markasgroup": "Merk som gruppe", + "@commandHint_markasgroup": {}, + "commandHint_ban": "Utesteng den gitte brukeren fra dette rommet", + "@commandHint_ban": { + "type": "String", + "description": "Usage hint for the command /ban" + }, + "commandHint_clearcache": "Tøm cache", + "@commandHint_clearcache": { + "type": "String", + "description": "Usage hint for the command /clearcache" + }, + "homeserverDescription": "Alle dataene dine lagres på hjemmeserveren, akkurat som hos en e-postleverandør. Du kan velge hvilken hjemmeserver du vil bruke, samtidig som du fortsatt kan kommunisere med alle. Lær mer på https://matrix.org.", + "@homeserverDescription": {}, + "doesNotSeemToBeAValidHomeserver": "Ser ikke ut til å være en kompatibel hjemmeserver. Feil URL?", + "@doesNotSeemToBeAValidHomeserver": {}, + "prepareSendingAttachment": "Forbered sending av vedlegg...", + "@prepareSendingAttachment": {}, + "generatingVideoThumbnail": "Genererer videominiatyrbilde ...", + "@generatingVideoThumbnail": {}, + "compressVideo": "Komprimerer video...", + "@compressVideo": {}, + "welcomeText": "Hei, hei! 👋 Dette er FluffyChat. Du kan logge på hvilken som helst hjemmeserver som er kompatibel med https://matrix.org. Og deretter chatte med hvem som helst. Det er et stort desentralisert meldingsnettverk!", + "@welcomeText": {}, + "notificationRuleIsUserMentionDescription": "Varsler brukeren når de er direkte nevnt i en melding.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleContainsDisplayName": "Inneholder visningsnavn", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleContainsDisplayNameDescription": "Varsler brukeren når en melding inneholder ens visningsnavnet.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsUserMention": "Brukeromtale", + "@notificationRuleIsUserMention": {}, + "notificationRuleIsRoomMention": "Romomtale", + "@notificationRuleIsRoomMention": {}, + "whatIsAHomeserver": "Hva er en hjemmeserver?", + "@whatIsAHomeserver": {}, + "commandHint_me": "Beskriv deg selv", + "@commandHint_me": { + "type": "String", + "description": "Usage hint for the command /me" + }, + "commandHint_myroomavatar": "Angi bilde for dette rommet (med mxc-uri)", + "@commandHint_myroomavatar": { + "type": "String", + "description": "Usage hint for the command /myroomavatar" + }, + "commandHint_myroomnick": "Angi visningsnavnet ditt for dette rommet", + "@commandHint_myroomnick": { + "type": "String", + "description": "Usage hint for the command /myroomnick" + }, + "commandHint_plain": "Send uformatert tekst", + "@commandHint_plain": { + "type": "String", + "description": "Usage hint for the command /plain" + }, + "commandHint_react": "Send svar som en reaksjon", + "@commandHint_react": { + "type": "String", + "description": "Usage hint for the command /react" + }, + "commandHint_send": "Send tekst", + "@commandHint_send": { + "type": "String", + "description": "Usage hint for the command /send" + }, + "commandHint_unban": "Opphev utestengelsen til den gitte brukeren fra dette rommet", + "@commandHint_unban": { + "type": "String", + "description": "Usage hint for the command /unban" + }, + "commandInvalid": "Ugyldig kommando", + "@commandInvalid": { + "type": "String" + }, + "commandMissing": "{command} er ikke en kommando.", + "@commandMissing": { + "type": "String", + "placeholders": { + "command": { + "type": "String" + } + }, + "description": "State that {command} is not a valid /command." + }, + "checkList": "Sjekkliste", + "@checkList": {}, + "createNewSpace": "Nytt område", + "@createNewSpace": { + "type": "String", + "placeholders": {} + }, + "emoteKeyboardNoRecents": "Nylig brukte emotes vil vises her ...", + "@emoteKeyboardNoRecents": { + "type": "String", + "placeholders": {} + }, + "unableToJoinChat": "Kan ikke bli med i chatten. Kanskje den andre parten allerede har lukket samtalen.", + "@unableToJoinChat": {}, + "otherPartyNotLoggedIn": "Den andre parten er ikke logget inn og kan derfor ikke motta meldinger!", + "@otherPartyNotLoggedIn": {}, + "notificationRuleIsRoomMentionDescription": "Varsler brukeren når det er en romomtale.", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomnotif": "Romvarsel", + "@notificationRuleRoomnotif": {}, + "notificationRuleCall": "Anrop", + "@notificationRuleCall": {}, + "notificationRuleCallDescription": "Varsler brukeren om anrop.", + "@notificationRuleCallDescription": {}, + "waitingPartnerAcceptRequest": "Venter på at partneren skal godta forespørselen…", + "@waitingPartnerAcceptRequest": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerEmoji": "Venter på at partneren skal godta emojien…", + "@waitingPartnerEmoji": { + "type": "String", + "placeholders": {} + }, + "wipeChatBackup": "Vil du slette sikkerhetskopien av chatten din for å opprette en ny gjenopprettingsnøkkel?", + "@wipeChatBackup": { + "type": "String", + "placeholders": {} + }, + "switchToAccount": "Bytt til konto {number}", + "@switchToAccount": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "widgetEtherpad": "Tekstnotat", + "@widgetEtherpad": {}, + "noOneCanJoin": "Ingen kan bli med", + "@noOneCanJoin": {}, + "userWouldLikeToChangeTheChat": "{user} vil gjerne bli med i chatten.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "noPublicLinkHasBeenCreatedYet": "Ingen offentlig lenke er opprettet ennå", + "@noPublicLinkHasBeenCreatedYet": {}, + "commandHint_html": "Send HTML-formatert tekst", + "@commandHint_html": { + "type": "String", + "description": "Usage hint for the command /html" + }, + "commandHint_kick": "Fjern den gitte brukeren fra dette rommet", + "@commandHint_kick": { + "type": "String", + "description": "Usage hint for the command /kick" + }, + "commandHint_leave": "Forlat dette rommet", + "@commandHint_leave": { + "type": "String", + "description": "Usage hint for the command /leave" + }, + "commandHint_discardsession": "Forkast økten", + "@commandHint_discardsession": { + "type": "String", + "description": "Usage hint for the command /discardsession" + }, + "commandHint_dm": "Start en direkte chat\nBruk --no-encryption for å deaktivere kryptering", + "@commandHint_dm": { + "type": "String", + "description": "Usage hint for the command /dm" + }, + "commandHint_create": "Opprett en tom gruppechat\nBruk --no-encryption for å deaktivere kryptering", + "@commandHint_create": { + "type": "String", + "description": "Usage hint for the command /create" + }, + "redactedBy": "Redigert av {username}", + "@redactedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "directChat": "Direkte chat", + "@directChat": {}, + "redactedByBecause": "Redigert av {username} fordi: «{reason}»", + "@redactedByBecause": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "reason": { + "type": "String" + } + } + }, + "redactMessage": "Rediger melding", + "@redactMessage": { + "type": "String", + "placeholders": {} + }, + "roomVersion": "Rom versjon", + "@roomVersion": { + "type": "String", + "placeholders": {} + }, + "newSpace": "Nytt område", + "@newSpace": {}, + "allSpaces": "Alle områder", + "@allSpaces": {}, + "numChats": "{number} chats", + "@numChats": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "wasDirectChatDisplayName": "Tom chat (var {oldDisplayName})", + "@wasDirectChatDisplayName": { + "type": "String", + "placeholders": { + "oldDisplayName": { + "type": "String" + } + } + }, + "moveDown": "Flytt ned", + "@moveDown": {}, + "removeFromSpaceDescription": "Chatten blir fjernet fra området, men vises fortsatt i chatlisten din.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} chats", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "donate": "Doner", + "@donate": {}, + "banUserDescription": "Brukeren vil bli utestengt fra chatten og vil ikke kunne delta i chatten igjen før utestengelsen er opphevet.", + "@banUserDescription": {}, + "unbanUserDescription": "Brukeren vil kunne gå inn i chatten igjen hvis vedkommende prøver.", + "@unbanUserDescription": {}, + "kickUserDescription": "Brukeren blir kastet ut av chatten, men ikke utestengt. I offentlige chatter kan brukeren bli med på nytt når som helst.", + "@kickUserDescription": {}, "sendTypingNotifications": "Send skrivevarsler", - "swipeRightToLeftToReply": "Sveip fra høyre til venstre for å svare", + "@sendTypingNotifications": {}, + "swipeRightToLeftToReply": "Sveip fra høyre mot venstre for å svare", + "@swipeRightToLeftToReply": {}, + "startFirstChat": "Start din første chat", + "@startFirstChat": {}, + "unlockOldMessages": "Lås opp gamle meldinger", + "@unlockOldMessages": {}, + "storeInAndroidKeystore": "Lagre i Android KeyStore", + "@storeInAndroidKeystore": {}, + "storeInAppleKeyChain": "Lagre i Apple nøkkelring", + "@storeInAppleKeyChain": {}, + "storeSecurlyOnThisDevice": "Lagre sikkert på denne enheten", + "@storeSecurlyOnThisDevice": {}, + "acceptedKeyVerification": "{sender} godtok nøkkelverifisering", + "@acceptedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "notificationRuleMessageDescription": "Varsler brukeren om generelle meldinger.", + "@notificationRuleMessageDescription": {}, + "notificationRuleSuppressEditsDescription": "Demper varsler for redigerte meldinger.", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleEncryptedDescription": "Varsler brukeren om meldinger i krypterte rom.", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleRoomServerAcl": "Romserverens tilgangskontrolliste", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomOneToOneDescription": "Varsler brukeren om meldinger i én-til-én-rom.", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleJitsiDescription": "Varsler brukeren om hendelser i Jitsi-widgeten.", + "@notificationRuleJitsiDescription": {}, + "customReaction": "Egendefinert reaksjon", + "@customReaction": {}, + "pause": "Pause", + "@pause": {}, + "moveToDifferentSpace": "Flytt til et annet område", + "@moveToDifferentSpace": {}, + "moveUp": "Flytt opp", + "@moveUp": {}, + "storeInSecureStorageDescription": "Oppbevar gjenopprettingsnøkkelen på en sikker lagringsplass på denne enheten.", + "@storeInSecureStorageDescription": {}, + "foregroundServiceRunning": "Denne varslingen vises når forgrunnstjenesten kjører.", + "@foregroundServiceRunning": {}, + "callingAccount": "Ringekonto", + "@callingAccount": {}, + "appearOnTopDetails": "Lar appen vises øverst (ikke nødvendig hvis du allerede har Fluffychat konfigurert som en ringekonto)", + "@appearOnTopDetails": {}, + "longPressToRecordVoiceMessage": "Langt trykk for å spille inn talemelding.", + "@longPressToRecordVoiceMessage": {}, + "startedAPoll": "{username} startet en avstemning.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Avstemning", + "@poll": {}, + "startPoll": "Start avstemning", + "@startPoll": {}, + "endPoll": "Avslutt avstemning", + "@endPoll": {}, + "answersVisible": "Svar synlige", + "@answersVisible": {}, + "answersHidden": "Svar skjult", + "@answersHidden": {}, + "addAnswerOption": "Legg til svaralternativ", + "@addAnswerOption": {}, + "answerOption": "Svaralternativ", + "@answerOption": {}, + "allowMultipleAnswers": "Tillat flere svar", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Avstemningen er avsluttet", + "@pollHasBeenEnded": {}, + "roomUpgradeDescription": "Chatten vil deretter bli gjenskapt med den nye romversjonen. Alle deltakere vil bli varslet om at de må bytte til den nye chatten. Du kan finne ut mer om romversjoner på https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "groupCanBeFoundViaSearch": "Gruppen kan finnes via søk", + "@groupCanBeFoundViaSearch": {}, + "commandHint_sendraw": "Send raw json", + "@commandHint_sendraw": {}, + "replyInThread": "Svar i tråden", + "@replyInThread": {}, + "sessionLostBody": "Sesjonen din er tapt. Vennligst rapporter denne feilen til utviklerne på {url}. Feilmeldingen er: {error}", + "@sessionLostBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "noticeChatBackupDeviceVerification": "Merk: Når du kobler alle enhetene dine til chat-sikkerhetskopien, blir de automatisk bekreftet.", + "@noticeChatBackupDeviceVerification": {}, + "notificationRuleTombstone": "Gravstein", + "@notificationRuleTombstone": {}, + "answersWillBeVisibleWhenPollHasEnded": "Svarene vil være synlige når avstemningen er avsluttet", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "thread": "Tråd", + "@thread": {}, + "backToMainChat": "Tilbake til hovedchatten", + "@backToMainChat": {}, + "saveChanges": "Lagre endringer", + "@saveChanges": {}, + "skipChatBackup": "Hopp over sikkerhetskopiering av chat", + "@skipChatBackup": {}, + "loadingMessages": "Laster inn meldinger", + "@loadingMessages": {}, + "setupChatBackup": "Konfigurer sikkerhetskopi av chat", + "@setupChatBackup": {}, + "commandHint_googly": "Send noen stirreøyne", + "@commandHint_googly": {}, + "commandHint_cuddle": "Send en kos", + "@commandHint_cuddle": {}, + "googlyEyesContent": "{senderName} sender deg noen stirreøyner", + "@googlyEyesContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "alwaysUse24HourFormat": "falsk", "countChatsAndCountParticipants": "{chats} chatter og {participants} deltakere", - "noMoreChatsFound": "Ingen flere chatter funnet...", "noChatsFoundHere": "Ingen chatter funnet her ennå. Start en ny chat med noen ved å bruke knappen nedenfor. ⤵️", "joinedChats": "Bli med i chatter", - "unread": "Ulest", - "space": "Rom", - "spaces": "Rom", - "yourChatBackupHasBeenSetUp": "Din chat-sikkerhetskopi er satt opp.", - "chatHasBeenAddedToThisSpace": "Chat er lagt til i dette rommet", - "chats": "Chatter", - "clearArchive": "Tøm arkiv", "commandHint_markasdm": "Merk som direktemeldingsrom for den gitte Matrix-ID-en", - "commandHint_markasgroup": "Merk som gruppe", - "commandHint_ban": "Forby den gitte brukeren fra dette rommet", - "commandHint_clearcache": "Tøm cache", - "commandHint_create": "Opprett en tom gruppechat\nBruk --no-encryption for å deaktivere kryptering", - "commandHint_discardsession": "Forklar økt", - "commandHint_dm": "Start en direktemelding\nBruk --no-encryption for å deaktivere kryptering", - "commandHint_html": "Send tekst i HTML-format", - "commandHint_invite": "Inviter brukeren til dette rommet", - "commandHint_join": "Bli med i dette rommet", - "commandHint_kick": "Fjern brukeren fra dette rommet", - "commandHint_leave": "Forlat dette rommet", - "commandHint_me": "Beskriv deg selv", - "commandHint_myroomavatar": "Sett bildet ditt for dette rommet (ved mxc-uri)", - "commandHint_myroomnick": "Sett visningsnavnet ditt for dette rommet", "commandHint_op": "Sett brukerens maktnivå (standard: 50)", - "commandHint_plain": "Send uformatert tekst", - "commandHint_react": "Send svar som en reaksjon", - "commandHint_send": "Send tekst", - "commandHint_unban": "Fjern banning av brukeren fra dette rommet", - "commandInvalid": "Ugyldig kommando", - "commandMissing": "{command} er ikke en kommando.", - "checkList": "Sjekkliste", - "countInvited": "{count} invitert", - "createGroup": "Opprett gruppe", - "createNewSpace": "Nytt rom", - "allRooms": "Alle gruppechatter", - "chatPermissions": "Chat-tillatelser", - "editRoomAliases": "Rediger romaliaser", - "emoteKeyboardNoRecents": "Nylig brukte emotes vil vises her...", - "globalChatId": "Global chat-ID", - "accessAndVisibility": "Tilgang og synlighet", - "accessAndVisibilityDescription": "Hvem som har lov til å bli med i denne chatten og hvordan chatten kan oppdages.", - "calls": "Samtaler", - "customEmojisAndStickers": "Egendefinerte emojis og klistremerker", - "customEmojisAndStickersBody": "Legg til eller del egendefinerte emojis eller klistremerker som kan brukes i hvilken som helst chat.", - "homeserver": "Hjemmeserver", - "errorObtainingLocation": "Feil ved henting av plassering: {error}", - "goToTheNewRoom": "Gå til det nye rommet", - "chatDescription": "Chatbeskrivelse", - "chatDescriptionHasBeenChanged": "Chatbeskrivelse endret", - "hideRedactedMessages": "Skjul redigerte meldinger", - "hideRedactedMessagesBody": "Hvis noen redigerer en melding, vil denne meldingen ikke lenger være synlig i chatten.", - "hideInvalidOrUnknownMessageFormats": "Skjul ugyldige eller ukjente meldingsformater", - "block": "Blokker", - "blockedUsers": "Blokkerte brukere", - "blockListDescription": "Du kan blokkere brukere som forstyrrer deg. Du vil ikke kunne motta meldinger eller rominvitasjoner fra brukerne på din personlige blokkeringliste.", - "blockUsername": "Ignorer brukernavn", - "inviteContactToGroupQuestion": "Vil du invitere {contact} til chatten \"{groupName}\"?", - "noChatDescriptionYet": "Ingen chatbeskrivelse opprettet ennå.", - "tryAgain": "Prøv igjen", - "invalidServerName": "Ugyldig servernavn", - "redactMessageDescription": "Meldingen vil bli redigert for alle deltakere i denne samtalen. Dette kan ikke angres.", - "optionalRedactReason": "(Valgfritt) Årsak til å redigere denne meldingen...", - "dehydrate": "Eksporter økt og slett enheten", - "dehydrateWarning": "Denne handlingen kan ikke angres. Sørg for å lagre sikkerhetskopifilen trygt.", - "dehydrateTor": "TOR-brukere: Eksporter økt", - "dehydrateTorLong": "For TOR-brukere anbefales det å eksportere økten før du lukker vinduet.", - "hydrateTor": "TOR-brukere: Importer økt fra eksport", - "hydrateTorLong": "Eksporterte du økten din sist gang på TOR? Importer den raskt og fortsett å chatte.", - "hydrate": "Gjenopprett fra sikkerhetskopifil", - "locationDisabledNotice": "Stedstjenester er deaktivert. Vennligst aktiver dem for å kunne dele din plassering.", - "locationPermissionDeniedNotice": "Plasseringstillatelse nektet. Vennligst gi tillatelse for å kunne dele din plassering.", - "messagesStyle": "Meldinger:", - "noEncryptionForPublicRooms": "Du kan bare aktivere kryptering når rommet ikke lenger er offentlig tilgjengelig.", - "noMatrixServer": "{server1} er ingen matrix-server, bruk {server2} i stedet?", - "shareInviteLink": "Del invitasjonslenke", - "scanQrCode": "Skann QR-kode", - "obtainingLocation": "Henter plassering…", - "oopsPushError": "Oi! Dessverre oppstod en feil under oppsett av push-varsler.", - "openVideoCamera": "Åpne kamera for en video", - "oneClientLoggedOut": "En av dine klienter har blitt logget ut", - "addAccount": "Legg til konto", "editBundlesForAccount": "Rediger pakker for denne kontoen", "addToBundle": "Legg til i pakke", "removeFromBundle": "Fjern fra denne pakken", "bundleName": "Pakke navn", - "enableMultiAccounts": "(BETA) Aktiver flere kontoer på denne enheten", - "openInMaps": "Åpne i kart", - "link": "Lenke", - "serverRequiresEmail": "Denne serveren må validere e-postadressen din for registrering.", - "or": "Eller", - "hideMemberChangesInPublicChats": "Skjul medlemsendringer i offentlige chatter", - "hideMemberChangesInPublicChatsBody": "Ikke vis i chat-historien hvis noen blir med eller forlater en offentlig chat for å forbedre lesbarheten.", - "overview": "Oversikt", - "notifyMeFor": "Varsle meg om", - "passwordRecoverySettings": "Gjenopprettingsinnstillinger for passord", - "people": "Folk", - "pleaseChoose": "Vennligst velg", - "pleaseChooseAPasscode": "Vennligst velg en passkode", - "pleaseEnter4Digits": "Vennligst skriv inn 4 sifre eller la det stå tomt for å deaktivere app-låsen.", - "pleaseEnterRecoveryKey": "Vennligst skriv inn gjenopprettingsnøkkelen din:", - "pleaseEnterYourPin": "Vennligst skriv inn din pin-kode", - "redactedBy": "Redigert av {username}", - "directChat": "Direktemeldingschat", - "redactedByBecause": "Redigert av {username} fordi: \"{reason}\"", - "redactMessage": "Rediger melding", "register": "Registrer", - "removeYourAvatar": "Fjern din avatar", - "roomVersion": "Romversjon", - "saveFile": "Lagre fil", - "recoveryKey": "Gjenopprettingsnøkkel", "recoveryKeyLost": "Gjenopprettingsnøkkel tapt?", - "sendAsText": "Send som tekst", - "sendImages": "Send {count} bilde", - "sendSticker": "Send klistremerke", "separateChatTypes": "Separat direktemeldinger og grupper", "setAsCanonicalAlias": "Sett som hovedalias", - "setChatDescription": "Sett chatbeskrivelse", - "shareLocation": "Del plassering", - "showPassword": "Vis passord", - "presenceStyle": "Tilstedeværelse:", - "presencesToggle": "Vis statusmeldinger fra andre brukere", - "singlesignon": "Enkeltpålogging", "spaceIsPublic": "Rom er offentlig", "spaceName": "Romnavn", - "startFirstChat": "Start din første chat", - "synchronizingPleaseWait": "Synkroniserer… Vennligst vent.", - "synchronizingPleaseWaitCounter": " Synkroniserer… ({percentage}%)", - "unverified": "Uverifisert", - "verified": "Verifisert", - "waitingPartnerAcceptRequest": "Venter på at partner skal godta forespørselen…", - "waitingPartnerEmoji": "Venter på at partner skal godta emoji…", - "wipeChatBackup": "Vil du slette chat-sikkerhetskopien for å opprette en ny gjenopprettingsnøkkel?", - "messageInfo": "Meldingsinformasjon", - "time": "Tid", - "messageType": "Meldings type", - "sender": "Avsender", - "openGallery": "Åpne galleriet", - "removeFromSpace": "Fjern fra rommet", - "addToSpaceDescription": "Velg et rom for å legge til denne samtalen.", - "start": "Start", - "pleaseEnterRecoveryKeyDescription": "For å låse opp dine gamle meldinger, skriv inn gjenopprettingsnøkkelen din som er generert i en tidligere økt. Gjenopprettingsnøkkelen din er IKKE passordet ditt.", - "publish": "Publiser", - "videoWithSize": "Video ({size})", - "openChat": "Åpne samtale", - "markAsRead": "Merk som lest", - "reportUser": "Rapporter bruker", - "dismiss": "Avvis", - "reactedWith": "{sender} reagerte med {reaction}", - "pinMessage": "Fest til rom", "confirmEventUnpin": "Er du sikker på at du vil fjerne festingen av arrangementet permanent?", - "emojis": "Emojis", - "placeCall": "Foreta samtale", - "voiceCall": "Talekall", - "unsupportedAndroidVersion": "Ikke-støttet Android-versjon", - "unsupportedAndroidVersionLong": "Denne funksjonen krever en nyere Android-versjon. Vennligst sjekk etter oppdateringer eller Lineage OS-støtte.", - "videoCallsBetaWarning": "Vennligst merk at videosamtaler for øyeblikket er i beta. De fungerer kanskje ikke som forventet eller i det hele tatt på alle plattformer.", - "experimentalVideoCalls": "Eksperimentelle videosamtaler", - "emailOrUsername": "E-post eller brukernavn", "indexedDbErrorTitle": "Problemer med privat modus", "indexedDbErrorLong": "Meldingslagringen er dessverre ikke aktivert i privat modus som standard.\nVennligst besøk\n - about:config\n - sett dom.indexedDB.privateBrowsing.enabled til true\nEllers er det ikke mulig å kjøre FluffyChat.", - "switchToAccount": "Bytt til konto {number}", - "nextAccount": "Neste konto", - "previousAccount": "Forrige konto", - "addWidget": "Legg til widget", - "widgetVideo": "Video", - "widgetEtherpad": "Tekstnotat", - "widgetJitsi": "Jitsi Meet", - "widgetCustom": "Egendefinert", - "widgetName": "Navn", - "widgetUrlError": "Dette er ikke en gyldig URL.", - "widgetNameError": "Vennligst oppgi et visningsnavn.", "errorAddingWidget": "Feil ved tillegg av widgeten.", - "youRejectedTheInvitation": "Du avviste invitasjonen", - "youJoinedTheChat": "Du ble med i chatten", - "youAcceptedTheInvitation": "👍 Du godtok invitasjonen", - "youBannedUser": "Du utestengte {user}", - "youHaveWithdrawnTheInvitationFor": "Du har trukket tilbake invitasjonen til {user}", - "youInvitedToBy": "📩 Du har blitt invitert via lenke til:\n{alias}", - "youInvitedBy": "📩 Du har blitt invitert av {user}", - "invitedBy": "📩 Inviter av {user}", - "youInvitedUser": "📩 Du inviterte {user}", - "youKicked": "👞 Du sparket {user}", - "youKickedAndBanned": "🙅‍♀️ Du sparket og utestengte {user}", - "youUnbannedUser": "Du fjernet utestengelsen for {user}", "hasKnocked": "🚪 {user} har banket på", "usersMustKnock": "Brukere må banke på", - "noOneCanJoin": "Ingen kan bli med", - "userWouldLikeToChangeTheChat": "{user} ønsker å bli med i chatten.", - "noPublicLinkHasBeenCreatedYet": "Ingen offentlig lenke er opprettet ennå", "knock": "Bank på", - "users": "Brukere", - "unlockOldMessages": "Lås opp gamle meldinger", - "storeInSecureStorageDescription": "Lagre gjenopprettingsnøkkelen i den sikre lagringen på denne enheten.", "saveKeyManuallyDescription": "Lagre denne nøkkelen manuelt ved å utløse systemets delingsdialog eller utklippstavle.", - "storeInAndroidKeystore": "Lagre i Android KeyStore", - "storeInAppleKeyChain": "Lagre i Apple KeyChain", - "storeSecurlyOnThisDevice": "Lagre sikkert på denne enheten", - "countFiles": "{count} filer", - "user": "Bruker", - "custom": "Egendefinert", - "foregroundServiceRunning": "Denne varslingen vises når forgrunnstjenesten kjører.", - "screenSharingTitle": "skjermdeling", - "screenSharingDetail": "Du deler skjermen din i FuffyChat", - "callingPermissions": "Tillatelser for samtale", - "callingAccount": "Samtalekonto", - "callingAccountDetails": "Tillater FluffyChat å bruke den native Android-dialer-appen.", - "appearOnTop": "Vis øverst", - "appearOnTopDetails": "Tillater appen å vises øverst (ikke nødvendig hvis du allerede har satt opp FluffyChat som en samtalekonto)", - "otherCallingPermissions": "Mikrofon, kamera og andre tillatelser for FluffyChat", - "whyIsThisMessageEncrypted": "Hvorfor er denne meldingen uleselig?", "noKeyForThisMessage": "Dette kan skje hvis meldingen ble sendt før du logget inn på kontoen din på denne enheten.\n\nDet er også mulig at avsenderen har blokkert enheten din eller at noe gikk galt med internettforbindelsen.\n\nEr du i stand til å lese meldingen på en annen økt? Da kan du overføre meldingen derfra! Gå til Innstillinger > Enheter og sørg for at enhetene dine har verifisert hverandre. Når du åpner rommet neste gang og begge øktene er i forgrunnen, vil nøklene bli overført automatisk.\n\nVil du ikke miste nøklene når du logger ut eller bytter enhet? Sørg for at du har aktivert chat-sikkerhetskopiering i innstillingene.", - "newGroup": "Ny gruppe", - "newSpace": "Nytt rom", "enterSpace": "Gå inn i rommet", "enterRoom": "Gå inn i rommet", - "allSpaces": "Alle rom", - "numChats": "{number} chatter", - "hideUnimportantStateEvents": "Skjul uviktige tilstandsoppdateringer", - "hidePresences": "Skjul statusliste?", - "doNotShowAgain": "Vis ikke igjen", - "wasDirectChatDisplayName": "Tom chat (var {oldDisplayName})", "newSpaceDescription": "Rom lar deg konsolidere dine chatter og bygge private eller offentlige fellesskap.", - "encryptThisChat": "Krypter denne chatten", - "disableEncryptionWarning": "Av sikkerhetsgrunner kan du ikke deaktivere kryptering i en chat der den har vært aktivert tidligere.", - "sorryThatsNotPossible": "Beklager... det er ikke mulig", - "deviceKeys": "Enhetsnøkler:", - "reopenChat": "Åpne chat igjen", "noBackupWarning": "Advarsel! Uten å aktivere chat-sikkerhetskopi vil du miste tilgangen til dine krypterte meldinger. Det anbefales sterkt å aktivere chat-sikkerhetskopi før du logger ut.", - "noOtherDevicesFound": "Ingen andre enheter funnet", - "fileIsTooBigForServer": "Kan ikke sende! Serveren støtter kun vedlegg opp til {max}.", - "fileHasBeenSavedAt": "Filen er lagret på {path}", - "jumpToLastReadMessage": "Hopp til siste leste melding", - "readUpToHere": "Les opp til her", - "jump": "Hopp", - "openLinkInBrowser": "Åpne lenke i nettleser", - "reportErrorDescription": "😞 Å nei. Noe gikk galt. Hvis du vil, kan du rapportere denne feilen til utviklerne.", "report": "rapporter", - "signInWithPassword": "Logg inn med passord", - "pleaseTryAgainLaterOrChooseDifferentServer": "Vennligst prøv igjen senere eller velg en annen server.", - "signInWith": "Logg inn med {provider}", - "profileNotFound": "Brukeren ble ikke funnet på serveren. Kanskje er det et tilkoblingsproblem eller brukeren finnes ikke.", - "setTheme": "Velg tema:", - "setColorTheme": "Velg fargetema:", - "invite": "Inviter", - "inviteGroupChat": "📨 Inviter gruppechat", - "invitePrivateChat": "📨 Inviter privat chat", - "invalidInput": "Ugyldig inndata!", - "wrongPinEntered": "Feil PIN-kode oppgitt! Prøv igjen om {seconds} sekunder...", - "pleaseEnterANumber": "Vennligst skriv inn et tall større enn 0", - "archiveRoomDescription": "Chaten vil bli flyttet til arkivet. Andre brukere vil kunne se at du har forlatt chatten.", - "roomUpgradeDescription": "Deretter vil chatten bli opprettet på nytt med den nye romversjonen. Alle deltakere vil bli varslet om at de må bytte til den nye chatten. Du kan finne mer informasjon om romversjoner på https://spec.matrix.org/latest/rooms/", - "removeDevicesDescription": "Du vil bli logget ut av denne enheten og vil ikke lenger kunne motta meldinger.", - "banUserDescription": "Brukeren vil bli utestengt fra chatten og vil ikke kunne komme inn igjen før de blir unbannet.", - "unbanUserDescription": "Brukeren vil kunne komme inn i chatten igjen hvis de prøver.", - "kickUserDescription": "Brukeren blir kastet ut av chatten, men er ikke utestengt. I offentlige chatter kan brukeren bli med igjen når som helst.", "makeAdminDescription": "Når du gjør denne brukeren til administrator, kan det hende du ikke kan angre dette, da de da vil ha de samme tillatelsene som deg.", - "pushNotificationsNotAvailable": "Push-varsler er ikke tilgjengelige", - "learnMore": "Lær mer", - "yourGlobalUserIdIs": "Din globale bruker-ID er: ", - "noUsersFoundWithQuery": "Dessverre ble ingen bruker funnet med \"{query}\". Vennligst sjekk om du har skrevet feil.", "knocking": "Banker på", - "chatCanBeDiscoveredViaSearchOnServer": "Chat kan oppdages via søk på {server}", - "searchChatsRooms": "Søk etter #chatter, @brukere...", - "nothingFound": "Ingen funnet...", - "groupName": "Gruppenavn", - "createGroupAndInviteUsers": "Opprett en gruppe og inviter brukere", - "groupCanBeFoundViaSearch": "Gruppen kan finnes via søk", - "wrongRecoveryKey": "Beklager... dette ser ikke ut til å være riktig gjenopprettingsnøkkel.", - "startConversation": "Start samtale", - "commandHint_sendraw": "Send rå json", - "databaseMigrationTitle": "Databasen er optimalisert", - "databaseMigrationBody": "Vennligst vent. Dette kan ta et øyeblikk.", - "leaveEmptyToClearStatus": "La stå tomt for å tømme statusen din.", - "select": "Velg", - "searchForUsers": "Søk etter @brukere...", - "pleaseEnterYourCurrentPassword": "Vennligst skriv inn ditt nåværende passord", - "newPassword": "Nytt passord", - "pleaseChooseAStrongPassword": "Vennligst velg et sterkt passord", - "passwordsDoNotMatch": "Passordene stemmer ikke overens", - "passwordIsWrong": "Det innskrevne passordet er feil", - "publicLink": "Offentlig lenke", - "publicChatAddresses": "Offentlige chatadresser", - "createNewAddress": "Opprett ny adresse", "joinSpace": "Bli med i rom", "publicSpaces": "Offentlige rom", "addChatOrSubSpace": "Legg til chat eller underrom", "subspace": "Underrom", - "decline": "Avslå", - "thisDevice": "Denne enheten:", - "initAppError": "En feil oppstod under initialisering av appen", - "userRole": "Brukerrolle", - "minimumPowerLevel": "{level} er det minste maktnivået.", - "searchIn": "Søk i chat \"{chat}\"...", - "searchMore": "Søk mer...", - "gallery": "Galleri", - "files": "Filer", "databaseBuildErrorBody": "Kan ikke bygge SQLite-databasen. Appen prøver for øyeblikket å bruke den eldre databasen. Vennligst rapporter denne feilen til utviklerne på {url}. Feilmeldingen er: {error}", - "sessionLostBody": "Økten din er tapt. Vennligst rapporter denne feilen til utviklerne på {url}. Feilmeldingen er: {error}", - "restoreSessionBody": "Appen prøver nå å gjenopprette økten din fra sikkerhetskopien. Vennligst rapporter denne feilen til utviklerne på {url}. Feilmeldingen er: {error}", - "forwardMessageTo": "Videresend melding til {roomName}?", - "sendReadReceipts": "Send lesebekreftelser", - "sendTypingNotificationsDescription": "Andre deltakere i en chat kan se når du skriver en ny melding.", - "sendReadReceiptsDescription": "Andre deltakere i en chat kan se når du har lest en melding.", - "formattedMessages": "Formaterte meldinger", - "formattedMessagesDescription": "Vis rik innhold i meldinger som fet tekst ved bruk av markdown.", - "verifyOtherUser": "🔐 Verifiser annen bruker", "verifyOtherUserDescription": "Hvis du verifiserer en annen bruker, kan du være sikker på at du vet hvem du faktisk skriver til. 💪\n\nNår du starter en verifisering, vil du og den andre brukeren se en popup i appen. Der vil dere se en serie av emojis eller tall som dere må sammenligne.\n\nDen beste måten å gjøre dette på er å møtes eller starte en videosamtale. 👩‍💻", - "verifyOtherDevice": "🔐 Verifiser annen enhet", "verifyOtherDeviceDescription": "Når du verifiserer en annen enhet, kan disse enhetene utveksle nøkler, noe som øker din totale sikkerhet. 💪 Når du starter en verifisering, vil en popup vises i appen på begge enhetene. Der vil dere se en serie av emojis eller tall som dere må sammenligne.\nDet er best å ha begge enhetene tilgjengelig før du starter verifiseringen. 🤳", - "acceptedKeyVerification": "{sender} aksepterte nøkkelverifisering", - "canceledKeyVerification": "{sender} avbrøt nøkkelverifisering", - "completedKeyVerification": "{sender} fullførte nøkkelverifisering", - "isReadyForKeyVerification": "{sender} er klar for nøkkelverifisering", - "requestedKeyVerification": "{sender} ba om nøkkelverifisering", - "startedKeyVerification": "{sender} startet nøkkelverifisering", - "transparent": "Gjennomsiktig", - "incomingMessages": "Innkommende meldinger", - "stickers": "Stickers", - "discover": "Oppdag", - "commandHint_ignore": "Ignorer den gitte matrix-ID-en", - "commandHint_unignore": "Fjern ignorering av den gitte matrix-ID-en", - "unreadChatsInApp": "{appname}: {unread} uleste chatter", - "noDatabaseEncryption": "Kryptering av database støttes ikke på denne plattformen", - "thereAreCountUsersBlocked": "Akkurat nå er det {count} brukere blokkert.", - "restricted": "Begrenset", "knockRestricted": "Knock-begrenset", - "goToSpace": "Gå til rom: {space}", - "markAsUnread": "Merk som ulest", - "userLevel": "{level} - Bruker", - "moderatorLevel": "{level} - Moderator", - "adminLevel": "{level} - Administrator", - "changeGeneralChatSettings": "Endre generelle chatinnstillinger", - "inviteOtherUsers": "Inviter andre brukere til denne chatten", - "changeTheChatPermissions": "Endre chat-tillatelser", - "changeTheVisibilityOfChatHistory": "Endre synligheten av chatthistorikken", - "changeTheCanonicalRoomAlias": "Endre den viktigste offentlige chat-adressen", - "sendRoomNotifications": "Send @rom varsler", - "changeTheDescriptionOfTheGroup": "Endre beskrivelsen av chatten", "chatPermissionsDescription": "Definer hvilket maktnivå som er nødvendig for visse handlinger i denne chatten. Maktnivåene 0, 50 og 100 representerer vanligvis brukere, moderatorer og administratorer, men alle grader er mulige.", - "updateInstalled": "🎉 Oppdatering {version} installert!", - "changelog": "Endringslogg", - "sendCanceled": "Sending avbrutt", - "loginWithMatrixId": "Logg inn med Matrix-ID", - "discoverHomeservers": "Oppdag hjemservere", - "whatIsAHomeserver": "Hva er en hjemserver?", - "homeserverDescription": "Alle dataene dine lagres på hjemserveren, akkurat som en e-postleverandør. Du kan velge hvilken hjemserver du vil bruke, samtidig som du kan kommunisere med alle. Lær mer på https://matrix.org.", - "doesNotSeemToBeAValidHomeserver": "Ser ikke ut til å være en kompatibel hjemserver. Feil URL?", - "calculatingFileSize": "Beregner filstørrelse...", - "prepareSendingAttachment": "Forbereder sending av vedlegg...", - "sendingAttachment": "Sender vedlegg...", - "generatingVideoThumbnail": "Genererer videominiatyr...", - "compressVideo": "Komprimerer video...", "sendingAttachmentCountOfCount": "Sender vedlegg {index} av {length}...", - "serverLimitReached": "Servergrensen er nådd! Venter {seconds} sekunder...", - "oneOfYourDevicesIsNotVerified": "En av enhetene dine er ikke verifisert", - "noticeChatBackupDeviceVerification": "Merk: Når du kobler alle enhetene dine til chat-sikkerhetskopien, blir de automatisk verifisert.", - "continueText": "Fortsett", - "welcomeText": "Hei Hei 👋 Dette er FluffyChat. Du kan logge inn på hvilken som helst hjemserver, som er kompatibel med https://matrix.org. Og deretter chatte med hvem som helst. Det er et stort desentralisert meldingsnettverk!", - "blur": "Uskarphet:", - "opacity": "Opacity:", - "setWallpaper": "Sett bakgrunnsbilde", - "manageAccount": "Administrer konto", - "noContactInformationProvided": "Serveren gir ingen gyldige kontaktopplysninger", - "contactServerAdmin": "Kontakt serveradministrator", "contactServerSecurity": "Kontakt serverens sikkerhet", - "supportPage": "Supportside", - "serverInformation": "Serverinformasjon:", - "name": "Navn", - "version": "Versjon", - "website": "Nettsted", - "compress": "Komprimer", - "boldText": "Fet tekst", - "italicText": "Kursiv tekst", - "strikeThrough": "Gjennomstreking", - "pleaseFillOut": "Vennligst fyll ut", - "invalidUrl": "Ugyldig URL", - "addLink": "Legg til lenke", - "unableToJoinChat": "Kan ikke bli med i chat. Kanskje den andre parten allerede har lukket samtalen.", - "previous": "Forrige", - "otherPartyNotLoggedIn": "Den andre parten er for øyeblikket ikke logget inn og kan derfor ikke motta meldinger!", - "appWantsToUseForLogin": "Bruk '{server}' for å logge inn", - "appWantsToUseForLoginDescription": "Du gir herved appen og nettstedet tillatelse til å dele informasjon om deg.", - "open": "Åpne", - "waitingForServer": "Venter på server...", - "appIntroduction": "FluffyChat lar deg chatte med vennene dine på tvers av ulike meldingsapper. Lær mer på https://matrix.org eller bare trykk *Fortsett*.", - "newChatRequest": "📩 Ny chatforespørsel", - "contentNotificationSettings": "Innholdsvarslingsinnstillinger", - "generalNotificationSettings": "Generelle varslingsinnstillinger", - "roomNotificationSettings": "Romvarslingsinnstillinger", - "userSpecificNotificationSettings": "Brukerspesifikke varslingsinnstillinger", - "otherNotificationSettings": "Andre varslingsinnstillinger", - "notificationRuleContainsUserName": "Inneholder brukernavn", - "notificationRuleContainsUserNameDescription": "Varsler brukeren når en melding inneholder deres brukernavn.", - "notificationRuleMaster": "Still alle varsler", - "notificationRuleMasterDescription": "Overstyrer alle andre regler og deaktiverer alle varsler.", - "notificationRuleSuppressNotices": "Undertrykk automatiserte meldinger", - "notificationRuleSuppressNoticesDescription": "Undertrykker varsler fra automatiserte klienter som roboter.", - "notificationRuleInviteForMe": "Inviter for meg", - "notificationRuleInviteForMeDescription": "Varsler brukeren når de blir invitert til et rom.", "notificationRuleMemberEvent": "Medlemsarrangement", "notificationRuleMemberEventDescription": "Undertrykker varsler for medlemsarrangementer.", - "notificationRuleIsUserMention": "Brukerhenvisning", - "notificationRuleIsUserMentionDescription": "Varsler brukeren når de blir direkte nevnt i en melding.", - "notificationRuleContainsDisplayName": "Inneholder visningsnavn", - "notificationRuleContainsDisplayNameDescription": "Varsler brukeren når en melding inneholder deres visningsnavn.", - "notificationRuleIsRoomMention": "Romhenvisning", - "notificationRuleIsRoomMentionDescription": "Varsler brukeren når det er en romhenvisning.", - "notificationRuleRoomnotif": "Romvarsling", - "notificationRuleRoomnotifDescription": "Varsler brukeren når en melding inneholder '@rom'.", - "notificationRuleTombstone": "Tombstone", - "notificationRuleTombstoneDescription": "Varsler brukeren om meldinger om deaktivering av rom.", - "notificationRuleReaction": "Reaksjon", - "notificationRuleReactionDescription": "Undertrykker varsler for reaksjoner.", - "notificationRuleRoomServerAcl": "Romserver ACL", "notificationRuleRoomServerAclDescription": "Undertrykker varsler for romserver tilgangskontrollister (ACL).", - "notificationRuleSuppressEdits": "Undertrykk endringer", - "notificationRuleSuppressEditsDescription": "Undertrykker varsler for redigerte meldinger.", - "notificationRuleCall": "Samtale", - "notificationRuleCallDescription": "Varsler brukeren om samtaler.", - "notificationRuleEncryptedRoomOneToOne": "Kryptert rom én-til-én", - "notificationRuleEncryptedRoomOneToOneDescription": "Varsler brukeren om meldinger i krypterte rom én-til-én.", - "notificationRuleRoomOneToOne": "Rom én-til-én", - "notificationRuleRoomOneToOneDescription": "Varsler brukeren om meldinger i én-til-én-rom.", - "notificationRuleMessage": "Melding", - "notificationRuleMessageDescription": "Varsler brukeren om generelle meldinger.", - "notificationRuleEncrypted": "Kryptert", - "notificationRuleEncryptedDescription": "Varsler brukeren om meldinger i krypterte rom.", - "notificationRuleJitsi": "Jitsi", - "notificationRuleJitsiDescription": "Varsler brukeren om Jitsi-widget hendelser.", "notificationRuleServerAcl": "Undertrykk server-ACL hendelser", "notificationRuleServerAclDescription": "Undertrykker varsler for server-ACL hendelser.", - "unknownPushRule": "Ukjent push-regel '{rule}'", - "sentVoiceMessage": "🎙️ {duration} - Talebeskjed fra {sender}", - "deletePushRuleCanNotBeUndone": "Hvis du sletter denne varslingsinnstillingen, kan den ikke angres.", - "more": "Mer", - "shareKeysWith": "Del nøkler med...", - "shareKeysWithDescription": "Hvilke enheter skal være tillit for å kunne lese dine meldinger i krypterte chatter?", - "allDevices": "Alle enheter", - "crossVerifiedDevicesIfEnabled": "Kryssverifiserte enheter hvis aktivert", - "crossVerifiedDevices": "Kryssverifiserte enheter", - "verifiedDevicesOnly": "Bare verifiserte enheter", - "takeAPhoto": "Ta et bilde", - "recordAVideo": "Spill inn en video", - "optionalMessage": "(Valgfritt) melding...", - "notSupportedOnThisDevice": "Ikke støttet på denne enheten", - "enterNewChat": "Start en ny chat", - "approve": "Godkjenn", "youHaveKnocked": "Du har ringt på", - "pleaseWaitUntilInvited": "Vennligst vent til noen i rommet inviterer deg.", - "commandHint_logout": "Logg ut av din nåværende enhet", - "commandHint_logoutall": "Logg ut av alle aktive enheter", "displayNavigationRail": "Vis navigasjonsstang på mobil", - "customReaction": "Egendefinert reaksjon", "writeAMessageLangCodes": "Skriv inn {l1} eller {l2}...", "requests": "Forespørsler", "holdForInfo": "Klikk og hold for ordinformasjon.", @@ -2334,7 +3386,6 @@ "updateLanguage": "Mine språk", "whatLanguageYouWantToLearn": "Hvilket språk vil du lære?", "whatIsYourBaseLanguage": "Hva er ditt hovedspråk?", - "saveChanges": "Lagre endringer", "publicProfileTitle": "La profilen min bli funnet i søk", "publicProfileDesc": "Ved å slå på dette, gjør du det mulig for andre brukere å finne profilen din i den globale søkefeltet og sende forespørsler om chat. På dette tidspunktet kan du velge å godta eller avvise forespørselen.", "errorDisableIT": "Oversettelsesassistanse er slått av.", @@ -3425,118 +4476,6 @@ "type": "String", "placeholders": {} }, - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@importNow": { - "type": "String", - "placeholders": {} - }, - "@importEmojis": { - "type": "String", - "placeholders": {} - }, - "@importFromZipFile": { - "type": "String", - "placeholders": {} - }, - "@exportEmotePack": { - "type": "String", - "placeholders": {} - }, - "@replace": { - "type": "String", - "placeholders": {} - }, - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@confirmMatrixId": { - "type": "String", - "placeholders": {} - }, - "@supposedMxid": { - "type": "String", - "placeholders": { - "mxid": { - "type": "String" - } - } - }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@commandHint_googly": { - "type": "String", - "placeholders": {} - }, - "@commandHint_cuddle": { - "type": "String", - "placeholders": {} - }, - "@commandHint_hug": { - "type": "String", - "placeholders": {} - }, - "@googlyEyesContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@cuddleContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@hugContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "@appLockDescription": { - "type": "String", - "placeholders": {} - }, - "@sendTypingNotifications": { - "type": "String", - "placeholders": {} - }, - "@swipeRightToLeftToReply": { - "type": "String", - "placeholders": {} - }, "@countChatsAndCountParticipants": { "type": "String", "placeholders": { @@ -3548,10 +4487,6 @@ } } }, - "@noMoreChatsFound": { - "type": "String", - "placeholders": {} - }, "@noChatsFoundHere": { "type": "String", "placeholders": {} @@ -3560,348 +4495,14 @@ "type": "String", "placeholders": {} }, - "@unread": { - "type": "String", - "placeholders": {} - }, - "@space": { - "type": "String", - "placeholders": {} - }, - "@spaces": { - "type": "String", - "placeholders": {} - }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, - "@chats": { - "type": "String", - "placeholders": {} - }, - "@clearArchive": { - "type": "String", - "placeholders": {} - }, "@commandHint_markasdm": { "type": "String", "placeholders": {} }, - "@commandHint_markasgroup": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ban": { - "type": "String", - "placeholders": {} - }, - "@commandHint_clearcache": { - "type": "String", - "placeholders": {} - }, - "@commandHint_create": { - "type": "String", - "placeholders": {} - }, - "@commandHint_discardsession": { - "type": "String", - "placeholders": {} - }, - "@commandHint_dm": { - "type": "String", - "placeholders": {} - }, - "@commandHint_html": { - "type": "String", - "placeholders": {} - }, - "@commandHint_invite": { - "type": "String", - "placeholders": {} - }, - "@commandHint_join": { - "type": "String", - "placeholders": {} - }, - "@commandHint_kick": { - "type": "String", - "placeholders": {} - }, - "@commandHint_leave": { - "type": "String", - "placeholders": {} - }, - "@commandHint_me": { - "type": "String", - "placeholders": {} - }, - "@commandHint_myroomavatar": { - "type": "String", - "placeholders": {} - }, - "@commandHint_myroomnick": { - "type": "String", - "placeholders": {} - }, "@commandHint_op": { "type": "String", "placeholders": {} }, - "@commandHint_plain": { - "type": "String", - "placeholders": {} - }, - "@commandHint_react": { - "type": "String", - "placeholders": {} - }, - "@commandHint_send": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unban": { - "type": "String", - "placeholders": {} - }, - "@commandInvalid": { - "type": "String", - "placeholders": {} - }, - "@commandMissing": { - "type": "String", - "placeholders": { - "command": { - "type": "String" - } - } - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, - "@createNewSpace": { - "type": "String", - "placeholders": {} - }, - "@allRooms": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, - "@editRoomAliases": { - "type": "String", - "placeholders": {} - }, - "@emoteKeyboardNoRecents": { - "type": "String", - "placeholders": {} - }, - "@globalChatId": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibility": { - "type": "String", - "placeholders": {} - }, - "@accessAndVisibilityDescription": { - "type": "String", - "placeholders": {} - }, - "@calls": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickers": { - "type": "String", - "placeholders": {} - }, - "@customEmojisAndStickersBody": { - "type": "String", - "placeholders": {} - }, - "@homeserver": { - "type": "String", - "placeholders": {} - }, - "@errorObtainingLocation": { - "type": "String", - "placeholders": { - "error": { - "type": "String" - } - } - }, - "@goToTheNewRoom": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessages": { - "type": "String", - "placeholders": {} - }, - "@hideRedactedMessagesBody": { - "type": "String", - "placeholders": {} - }, - "@hideInvalidOrUnknownMessageFormats": { - "type": "String", - "placeholders": {} - }, - "@block": { - "type": "String", - "placeholders": {} - }, - "@blockedUsers": { - "type": "String", - "placeholders": {} - }, - "@blockListDescription": { - "type": "String", - "placeholders": {} - }, - "@blockUsername": { - "type": "String", - "placeholders": {} - }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@locationDisabledNotice": { - "type": "String", - "placeholders": {} - }, - "@locationPermissionDeniedNotice": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, - "@noEncryptionForPublicRooms": { - "type": "String", - "placeholders": {} - }, - "@noMatrixServer": { - "type": "String", - "placeholders": { - "server1": { - "type": "String" - }, - "server2": { - "type": "String" - } - } - }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@scanQrCode": { - "type": "String", - "placeholders": {} - }, - "@obtainingLocation": { - "type": "String", - "placeholders": {} - }, - "@oopsPushError": { - "type": "String", - "placeholders": {} - }, - "@openVideoCamera": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, - "@addAccount": { - "type": "String", - "placeholders": {} - }, "@editBundlesForAccount": { "type": "String", "placeholders": {} @@ -3918,137 +4519,14 @@ "type": "String", "placeholders": {} }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, - "@openInMaps": { - "type": "String", - "placeholders": {} - }, - "@link": { - "type": "String", - "placeholders": {} - }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@or": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChats": { - "type": "String", - "placeholders": {} - }, - "@hideMemberChangesInPublicChatsBody": { - "type": "String", - "placeholders": {} - }, - "@overview": { - "type": "String", - "placeholders": {} - }, - "@notifyMeFor": { - "type": "String", - "placeholders": {} - }, - "@passwordRecoverySettings": { - "type": "String", - "placeholders": {} - }, - "@people": { - "type": "String", - "placeholders": {} - }, - "@pleaseChoose": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAPasscode": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnter4Digits": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourPin": { - "type": "String", - "placeholders": {} - }, - "@redactedBy": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, - "@redactedByBecause": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "reason": { - "type": "String" - } - } - }, - "@redactMessage": { - "type": "String", - "placeholders": {} - }, "@register": { "type": "String", "placeholders": {} }, - "@removeYourAvatar": { - "type": "String", - "placeholders": {} - }, - "@roomVersion": { - "type": "String", - "placeholders": {} - }, - "@saveFile": { - "type": "String", - "placeholders": {} - }, - "@recoveryKey": { - "type": "String", - "placeholders": {} - }, "@recoveryKeyLost": { "type": "String", "placeholders": {} }, - "@sendAsText": { - "type": "String", - "placeholders": {} - }, - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@sendSticker": { - "type": "String", - "placeholders": {} - }, "@separateChatTypes": { "type": "String", "placeholders": {} @@ -4057,30 +4535,6 @@ "type": "String", "placeholders": {} }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, - "@shareLocation": { - "type": "String", - "placeholders": {} - }, - "@showPassword": { - "type": "String", - "placeholders": {} - }, - "@presenceStyle": { - "type": "String", - "placeholders": {} - }, - "@presencesToggle": { - "type": "String", - "placeholders": {} - }, - "@singlesignon": { - "type": "String", - "placeholders": {} - }, "@spaceIsPublic": { "type": "String", "placeholders": {} @@ -4089,157 +4543,10 @@ "type": "String", "placeholders": {} }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWait": { - "type": "String", - "placeholders": {} - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, - "@unverified": { - "type": "String", - "placeholders": {} - }, - "@verified": { - "type": "String", - "placeholders": {} - }, - "@waitingPartnerAcceptRequest": { - "type": "String", - "placeholders": {} - }, - "@waitingPartnerEmoji": { - "type": "String", - "placeholders": {} - }, - "@wipeChatBackup": { - "type": "String", - "placeholders": {} - }, - "@messageInfo": { - "type": "String", - "placeholders": {} - }, - "@time": { - "type": "String", - "placeholders": {} - }, - "@messageType": { - "type": "String", - "placeholders": {} - }, - "@sender": { - "type": "String", - "placeholders": {} - }, - "@openGallery": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@start": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, - "@publish": { - "type": "String", - "placeholders": {} - }, - "@videoWithSize": { - "type": "String", - "placeholders": { - "size": { - "type": "String" - } - } - }, - "@openChat": { - "type": "String", - "placeholders": {} - }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, - "@reportUser": { - "type": "String", - "placeholders": {} - }, - "@dismiss": { - "type": "String", - "placeholders": {} - }, - "@reactedWith": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - }, - "reaction": { - "type": "String" - } - } - }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, "@confirmEventUnpin": { "type": "String", "placeholders": {} }, - "@emojis": { - "type": "String", - "placeholders": {} - }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, - "@voiceCall": { - "type": "String", - "placeholders": {} - }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, "@indexedDbErrorTitle": { "type": "String", "placeholders": {} @@ -4248,142 +4555,10 @@ "type": "String", "placeholders": {} }, - "@switchToAccount": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "@nextAccount": { - "type": "String", - "placeholders": {} - }, - "@previousAccount": { - "type": "String", - "placeholders": {} - }, - "@addWidget": { - "type": "String", - "placeholders": {} - }, - "@widgetVideo": { - "type": "String", - "placeholders": {} - }, - "@widgetEtherpad": { - "type": "String", - "placeholders": {} - }, - "@widgetJitsi": { - "type": "String", - "placeholders": {} - }, - "@widgetCustom": { - "type": "String", - "placeholders": {} - }, - "@widgetName": { - "type": "String", - "placeholders": {} - }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, "@errorAddingWidget": { "type": "String", "placeholders": {} }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@youBannedUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youHaveWithdrawnTheInvitationFor": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youInvitedToBy": { - "type": "String", - "placeholders": { - "alias": { - "type": "String" - } - } - }, - "@youInvitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@invitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youInvitedUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youKicked": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youKickedAndBanned": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@youUnbannedUser": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, "@hasKnocked": { "type": "String", "placeholders": { @@ -4396,122 +4571,18 @@ "type": "String", "placeholders": {} }, - "@noOneCanJoin": { - "type": "String", - "placeholders": {} - }, - "@userWouldLikeToChangeTheChat": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@noPublicLinkHasBeenCreatedYet": { - "type": "String", - "placeholders": {} - }, "@knock": { "type": "String", "placeholders": {} }, - "@users": { - "type": "String", - "placeholders": {} - }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, "@saveKeyManuallyDescription": { "type": "String", "placeholders": {} }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@countFiles": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@user": { - "type": "String", - "placeholders": {} - }, - "@custom": { - "type": "String", - "placeholders": {} - }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, - "@screenSharingTitle": { - "type": "String", - "placeholders": {} - }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, - "@callingPermissions": { - "type": "String", - "placeholders": {} - }, - "@callingAccount": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, "@noKeyForThisMessage": { "type": "String", "placeholders": {} }, - "@newGroup": { - "type": "String", - "placeholders": {} - }, - "@newSpace": { - "type": "String", - "placeholders": {} - }, "@enterSpace": { "type": "String", "placeholders": {} @@ -4520,310 +4591,26 @@ "type": "String", "placeholders": {} }, - "@allSpaces": { - "type": "String", - "placeholders": {} - }, - "@numChats": { - "type": "number", - "placeholders": { - "number": { - "type": "String" - } - } - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@hidePresences": { - "type": "String", - "placeholders": {} - }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, - "@wasDirectChatDisplayName": { - "type": "String", - "placeholders": { - "oldDisplayName": { - "type": "String" - } - } - }, "@newSpaceDescription": { "type": "String", "placeholders": {} }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, "@noBackupWarning": { "type": "String", "placeholders": {} }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "@fileHasBeenSavedAt": { - "type": "String", - "placeholders": { - "path": { - "type": "String" - } - } - }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} - }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, "@report": { "type": "String", "placeholders": {} }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, - "@wrongPinEntered": { - "type": "String", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, "@makeAdminDescription": { "type": "String", "placeholders": {} }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, - "@yourGlobalUserIdIs": { - "type": "String", - "placeholders": {} - }, - "@noUsersFoundWithQuery": { - "type": "String", - "placeholders": { - "query": { - "type": "String" - } - } - }, "@knocking": { "type": "String", "placeholders": {} }, - "@chatCanBeDiscoveredViaSearchOnServer": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@searchChatsRooms": { - "type": "String", - "placeholders": {} - }, - "@nothingFound": { - "type": "String", - "placeholders": {} - }, - "@groupName": { - "type": "String", - "placeholders": {} - }, - "@createGroupAndInviteUsers": { - "type": "String", - "placeholders": {} - }, - "@groupCanBeFoundViaSearch": { - "type": "String", - "placeholders": {} - }, - "@wrongRecoveryKey": { - "type": "String", - "placeholders": {} - }, - "@startConversation": { - "type": "String", - "placeholders": {} - }, - "@commandHint_sendraw": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationTitle": { - "type": "String", - "placeholders": {} - }, - "@databaseMigrationBody": { - "type": "String", - "placeholders": {} - }, - "@leaveEmptyToClearStatus": { - "type": "String", - "placeholders": {} - }, - "@select": { - "type": "String", - "placeholders": {} - }, - "@searchForUsers": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterYourCurrentPassword": { - "type": "String", - "placeholders": {} - }, - "@newPassword": { - "type": "String", - "placeholders": {} - }, - "@pleaseChooseAStrongPassword": { - "type": "String", - "placeholders": {} - }, - "@passwordsDoNotMatch": { - "type": "String", - "placeholders": {} - }, - "@passwordIsWrong": { - "type": "String", - "placeholders": {} - }, - "@publicLink": { - "type": "String", - "placeholders": {} - }, - "@publicChatAddresses": { - "type": "String", - "placeholders": {} - }, - "@createNewAddress": { - "type": "String", - "placeholders": {} - }, "@joinSpace": { "type": "String", "placeholders": {} @@ -4840,50 +4627,6 @@ "type": "String", "placeholders": {} }, - "@decline": { - "type": "String", - "placeholders": {} - }, - "@thisDevice": { - "type": "String", - "placeholders": {} - }, - "@initAppError": { - "type": "String", - "placeholders": {} - }, - "@userRole": { - "type": "String", - "placeholders": {} - }, - "@minimumPowerLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "String" - } - } - }, - "@searchIn": { - "type": "String", - "placeholders": { - "chat": { - "type": "String" - } - } - }, - "@searchMore": { - "type": "String", - "placeholders": {} - }, - "@gallery": { - "type": "String", - "placeholders": {} - }, - "@files": { - "type": "String", - "placeholders": {} - }, "@databaseBuildErrorBody": { "type": "String", "placeholders": { @@ -4895,295 +4638,22 @@ } } }, - "@sessionLostBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@restoreSessionBody": { - "type": "String", - "placeholders": { - "url": { - "type": "String" - }, - "error": { - "type": "String" - } - } - }, - "@forwardMessageTo": { - "type": "String", - "placeholders": { - "roomName": { - "type": "String" - } - } - }, - "@sendReadReceipts": { - "type": "String", - "placeholders": {} - }, - "@sendTypingNotificationsDescription": { - "type": "String", - "placeholders": {} - }, - "@sendReadReceiptsDescription": { - "type": "String", - "placeholders": {} - }, - "@formattedMessages": { - "type": "String", - "placeholders": {} - }, - "@formattedMessagesDescription": { - "type": "String", - "placeholders": {} - }, - "@verifyOtherUser": { - "type": "String", - "placeholders": {} - }, "@verifyOtherUserDescription": { "type": "String", "placeholders": {} }, - "@verifyOtherDevice": { - "type": "String", - "placeholders": {} - }, "@verifyOtherDeviceDescription": { "type": "String", "placeholders": {} }, - "@acceptedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@canceledKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@completedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@isReadyForKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@requestedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@startedKeyVerification": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - } - } - }, - "@transparent": { - "type": "String", - "placeholders": {} - }, - "@incomingMessages": { - "type": "String", - "placeholders": {} - }, - "@stickers": { - "type": "String", - "placeholders": {} - }, - "@discover": { - "type": "String", - "placeholders": {} - }, - "@commandHint_ignore": { - "type": "String", - "placeholders": {} - }, - "@commandHint_unignore": { - "type": "String", - "placeholders": {} - }, - "@unreadChatsInApp": { - "type": "String", - "placeholders": { - "appname": { - "type": "String" - }, - "unread": { - "type": "String" - } - } - }, - "@noDatabaseEncryption": { - "type": "String", - "placeholders": {} - }, - "@thereAreCountUsersBlocked": { - "type": "String", - "placeholders": { - "count": {} - } - }, - "@restricted": { - "type": "String", - "placeholders": {} - }, "@knockRestricted": { "type": "String", "placeholders": {} }, - "@goToSpace": { - "type": "String", - "placeholders": { - "space": {} - } - }, - "@markAsUnread": { - "type": "String", - "placeholders": {} - }, - "@userLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@moderatorLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@adminLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@changeGeneralChatSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsers": { - "type": "String", - "placeholders": {} - }, - "@changeTheChatPermissions": { - "type": "String", - "placeholders": {} - }, - "@changeTheVisibilityOfChatHistory": { - "type": "String", - "placeholders": {} - }, - "@changeTheCanonicalRoomAlias": { - "type": "String", - "placeholders": {} - }, - "@sendRoomNotifications": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescriptionOfTheGroup": { - "type": "String", - "placeholders": {} - }, "@chatPermissionsDescription": { "type": "String", "placeholders": {} }, - "@updateInstalled": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - } - } - }, - "@changelog": { - "type": "String", - "placeholders": {} - }, - "@sendCanceled": { - "type": "String", - "placeholders": {} - }, - "@loginWithMatrixId": { - "type": "String", - "placeholders": {} - }, - "@discoverHomeservers": { - "type": "String", - "placeholders": {} - }, - "@whatIsAHomeserver": { - "type": "String", - "placeholders": {} - }, - "@homeserverDescription": { - "type": "String", - "placeholders": {} - }, - "@doesNotSeemToBeAValidHomeserver": { - "type": "String", - "placeholders": {} - }, - "@calculatingFileSize": { - "type": "String", - "placeholders": {} - }, - "@prepareSendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@generatingVideoThumbnail": { - "type": "String", - "placeholders": {} - }, - "@compressVideo": { - "type": "String", - "placeholders": {} - }, "@sendingAttachmentCountOfCount": { "type": "integer", "placeholders": { @@ -5195,198 +4665,10 @@ } } }, - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@oneOfYourDevicesIsNotVerified": { - "type": "String", - "placeholders": {} - }, - "@noticeChatBackupDeviceVerification": { - "type": "String", - "placeholders": {} - }, - "@continueText": { - "type": "String", - "placeholders": {} - }, - "@welcomeText": { - "type": "String", - "placeholders": {} - }, - "@blur": { - "type": "String", - "placeholders": {} - }, - "@opacity": { - "type": "String", - "placeholders": {} - }, - "@setWallpaper": { - "type": "String", - "placeholders": {} - }, - "@manageAccount": { - "type": "String", - "placeholders": {} - }, - "@noContactInformationProvided": { - "type": "String", - "placeholders": {} - }, - "@contactServerAdmin": { - "type": "String", - "placeholders": {} - }, "@contactServerSecurity": { "type": "String", "placeholders": {} }, - "@supportPage": { - "type": "String", - "placeholders": {} - }, - "@serverInformation": { - "type": "String", - "placeholders": {} - }, - "@name": { - "type": "String", - "placeholders": {} - }, - "@version": { - "type": "String", - "placeholders": {} - }, - "@website": { - "type": "String", - "placeholders": {} - }, - "@compress": { - "type": "String", - "placeholders": {} - }, - "@boldText": { - "type": "String", - "placeholders": {} - }, - "@italicText": { - "type": "String", - "placeholders": {} - }, - "@strikeThrough": { - "type": "String", - "placeholders": {} - }, - "@pleaseFillOut": { - "type": "String", - "placeholders": {} - }, - "@invalidUrl": { - "type": "String", - "placeholders": {} - }, - "@addLink": { - "type": "String", - "placeholders": {} - }, - "@unableToJoinChat": { - "type": "String", - "placeholders": {} - }, - "@previous": { - "type": "String", - "placeholders": {} - }, - "@otherPartyNotLoggedIn": { - "type": "String", - "placeholders": {} - }, - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@appWantsToUseForLoginDescription": { - "type": "String", - "placeholders": {} - }, - "@open": { - "type": "String", - "placeholders": {} - }, - "@waitingForServer": { - "type": "String", - "placeholders": {} - }, - "@appIntroduction": { - "type": "String", - "placeholders": {} - }, - "@newChatRequest": { - "type": "String", - "placeholders": {} - }, - "@contentNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@generalNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@roomNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@userSpecificNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@otherNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMaster": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMasterDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNotices": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNoticesDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMe": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, "@notificationRuleMemberEvent": { "type": "String", "placeholders": {} @@ -5395,118 +4677,10 @@ "type": "String", "placeholders": {} }, - "@notificationRuleIsUserMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReaction": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReactionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, "@notificationRuleRoomServerAclDescription": { "type": "String", "placeholders": {} }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEditsDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCall": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCallDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessage": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncrypted": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsi": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, "@notificationRuleServerAcl": { "type": "String", "placeholders": {} @@ -5515,105 +4689,14 @@ "type": "String", "placeholders": {} }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@deletePushRuleCanNotBeUndone": { - "type": "String", - "placeholders": {} - }, - "@more": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWith": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@allDevices": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@takeAPhoto": { - "type": "String", - "placeholders": {} - }, - "@recordAVideo": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@enterNewChat": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, "@youHaveKnocked": { "type": "String", "placeholders": {} }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, "@displayNavigationRail": { "type": "String", "placeholders": {} }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -5789,10 +4872,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -12158,4 +11237,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 87b5b670e..fc2c5f651 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -324,7 +324,7 @@ } } }, - "changedTheRoomAliases": "{username} heeft de kameraliassen gewijzigd", + "changedTheRoomAliases": "{username} heeft de chat aliassen gewijzigd", "@changedTheRoomAliases": { "type": "String", "placeholders": { @@ -382,7 +382,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "Je oude berichten zijn beveiligd met een herstelsleutel. Zorg ervoor dat je deze niet verliest.", + "chatBackupDescription": "Je berichten zijn beveiligd met een herstelsleutel. Zorg ervoor dat je deze niet verliest.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -411,7 +411,7 @@ "type": "String", "placeholders": {} }, - "commandHint_ban": "Persoon uit deze kamer verbannen", + "commandHint_ban": "Persoon uit deze chat verbannen", "@commandHint_ban": { "type": "String", "description": "Usage hint for the command /ban" @@ -421,22 +421,22 @@ "type": "String", "description": "Usage hint for the command /html" }, - "commandHint_invite": "Persoon in deze kamer uitnodigen", + "commandHint_invite": "Persoon in deze chat uitnodigen", "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" }, - "commandHint_join": "Toetreden tot de vermelde kamer", + "commandHint_join": "Toetreden tot de vermelde chat", "@commandHint_join": { "type": "String", "description": "Usage hint for the command /join" }, - "commandHint_kick": "Persoon uit deze kamer verwijderen", + "commandHint_kick": "Persoon uit deze chat verwijderen", "@commandHint_kick": { "type": "String", "description": "Usage hint for the command /kick" }, - "commandHint_leave": "Deze kamer verlaten", + "commandHint_leave": "Deze chat verlaten", "@commandHint_leave": { "type": "String", "description": "Usage hint for the command /leave" @@ -446,12 +446,12 @@ "type": "String", "description": "Usage hint for the command /me" }, - "commandHint_myroomavatar": "Jouw avatar voor deze kamer instellen (met mxc-uri)", + "commandHint_myroomavatar": "Jouw avatar voor deze chat instellen (met mxc-uri)", "@commandHint_myroomavatar": { "type": "String", "description": "Usage hint for the command /myroomavatar" }, - "commandHint_myroomnick": "Jouw naam voor deze kamer instellen", + "commandHint_myroomnick": "Jouw naam voor deze chat instellen", "@commandHint_myroomnick": { "type": "String", "description": "Usage hint for the command /myroomnick" @@ -476,7 +476,7 @@ "type": "String", "description": "Usage hint for the command /send" }, - "commandHint_unban": "Persoon weer in deze kamer toestaan", + "commandHint_unban": "Persoon weer in deze chat toestaan", "@commandHint_unban": { "type": "String", "description": "Usage hint for the command /unban" @@ -711,12 +711,12 @@ "type": "String", "placeholders": {} }, - "editRoomAliases": "Kameraliassen wijzigen", + "editRoomAliases": "Chat aliassen wijzigen", "@editRoomAliases": { "type": "String", "placeholders": {} }, - "editRoomAvatar": "Kameravatar wijzigen", + "editRoomAvatar": "Chat avatar wijzigen", "@editRoomAvatar": { "type": "String", "placeholders": {} @@ -731,7 +731,7 @@ "type": "String", "placeholders": {} }, - "emotePacks": "Emoticonpakketten voor de kamer", + "emotePacks": "Emoticonpakketten voor de chat", "@emotePacks": { "type": "String", "placeholders": {} @@ -795,7 +795,7 @@ } } }, - "enterAnEmailAddress": "Voer een email in", + "enterAnEmailAddress": "Voer een emailadres in", "@enterAnEmailAddress": { "type": "String", "placeholders": {} @@ -854,7 +854,7 @@ "type": "String", "placeholders": {} }, - "goToTheNewRoom": "Ga naar de nieuwe kamer", + "goToTheNewRoom": "Ga naar de nieuwe chat", "@goToTheNewRoom": { "type": "String", "placeholders": {} @@ -1027,7 +1027,7 @@ } } }, - "joinRoom": "Toetreden tot de kamer", + "joinRoom": "Toetreden tot de chat", "@joinRoom": { "type": "String", "placeholders": {} @@ -1163,7 +1163,7 @@ "type": "String", "placeholders": {} }, - "needPantalaimonWarning": "Houd er rekening mee dat je voorlopig Pantalaimon nodig hebt om eind-tot-eindversleuteling te gebruiken.", + "needPantalaimonWarning": "Houd er rekening mee dat je voorlopig Pantalaimon nodig hebt om eind-tot-eind versleuteling te gebruiken.", "@needPantalaimonWarning": { "type": "String", "placeholders": {} @@ -1203,12 +1203,12 @@ "type": "String", "placeholders": {} }, - "noEncryptionForPublicRooms": "Je kunt de versleuteling pas activeren zodra de kamer niet meer openbaar toegankelijk is.", + "noEncryptionForPublicRooms": "Je kunt de versleuteling pas activeren zodra de chat niet meer openbaar toegankelijk is.", "@noEncryptionForPublicRooms": { "type": "String", "placeholders": {} }, - "noGoogleServicesWarning": "Firebase Cloud Messaging lijkt niet beschikbaar op je apparaat. Om nog steeds pushmeldingen te krijgen, adviseren we om ntfy te installeren. Met ntfy of een andere Unified Push provider kun je pushmeldingen ontvangen op een veilige manier. Je kunt ntfy downloaden van de PlayStore of van F-Droid.", + "noGoogleServicesWarning": "Firebase Cloud Messaging lijkt niet beschikbaar op je apparaat. Om pushmeldingen te krijgen, adviseren we om ntfy te installeren. Met ntfy of een andere Unified Push-provider kun je pushmeldingen ontvangen op een veilige manier. Je kunt ntfy downloaden in de PlayStore of F-Droid.", "@noGoogleServicesWarning": { "type": "String", "placeholders": {} @@ -1240,7 +1240,7 @@ "type": "String", "placeholders": {} }, - "noRoomsFound": "Geen kamers gevonden …", + "noRoomsFound": "Geen chats gevonden …", "@noRoomsFound": { "type": "String", "placeholders": {} @@ -1423,7 +1423,7 @@ "type": "String", "placeholders": {} }, - "publicRooms": "Openbare kamers", + "publicRooms": "Openbare chats", "@publicRooms": { "type": "String", "placeholders": {} @@ -1535,7 +1535,7 @@ "type": "String", "placeholders": {} }, - "roomHasBeenUpgraded": "Kamer is geüpgrade", + "roomHasBeenUpgraded": "Chat is geüpgrade", "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} @@ -1877,7 +1877,7 @@ "type": "String", "placeholders": {} }, - "unpin": "Losmaken", + "unpin": "Pin losmaken", "@unpin": { "type": "String", "placeholders": {} @@ -2095,7 +2095,7 @@ "@serverRequiresEmail": {}, "oneClientLoggedOut": "Één van jouw apparaten is uitgelogd", "@oneClientLoggedOut": {}, - "enableMultiAccounts": "(BETA) Multi-accounts inschakelen op dit apparaat", + "enableMultiAccounts": "(BETA) Meerdere accounts op dit apparaat inschakelen", "@enableMultiAccounts": {}, "bundleName": "Bundelnaam", "@bundleName": {}, @@ -2187,7 +2187,7 @@ } } }, - "pinMessage": "Maak vast aan kamer", + "pinMessage": "Maak vast aan chat", "@pinMessage": {}, "emojis": "Emoji's", "@emojis": {}, @@ -2195,7 +2195,7 @@ "@placeCall": {}, "unsupportedAndroidVersion": "Niet-ondersteunde Android-versie", "@unsupportedAndroidVersion": {}, - "unsupportedAndroidVersionLong": "Voor deze functie is een nieuwere Android-versie vereist. Controleer op updates of Lineage OS-ondersteuning.", + "unsupportedAndroidVersionLong": "Voor deze functie is een nieuwe Android-versie verplicht. Controleer je updates of Lineage OS-ondersteuning.", "@unsupportedAndroidVersionLong": {}, "videoCallsBetaWarning": "Houd er rekening mee dat videogesprekken momenteel in bèta zijn. Ze werken misschien niet zoals je verwacht of werken niet op alle platformen.", "@videoCallsBetaWarning": {}, @@ -2368,13 +2368,13 @@ } } }, - "commandHint_markasdm": "Markeer als privéberichtenkamer voor Matrix ID", + "commandHint_markasdm": "Markeer als privé-chat voor de Matrix-ID", "@commandHint_markasdm": {}, "commandHint_markasgroup": "Markeer als groep", "@commandHint_markasgroup": {}, "whyIsThisMessageEncrypted": "Waarom is dit bericht onleesbaar?", "@whyIsThisMessageEncrypted": {}, - "noKeyForThisMessage": "Dit kan gebeuren als het bericht is verzonden voordat je bij je account op dit apparaat hebt aangemeld.\n\nHet is ook mogelijk dat de afzender je apparaat heeft geblokkeerd of dat er iets mis is gegaan met de internetverbinding.\n\nKan je het bericht wel lezen op een andere sessie? Dan kan je het bericht daarvandaan overzetten! Ga naar Instellingen > Apparaten en zorg ervoor dat je apparaten elkaar hebben geverifieerd. Wanneer je de kamer de volgende keer opent en beide sessies op de voorgrond staan, zullen de sleutels automatisch worden verzonden.\n\nWil je de sleutels niet verliezen als je uitlogt of van apparaat wisselt? Zorg er dan voor dat je de chatback-up hebt aangezet in de instellingen.", + "noKeyForThisMessage": "Dit kan gebeuren als het bericht is verzonden voordat je bij je account op dit apparaat hebt aangemeld.\n\nHet is ook mogelijk dat de afzender je apparaat heeft geblokkeerd of dat er iets mis is gegaan met de internetverbinding.\n\nKan je het bericht wel lezen in een andere sessie? Dan kan je het bericht daarvandaan overzetten! Ga naar Instellingen > Apparaten en zorg ervoor dat je apparaten elkaar hebben geverifieerd. Wanneer je de chat de volgende keer opent en beide sessies op de voorgrond staan, zullen de sleutels automatisch worden verzonden.\n\nWil je de sleutels niet verliezen als je uitlogt of van apparaat wisselt? Zorg er dan voor dat je de chatback-up hebt aangezet in de instellingen.", "@noKeyForThisMessage": {}, "enterSpace": "Space betreden", "@enterSpace": {}, @@ -2394,7 +2394,7 @@ "@callingAccountDetails": {}, "appearOnTop": "Bovenaan verschijnen", "@appearOnTop": {}, - "appearOnTopDetails": "Laat de app bovenaan verschijnen (niet nodig als je FluffyChat al hebt ingesteld als een belaccount)", + "appearOnTopDetails": "Laat de app bovenaan verschijnen (niet nodig als je FluffyChat al hebt ingesteld als een bel-account)", "@appearOnTopDetails": {}, "otherCallingPermissions": "Microfoon, camera en andere FluffyChat-rechten", "@otherCallingPermissions": {}, @@ -2402,7 +2402,7 @@ "@newGroup": {}, "newSpace": "Space aanmaken", "@newSpace": {}, - "enterRoom": "Kamer betreden", + "enterRoom": "Chat betreden", "@enterRoom": {}, "numChats": "{number} chats", "@numChats": { @@ -2501,15 +2501,6 @@ "type": "String", "placeholders": {} }, - "signInWith": "Aanmelden met {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Geen afbeeldingsbestand.", "@notAnImage": {}, "importNow": "Nu importeren", @@ -2654,23 +2645,23 @@ "type": "String", "placeholders": {} }, - "searchChatsRooms": "Zoek naar #chats, @personen...", + "searchChatsRooms": "Zoek #chats, @personen...", "@searchChatsRooms": {}, "swipeRightToLeftToReply": "Veeg van rechts naar links om te reageren", "@swipeRightToLeftToReply": {}, "calls": "Gesprekken", "@calls": {}, - "customEmojisAndStickers": "Aangepaste emoticons en stickers", + "customEmojisAndStickers": "Aangepaste emoji's en stickers", "@customEmojisAndStickers": {}, "accessAndVisibilityDescription": "Wie mag toetreden tot deze chat en hoe de chat ontdekt kan worden.", "@accessAndVisibilityDescription": {}, - "customEmojisAndStickersBody": "Voeg toe of deel aangepaste emoji's of stickers die gebruikt kunnen worden in elke chat.", + "customEmojisAndStickersBody": "Voeg toe of deel aangepaste emoji's en stickers die in elke chat gebruikt kunnen worden.", "@customEmojisAndStickersBody": {}, - "hideRedactedMessages": "Verberg verwijderde berichten", + "hideRedactedMessages": "Verwijderde berichten verbergen", "@hideRedactedMessages": {}, "hideRedactedMessagesBody": "Als iemand een bericht verwijdert, is dit bericht niet meer zichtbaar in de chat.", "@hideRedactedMessagesBody": {}, - "hideInvalidOrUnknownMessageFormats": "Verberg ongeldige of onbekende berichtformaten", + "hideInvalidOrUnknownMessageFormats": "Ongeldige of onbekende berichtformaten verbergen", "@hideInvalidOrUnknownMessageFormats": {}, "passwordRecoverySettings": "Wachtwoordherstel-instellingen", "@passwordRecoverySettings": {}, @@ -2690,7 +2681,7 @@ "@hidePresences": {}, "noOneCanJoin": "Niemand kan deelnemen", "@noOneCanJoin": {}, - "yourGlobalUserIdIs": "Je Matrix ID is: ", + "yourGlobalUserIdIs": "Je Matrix-ID is: ", "@yourGlobalUserIdIs": {}, "appLockDescription": "Vergendel de app wanneer het niet gebruikt wordt met een pincode", "@appLockDescription": {}, @@ -2756,7 +2747,7 @@ "@changeGeneralChatSettings": {}, "restricted": "Beperkt", "@restricted": {}, - "searchForUsers": "Zoek naar @personen...", + "searchForUsers": "Zoek @personen...", "@searchForUsers": {}, "searchMore": "Zoek meer...", "@searchMore": {}, @@ -2835,7 +2826,7 @@ "@incomingMessages": {}, "discover": "Ontdek", "@discover": {}, - "commandHint_ignore": "Negeer de gegeven Matrix ID", + "commandHint_ignore": "Negeer de gegeven Matrix-ID", "@commandHint_ignore": {}, "noChatsFoundHere": "Hier zijn nog geen chats. Begin een nieuwe chat met iemand door op de onderstaande knop te klikken. ⤵️", "@noChatsFoundHere": {}, @@ -2870,7 +2861,7 @@ "@otherPartyNotLoggedIn": {}, "notifyMeFor": "Waarschuw mij voor", "@notifyMeFor": {}, - "blockListDescription": "Je kunt personen blokkeren die je lastig vallen. Je kan dan geen berichten meer ontvangen of kameruitnodigingen krijgen van de personen op je blokkeerlijst.", + "blockListDescription": "Je kunt personen blokkeren die je lastig vallen. Je kan dan geen berichten meer ontvangen of chat uitnodigingen krijgen van de personen op je blokkeerlijst.", "@blockListDescription": {}, "sendImages": "Stuur {count} afbeelding(en)", "@sendImages": { @@ -2881,7 +2872,7 @@ } } }, - "presencesToggle": "Toon statusberichten van andere personen", + "presencesToggle": "Statusberichten van andere personen tonen", "@presencesToggle": { "type": "String", "placeholders": {} @@ -2975,13 +2966,13 @@ "@otherNotificationSettings": {}, "notificationRuleContainsUserName": "Bevat naam van persoon", "@notificationRuleContainsUserName": {}, - "notificationRuleContainsUserNameDescription": "Stuurt een melding als een bericht de persoon vermeld.", + "notificationRuleContainsUserNameDescription": "Stuur een melding als je inlognaam vermeld wordt in een bericht.", "@notificationRuleContainsUserNameDescription": {}, "notificationRuleMaster": "Alle meldingen dempen", "@notificationRuleMaster": {}, - "notificationRuleMasterDescription": "Overschrijf alle andere regels en meldingen uitschakelen.", + "notificationRuleMasterDescription": "Overschrijf alle andere regels en schakel alle meldingen uit.", "@notificationRuleMasterDescription": {}, - "notificationRuleMemberEventDescription": "Meldingen voor kamer-gebeurtenissen uitschakelen.", + "notificationRuleMemberEventDescription": "Meldingen voor chat-gebeurtenissen uitschakelen.", "@notificationRuleMemberEventDescription": {}, "notificationRuleIsUserMention": "Persoonvermelding", "@notificationRuleIsUserMention": {}, @@ -3024,11 +3015,11 @@ "@sendRoomNotifications": {}, "noticeChatBackupDeviceVerification": "Opmerking: Als al je apparaten zijn verbonden met de chat back-up worden ze automatisch geverifieerd.", "@noticeChatBackupDeviceVerification": {}, - "notificationRuleMemberEvent": "Kamer-gebeurtenis", + "notificationRuleMemberEvent": "Chat-gebeurtenis", "@notificationRuleMemberEvent": {}, "notificationRuleSuppressNotices": "Automatische berichten uitschakelen", "@notificationRuleSuppressNotices": {}, - "setWallpaper": "Wallpaper instellen", + "setWallpaper": "Achtergrond instellen", "@setWallpaper": {}, "oneOfYourDevicesIsNotVerified": "Een van jouw apparaten is niet geverifieerd", "@oneOfYourDevicesIsNotVerified": {}, @@ -3042,7 +3033,7 @@ "@waitingForServer": {}, "generalNotificationSettings": "Algemene melding instellingen", "@generalNotificationSettings": {}, - "notificationRuleInviteForMeDescription": "Stuur een melding wanneer een persoon wordt uitgenodigd voor een kamer.", + "notificationRuleInviteForMeDescription": "Stuur een melding wanneer je wordt uitgenodigd voor een chat.", "@notificationRuleInviteForMeDescription": {}, "notificationRuleSuppressNoticesDescription": "Meldingen van automatische accounts zoals bots uitschakelen.", "@notificationRuleSuppressNoticesDescription": {}, @@ -3116,7 +3107,7 @@ "@discoverHomeservers": {}, "changelog": "Wijzigingengeschiedenis", "@changelog": {}, - "loginWithMatrixId": "Inloggen met Matrix ID", + "loginWithMatrixId": "Inloggen met Matrix-ID", "@loginWithMatrixId": {}, "calculatingFileSize": "Bestandsgrootte berekenen...", "@calculatingFileSize": {}, @@ -3147,7 +3138,7 @@ "@name": {}, "verifyOtherDeviceDescription": "Een geverifieerd ander apparaat zorgt ervoor dat de apparaten sleutels uitwisselen, wat je beveiliging versterkt. 💪 Als je de verificatie start verschijnt er een popup op beide apparaten. Hier staat een reeks emoji's of getallen die je met elkaar moet vergelijken. Het is handig om beide apparaten bij de hand te hebben voordat je de verificatie start. 🤳", "@verifyOtherDeviceDescription": {}, - "commandHint_unignore": "Herstel de negeerde Matrix ID", + "commandHint_unignore": "Herstel de negeerde Matrix-ID", "@commandHint_unignore": {}, "forwardMessageTo": "Bericht doorsturen naar {roomName}?", "@forwardMessageTo": { @@ -3243,25 +3234,25 @@ "@homeserverDescription": {}, "notificationRuleContainsDisplayName": "Bevat de naam", "@notificationRuleContainsDisplayName": {}, - "notificationRuleIsUserMentionDescription": "Stuur een melding als de persoon direct genoemd wordt in een bericht.", + "notificationRuleIsUserMentionDescription": "Stuur een melding als je direct genoemd wordt in een bericht.", "@notificationRuleIsUserMentionDescription": {}, - "notificationRuleContainsDisplayNameDescription": "Stuur een melding als de persoon genoemd wordt in het bericht.", + "notificationRuleContainsDisplayNameDescription": "Stuur een melding als je genoemd wordt in een bericht.", "@notificationRuleContainsDisplayNameDescription": {}, "notificationRuleIsRoomMention": "Kamervermelding", "@notificationRuleIsRoomMention": {}, - "notificationRuleIsRoomMentionDescription": "Stuur een melding naar de persoon als er in een kamervermelding is.", + "notificationRuleIsRoomMentionDescription": "Stuur een melding als er een kamervermelding is.", "@notificationRuleIsRoomMentionDescription": {}, "notificationRuleRoomnotif": "Kamermelding", "@notificationRuleRoomnotif": {}, - "notificationRuleRoomnotifDescription": "Stuur een melding naar de persoon wanneer een bericht '@room' bevat.", + "notificationRuleRoomnotifDescription": "Stuur je een melding als een bericht '@room' bevat.", "@notificationRuleRoomnotifDescription": {}, - "notificationRuleTombstone": "Sleutingsbericht", + "notificationRuleTombstone": "Sluitingsbericht", "@notificationRuleTombstone": {}, "notificationRuleReaction": "Reactie", "@notificationRuleReaction": {}, "notificationRuleRoomServerAcl": "Kamer Server ACL", "@notificationRuleRoomServerAcl": {}, - "notificationRuleTombstoneDescription": "Stuur een melding naar de persoon over kamersluitingsberichten.", + "notificationRuleTombstoneDescription": "Stuur een melding naar je over chat-sluitingsberichten.", "@notificationRuleTombstoneDescription": {}, "notificationRuleReactionDescription": "Meldingen voor reacties uitschakelen.", "@notificationRuleReactionDescription": {}, @@ -3273,25 +3264,25 @@ "@notificationRuleCall": {}, "notificationRuleSuppressEditsDescription": "Meldingen voor bewerkte berichten uitschakelen.", "@notificationRuleSuppressEditsDescription": {}, - "notificationRuleEncryptedRoomOneToOneDescription": "Stuur een melding naar de persoon over berichten in versleutelde een-op-een kamers.", + "notificationRuleEncryptedRoomOneToOneDescription": "Stuur een melding naar je over berichten in versleutelde een-op-een chats.", "@notificationRuleEncryptedRoomOneToOneDescription": {}, - "notificationRuleEncryptedRoomOneToOne": "Versleutelde een-op-een kamer", + "notificationRuleEncryptedRoomOneToOne": "Versleutelde een-op-een chat", "@notificationRuleEncryptedRoomOneToOne": {}, - "notificationRuleRoomOneToOne": "Een-op-een kamer", + "notificationRuleRoomOneToOne": "Een-op-een chat", "@notificationRuleRoomOneToOne": {}, "notificationRuleMessage": "Bericht", "@notificationRuleMessage": {}, "notificationRuleEncrypted": "Versleuteld", "@notificationRuleEncrypted": {}, - "notificationRuleRoomOneToOneDescription": "Stuur een melding naar de persoon over berichten in een-op-een kamers.", + "notificationRuleRoomOneToOneDescription": "Stuur een melding naar je over berichten in een-op-een chats.", "@notificationRuleRoomOneToOneDescription": {}, - "notificationRuleMessageDescription": "Stuur een melding naar de persoon over algemene berichten.", + "notificationRuleMessageDescription": "Stuur een melding naar je over algemene berichten.", "@notificationRuleMessageDescription": {}, "notificationRuleJitsi": "Jitsi", "@notificationRuleJitsi": {}, - "notificationRuleEncryptedDescription": "Stuur een melding naar de persoon over berichten in versleutelde kamers.", + "notificationRuleEncryptedDescription": "Stuur een melding naar je over berichten in versleutelde chats.", "@notificationRuleEncryptedDescription": {}, - "notificationRuleJitsiDescription": "Stuur een melding naar de persoon over Jitsi widget gebeurtenissen.", + "notificationRuleJitsiDescription": "Stuur een melding naar je over Jitsi widget-gebeurtenissen.", "@notificationRuleJitsiDescription": {}, "unknownPushRule": "Onbekende notificatieregel '{rule}'", "@unknownPushRule": { @@ -3302,9 +3293,9 @@ } } }, - "notificationRuleServerAcl": "Server ACL gebeurtenissen uitschakelen", + "notificationRuleServerAcl": "Server ACL-gebeurtenissen uitschakelen", "@notificationRuleServerAcl": {}, - "notificationRuleServerAclDescription": "Meldingen over server ACL gebeurtenissen uitschakelen.", + "notificationRuleServerAclDescription": "Meldingen over server ACL-gebeurtenissen uitschakelen.", "@notificationRuleServerAclDescription": {}, "more": "Meer", "@more": {}, @@ -3322,19 +3313,19 @@ "@crossVerifiedDevicesIfEnabled": {}, "shareKeysWith": "Deel sleutels met...", "@shareKeysWith": {}, - "notificationRuleCallDescription": "Stuur een melding naar de persoon over oproepen.", + "notificationRuleCallDescription": "Stuur een melding naar je over oproepen.", "@notificationRuleCallDescription": {}, "deletePushRuleCanNotBeUndone": "Als je deze melding-instelling verwijderd, kan dit niet ongedaan gemaakt worden.", "@deletePushRuleCanNotBeUndone": {}, - "takeAPhoto": "Neem een foto", + "takeAPhoto": "Foto maken", "@takeAPhoto": {}, - "recordAVideo": "Neem een video", + "recordAVideo": "Video opnemen", "@recordAVideo": {}, "optionalMessage": "(Optioneel) bericht...", "@optionalMessage": {}, "notSupportedOnThisDevice": "Niet ondersteund op dit apparaat", "@notSupportedOnThisDevice": {}, - "commandHint_roomupgrade": "Upgradeer deze kamer naar de aangegeven kamerversie", + "commandHint_roomupgrade": "Upgradeer deze chat naar de aangegeven kamerversie", "@commandHint_roomupgrade": {}, "setCustomPermissionLevel": "Aangepast rechten-niveau instellen", "@setCustomPermissionLevel": {}, @@ -3344,13 +3335,13 @@ "@ignoreUser": {}, "normalUser": "Normaal persoon", "@normalUser": {}, - "pleaseWaitUntilInvited": "Wacht even alsjeblieft tot iemand van de kamer je uitnodigt.", + "pleaseWaitUntilInvited": "Wacht even alsjeblieft tot iemand van de chat je uitnodigt.", "@pleaseWaitUntilInvited": {}, "approve": "Goedkeuren", "@approve": {}, "youHaveKnocked": "Je hebt geklopt", "@youHaveKnocked": {}, - "sentVoiceMessage": "🎙️ {duration} - {sender}", + "sentVoiceMessage": "🎙️ {duration} - Spraakbericht van {sender}", "@sentVoiceMessage": { "type": "String", "placeholders": { @@ -3373,10 +3364,148 @@ }, "checkList": "Checklist", "@checkList": {}, - "commandHint_logout": "Uitloggen van je huidige apparaat", - "commandHint_logoutall": "Log uit van alle actieve apparaten", - "displayNavigationRail": "Toon navigatiebalk op mobiel", + "commandHint_logout": "Uw huidige apparaat uitloggen", + "@commandHint_logout": {}, + "commandHint_logoutall": "Alle actieve apparaten uitloggen", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Navigatiebalk op mobiel tonen", + "@displayNavigationRail": {}, + "moreEvents": "Meer gebeurtenissen", + "@moreEvents": {}, "customReaction": "Aangepaste reactie", + "@customReaction": {}, + "declineInvitation": "Uitnodiging afwijzen", + "@declineInvitation": {}, + "noMessagesYet": "Nog geen berichten", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Lang drukken om een spraakbericht op te nemen.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Pauzeer", + "@pause": {}, + "resume": "Hervat", + "@resume": {}, + "donate": "Doneer", + "@donate": {}, + "newSubSpace": "Nieuwe sub-space", + "@newSubSpace": {}, + "moveToDifferentSpace": "Naar andere space verplaatsen", + "@moveToDifferentSpace": {}, + "moveUp": "Omhoog verplaatsen", + "@moveUp": {}, + "moveDown": "Omlaag verplaatsen", + "@moveDown": {}, + "removeFromSpaceDescription": "De chat zal worden verwijderd uit de space, maar blijft in je chats.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} chats", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "{spaces} personen in space", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "{spaces} personen in space kunnen kloppen", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "startedAPoll": "{username} is een peiling begonnen.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Peiling", + "@poll": {}, + "startPoll": "Peiling starten", + "@startPoll": {}, + "endPoll": "Peiling beëindigen", + "@endPoll": {}, + "answersVisible": "Zichtbaar invullen", + "@answersVisible": {}, + "answersHidden": "Antwoorden verborgen", + "@answersHidden": {}, + "pollQuestion": "Peiling vraag", + "@pollQuestion": {}, + "answerOption": "Antwoord optie", + "@answerOption": {}, + "addAnswerOption": "Antwoord optie toevoegen", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Meerdere antwoorden toestaan", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Peiling is geëindigd", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Één stem} other{{count} stemmen}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "Antwoorden zullen zichtbaar zijn wanneer de peiling is geëindigd", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Antwoord in draad", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Één antwoord} other{{count} antwoorden}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Draad", + "@thread": {}, + "backToMainChat": "Terug naar hoofdchat", + "@backToMainChat": {}, + "saveChanges": "Wijzigingen opslaan", + "@saveChanges": {}, + "createSticker": "Sticker of emoji maken", + "@createSticker": {}, + "useAsSticker": "Gebruik als sticker", + "@useAsSticker": {}, + "useAsEmoji": "Gebruik als emoji", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Stickerpakketnaam bestaat al", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Nieuw stickerpakket", + "@newStickerPack": {}, + "stickerPackName": "Stickerpakketnaam", + "@stickerPackName": {}, + "attribution": "Toeschrijving", + "@attribution": {}, + "skipChatBackup": "Chatback-up overslaan", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Weet je het zeker? Zonder chat back-up verlies je toegang tot je berichten als je van apparaat wisselt.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Berichten laden", + "@loadingMessages": {}, + "setupChatBackup": "Chatback-up instellen", + "@setupChatBackup": {}, + "changedTheChatDescription": "{username} heeft de chatomschrijving gewijzigd", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} heeft de chatnaam gewijzigd", + "@changedTheChatName": {}, "writeAMessageLangCodes": "Typ in {l1} of {l2}...", "requests": "Verzoeken", "holdForInfo": "Klik en houd vast voor woordinformatie.", @@ -3418,7 +3547,6 @@ "updateLanguage": "Mijn talen", "whatLanguageYouWantToLearn": "Welke taal wil je leren?", "whatIsYourBaseLanguage": "Wat is jouw brontaal?", - "saveChanges": "Wijzigingen opslaan", "publicProfileTitle": "Sta toe dat mijn profiel gevonden kan worden in zoekopdrachten", "publicProfileDesc": "Door deze optie in te schakelen, kunnen andere gebruikers jouw profiel vinden in de wereldwijde zoekbalk en verzoeken sturen om te chatten. Op dat moment kun je kiezen om het verzoek te accepteren of te weigeren.", "errorDisableIT": "Vertaalhulp is uitgeschakeld.", @@ -4505,22 +4633,6 @@ "playWithAI": "Speel voorlopig met AI", "courseStartDesc": "Pangea Bot is klaar om op elk moment te starten!\n\n...maar leren is beter met vrienden!", "@@locale": "nl", - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4696,10 +4808,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11065,4 +11173,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index f9efba83e..6c1c95fd6 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1727,7 +1727,7 @@ }, "link": "Link", "@link": {}, - "roomHasBeenUpgraded": "Pokój zostać zaktualizowany", + "roomHasBeenUpgraded": "Pokój został zaktualizowany", "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} @@ -2531,15 +2531,6 @@ } } }, - "signInWith": "Zaloguj się z {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "optionalRedactReason": "(Opcjonalnie) Powód usunięcia tej wiadomości...", "@optionalRedactReason": {}, "exportEmotePack": "Eksportuj pakiet emotikonów jako .zip", @@ -2920,7 +2911,7 @@ } } }, - "oneOfYourDevicesIsNotVerified": "Tylko kiedy Twoje urządzenie nie jest zweryfikowane", + "oneOfYourDevicesIsNotVerified": "Jedno z Twoich urządzeń nie jest zweryfikowane", "@oneOfYourDevicesIsNotVerified": {}, "supportPage": "Strona obsługi użytkownika", "@supportPage": {}, @@ -3346,7 +3337,7 @@ } } }, - "sentVoiceMessage": "🎙️ {duration} - {sender}", + "sentVoiceMessage": "🎙️ {duration} - Wiadomość głosowa od: {sender}", "@sentVoiceMessage": { "type": "String", "placeholders": { @@ -3374,10 +3365,18 @@ "@youHaveKnocked": {}, "pleaseWaitUntilInvited": "Proszę zaczekać na zaproszenie przez kogoś z pokoju.", "@pleaseWaitUntilInvited": {}, - "commandHint_logout": "Wyloguj się z aktualnego urządzenia", - "commandHint_logoutall": "Wyloguj się ze wszystkich aktywnych urządzeń", - "displayNavigationRail": "Pokaż pasek nawigacji na telefonie", - "customReaction": "Niestandardowa reakcja", + "commandHint_logout": "Wyloguj bieżące urządzenie", + "@commandHint_logout": {}, + "commandHint_logoutall": "Wyloguj wszystkie aktywne urządzenia", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Pokazuj pasek nawigacyjny na urządzeniach mobilnych", + "@displayNavigationRail": {}, + "customReaction": "Własna reakcja", + "@customReaction": {}, + "moreEvents": "Więcej zdarzeń", + "@moreEvents": {}, + "declineInvitation": "Odrzuć zaproszenie", + "@declineInvitation": {}, "writeAMessageLangCodes": "Piszesz w {l1} lub {l2}...", "requests": "Prośby", "holdForInfo": "Kliknij i przytrzymaj, aby uzyskać informacje o słowie.", @@ -4505,22 +4504,6 @@ "inviteYourFriends": "Zaproś swoich znajomych", "playWithAI": "Na razie pobaw się AI", "courseStartDesc": "Pangea Bot jest gotowy do działania w każdej chwili!\n\n...ale nauka jest lepsza z przyjaciółmi!", - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -11063,4 +11046,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 5de04b43c..e2236b526 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -131,6 +131,18 @@ } } }, + "alwaysUse24HourFormat": "true", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "repeatPassword": "Repita a senha", + "@repeatPassword": {}, + "notAnImage": "Não é um arquivo de imagem.", + "@notAnImage": {}, + "setCustomPermissionLevel": "Definir nível de permissão personalizado", + "@setCustomPermissionLevel": {}, + "addChatDescription": "Adicionar uma descrição de chat...", + "@addChatDescription": {}, "@showPassword": { "type": "String", "placeholders": {} @@ -1128,14 +1140,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@username": { "type": "String", "placeholders": {} @@ -1187,10 +1191,6 @@ "type": "String", "placeholders": {} }, - "@repeatPassword": { - "type": "String", - "placeholders": {} - }, "@setStatus": { "type": "String", "placeholders": {} @@ -1801,10 +1801,6 @@ "type": "String", "placeholders": {} }, - "@addChatDescription": { - "type": "String", - "placeholders": {} - }, "@sentAnAudio": { "type": "String", "placeholders": { @@ -2013,10 +2009,6 @@ "type": "String", "placeholders": {} }, - "@notAnImage": { - "type": "String", - "placeholders": {} - }, "@openGallery": { "type": "String", "placeholders": {} @@ -2609,10 +2601,6 @@ "type": "String", "placeholders": {} }, - "alwaysUse24HourFormat": "falso", - "repeatPassword": "Repetir senha", - "notAnImage": "Não é um arquivo de imagem.", - "setCustomPermissionLevel": "Definir nível de permissão personalizado", "setPermissionsLevelDescription": "Por favor, escolha um papel predefinido abaixo ou insira um nível de permissão personalizado entre 0 e 100.", "ignoreUser": "Ignorar usuário", "normalUser": "Usuário normal", @@ -2629,7 +2617,6 @@ "addEmail": "Adicionar email", "confirmMatrixId": "Por favor, confirme seu ID Matrix para excluir sua conta.", "supposedMxid": "Isto deve ser {mxid}", - "addChatDescription": "Adicionar uma descrição ao chat...", "addToSpace": "Adicionar ao espaço", "alias": "apelido", "all": "Todos", @@ -3152,7 +3139,6 @@ "report": "denunciar", "signInWithPassword": "Entrar com senha", "pleaseTryAgainLaterOrChooseDifferentServer": "Por favor, tente novamente mais tarde ou escolha um servidor diferente.", - "signInWith": "Entrar com {provider}", "profileNotFound": "O usuário não pôde ser encontrado no servidor. Talvez haja um problema de conexão ou o usuário não exista.", "setTheme": "Definir tema:", "setColorTheme": "Definir tema de cores:", @@ -4496,14 +4482,6 @@ "playWithAI": "Brinque com IA por enquanto", "courseStartDesc": "Pangea Bot está pronto para começar a qualquer momento!\n\n...mas aprender é melhor com amigos!", "@@locale": "pt", - "@alwaysUse24HourFormat": { - "type": "String", - "placeholders": {} - }, - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, "@setPermissionsLevelDescription": { "type": "String", "placeholders": {} @@ -5057,9 +5035,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12165,4 +12141,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index ca5814119..b64fd28e9 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -24,7 +24,7 @@ "type": "String", "placeholders": {} }, - "activatedEndToEndEncryption": "🔐 {username} ativou a criptografia ponta-a-ponta", + "activatedEndToEndEncryption": "🔐 {username} ativou a criptografia de ponta-a-ponta", "@activatedEndToEndEncryption": { "type": "String", "placeholders": { @@ -33,7 +33,7 @@ } } }, - "addEmail": "Adicionar email", + "addEmail": "Adicionar e-mail", "@addEmail": { "type": "String", "placeholders": {} @@ -48,7 +48,7 @@ "type": "String", "placeholders": {} }, - "all": "Todas", + "all": "Tudo", "@all": { "type": "String", "placeholders": {} @@ -67,12 +67,12 @@ } } }, - "anyoneCanJoin": "Qualquer pessoa pode participar", + "anyoneCanJoin": "Qualquer pessoa pode entrar", "@anyoneCanJoin": { "type": "String", "placeholders": {} }, - "appLock": "Trava do aplicativo", + "appLock": "Bloqueio do app", "@appLock": { "type": "String", "placeholders": {} @@ -82,7 +82,7 @@ "type": "String", "placeholders": {} }, - "areGuestsAllowedToJoin": "Usuários convidados podem participar", + "areGuestsAllowedToJoin": "Visitantes podem entrar", "@areGuestsAllowedToJoin": { "type": "String", "placeholders": {} @@ -92,12 +92,12 @@ "type": "String", "placeholders": {} }, - "areYouSureYouWantToLogout": "Tem certeza que deseja encerrar a sessão?", + "areYouSureYouWantToLogout": "Tem certeza que deseja se desconectar?", "@areYouSureYouWantToLogout": { "type": "String", "placeholders": {} }, - "askSSSSSign": "Para poder validar a outra pessoa, por favor, insira sua frase secreta ou chave de recuperação.", + "askSSSSSign": "Para poder validar a outra pessoa, digite sua frase secreta ou chave de recuperação.", "@askSSSSSign": { "type": "String", "placeholders": {} @@ -116,7 +116,7 @@ "type": "String", "placeholder": {} }, - "badServerLoginTypesException": "O servidor matriz suporta os tipos de login:\n{serverVersions}\nMas este app suporta apenas:\n{supportedVersions}", + "badServerLoginTypesException": "O servidor suporta os tipos de entrada/login:\n{serverVersions}\nMas este app suporta apenas:\n{supportedVersions}", "@badServerLoginTypesException": { "type": "String", "placeholders": { @@ -128,7 +128,7 @@ } } }, - "badServerVersionsException": "O servidor matriz suporta as versões Spec:\n{serverVersions}\nMas este app suporta apenas {supportedVersions}", + "badServerVersionsException": "O servidor suporta as versões de especificação:\n{serverVersions}\nMas este app suporta apenas {supportedVersions}", "@badServerVersionsException": { "type": "String", "placeholders": { @@ -145,7 +145,7 @@ "type": "String", "placeholders": {} }, - "banned": "Banido", + "banned": "Banidos", "@banned": { "type": "String", "placeholders": {} @@ -172,7 +172,7 @@ "type": "String", "placeholders": {} }, - "botMessages": "Mensagens de robôs", + "botMessages": "Mensagens de bots", "@botMessages": { "type": "String", "placeholders": {} @@ -250,7 +250,7 @@ } } }, - "changedTheGuestAccessRules": "{username} alterou as regras de acesso dos convidados", + "changedTheGuestAccessRules": "{username} alterou as regras de acesso dos visitantes", "@changedTheGuestAccessRules": { "type": "String", "placeholders": { @@ -259,7 +259,7 @@ } } }, - "changedTheGuestAccessRulesTo": "{username} alterou as regras de acesso dos convidados para: {rules}", + "changedTheGuestAccessRulesTo": "{username} alterou as regras de acesso dos visitantes para: {rules}", "@changedTheGuestAccessRulesTo": { "type": "String", "placeholders": { @@ -292,7 +292,7 @@ } } }, - "changedTheJoinRules": "{username} alterou as regras para participação", + "changedTheJoinRules": "{username} alterou as regras de entrada", "@changedTheJoinRules": { "type": "String", "placeholders": { @@ -301,7 +301,7 @@ } } }, - "changedTheJoinRulesTo": "{username} alterou as regras para participação para: {joinRules}", + "changedTheJoinRulesTo": "{username} alterou as regras de entrada para: {joinRules}", "@changedTheJoinRulesTo": { "type": "String", "placeholders": { @@ -322,7 +322,7 @@ } } }, - "changedTheRoomAliases": "{username} alterou os cognomes da sala", + "changedTheRoomAliases": "{username} alterou os apelidos da sala", "@changedTheRoomAliases": { "type": "String", "placeholders": { @@ -345,7 +345,7 @@ "type": "String", "placeholders": {} }, - "changeTheHomeserver": "Alterar o servidor matriz", + "changeTheHomeserver": "Alterar o servidor", "@changeTheHomeserver": { "type": "String", "placeholders": {} @@ -370,17 +370,17 @@ "type": "String", "placeholders": {} }, - "chat": "Conversas", + "chat": "Conversar", "@chat": { "type": "String", "placeholders": {} }, - "chatBackup": "Backup da conversa", + "chatBackup": "Backup de conversas", "@chatBackup": { "type": "String", "placeholders": {} }, - "chatBackupDescription": "Suas mensagens antigas são protegidas com sua chave de recuperação. Por favor, evite perdê-la.", + "chatBackupDescription": "Suas mensagens são protegidas com sua chave de recuperação. Evite perdê-la.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -407,7 +407,7 @@ "type": "String", "placeholders": {} }, - "commandHint_ban": "Banir um(a) usuário(a) desta sala", + "commandHint_ban": "Banir o usuário especificado desta sala", "@commandHint_ban": { "type": "String", "description": "Usage hint for the command /ban" @@ -417,17 +417,17 @@ "type": "String", "description": "Usage hint for the command /html" }, - "commandHint_invite": "Convidar um(a) usuário(a) para esta sala", + "commandHint_invite": "Convidar o usuário especificado para esta sala", "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" }, - "commandHint_join": "Entrar numa sala", + "commandHint_join": "Entrar na sala especificada", "@commandHint_join": { "type": "String", "description": "Usage hint for the command /join" }, - "commandHint_kick": "Remover um(a) usuário(a) desta sala", + "commandHint_kick": "Remover o usuário especificado da sala", "@commandHint_kick": { "type": "String", "description": "Usage hint for the command /kick" @@ -442,17 +442,17 @@ "type": "String", "description": "Usage hint for the command /me" }, - "commandHint_myroomavatar": "Determinar sua imagem para esta sala (via mxc-uri)", + "commandHint_myroomavatar": "Configurar sua imagem para esta sala (via mxc-uri)", "@commandHint_myroomavatar": { "type": "String", "description": "Usage hint for the command /myroomavatar" }, - "commandHint_myroomnick": "Determinar seu nome de exibição para esta sala", + "commandHint_myroomnick": "Configurar seu nome de exibição para esta sala", "@commandHint_myroomnick": { "type": "String", "description": "Usage hint for the command /myroomnick" }, - "commandHint_op": "Determinar o grau de poderes de um(a) usuário(a) (padrão: 50)", + "commandHint_op": "Determinar o nível de poderes do usuário especificado (padrão: 50)", "@commandHint_op": { "type": "String", "description": "Usage hint for the command /op" @@ -472,7 +472,7 @@ "type": "String", "description": "Usage hint for the command /send" }, - "commandHint_unban": "Revogar o banimento de um(a) usuário(a) desta sala", + "commandHint_unban": "Desbanir o usuário especificado desta sala", "@commandHint_unban": { "type": "String", "description": "Usage hint for the command /unban" @@ -491,12 +491,12 @@ }, "description": "State that {command} is not a valid /command." }, - "compareEmojiMatch": "Por favor compare os emojis", + "compareEmojiMatch": "Compare os emojis", "@compareEmojiMatch": { "type": "String", "placeholders": {} }, - "compareNumbersMatch": "Por favor compare os números", + "compareNumbersMatch": "Compare os números", "@compareNumbersMatch": { "type": "String", "placeholders": {} @@ -506,7 +506,7 @@ "type": "String", "placeholders": {} }, - "confirm": "Confirma", + "confirm": "Confirmar", "@confirm": { "type": "String", "placeholders": {} @@ -531,12 +531,12 @@ "type": "String", "placeholders": {} }, - "contentHasBeenReported": "O conteúdo foi denunciado para quem administra o servidor", + "contentHasBeenReported": "O conteúdo foi denunciado para os administradores do servidor", "@contentHasBeenReported": { "type": "String", "placeholders": {} }, - "copiedToClipboard": "Copiado para área de transferência", + "copiedToClipboard": "Copiado para a área de transferência", "@copiedToClipboard": { "type": "String", "placeholders": {} @@ -551,7 +551,7 @@ "type": "String", "placeholders": {} }, - "couldNotDecryptMessage": "Não foi possível decriptar a mensagem: {error}", + "couldNotDecryptMessage": "Não foi possível descriptografar a mensagem: {error}", "@couldNotDecryptMessage": { "type": "String", "placeholders": { @@ -598,7 +598,7 @@ "type": "String", "placeholders": {} }, - "dateAndTimeOfDay": "{date}, {timeOfDay}", + "dateAndTimeOfDay": "{date} às {timeOfDay}", "@dateAndTimeOfDay": { "type": "String", "placeholders": { @@ -637,12 +637,12 @@ } } }, - "deactivateAccountWarning": "Isto desativará a conta do usuário. É irreversível! Tem certeza?", + "deactivateAccountWarning": "Isto desativará a sua conta. Isto é irreversível! Tem certeza?", "@deactivateAccountWarning": { "type": "String", "placeholders": {} }, - "defaultPermissionLevel": "Nível de permissão padrão", + "defaultPermissionLevel": "Nível de permissão padrão para usuários novos", "@defaultPermissionLevel": { "type": "String", "placeholders": {} @@ -707,7 +707,7 @@ "type": "String", "placeholders": {} }, - "editRoomAliases": "Editar cognome da sala", + "editRoomAliases": "Editar apelidos da sala", "@editRoomAliases": { "type": "String", "placeholders": {} @@ -757,12 +757,12 @@ "type": "String", "placeholders": {} }, - "enableEncryption": "Habilitar criptografia", + "enableEncryption": "Ativar criptografia", "@enableEncryption": { "type": "String", "placeholders": {} }, - "enableEncryptionWarning": "Você não poderá desabilitar a criptografia posteriormente. Tem certeza?", + "enableEncryptionWarning": "Você não poderá desativar a criptografia posteriormente. Tem certeza?", "@enableEncryptionWarning": { "type": "String", "placeholders": {} @@ -777,7 +777,7 @@ "type": "String", "placeholders": {} }, - "encryptionNotEnabled": "A criptografia não está habilitada", + "encryptionNotEnabled": "A criptografia não está ativada", "@encryptionNotEnabled": { "type": "String", "placeholders": {} @@ -791,17 +791,17 @@ } } }, - "enterAnEmailAddress": "Inserir endereço de e-mail", + "enterAnEmailAddress": "Digite um endereço de e-mail", "@enterAnEmailAddress": { "type": "String", "placeholders": {} }, - "enterYourHomeserver": "Insira um servidor matriz", + "enterYourHomeserver": "Digite o seu servidor", "@enterYourHomeserver": { "type": "String", "placeholders": {} }, - "errorObtainingLocation": "Erro ao obter local: {error}", + "errorObtainingLocation": "Erro ao obter localização: {error}", "@errorObtainingLocation": { "type": "String", "placeholders": { @@ -860,7 +860,7 @@ "type": "String", "placeholders": {} }, - "groupIsPublic": "Grupo público", + "groupIsPublic": "O grupo é público", "@groupIsPublic": { "type": "String", "placeholders": {} @@ -879,12 +879,12 @@ } } }, - "guestsAreForbidden": "Convidados estão proibidos", + "guestsAreForbidden": "Visitantes são proibidos", "@guestsAreForbidden": { "type": "String", "placeholders": {} }, - "guestsCanJoin": "Convidados podem participar", + "guestsCanJoin": "Visitantes podem entrar", "@guestsCanJoin": { "type": "String", "placeholders": {} @@ -970,7 +970,7 @@ } } }, - "invited": "Foi convidado", + "invited": "Convidado", "@invited": { "type": "String", "placeholders": {} @@ -997,7 +997,7 @@ "type": "String", "placeholders": {} }, - "inviteText": "{username} convidou você para o FluffyChat. \n1. Visite fluffychat.im e instale o aplicativo\n2. Entre ou crie uma conta \n3. Abra o link do convite:\n {link}", + "inviteText": "{username} convidou você para o FluffyChat.\n1. Visite fluffychat.im e instale o aplicativo\n2. Entre ou crie uma conta\n3. Abra o link do convite:\n{link}", "@inviteText": { "type": "String", "placeholders": { @@ -1009,7 +1009,7 @@ } } }, - "isTyping": "está escrevendo…", + "isTyping": "está digitando…", "@isTyping": { "type": "String", "placeholders": {} @@ -1028,7 +1028,7 @@ "type": "String", "placeholders": {} }, - "kicked": "👞 {username} enxotou {targetName}", + "kicked": "👞 {username} expulsou {targetName}", "@kicked": { "type": "String", "placeholders": { @@ -1052,7 +1052,7 @@ } } }, - "kickFromChat": "Expulso da conversa", + "kickFromChat": "Expulsar da conversa", "@kickFromChat": { "type": "String", "placeholders": {} @@ -1071,7 +1071,7 @@ "type": "String", "placeholders": {} }, - "leftTheChat": "Sair da conversa", + "leftTheChat": "Saiu da conversa", "@leftTheChat": { "type": "String", "placeholders": {} @@ -1086,7 +1086,7 @@ "type": "String", "placeholders": {} }, - "loadCountMoreParticipants": "Carregue {count} mais participantes", + "loadCountMoreParticipants": "Carregar mais {count} participantes", "@loadCountMoreParticipants": { "type": "String", "placeholders": { @@ -1100,27 +1100,27 @@ "type": "String", "placeholders": {} }, - "loadMore": "Carregando mais…", + "loadMore": "Carregar mais…", "@loadMore": { "type": "String", "placeholders": {} }, - "locationDisabledNotice": "O serviço de localização está desabilitado. Por favor, habilite-o para compartilhar sua localização.", + "locationDisabledNotice": "Os serviços de localização estão desativados. Ative-os para compartilhar sua localização.", "@locationDisabledNotice": { "type": "String", "placeholders": {} }, - "locationPermissionDeniedNotice": "Permissão de localização negada. Conceda as permissões para habilitar o compartilhamento de localização.", + "locationPermissionDeniedNotice": "Permissão de localização bloqueada. Conceda as permissões para poder compartilhar sua localização.", "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} }, - "login": "Iniciar sessão", + "login": "Conectar-se", "@login": { "type": "String", "placeholders": {} }, - "logInTo": "Conectar a {homeserver}", + "logInTo": "Conectar com {homeserver}", "@logInTo": { "type": "String", "placeholders": { @@ -1129,7 +1129,7 @@ } } }, - "logout": "Encerrar sessão", + "logout": "Desconectar-se", "@logout": { "type": "String", "placeholders": {} @@ -1154,12 +1154,12 @@ "type": "String", "placeholders": {} }, - "muteChat": "Silenciar", + "muteChat": "Silenciar conversa", "@muteChat": { "type": "String", "placeholders": {} }, - "needPantalaimonWarning": "Por favor, observe que, por enquanto, você precisa do Pantalaimon para usar criptografia ponta-a-ponta.", + "needPantalaimonWarning": "Esteja ciente que você precisa do Pantalaimon para usar a criptografia de ponta a ponta.", "@needPantalaimonWarning": { "type": "String", "placeholders": {} @@ -1179,7 +1179,7 @@ "type": "String", "placeholders": {} }, - "next": "Próximo", + "next": "Avançar", "@next": { "type": "String", "placeholders": {} @@ -1199,17 +1199,17 @@ "type": "String", "placeholders": {} }, - "noEncryptionForPublicRooms": "Você só pode ativar criptografia quando a sala não for mais publicamente acessível.", + "noEncryptionForPublicRooms": "Você só poderá ativar a criptografia quando a sala não for mais publicamente acessível.", "@noEncryptionForPublicRooms": { "type": "String", "placeholders": {} }, - "noGoogleServicesWarning": "Aparentemente você não tem serviços Google no seu celular. Para receber notificações no FluffyChat, recomendamos instalar ntfy.", + "noGoogleServicesWarning": "O Firebase Cloud Messaging não parece estar disponível no seu dispositivo. Para receber notificações push, recomendados que você instwl3 o ntfy. Com o ntfy outro provedor do UnifiedPush, você pode receber notificações push de um modo seguro em relação aos dados. Você pode baixar o ntfy da Play Store ou do F-Droid.", "@noGoogleServicesWarning": { "type": "String", "placeholders": {} }, - "noMatrixServer": "{server1} não é um servidor matrix, usar {server2} talvez?", + "noMatrixServer": "{server1} não é um servidor matrix, usar {server2}?", "@noMatrixServer": { "type": "String", "placeholders": { @@ -1226,7 +1226,7 @@ "type": "String", "placeholders": {} }, - "noPasswordRecoveryDescription": "Você ainda não adicionou uma forma de recuparar sua senha.", + "noPasswordRecoveryDescription": "Você ainda não adicionou uma forma de recuperar sua senha.", "@noPasswordRecoveryDescription": { "type": "String", "placeholders": {} @@ -1246,7 +1246,7 @@ "type": "String", "placeholders": {} }, - "notificationsEnabledForThisAccount": "Notificações habilitadas para esta conta", + "notificationsEnabledForThisAccount": "Notificações ativadas para esta conta", "@notificationsEnabledForThisAccount": { "type": "String", "placeholders": {} @@ -1285,12 +1285,12 @@ "type": "String", "placeholders": {} }, - "onlineKeyBackupEnabled": "Backup de chaves está ativado", + "onlineKeyBackupEnabled": "O backup de chaves on-line está ativado", "@onlineKeyBackupEnabled": { "type": "String", "placeholders": {} }, - "oopsPushError": "Opa! Infelizmente, um erro ocorreu ao configurar as notificações.", + "oopsPushError": "Opa! Infelizmente, um erro ocorreu ao configurar as notificações push.", "@oopsPushError": { "type": "String", "placeholders": {} @@ -1305,12 +1305,12 @@ "type": "String", "placeholders": {} }, - "openCamera": "Abra a câmera", + "openCamera": "Abrir câmera", "@openCamera": { "type": "String", "placeholders": {} }, - "openInMaps": "Abrir no mapas", + "openInMaps": "Abrir no mapa", "@openInMaps": { "type": "String", "placeholders": {} @@ -1355,12 +1355,12 @@ "type": "String", "placeholders": {} }, - "pickImage": "Escolha uma imagem", + "pickImage": "Selecione uma imagem", "@pickImage": { "type": "String", "placeholders": {} }, - "pin": "Alfinetar", + "pin": "Fixar", "@pin": { "type": "String", "placeholders": {} @@ -1374,42 +1374,42 @@ } } }, - "pleaseChoose": "Por favor, selecione", + "pleaseChoose": "Selecione", "@pleaseChoose": { "type": "String", "placeholders": {} }, - "pleaseChooseAPasscode": "Por favor, escolha um código", + "pleaseChooseAPasscode": "Escolha um código", "@pleaseChooseAPasscode": { "type": "String", "placeholders": {} }, - "pleaseClickOnLink": "Por favor, clique a ligação no e-mail para prosseguir.", + "pleaseClickOnLink": "Clique no link do e-mail para prosseguir.", "@pleaseClickOnLink": { "type": "String", "placeholders": {} }, - "pleaseEnter4Digits": "Por favor, insira 4 dígitos ou deixe em branco para desativar a trava do aplicativo.", + "pleaseEnter4Digits": "Digite 4 dígitos ou deixe em branco para desativar o bloqueio do app.", "@pleaseEnter4Digits": { "type": "String", "placeholders": {} }, - "pleaseEnterYourPassword": "Por favor, insira sua senha", + "pleaseEnterYourPassword": "Digite sua senha", "@pleaseEnterYourPassword": { "type": "String", "placeholders": {} }, - "pleaseEnterYourPin": "Por favor, insira seu PIN", + "pleaseEnterYourPin": "Digite seu PIN", "@pleaseEnterYourPin": { "type": "String", "placeholders": {} }, - "pleaseEnterYourUsername": "Por favor, insira seu nome de usuário", + "pleaseEnterYourUsername": "Digite seu nome de usuário", "@pleaseEnterYourUsername": { "type": "String", "placeholders": {} }, - "pleaseFollowInstructionsOnWeb": "Por favor, siga as instruções no site e toque em próximo.", + "pleaseFollowInstructionsOnWeb": "Siga as instruções no site e toque em avançar.", "@pleaseFollowInstructionsOnWeb": { "type": "String", "placeholders": {} @@ -1424,7 +1424,7 @@ "type": "String", "placeholders": {} }, - "pushRules": "Regras de notificação", + "pushRules": "Regras de push", "@pushRules": { "type": "String", "placeholders": {} @@ -1434,12 +1434,12 @@ "type": "String", "placeholders": {} }, - "recording": "Gravando", + "recording": "Gravação", "@recording": { "type": "String", "placeholders": {} }, - "redactedAnEvent": "{username} removeu um evento", + "redactedAnEvent": "{username} apagou um evento", "@redactedAnEvent": { "type": "String", "placeholders": { @@ -1448,12 +1448,12 @@ } } }, - "redactMessage": "Retratar mensagem", + "redactMessage": "Apagar mensagem", "@redactMessage": { "type": "String", "placeholders": {} }, - "register": "Registrar", + "register": "Cadastrar-se", "@register": { "type": "String", "placeholders": {} @@ -1472,7 +1472,7 @@ } } }, - "rejoin": "Retornar", + "rejoin": "Entrar novamente", "@rejoin": { "type": "String", "placeholders": {} @@ -1501,7 +1501,7 @@ "type": "String", "placeholders": {} }, - "unbanFromChat": "Revogar banimento", + "unbanFromChat": "Desbanir da conversa", "@unbanFromChat": { "type": "String", "placeholders": {} @@ -1531,7 +1531,7 @@ "type": "String", "placeholders": {} }, - "roomHasBeenUpgraded": "Sala foi atualizada", + "roomHasBeenUpgraded": "A sala foi atualizada", "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} @@ -1546,7 +1546,7 @@ "type": "String", "placeholders": {} }, - "search": "Buscar", + "search": "Pesquisar", "@search": { "type": "String", "placeholders": {} @@ -1570,7 +1570,7 @@ "type": "String", "placeholders": {} }, - "sendAMessage": "Enviar mensagem", + "sendAMessage": "Enviar uma mensagem", "@sendAMessage": { "type": "String", "placeholders": {} @@ -1579,7 +1579,7 @@ "@sendAsText": { "type": "String" }, - "sendAudio": "Enviar audio", + "sendAudio": "Enviar áudio", "@sendAudio": { "type": "String", "placeholders": {} @@ -1623,7 +1623,7 @@ } } }, - "sentAnAudio": "🎤 {username} enviou um audio", + "sentAnAudio": "🎤 {username} enviou um áudio", "@sentAnAudio": { "type": "String", "placeholders": { @@ -1668,7 +1668,7 @@ } } }, - "setAsCanonicalAlias": "Fixar como cognome principal", + "setAsCanonicalAlias": "Configurar como apelido principal", "@setAsCanonicalAlias": { "type": "String", "placeholders": {} @@ -1683,12 +1683,12 @@ "type": "String", "placeholders": {} }, - "setPermissionsLevel": "Determinar níveis de permissão", + "setPermissionsLevel": "Configurar níveis de permissão", "@setPermissionsLevel": { "type": "String", "placeholders": {} }, - "setStatus": "Alterar o status", + "setStatus": "Alterar estado", "@setStatus": { "type": "String", "placeholders": {} @@ -1722,7 +1722,7 @@ "type": "String", "placeholders": {} }, - "singlesignon": "Identidade Única", + "singlesignon": "Entrada única", "@singlesignon": { "type": "String", "placeholders": {} @@ -1732,12 +1732,12 @@ "type": "String", "placeholders": {} }, - "sourceCode": "Código fonte", + "sourceCode": "Código-fonte", "@sourceCode": { "type": "String", "placeholders": {} }, - "spaceIsPublic": "Espaço é público", + "spaceIsPublic": "O espaço é público", "@spaceIsPublic": { "type": "String", "placeholders": {} @@ -1756,7 +1756,7 @@ } } }, - "status": "Status", + "status": "Estado", "@status": { "type": "String", "placeholders": {} @@ -1766,12 +1766,12 @@ "type": "String", "placeholders": {} }, - "submit": "Submeter", + "submit": "Enviar", "@submit": { "type": "String", "placeholders": {} }, - "synchronizingPleaseWait": "Sincronizando… Por favor, aguarde.", + "synchronizingPleaseWait": "Sincronizando… Aguarde.", "@synchronizingPleaseWait": { "type": "String", "placeholders": {} @@ -1822,7 +1822,7 @@ "type": "String", "placeholders": {} }, - "tryToSendAgain": "Tente enviar novamente", + "tryToSendAgain": "Tentar enviar novamente", "@tryToSendAgain": { "type": "String", "placeholders": {} @@ -1832,7 +1832,7 @@ "type": "String", "placeholders": {} }, - "unbannedUser": "{username} revogou o banimento de {targetName}", + "unbannedUser": "{username} desbaniu {targetName}", "@unbannedUser": { "type": "String", "placeholders": { @@ -1868,12 +1868,12 @@ } } }, - "unmuteChat": "Cancelar silenciamento", + "unmuteChat": "Dessilenciar", "@unmuteChat": { "type": "String", "placeholders": {} }, - "unpin": "Desalfinetar", + "unpin": "Desafixar", "@unpin": { "type": "String", "placeholders": {} @@ -1934,7 +1934,7 @@ "type": "String", "placeholders": {} }, - "userSentUnknownEvent": "{username} enviou um evento {type}", + "userSentUnknownEvent": "{username} enviou um evento de {type}", "@userSentUnknownEvent": { "type": "String", "placeholders": { @@ -1971,7 +1971,7 @@ "type": "String", "placeholders": {} }, - "videoCall": "Vídeochamada", + "videoCall": "Chamada de vídeo", "@videoCall": { "type": "String", "placeholders": {} @@ -1981,12 +1981,12 @@ "type": "String", "placeholders": {} }, - "visibleForAllParticipants": "Visível aos participantes", + "visibleForAllParticipants": "Visível para todos os participantes", "@visibleForAllParticipants": { "type": "String", "placeholders": {} }, - "visibleForEveryone": "Visível a qualquer pessoa", + "visibleForEveryone": "Visível para qualquer pessoa", "@visibleForEveryone": { "type": "String", "placeholders": {} @@ -2001,7 +2001,7 @@ "type": "String", "placeholders": {} }, - "waitingPartnerEmoji": "Esperando que a outra pessoa aceite os emoji…", + "waitingPartnerEmoji": "Esperando que a outra pessoa aceite os emojis…", "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -2011,7 +2011,7 @@ "type": "String", "placeholders": {} }, - "wallpaper": "Pano de fundo:", + "wallpaper": "Plano de fundo:", "@wallpaper": { "type": "String", "placeholders": {} @@ -2031,17 +2031,17 @@ "type": "String", "placeholders": {} }, - "whoIsAllowedToJoinThisGroup": "Quais pessoas são permitidas participar deste grupo", + "whoIsAllowedToJoinThisGroup": "Quem pode entrar no grupo", "@whoIsAllowedToJoinThisGroup": { "type": "String", "placeholders": {} }, - "whyDoYouWantToReportThis": "Por que você quer denunciar isto?", + "whyDoYouWantToReportThis": "Por que quer denunciar isto?", "@whyDoYouWantToReportThis": { "type": "String", "placeholders": {} }, - "wipeChatBackup": "Limpar o backup da conversa para criar uma nova chave de recuperação?", + "wipeChatBackup": "Apagar o backup de conversas para criar uma nova chave de recuperação?", "@wipeChatBackup": { "type": "String", "placeholders": {} @@ -2051,7 +2051,7 @@ "type": "String", "placeholders": {} }, - "writeAMessage": "Escreva uma mensagem…", + "writeAMessage": "Digite sua mensagem…", "@writeAMessage": { "type": "String", "placeholders": {} @@ -2081,7 +2081,7 @@ "type": "String", "placeholders": {} }, - "oneClientLoggedOut": "Um dos seus clientes foi desvinculado", + "oneClientLoggedOut": "Um dos seus clientes foi desconectado", "@oneClientLoggedOut": {}, "addAccount": "Adicionar conta", "@addAccount": {}, @@ -2091,7 +2091,7 @@ "@yourChatBackupHasBeenSetUp": {}, "editBundlesForAccount": "Editar coleções para esta conta", "@editBundlesForAccount": {}, - "serverRequiresEmail": "Este servidor precisa validar seu email para efetuar o registro.", + "serverRequiresEmail": "Este servidor precisa validar seu e-mail para efetuar o cadastro.", "@serverRequiresEmail": {}, "messageInfo": "Informações da mensagem", "@messageInfo": {}, @@ -2103,7 +2103,7 @@ "@removeFromSpace": {}, "link": "Link", "@link": {}, - "start": "Começar", + "start": "Iniciar", "@start": {}, "repeatPassword": "Repita a senha", "@repeatPassword": {}, @@ -2111,7 +2111,7 @@ "@addToSpace": {}, "sendOnEnter": "Enviar ao pressionar enter", "@sendOnEnter": {}, - "homeserver": "Servidor matriz", + "homeserver": "Servidor", "@homeserver": {}, "chatHasBeenAddedToThisSpace": "A conversa foi adicionada a este espaço", "@chatHasBeenAddedToThisSpace": {}, @@ -2120,7 +2120,7 @@ "type": "String", "description": "Usage hint for the command /clearcache" }, - "commandHint_create": "Criar uma sala vazia.\nUse --no-encryption para desabilitar a criptografia", + "commandHint_create": "Criar uma conversa em grupo vazia.\nUse --no-encryption para desativar a criptografia", "@commandHint_create": { "type": "String", "description": "Usage hint for the command /create" @@ -2130,14 +2130,14 @@ "type": "String", "description": "Usage hint for the command /discardsession" }, - "commandHint_dm": "Iniciar uma conversa direta\nUse --no-encryption para desabilitar a criptografia", + "commandHint_dm": "Iniciar uma conversa direta\nUse --no-encryption para desativar a criptografia", "@commandHint_dm": { "type": "String", "description": "Usage hint for the command /dm" }, - "scanQrCode": "Escanear o código QR", + "scanQrCode": "Ler código QR", "@scanQrCode": {}, - "openVideoCamera": "Abra a câmera para um vídeo", + "openVideoCamera": "Abrir a câmera para um vídeo", "@openVideoCamera": { "type": "String", "placeholders": {} @@ -2148,9 +2148,9 @@ "@removeFromBundle": {}, "bundleName": "Nome da coleção", "@bundleName": {}, - "enableMultiAccounts": "(BETA) Habilitar múltiplas contas neste dispositivo", + "enableMultiAccounts": "(BETA) Ativar múltiplas contas neste dispositivo", "@enableMultiAccounts": {}, - "time": "Hora", + "time": "Horário", "@time": {}, "messageType": "Tipo da mensagem", "@messageType": {}, @@ -2171,14 +2171,14 @@ "@markAsRead": {}, "dismiss": "Descartar", "@dismiss": {}, - "separateChatTypes": "Separar Conversas Diretas e Grupos", + "separateChatTypes": "Separar conversas diretas e grupos", "@separateChatTypes": { "type": "String", "placeholders": {} }, "openChat": "Abrir conversa", "@openChat": {}, - "reportUser": "Delatar usuário", + "reportUser": "Denunciar usuário", "@reportUser": {}, "emojis": "Emojis", "@emojis": {}, @@ -2198,29 +2198,29 @@ }, "confirmEventUnpin": "Tem certeza que quer desafixar o evento permanentemente?", "@confirmEventUnpin": {}, - "pinMessage": "Afixar à sala", + "pinMessage": "Fixar na sala", "@pinMessage": {}, "voiceCall": "Chamada de voz", "@voiceCall": {}, - "unsupportedAndroidVersion": "Versão Android não suportada", + "unsupportedAndroidVersion": "Versão Android sem suporte", "@unsupportedAndroidVersion": {}, - "widgetNameError": "Por favor, forneça um nome de exibição.", + "widgetNameError": "Forneça um nome de exibição.", "@widgetNameError": {}, - "unsupportedAndroidVersionLong": "Esta funcionalidade requer uma versão mais nova do Android. Por favor, busque atualizações ou suporte ao Lineage OS.", + "unsupportedAndroidVersionLong": "Esta funcionalidade requer uma versão mais nova do Android. Verifique se há atualizações ou suporte ao LineageOS.", "@unsupportedAndroidVersionLong": {}, - "emailOrUsername": "Email ou nome de usuário", + "emailOrUsername": "E-mail ou nome de usuário", "@emailOrUsername": {}, - "videoCallsBetaWarning": "Por favor, note que chamadas de vídeo estão atualmente em teste. Podem não funcionar como esperado ou sequer funcionar em algumas plataformas.", + "videoCallsBetaWarning": "Observe que chamadas de vídeo estão atualmente em teste. Podem não funcionar como esperado ou sequer funcionar em algumas plataformas.", "@videoCallsBetaWarning": {}, "experimentalVideoCalls": "Vídeo chamadas experimentais", "@experimentalVideoCalls": {}, "widgetVideo": "Vídeo", "@widgetVideo": {}, - "errorAddingWidget": "Erro ao adicionar a ferramenta.", + "errorAddingWidget": "Erro ao adicionar o widget.", "@errorAddingWidget": {}, - "addWidget": "Adicionar ferramenta", + "addWidget": "Adicionar widget", "@addWidget": {}, - "widgetEtherpad": "Anotação", + "widgetEtherpad": "Anotação de texto", "@widgetEtherpad": {}, "widgetUrlError": "Isto não é uma URL válida.", "@widgetUrlError": {}, @@ -2297,7 +2297,7 @@ } } }, - "youUnbannedUser": "Você revogou o banimento de {user}", + "youUnbannedUser": "Você desbaniu {user}", "@youUnbannedUser": { "placeholders": { "user": { @@ -2314,13 +2314,13 @@ } } }, - "pleaseEnterRecoveryKeyDescription": "Para destrancar suas mensagens antigas, por favor, insira sua chave de recuperação gerada numa sessão prévia. Suas chave de recuperação NÃO é sua senha.", + "pleaseEnterRecoveryKeyDescription": "Para desbloquear as suas mensagens antigas, digite a sua chave de recuperação gerada numa sessão prévia. Sua chave de recuperação NÃO é sua senha.", "@pleaseEnterRecoveryKeyDescription": {}, - "indexedDbErrorLong": "Infelizmente, o armazenamento de mensagens não é habilitado por padrão no modo privado.\nPor favor, visite\n- about:config\n- atribua \"true\" a \"dom.indexedDB.privateBrowsing.enabled\"\nDe outro modo, não será possível executar o FluffyChat.", + "indexedDbErrorLong": "Infelizmente, o armazenamento de mensagens não é ativado por padrão no modo privado.\nVisite\n- about:config\n- atribua \"true\" a \"dom.indexedDB.privateBrowsing.enabled\"\nSe não fizer isto, não será possível executar o FluffyChat.", "@indexedDbErrorLong": {}, "users": "Usuários", "@users": {}, - "confirmMatrixId": "Por favor, confirme seu ID Matrix para apagar sua conta.", + "confirmMatrixId": "Confirme seu ID Matrix para apagar sua conta.", "@confirmMatrixId": {}, "supposedMxid": "Isto deveria ser {mxid}", "@supposedMxid": { @@ -2361,7 +2361,7 @@ } } }, - "commandHint_markasdm": "Marcar como conversa direta para o ID Matrix dado", + "commandHint_markasdm": "Marcar como sala de mensagens diretas para o ID Matrix fornecido", "@commandHint_markasdm": {}, "commandHint_markasgroup": "Marcar como grupo", "@commandHint_markasgroup": {}, @@ -2369,9 +2369,9 @@ "@hydrateTor": {}, "hydrateTorLong": "Você exportou sua última sessão no TOR? Importe ela rapidamente e continue conversando.", "@hydrateTorLong": {}, - "hydrate": "Restaurar a partir de arquivo backup", + "hydrate": "Restaurar a partir de um arquivo de backup", "@hydrate": {}, - "pleaseEnterRecoveryKey": "Por favor, insira sua chave de recuperação:", + "pleaseEnterRecoveryKey": "Digite sua chave de recuperação:", "@pleaseEnterRecoveryKey": {}, "indexedDbErrorTitle": "Problemas no modo privado", "@indexedDbErrorTitle": {}, @@ -2379,7 +2379,7 @@ "@storeInSecureStorageDescription": {}, "saveKeyManuallyDescription": "Salvar esta chave manualmente via compartilhamento do sistema ou área de transferência.", "@saveKeyManuallyDescription": {}, - "storeInAndroidKeystore": "Guardar no cofre do Android", + "storeInAndroidKeystore": "Guardar no cofre do Android (KeyStore)", "@storeInAndroidKeystore": {}, "storeInAppleKeyChain": "Guardar no chaveiro da Apple", "@storeInAppleKeyChain": {}, @@ -2389,7 +2389,7 @@ "@user": {}, "custom": "Personalizado", "@custom": {}, - "foregroundServiceRunning": "Esta notificação aparece quando um serviço está executando.", + "foregroundServiceRunning": "Esta notificação aparece quando o serviço de primeiro plano está sendo executado.", "@foregroundServiceRunning": {}, "callingPermissions": "Permissões de chamada", "@callingPermissions": {}, @@ -2399,7 +2399,7 @@ "@callingAccountDetails": {}, "appearOnTop": "Aparecer no topo", "@appearOnTop": {}, - "appearOnTopDetails": "Permitir que o app apareça no topo (desnecessário caso FluffyChat já esteja configurado como conta para chamadas)", + "appearOnTopDetails": "Permite que o app apareça no topo (desnecessário caso o FluffyChat já esteja configurado como conta para chamadas)", "@appearOnTopDetails": {}, "otherCallingPermissions": "Microfone, câmera e outras permissões do FluffyChat", "@otherCallingPermissions": {}, @@ -2407,13 +2407,13 @@ "@newGroup": {}, "newSpace": "Novo espaço", "@newSpace": {}, - "enterSpace": "Entrar no espaço", + "enterSpace": "Abrir espaço", "@enterSpace": {}, - "enterRoom": "Entrar na conversa", + "enterRoom": "Abrir sala", "@enterRoom": {}, - "allSpaces": "Todos espaços", + "allSpaces": "Todos os espaços", "@allSpaces": {}, - "hideUnimportantStateEvents": "Ocultar eventos desimportantes", + "hideUnimportantStateEvents": "Ocultar eventos de estado desimportantes", "@hideUnimportantStateEvents": {}, "countFiles": "{count} arquivos", "@countFiles": { @@ -2425,17 +2425,17 @@ }, "doNotShowAgain": "Não mostrar novamente", "@doNotShowAgain": {}, - "unlockOldMessages": "Destrancar mensagens antigas", + "unlockOldMessages": "Desbloquear mensagens antigas", "@unlockOldMessages": {}, - "dehydrate": "Exportar sessão e limpar dispositivo", + "dehydrate": "Exportar sessão e apagar dispositivo", "@dehydrate": {}, - "dehydrateWarning": "Esta ação não pode ser desfeita. Certifique-se de que o arquivo backup está guardado e seguro.", + "dehydrateWarning": "Esta ação não pode ser desfeita. Certifique-se de que o arquivo de backup está guardado e seguro.", "@dehydrateWarning": {}, "dehydrateTorLong": "Para usuários TOR, é recomendado exportar a sessão antes de fechar a janela.", "@dehydrateTorLong": {}, - "whyIsThisMessageEncrypted": "Por que esta mensagem está ilegível?", + "whyIsThisMessageEncrypted": "Por que não consigo ler esta mensagem?", "@whyIsThisMessageEncrypted": {}, - "screenSharingTitle": "Compartilhar tela", + "screenSharingTitle": "compartilhamento de tela", "@screenSharingTitle": {}, "screenSharingDetail": "Você está compartilhando sua tela no FluffyChat", "@screenSharingDetail": {}, @@ -2448,26 +2448,26 @@ } } }, - "noKeyForThisMessage": "Isto pode ocorrer caso a mensagem tenha sido enviada antes da entrada na sua conta com este dispositivo.\n\nTambém é possível que o remetente tenha bloqueado o seu dispositivo ou ocorreu algum problema com a conexão.\n\nVocê consegue ler as mensagens em outra sessão? Então, pode transferir as mensagens de lá! Vá em Configurações > Dispositivos e confira se os dispositivos verificaram um ao outro. Quando abrir a sala da próxima vez e ambas as sessões estiverem abertas, as chaves serão transmitidas automaticamente.\n\nNão gostaria de perder suas chaves quando sair ou trocar de dispositivos? Certifique-se que o backup de conversas esteja habilitado nas configurações.", + "noKeyForThisMessage": "Isto pode ocorrer caso a mensagem tenha sido enviada antes de você ter se conectado à sua conta com este dispositivo.\n\nTambém é possível que o remetente tenha bloqueado o seu dispositivo ou ocorreu algum problema com a conexão.\n\nVocê consegue ler as mensagens em outra sessão? Então, pode transferir as mensagens de lá! Vá em Configurações > Dispositivos e confira se os dispositivos verificaram um ao outro. Quando abrir a sala da próxima vez e ambas as sessões estiverem abertas, as chaves serão transmitidas automaticamente.\n\nNão gostaria de perder suas chaves ao desconectar ou trocar de dispositivos? Certifique-se que o backup de conversas esteja ativado nas configurações.", "@noKeyForThisMessage": {}, - "allRooms": "Todos os Chats em Grupo", + "allRooms": "Todas as conversas em grupo", "@allRooms": { "type": "String", "placeholders": {} }, - "notAnImage": "Não é uma imagem.", + "notAnImage": "Não é um arquivo de imagem.", "@notAnImage": {}, "importNow": "Importar agora", "@importNow": {}, - "importEmojis": "Importar Emojis", + "importEmojis": "Importar emojis", "@importEmojis": {}, "importFromZipFile": "Importar de arquivo .zip", "@importFromZipFile": {}, - "sendTypingNotifications": "Enviar notificações de digitação", + "sendTypingNotifications": "Enviar indicadores de digitação", "@sendTypingNotifications": {}, - "startFirstChat": "Comece seu primeiro chat", + "startFirstChat": "Inicie sua primeira conversa", "@startFirstChat": {}, - "exportEmotePack": "Exportar pacote de Emotes como .zip", + "exportEmotePack": "Exportar pacote de emojis como .zip", "@exportEmotePack": {}, "replace": "Substituir", "@replace": {}, @@ -2477,39 +2477,39 @@ "@reportErrorDescription": {}, "setColorTheme": "Aplicar paleta de cor:", "@setColorTheme": {}, - "banUserDescription": "O usuário será banido da conversa e não poderá participar novamente até que isto seja revogado.", + "banUserDescription": "O usuário será banido da conversa e não poderá participar novamente até que sejam desbanidos.", "@banUserDescription": {}, - "removeDevicesDescription": "Você encerrará a sessão neste dispositivo e não poderá mais receber mensagens.", + "removeDevicesDescription": "Você será desconectado deste dispositivo e não poderá mais receber mensagens.", "@removeDevicesDescription": {}, - "tryAgain": "Tente novamente", + "tryAgain": "Tentar novamente", "@tryAgain": {}, - "unbanUserDescription": "O usuário poderá ingressar novamente na conversa, caso tente.", + "unbanUserDescription": "O usuário poderá entrar novamente na conversa, caso tente.", "@unbanUserDescription": {}, "messagesStyle": "Mensagens:", "@messagesStyle": {}, - "newSpaceDescription": "Espaços permitem que você consolide suas conversas e construa comunidades públicas ou privadas.", + "newSpaceDescription": "Os espaços permitem que você consolide suas conversas e construa comunidades públicas ou privadas.", "@newSpaceDescription": {}, "chatDescription": "Descrição da conversa", "@chatDescription": {}, - "encryptThisChat": "Encriptar esta conversa", + "encryptThisChat": "Criptografar esta conversa", "@encryptThisChat": {}, "reopenChat": "Reabrir conversa", "@reopenChat": {}, - "pushNotificationsNotAvailable": "Notificações não estão disponíveis", + "pushNotificationsNotAvailable": "Notificações push não estão disponíveis", "@pushNotificationsNotAvailable": {}, - "invalidServerName": "Nome de usuário inválido", + "invalidServerName": "Nome do servidor inválido", "@invalidServerName": {}, "chatPermissions": "Permissões da conversa", "@chatPermissions": {}, - "signInWithPassword": "Autenticar com senha", + "signInWithPassword": "Conectar com senha", "@signInWithPassword": {}, - "makeAdminDescription": "Assim que promover este usuário a administrador, não poderá desfazê-lo e ele terá as mesmas permissões que você.", + "makeAdminDescription": "Assim que promover este usuário a administrador, não poderá desfazer isto e ele terá as mesmas permissões que você.", "@makeAdminDescription": {}, - "setChatDescription": "Inserir descrição da conversa", + "setChatDescription": "Configurar descrição da conversa", "@setChatDescription": {}, "noOtherDevicesFound": "Nenhum outro dispositivo encontrado", "@noOtherDevicesFound": {}, - "redactedBy": "Removido por {username}", + "redactedBy": "Apagado por {username}", "@redactedBy": { "type": "String", "placeholders": { @@ -2518,26 +2518,17 @@ } } }, - "signInWith": "Autenticar com {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, - "fileIsTooBigForServer": "O servidor avisa que o arquivo é grande demais para ser enviado.", + "fileIsTooBigForServer": "Não foi possível enviar! O servidor suporta anexos somente até {max}.", "@fileIsTooBigForServer": {}, - "readUpToHere": "Marcar como lido até aqui", + "readUpToHere": "Lido até aqui", "@readUpToHere": {}, - "optionalRedactReason": "(Opcional) Motivo para remover esta mensagem.", + "optionalRedactReason": "(Opcional) Motivo para apagar esta mensagem.", "@optionalRedactReason": {}, "archiveRoomDescription": "A conversa será movida para o arquivo. Outros usuários verão que você deixou a conversa.", "@archiveRoomDescription": {}, - "inviteContactToGroupQuestion": "Você quer convidar {contact} para a conversa \"{groupName}\"?", + "inviteContactToGroupQuestion": "Deseja convidar {contact} para a conversa \"{groupName}\"?", "@inviteContactToGroupQuestion": {}, - "redactedByBecause": "Removido por {username}, pois: \"{reason}\"", + "redactedByBecause": "Apagado por {username}, pois: \"{reason}\"", "@redactedByBecause": { "type": "String", "placeholders": { @@ -2558,13 +2549,13 @@ } } }, - "redactMessageDescription": "A mensagem será removida para todos participantes desta conversa. Isto não poderá ser desfeito.", + "redactMessageDescription": "A mensagem será apagada para todos os participantes desta conversa. Isto não poderá ser desfeito.", "@redactMessageDescription": {}, - "invalidInput": "Inserção inválida!", + "invalidInput": "Entrada inválida!", "@invalidInput": {}, - "report": "Relatar", + "report": "relatar", "@report": {}, - "addChatDescription": "Inserir descrição da conversa...", + "addChatDescription": "Adicionar uma descrição à conversa...", "@addChatDescription": {}, "hasKnocked": "🚪 {user} bateu na porta", "@hasKnocked": { @@ -2574,9 +2565,9 @@ } } }, - "openLinkInBrowser": "Abrir no navegador", + "openLinkInBrowser": "Abrir link no navegador", "@openLinkInBrowser": {}, - "disableEncryptionWarning": "Por razões de segurança, não possível desabilitar a encriptação uma vez habilitada.", + "disableEncryptionWarning": "Por razões de segurança, não é possível desativar a criptografada uma vez ativada.", "@disableEncryptionWarning": {}, "directChat": "Conversa direta", "@directChat": {}, @@ -2589,9 +2580,9 @@ } } }, - "inviteGroupChat": "📨 Convidar para o grupo", + "inviteGroupChat": "📨 Convite para conversa em grupo", "@inviteGroupChat": {}, - "invitePrivateChat": "📨 Convidar para uma conversa privada", + "invitePrivateChat": "📨 Convite para conversa privada", "@invitePrivateChat": {}, "wasDirectChatDisplayName": "Conversa vazia (era {oldDisplayName})", "@wasDirectChatDisplayName": { @@ -2602,7 +2593,7 @@ } } }, - "noChatDescriptionYet": "Nenhuma descrição da conversa disponível.", + "noChatDescriptionYet": "Nenhuma descrição para a conversa foi criada ainda.", "@noChatDescriptionYet": {}, "learnMore": "Saiba mais", "@learnMore": {}, @@ -2610,7 +2601,7 @@ "@chatDescriptionHasBeenChanged": {}, "roomUpgradeDescription": "A conversa será recriada com a nova versão de sala. Todos participantes será notificados e terão que migrar para a nova sala. Você pode encontrar mais informações sobre versões de sala em https://spec.matrix.org/latest/room/", "@roomUpgradeDescription": {}, - "pleaseEnterANumber": "Por favor, insira um número maior que 0", + "pleaseEnterANumber": "Digite um número maior que 0", "@pleaseEnterANumber": {}, "profileNotFound": "O usuário não foi encontrado neste servidor. Talvez um problema de conexão ou o usuário não existe.", "@profileNotFound": {}, @@ -2618,48 +2609,48 @@ "@jump": {}, "sorryThatsNotPossible": "Desculpe... isto não é possível", "@sorryThatsNotPossible": {}, - "shareInviteLink": "Compartilhar convite", + "shareInviteLink": "Compartilhar link de convite", "@shareInviteLink": {}, "deviceKeys": "Chaves de dispositivo:", "@deviceKeys": {}, - "emoteKeyboardNoRecents": "Emotes recentes aparecem aqui...", + "emoteKeyboardNoRecents": "Emojis recentes aparecem aqui...", "@emoteKeyboardNoRecents": { "type": "String", "placeholders": {} }, "setTheme": "Aplicar tema:", "@setTheme": {}, - "pleaseTryAgainLaterOrChooseDifferentServer": "Por favor, tente novamente mais tarde ou escolha um servidor diferente.", + "pleaseTryAgainLaterOrChooseDifferentServer": "Tente novamente mais tarde ou escolha um servidor diferente.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, "createGroup": "Criar grupo", "@createGroup": {}, - "noBackupWarning": "Atenção! Sem habilitar o backup de conversa, você perderá acesso a suas mensagens encriptadas. É altamente recomendável habilitar o backup antes de encerrar a sessão.", + "noBackupWarning": "Atenção! Se não ativar o backup de conversas, você perderá acesso a suas mensagens criptografadas. É altamente recomendável ativar o backup antes de sair.", "@noBackupWarning": {}, - "kickUserDescription": "O usuário foi enxotado da conversa, mas não banido. Em conversas públicas, o usuário pode reingressar a qualquer momento.", + "kickUserDescription": "O usuário foi expulso da conversa, mas não banido. Em conversas públicas, o usuário pode entrar novamente a qualquer momento.", "@kickUserDescription": {}, "invite": "Convidar", "@invite": {}, - "blockListDescription": "Você pode bloquear usuários que estejam perturbando. Você não receberá mensagens ou convites de usuários na sua lista pessoal de bloqueios.", + "blockListDescription": "Você pode bloquear usuários que estejam te perturbando. Você não receberá mensagens ou convites de usuários na sua lista pessoal de bloqueios.", "@blockListDescription": {}, "createGroupAndInviteUsers": "Criar um grupo e convidar pessoas", "@createGroupAndInviteUsers": {}, "thisDevice": "Este dispositivo:", "@thisDevice": {}, - "startConversation": "Começar uma conversa", + "startConversation": "Iniciar uma conversa", "@startConversation": {}, "publicSpaces": "Espaços públicos", "@publicSpaces": {}, "blockedUsers": "Usuários bloqueados", "@blockedUsers": {}, - "passwordIsWrong": "A senha inserida está incorreta", + "passwordIsWrong": "A senha digitada está incorreta", "@passwordIsWrong": {}, - "pleaseEnterYourCurrentPassword": "Por favor, insira sua senha atual", + "pleaseEnterYourCurrentPassword": "Digite sua senha atual", "@pleaseEnterYourCurrentPassword": {}, - "groupCanBeFoundViaSearch": "Grupos podem ser encontrados via busca", + "groupCanBeFoundViaSearch": "Grupos podem ser encontrados por pesquisa", "@groupCanBeFoundViaSearch": {}, "publicLink": "Link público", "@publicLink": {}, - "noUsersFoundWithQuery": "Infelizmente, não foi encontrado usuário via \"{query}\". Por favor, verifique se digitou corretamente.", + "noUsersFoundWithQuery": "Infelizmente, não foi encontrado usuário com \"{query}\". Verifique se digitou corretamente.", "@noUsersFoundWithQuery": { "type": "String", "placeholders": { @@ -2672,13 +2663,13 @@ "@block": {}, "nothingFound": "Nada foi encontrado...", "@nothingFound": {}, - "yourGlobalUserIdIs": "Seu ID global é: ", + "yourGlobalUserIdIs": "Seu ID global de usuário é: ", "@yourGlobalUserIdIs": {}, "decline": "Rejeitar", "@decline": {}, "newPassword": "Nova senha", "@newPassword": {}, - "passwordsDoNotMatch": "Senhas não batem", + "passwordsDoNotMatch": "As senhas não correspondem", "@passwordsDoNotMatch": {}, "commandHint_sendraw": "Enviar JSON puro", "@commandHint_sendraw": {}, @@ -2688,27 +2679,27 @@ "@subspace": {}, "select": "Selecionar", "@select": {}, - "pleaseChooseAStrongPassword": "Por favor, escolha uma senha forte", + "pleaseChooseAStrongPassword": "Escolha uma senha forte", "@pleaseChooseAStrongPassword": {}, - "blockUsername": "Ignore usuário", + "blockUsername": "Ignorar nome de usuário", "@blockUsername": {}, "addChatOrSubSpace": "Adicionar conversa ou subespaço", "@addChatOrSubSpace": {}, "groupName": "Nome do grupo", "@groupName": {}, - "leaveEmptyToClearStatus": "Deixe em branco para limpar seu Status.", + "leaveEmptyToClearStatus": "Deixe em branco para limpar seu estado.", "@leaveEmptyToClearStatus": {}, - "joinSpace": "Ingressar no espaço", + "joinSpace": "Entrar no espaço", "@joinSpace": {}, - "searchForUsers": "Buscar por @usuários...", + "searchForUsers": "Pesquisar por @usuários...", "@searchForUsers": {}, - "databaseMigrationTitle": "Banco de dados otimizado", + "databaseMigrationTitle": "O banco de dados está otimizado", "@databaseMigrationTitle": {}, - "searchChatsRooms": "Buscar por #conversas, @usuários...", + "searchChatsRooms": "Pesquisar por #conversas, @usuários...", "@searchChatsRooms": {}, - "databaseMigrationBody": "Por favor, espere. Isto pode demorar um pouco.", + "databaseMigrationBody": "Aguarde. Isto pode demorar um pouco.", "@databaseMigrationBody": {}, - "youInvitedToBy": "Você foi convidado através do link para:\n{alias}", + "youInvitedToBy": "📩 Você foi convidado através do link para:\n{alias}", "@youInvitedToBy": { "placeholders": { "alias": { @@ -2725,13 +2716,13 @@ } } }, - "formattedMessagesDescription": "Mostrar mensagens ricas com conteúdos tipo negrito usando markdown.", + "formattedMessagesDescription": "Exibir conteúdo de mensagem rico, como texto em negrito usando markdown.", "@formattedMessagesDescription": {}, "verifyOtherUser": "🔐 Verificar outro usuário", "@verifyOtherUser": {}, - "verifyOtherDevice": "🔐 Verificar outro aparelho", + "verifyOtherDevice": "🔐 Verificar outro dispositivo", "@verifyOtherDevice": {}, - "acceptedKeyVerification": "{sender} aceitou sua chave de verificação", + "acceptedKeyVerification": "{sender} aceitou a verificação de chaves", "@acceptedKeyVerification": { "type": "String", "placeholders": { @@ -2740,7 +2731,7 @@ } } }, - "startedKeyVerification": "{sender} iniciou a chave de verificação", + "startedKeyVerification": "{sender} iniciou a verificação de chaves", "@startedKeyVerification": { "type": "String", "placeholders": { @@ -2751,7 +2742,7 @@ }, "transparent": "Transparente", "@transparent": {}, - "databaseBuildErrorBody": "Não foi possível construir o banco de dados SQLite. O aplicativo tentará utilizar o banco de dados legado por enquanto. Por favor, reporte este erro aos desenvolvedores em {url}. A mensagem de erro é: {error}", + "databaseBuildErrorBody": "Não foi possível construir o banco de dados SQLite. O app tentará utilizar o banco de dados legado por enquanto. Relate este erro aos desenvolvedores em {url}. A mensagem de erro é: {error}", "@databaseBuildErrorBody": { "type": "String", "placeholders": { @@ -2763,9 +2754,9 @@ } } }, - "initAppError": "Ocorreu um erro enquanto o aplicativo era iniciado", + "initAppError": "Ocorreu um erro enquanto o app era iniciado", "@initAppError": {}, - "restoreSessionBody": "O aplicativo tentará agora restaurar sua sessão a partir do backup. Por favor, reporte este ao desenvolvedor em {url}. A mensagem de erro é: {error}", + "restoreSessionBody": "O app tentará agora restaurar sua sessão a partir do backup. Relate isto ao desenvolvedor em {url}. A mensagem de erro é: {error}", "@restoreSessionBody": { "type": "String", "placeholders": { @@ -2779,7 +2770,7 @@ }, "sendReadReceipts": "Enviar recibos de leitura", "@sendReadReceipts": {}, - "sendTypingNotificationsDescription": "Outros participantes neste chat podem ver quando você está digitando uma nova mensagem.", + "sendTypingNotificationsDescription": "Outros participantes nesta conversa podem ver quando você está digitando uma nova mensagem.", "@sendTypingNotificationsDescription": {}, "formattedMessages": "Mensagens formatadas", "@formattedMessages": {}, @@ -2788,18 +2779,18 @@ "type": "String", "placeholders": {} }, - "presencesToggle": "Mostrar o status das mensagens de outros usuários", + "presencesToggle": "Mostrar as mensagens de estado de outros usuários", "@presencesToggle": { "type": "String", "placeholders": {} }, - "commandHint_ignore": "Ignorar o seguinte ID Matrix", + "commandHint_ignore": "Ignorar o ID Matrix especificado", "@commandHint_ignore": {}, - "commandHint_unignore": "Designorar o seguinte ID Matrix", + "commandHint_unignore": "Parar de ignorar o ID Matrix especificado", "@commandHint_unignore": {}, - "hidePresences": "Esconder lista de status?", + "hidePresences": "Ocultar lista de estado?", "@hidePresences": {}, - "sessionLostBody": "Sua sessão foi desconectada. Por favor, reporte este ao desenvolvedor em {url}. A mensagem de erro é: {error}", + "sessionLostBody": "Sua sessão foi desconectada. Relate este ao desenvolvedor em {url}. A mensagem de erro é: {error}", "@sessionLostBody": { "type": "String", "placeholders": { @@ -2811,11 +2802,11 @@ } } }, - "sendReadReceiptsDescription": "Outros participantes neste chat podem ver quando você tiver lido uma mensagem.", + "sendReadReceiptsDescription": "Outros participantes nesta conversa podem ver quando você tiver lido uma mensagem.", "@sendReadReceiptsDescription": {}, - "verifyOtherUserDescription": "Se você verificar outro usuário, você terá certeza que você conhece com quem está conversando. 💪\n\nQuando iniciar uma verificação, você e o outro usuário receberão um popup no aplicativo. Então vocês receberão uma série de emojis ou números para comparar um com o outro.\n\nA melhor maneira de fazer este procedimento é encontrar pessoalmente ou através de um vídeochamada. 👭", + "verifyOtherUserDescription": "Se você verificar outro usuário, você terá certeza que você conhece com quem está conversando. 💪\n\nAo iniciar uma verificação, você e o outro usuário receberão um pop-up no app. Então vocês receberão uma série de emojis ou números para comparar um com o outro.\n\nA melhor maneira de fazer este procedimento é se encontrar pessoalmente ou através de uma chamada de vídeo. 👭", "@verifyOtherUserDescription": {}, - "requestedKeyVerification": "{sender} enviou uma chave de verificação", + "requestedKeyVerification": "{sender} solicitou uma verificação de chaves", "@requestedKeyVerification": { "type": "String", "placeholders": { @@ -2824,9 +2815,9 @@ } } }, - "verifyOtherDeviceDescription": "Quando você verifica outro aparelho, estes aparelhos poderão trocar chaves, aumentando sua segurança. 💪\n\nQuando iniciar a verificação, um popup aparecerá no aplicativo em ambos os aparelhos. Então você verá uma série de emojis ou números que você terá que comparar um com o outro.\n\nÉ melhor fazer esse procedimento com ambos os aparelhos em mãos antes de começar a verificação. 🤳", + "verifyOtherDeviceDescription": "Ao verificar outro dispositivo, os dispositivos poderão trocar chaves, aumentando sua segurança. 💪 Ao iniciar a verificação, um pop-up aparecerá no app em ambos os aparelhos. Então você verá uma série de emojis ou números que você terá que comparar um com o outro. É melhor fazer esse procedimento com ambos os dispositivos em mãos antes de começar a verificação. 🤳", "@verifyOtherDeviceDescription": {}, - "canceledKeyVerification": "{sender} cancelou sua chave de verificação", + "canceledKeyVerification": "{sender} cancelou a verificação de chaves", "@canceledKeyVerification": { "type": "String", "placeholders": { @@ -2835,7 +2826,7 @@ } } }, - "isReadyForKeyVerification": "{sender} está pronto para a chave de verificação", + "isReadyForKeyVerification": "{sender} está pronto para a verificação de chaves", "@isReadyForKeyVerification": { "type": "String", "placeholders": { @@ -2844,7 +2835,7 @@ } } }, - "completedKeyVerification": "{sender} completou a chave de verificação", + "completedKeyVerification": "{sender} concluiu a verificação de chaves", "@completedKeyVerification": { "type": "String", "placeholders": { @@ -2853,13 +2844,13 @@ } } }, - "stickers": "Stickers", + "stickers": "Figurinhas", "@stickers": {}, - "discover": "Descubra", + "discover": "Explorar", "@discover": {}, "incomingMessages": "Mensagens recebidas", "@incomingMessages": {}, - "unreadChatsInApp": "{appname}: {unread} mensagens não lidas", + "unreadChatsInApp": "{appname}: {unread} conversas não lidas", "@unreadChatsInApp": { "type": "String", "placeholders": { @@ -2873,23 +2864,23 @@ }, "appLockDescription": "Bloquear o app com um código PIN quando não estiver usando", "@appLockDescription": {}, - "accessAndVisibilityDescription": "Quem é permitido entrar nesse chat e como pode ser descoberto.", + "accessAndVisibilityDescription": "Quem pode entrar nesta conversa e como a conversa pode ser descoberta.", "@accessAndVisibilityDescription": {}, "calls": "Chamadas", "@calls": {}, "customEmojisAndStickers": "Emojis e stickers customizados", "@customEmojisAndStickers": {}, - "customEmojisAndStickersBody": "Adicionar ou compartilhar emojis ou stickers customizados que podem ser usados em qualquer chat.", + "customEmojisAndStickersBody": "Adicionar ou compartilhar emojis ou stickers customizados que podem ser usados em qualquer conversa.", "@customEmojisAndStickersBody": {}, - "hideRedactedMessages": "Esconder mensagens excluídas", + "hideRedactedMessages": "Ocultar mensagens apagadas", "@hideRedactedMessages": {}, - "hideRedactedMessagesBody": "Se alguém excluir uma mensagem, esta mensagem não será mais visível no chat.", + "hideRedactedMessagesBody": "Se alguém apagar uma mensagem, esta mensagem não será mais visível na conversa.", "@hideRedactedMessagesBody": {}, - "hideInvalidOrUnknownMessageFormats": "Esconder formatos de mensagem inválidos ou desconhecidos", + "hideInvalidOrUnknownMessageFormats": "Ocultar formatos de mensagem inválidos ou desconhecidos", "@hideInvalidOrUnknownMessageFormats": {}, - "hideMemberChangesInPublicChats": "Esconder mudanças de membro em chats públicos", + "hideMemberChangesInPublicChats": "Ocultar mudanças de membro em conversas públicas", "@hideMemberChangesInPublicChats": {}, - "hideMemberChangesInPublicChatsBody": "Não mostre se alguém entrou ou saiu no chat para melhorar a legibilidade.", + "hideMemberChangesInPublicChatsBody": "Não mostre se alguém entrou ou saiu da conversa para melhorar a legibilidade.", "@hideMemberChangesInPublicChatsBody": {}, "overview": "Visão geral", "@overview": {}, @@ -2901,7 +2892,7 @@ "@noOneCanJoin": {}, "knocking": "Batendo na porta", "@knocking": {}, - "chatCanBeDiscoveredViaSearchOnServer": "O chat pode ser descoberto pela pesquisa em {server}", + "chatCanBeDiscoveredViaSearchOnServer": "A conversa pode ser descoberta por pesquisa em {server}", "@chatCanBeDiscoveredViaSearchOnServer": { "type": "String", "placeholders": { @@ -2910,20 +2901,20 @@ } } }, - "publicChatAddresses": "Endereços de chat públicos", + "publicChatAddresses": "Endereços de conversas públicas", "@publicChatAddresses": {}, "thereAreCountUsersBlocked": "Nesse momento, há {count} usuários bloqueados.", "@thereAreCountUsersBlocked": { "type": "String", "count": {} }, - "globalChatId": "ID de chat global", + "globalChatId": "ID global de conversa", "@globalChatId": {}, "accessAndVisibility": "Acesso e visibilidade", "@accessAndVisibility": {}, "passwordRecoverySettings": "Configurações de recuperação de senha", "@passwordRecoverySettings": {}, - "userWouldLikeToChangeTheChat": "{user} gostaria de entrar no chat.", + "userWouldLikeToChangeTheChat": "{user} gostaria de entrar na conversa.", "@userWouldLikeToChangeTheChat": { "placeholders": { "user": { @@ -2950,7 +2941,7 @@ "@createNewAddress": {}, "knock": "Bater na porta", "@knock": {}, - "searchIn": "Pesquisar em {chat}...", + "searchIn": "Procurar na conversa {chat}...", "@searchIn": { "type": "String", "placeholders": { @@ -2971,160 +2962,550 @@ "@files": {}, "more": "Mais", "@more": {}, - "deletePushRuleCanNotBeUndone": "Se você excluir esta configuração de notificação, isso não pode ser desfeito.", + "deletePushRuleCanNotBeUndone": "Se você apagar esta configuração de notificação, isso não poderá ser desfeito.", "@deletePushRuleCanNotBeUndone": {}, - "alwaysUse24HourFormat": "Falso", + "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, - "setPermissionsLevelDescription": "Por favor, escolha uma função predefinida abaixo ou insira um nível de permissão personalizado entre 0 e 100.", + "setPermissionsLevelDescription": "Selecione um cargo pré-definido abaixo ou digite um nível de permissão personalizado entre 0 e 100.", "@setPermissionsLevelDescription": {}, "shareKeysWith": "Compartilhar chaves com...", "@shareKeysWith": {}, - "setCustomPermissionLevel": "Definir nível de permissão personalizado", + "setCustomPermissionLevel": "Configurar nível personalizado de permissão", "@setCustomPermissionLevel": {}, "ignoreUser": "Ignorar usuário", "@ignoreUser": {}, - "normalUser": "Usuário comum", - "aboutHomeserver": "Sobre {homeserver}", - "commandHint_roomupgrade": "Atualize esta sala para a versão de sala fornecida", - "swipeRightToLeftToReply": "Deslize da direita para a esquerda para responder", - "countChatsAndCountParticipants": "{chats} conversas e {participants} participantes", - "noMoreChatsFound": "Nenhuma conversa adicional encontrada...", - "noChatsFoundHere": "Ainda não há conversas aqui. Comece uma nova conversa com alguém usando o botão abaixo. ⤵️", - "joinedChats": "Conversas participadas", - "unread": "Não lidas", - "space": "Espaço", - "spaces": "Espaços", - "checkList": "Lista de verificação", - "countInvited": "{count} convidado(s)", - "sendImages": "Enviar {count} imagem(ns)", - "synchronizingPleaseWaitCounter": " Sincronizando… ({percentage}%)", - "invitedBy": "📩 Convidado por {user}", - "goToSpace": "Ir para espaço: {space}", - "markAsUnread": "Marcar como não lida", - "userLevel": "{level} - Usuário", - "moderatorLevel": "{level} - Moderador", - "adminLevel": "{level} - Administrador", - "changeGeneralChatSettings": "Alterar configurações gerais do chat", - "inviteOtherUsers": "Convidar outros usuários para este chat", - "changeTheChatPermissions": "Alterar permissões do chat", - "changeTheVisibilityOfChatHistory": "Alterar a visibilidade do histórico do chat", - "changeTheCanonicalRoomAlias": "Alterar o endereço principal do chat público", - "sendRoomNotifications": "Enviar notificações @room", - "changeTheDescriptionOfTheGroup": "Alterar a descrição do chat", - "chatPermissionsDescription": "Defina qual nível de poder é necessário para certas ações neste chat. Os níveis de poder 0, 50 e 100 geralmente representam usuários, moderadores e administradores, mas qualquer gradação é possível.", - "updateInstalled": "🎉 Atualização {version} instalada!", - "changelog": "Registro de alterações", - "sendCanceled": "Envio cancelado", - "loginWithMatrixId": "Entrar com Matrix-ID", - "discoverHomeservers": "Descobrir servidores principais", - "whatIsAHomeserver": "O que é um servidor principal?", - "homeserverDescription": "Todos os seus dados são armazenados no servidor principal, assim como um provedor de email. Você pode escolher qual servidor principal deseja usar, enquanto ainda pode se comunicar com todos. Saiba mais em https://matrix.org.", - "doesNotSeemToBeAValidHomeserver": "Parece não ser um servidor principal compatível. URL incorreta?", - "calculatingFileSize": "Calculando o tamanho do arquivo...", - "prepareSendingAttachment": "Preparando envio de anexo...", - "sendingAttachment": "Enviando anexo...", - "generatingVideoThumbnail": "Gerando miniatura de vídeo...", - "compressVideo": "Comprimindo vídeo...", - "sendingAttachmentCountOfCount": "Enviando anexo {index} de {length}...", - "serverLimitReached": "Limite do servidor atingido! Aguardando {seconds} segundos...", - "oneOfYourDevicesIsNotVerified": "Um dos seus dispositivos não está verificado", - "noticeChatBackupDeviceVerification": "Observação: Quando você conecta todos os seus dispositivos ao backup de chat, eles são verificados automaticamente.", - "continueText": "Continuar", - "welcomeText": "Ei Ei 👋 Este é o FluffyChat. Você pode entrar em qualquer servidor doméstico, que seja compatível com https://matrix.org. E então conversar com qualquer pessoa. É uma grande rede de mensagens descentralizada!", - "blur": "Desfoque:", - "opacity": "Opacidade:", - "setWallpaper": "Definir papel de parede", - "manageAccount": "Gerenciar conta", - "noContactInformationProvided": "O servidor não fornece nenhuma informação de contato válida", - "contactServerAdmin": "Contatar administrador do servidor", - "contactServerSecurity": "Contatar segurança do servidor", - "supportPage": "Página de suporte", - "serverInformation": "Informações do servidor:", - "name": "Nome", - "version": "Versão", - "website": "Site", - "compress": "Comprimir", - "boldText": "Texto em negrito", - "italicText": "Texto em itálico", - "strikeThrough": "Rasurado", - "pleaseFillOut": "Por favor, preencha", - "invalidUrl": "URL inválida", - "addLink": "Adicionar link", - "unableToJoinChat": "Não foi possível entrar no chat. Talvez a outra parte já tenha encerrado a conversa.", - "previous": "Anterior", - "otherPartyNotLoggedIn": "A outra parte não está conectada no momento e, portanto, não pode receber mensagens!", - "appWantsToUseForLogin": "Usar '{server}' para fazer login", - "appWantsToUseForLoginDescription": "Você autoriza o aplicativo e o site a compartilharem informações sobre você.", - "open": "Abrir", - "waitingForServer": "Aguardando o servidor...", - "appIntroduction": "FluffyChat permite que você converse com seus amigos em diferentes mensageiros. Saiba mais em https://matrix.org ou apenas toque em *Continuar*.", - "newChatRequest": "📩 Novo pedido de chat", - "contentNotificationSettings": "Configurações de notificação de conteúdo", - "generalNotificationSettings": "Configurações gerais de notificação", - "roomNotificationSettings": "Configurações de notificação da sala", - "userSpecificNotificationSettings": "Configurações de notificação específicas do usuário", - "otherNotificationSettings": "Outras configurações de notificação", - "notificationRuleContainsUserName": "Contém nome de usuário", - "notificationRuleContainsUserNameDescription": "Notifica o usuário quando uma mensagem contém seu nome de usuário.", + "contentNotificationSettings": "Configurações de notificações de conteúdo", + "@contentNotificationSettings": {}, + "generalNotificationSettings": "Configurações de notificações gerais", + "@generalNotificationSettings": {}, + "notificationRuleContainsUserNameDescription": "Notifica o usuário quando a mensagem contém o seu nome de usuário.", + "@notificationRuleContainsUserNameDescription": {}, "notificationRuleMaster": "Silenciar todas as notificações", + "@notificationRuleMaster": {}, "notificationRuleMasterDescription": "Sobrescreve todas as outras regras e desativa todas as notificações.", - "notificationRuleSuppressNotices": "Suprimir mensagens automáticas", - "notificationRuleSuppressNoticesDescription": "Suprime notificações de clientes automatizados como bots.", - "notificationRuleInviteForMe": "Convite para mim", - "notificationRuleInviteForMeDescription": "Notifica o usuário quando ele é convidado para uma sala.", - "notificationRuleMemberEvent": "Evento de membro", - "notificationRuleMemberEventDescription": "Suprime notificações de eventos de membresia.", - "notificationRuleIsUserMention": "Menção de usuário", - "notificationRuleIsUserMentionDescription": "Notifica o usuário quando ele é mencionado diretamente em uma mensagem.", - "notificationRuleContainsDisplayName": "Contém nome de exibição", - "notificationRuleContainsDisplayNameDescription": "Notifica o usuário quando uma mensagem contém seu nome de exibição.", - "notificationRuleIsRoomMention": "Menção na sala", - "notificationRuleIsRoomMentionDescription": "Notifica o usuário quando há uma menção na sala.", - "notificationRuleRoomnotif": "Notificação da sala", + "@notificationRuleMasterDescription": {}, + "notificationRuleContainsDisplayName": "Contém o nome de exibição", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleRoomnotif": "Notificação de sala", + "@notificationRuleRoomnotif": {}, + "notificationRuleIsRoomMentionDescription": "Notifica o usuário quando há uma menção de sala.", + "@notificationRuleIsRoomMentionDescription": {}, "notificationRuleRoomnotifDescription": "Notifica o usuário quando uma mensagem contém '@room'.", - "notificationRuleTombstone": "Lápide", - "notificationRuleTombstoneDescription": "Notifica o usuário sobre mensagens de desativação de sala.", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleTombstone": "Morte", + "@notificationRuleTombstone": {}, "notificationRuleReaction": "Reação", - "notificationRuleReactionDescription": "Suprime notificações de reações.", - "notificationRuleRoomServerAcl": "ACL do Servidor da Sala", - "notificationRuleRoomServerAclDescription": "Suprime notificações de listas de controle de acesso do servidor da sala (ACL).", - "notificationRuleSuppressEdits": "Suprimir Edições", - "notificationRuleSuppressEditsDescription": "Suprime notificações de mensagens editadas.", - "notificationRuleCall": "Chamada", - "notificationRuleCallDescription": "Notifica o usuário sobre chamadas.", - "notificationRuleEncryptedRoomOneToOne": "Sala Encriptada Um-para-Um", - "notificationRuleEncryptedRoomOneToOneDescription": "Notifica o usuário sobre mensagens em salas encriptadas um-para-um.", - "notificationRuleRoomOneToOne": "Sala Um-para-Um", - "notificationRuleRoomOneToOneDescription": "Notifica o usuário sobre mensagens em salas um-para-um.", - "notificationRuleMessage": "Mensagem", - "notificationRuleMessageDescription": "Notifica o usuário sobre mensagens gerais.", - "notificationRuleEncrypted": "Encriptado", - "notificationRuleEncryptedDescription": "Notifica o usuário sobre mensagens em salas encriptadas.", - "notificationRuleJitsi": "Jitsi", - "notificationRuleJitsiDescription": "Notifica o usuário sobre eventos do widget Jitsi.", - "notificationRuleServerAcl": "Suprimir Eventos de ACL do Servidor", - "notificationRuleServerAclDescription": "Suprime notificações para eventos de ACL do servidor.", + "@notificationRuleReaction": {}, + "notificationRuleSuppressEditsDescription": "Omite notificações de mensagens editadas.", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Notifica o usuário de mensagens em salas criptografas de 2 pessoas.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "Sala de 2 pessoas", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleMessageDescription": "Notifica o usuário de mensagens gerais.", + "@notificationRuleMessageDescription": {}, + "notificationRuleEncrypted": "Criptografado", + "@notificationRuleEncrypted": {}, "unknownPushRule": "Regra de push desconhecida '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "notificationRuleServerAclDescription": "Omite notificações de eventos de ACL de servidor.", + "@notificationRuleServerAclDescription": {}, + "unread": "Não lido", + "@unread": {}, + "changeTheCanonicalRoomAlias": "Alterar o endereço público principal da conversa", + "@changeTheCanonicalRoomAlias": {}, + "doesNotSeemToBeAValidHomeserver": "Não parece ser um servidor compatível. URL errada?", + "@doesNotSeemToBeAValidHomeserver": {}, + "website": "Site", + "@website": {}, + "compress": "Comprimir", + "@compress": {}, + "invalidUrl": "URL inválida", + "@invalidUrl": {}, + "appWantsToUseForLoginDescription": "Aqui, você permite que o app e o site compartilhem informações sobre você.", + "@appWantsToUseForLoginDescription": {}, + "notificationRuleMemberEvent": "Evento de membro", + "@notificationRuleMemberEvent": {}, + "crossVerifiedDevicesIfEnabled": "Dispositivos verificados por ambos se ativado", + "@crossVerifiedDevicesIfEnabled": {}, + "countInvited": "{count} convidados", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "checkList": "Lista de tarefas", + "@checkList": {}, + "synchronizingPleaseWaitCounter": " Sincronizando… ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "invitedBy": "📩 Convidado por {user}", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "markAsUnread": "Marcar como não lido", + "@markAsUnread": {}, + "updateInstalled": "🎉 Atualização da versão {version} instalada!", + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "changelog": "Registro de mudanças", + "@changelog": {}, + "sendCanceled": "Envio cancelado", + "@sendCanceled": {}, + "unableToJoinChat": "Não foi possível entrar na conversa. Talvez a outra pessoa já fechou a conversa.", + "@unableToJoinChat": {}, + "whatIsAHomeserver": "O que é um servidor?", + "@whatIsAHomeserver": {}, + "prepareSendingAttachment": "Preparando o envio do anexo...", + "@prepareSendingAttachment": {}, + "sendingAttachment": "Enviando o anexo...", + "@sendingAttachment": {}, + "noticeChatBackupDeviceVerification": "Observação: Quando você conecta todos os seus dispositivos ao backup de conversas, eles são verificados automaticamente.", + "@noticeChatBackupDeviceVerification": {}, + "continueText": "Continuar", + "@continueText": {}, + "blur": "Borrar:", + "@blur": {}, + "opacity": "Opacidade:", + "@opacity": {}, + "setWallpaper": "Configurar plano de fundo", + "@setWallpaper": {}, + "noContactInformationProvided": "O servidor não fornece nenhuma informação válida de contato", + "@noContactInformationProvided": {}, + "contactServerAdmin": "Contatar o administrador do servidor", + "@contactServerAdmin": {}, + "contactServerSecurity": "Contatar a segurança do servidor", + "@contactServerSecurity": {}, + "supportPage": "Página de ajuda", + "@supportPage": {}, + "italicText": "Texto em itálico", + "@italicText": {}, + "strikeThrough": "Risco", + "@strikeThrough": {}, + "appWantsToUseForLogin": "Usar '{server}' para conectar", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "open": "Abrir", + "@open": {}, + "roomNotificationSettings": "Configurações de notificações de sala", + "@roomNotificationSettings": {}, + "userSpecificNotificationSettings": "Configurações de notificações específicas ao usuário", + "@userSpecificNotificationSettings": {}, + "otherNotificationSettings": "Configurações de outras notificações", + "@otherNotificationSettings": {}, + "notificationRuleContainsUserName": "Contém o nome de usuário", + "@notificationRuleContainsUserName": {}, + "notificationRuleSuppressNotices": "Omitir mensagens automáticas", + "@notificationRuleSuppressNotices": {}, + "notificationRuleInviteForMe": "Convite para mim", + "@notificationRuleInviteForMe": {}, + "notificationRuleMemberEventDescription": "Omite todas as notificações de eventos de membro.", + "@notificationRuleMemberEventDescription": {}, + "notificationRuleIsUserMention": "Menção de usuário", + "@notificationRuleIsUserMention": {}, + "notificationRuleContainsDisplayNameDescription": "Notifica o usuário quando uma mensagem contém seu nome de exibição.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleCall": "Chamada", + "@notificationRuleCall": {}, "sentVoiceMessage": "🎙️ {duration} - Mensagem de voz de {sender}", - "shareKeysWithDescription": "Quais dispositivos devem ser confiáveis para que possam ler suas mensagens em chats criptografados?", - "allDevices": "Todos os dispositivos", - "crossVerifiedDevicesIfEnabled": "Dispositivos verificados cruzados se ativado", - "crossVerifiedDevices": "Dispositivos verificados cruzados", - "verifiedDevicesOnly": "Apenas dispositivos verificados", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "crossVerifiedDevices": "Dispositivos verificados por ambos", + "@crossVerifiedDevices": {}, "takeAPhoto": "Tirar uma foto", - "recordAVideo": "Gravar um vídeo", - "optionalMessage": "(Opcional) mensagem...", - "notSupportedOnThisDevice": "Não suportado neste dispositivo", - "enterNewChat": "Entrar em novo chat", + "@takeAPhoto": {}, + "youHaveKnocked": "Bateram na sua porta", + "@youHaveKnocked": {}, + "changeGeneralChatSettings": "Alterar configurações gerais de conversa", + "@changeGeneralChatSettings": {}, + "inviteOtherUsers": "Convidar outros usuários para esta conversa", + "@inviteOtherUsers": {}, + "adminLevel": "{level} - Administrador", + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "changeTheChatPermissions": "Alterar as permissões da conversa", + "@changeTheChatPermissions": {}, + "changeTheVisibilityOfChatHistory": "Alterar a visibilidade do histórico de conversa", + "@changeTheVisibilityOfChatHistory": {}, + "sendImages": "Enviar {count} imagens", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "goToSpace": "Ir ao espaço: {space}", + "@goToSpace": { + "type": "String", + "space": {} + }, + "discoverHomeservers": "Explorar servidores", + "@discoverHomeservers": {}, + "loginWithMatrixId": "Conectar com ID Matrix", + "@loginWithMatrixId": {}, + "calculatingFileSize": "Calculando o tamanho do arquivo...", + "@calculatingFileSize": {}, + "compressVideo": "Comprimindo o vídeo...", + "@compressVideo": {}, + "generatingVideoThumbnail": "Gerando a miniatura do vídeo...", + "@generatingVideoThumbnail": {}, + "oneOfYourDevicesIsNotVerified": "Um dos seus dispositivos não está verificado", + "@oneOfYourDevicesIsNotVerified": {}, + "serverLimitReached": "Limite do servidor alcançado! Esperando {seconds} segundos...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "manageAccount": "Gerenciar conta", + "@manageAccount": {}, + "newChatRequest": "📩 Nova solicitação de conversa", + "@newChatRequest": {}, + "userLevel": "{level} - Usuário", + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "enterNewChat": "Abrir a conversa nova", + "@enterNewChat": {}, + "notificationRuleSuppressNoticesDescription": "Omite notificações de clientes automatizados, como bots.", + "@notificationRuleSuppressNoticesDescription": {}, + "notificationRuleInviteForMeDescription": "Notifica o usuário quando for convidado para uma sala.", + "@notificationRuleInviteForMeDescription": {}, "approve": "Aprovar", - "youHaveKnocked": "Você bateu", - "pleaseWaitUntilInvited": "Por favor, aguarde até que alguém da sala o convide.", - "commandHint_logout": "Fazer logout do seu dispositivo atual", - "commandHint_logoutall": "Fazer logout de todos os dispositivos ativos", - "displayNavigationRail": "Mostrar trilho de navegação no celular", + "@approve": {}, + "homeserverDescription": "Todos os seus dados são armazenados no servidor, parecido como um provedor de e-mail. Pode escolher qual servidor quer usar, enquanto ainda conversa com todo mundo. Aprenda mais em https://matrix.org.", + "@homeserverDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "Sala criptografada de 2 pessoas", + "@notificationRuleEncryptedRoomOneToOne": {}, + "pleaseWaitUntilInvited": "Aguarde até que alguém da sala te convide.", + "@pleaseWaitUntilInvited": {}, + "waitingForServer": "Aguardando o servidor...", + "@waitingForServer": {}, + "notificationRuleEncryptedDescription": "Notifica o usuário de mensagens em salas criptografadas.", + "@notificationRuleEncryptedDescription": {}, + "sendRoomNotifications": "Enviar notificações de @room", + "@sendRoomNotifications": {}, + "sendingAttachmentCountOfCount": "Enviando o {index}° anexo de {length}...", + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } + } + }, + "welcomeText": "Olá! 👋 Este é o FluffyChat. Você pode se conectar com qualquer servidor que é compatível com o https://matrix.org. E então conversar com qualquer um. É uma rede gigante e descentralizada de conversa!", + "@welcomeText": {}, + "notificationRuleRoomServerAclDescription": "Omite notificações de listas de controle de acesso de servidor de uma sala (ACL).", + "@notificationRuleRoomServerAclDescription": {}, + "appIntroduction": "O FluffyChat permite que você converse com os seus amigos entre mensageiros diferentes. Aprenda mais em https://matrix.org ou toque em *Continuar*.", + "@appIntroduction": {}, + "notificationRuleIsUserMentionDescription": "Notifica o usuário quando é mencionado diretamente em uma mensagem.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleSuppressEdits": "Omitir edições", + "@notificationRuleSuppressEdits": {}, + "shareKeysWithDescription": "Quais dispositivos devem ser confiados para que possam ler suas mensagens em conversas criptografas?", + "@shareKeysWithDescription": {}, + "allDevices": "Todos os dispositivos", + "@allDevices": {}, + "aboutHomeserver": "Sobre {homeserver}", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "swipeRightToLeftToReply": "Deslizar da direita pra esquerda para responder", + "@swipeRightToLeftToReply": {}, + "verifiedDevicesOnly": "Somente dispositivos verificados", + "@verifiedDevicesOnly": {}, + "boldText": "Texto em negrito", + "@boldText": {}, + "recordAVideo": "Gravar um vídeo", + "@recordAVideo": {}, + "optionalMessage": "(Opcional) mensagem...", + "@optionalMessage": {}, + "joinedChats": "Conversas que entrou", + "@joinedChats": {}, + "notSupportedOnThisDevice": "Não há suporte neste dispositivo", + "@notSupportedOnThisDevice": {}, + "normalUser": "Usuário normal", + "@normalUser": {}, + "commandHint_roomupgrade": "Atualizar esta sala para a versão de sala especificada", + "@commandHint_roomupgrade": {}, + "countChatsAndCountParticipants": "{chats} conversas e {participants} participantes", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noMoreChatsFound": "Mais nenhuma conversa foi encontrada...", + "@noMoreChatsFound": {}, + "noChatsFoundHere": "Nenhuma conversa encontrada aqui ainda. Inicie uma nova conversa com alguém usando o botão abaixo. ⤵️", + "@noChatsFoundHere": {}, + "space": "Espaço", + "@space": {}, + "spaces": "Espaços", + "@spaces": {}, + "moderatorLevel": "{level} - Moderador", + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "changeTheDescriptionOfTheGroup": "Alterar a descrição da conversa", + "@changeTheDescriptionOfTheGroup": {}, + "chatPermissionsDescription": "Configurar qual o nível de poder é necessário para certas ações nesta conversa. Os níveis de poder 0, 50, e 100 são normalmente para representar usuários, moderadores, e administradores, mas qualquer configuração é possível.", + "@chatPermissionsDescription": {}, + "serverInformation": "Informações do servidor:", + "@serverInformation": {}, + "name": "Nome", + "@name": {}, + "version": "Versão", + "@version": {}, + "pleaseFillOut": "Preencha", + "@pleaseFillOut": {}, + "addLink": "Adicionar link", + "@addLink": {}, + "notificationRuleRoomServerAcl": "ACL de servidores de sala", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleCallDescription": "Notifica o usuário de chamadas.", + "@notificationRuleCallDescription": {}, + "notificationRuleRoomOneToOneDescription": "Notifica o usuário de mensagens em salas de duas pessoas.", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleMessage": "Mensagem", + "@notificationRuleMessage": {}, + "notificationRuleJitsi": "Jitsi", + "@notificationRuleJitsi": {}, + "notificationRuleJitsiDescription": "Notifica o usuário de eventos de widget do Jitsi.", + "@notificationRuleJitsiDescription": {}, + "notificationRuleServerAcl": "Omitir eventos de ACL de servidor", + "@notificationRuleServerAcl": {}, + "notificationRuleIsRoomMention": "Menção de sala", + "@notificationRuleIsRoomMention": {}, + "notificationRuleTombstoneDescription": "Notifica o usuário de mensagens de desativação de salas.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleReactionDescription": "Omite notificações de reações.", + "@notificationRuleReactionDescription": {}, + "commandHint_logout": "Desconecte-se do seu dispositivo atual", + "@commandHint_logout": {}, + "commandHint_logoutall": "Desconecte-se de todos os dispositivos ativos", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Mostrar trilha de navegação em dispositivo móvel", + "@displayNavigationRail": {}, + "previous": "Anterior", + "@previous": {}, + "otherPartyNotLoggedIn": "A outra pessoa não tem nenhum dispositivo conectado no momento e portanto não consegue receber mensagens!", + "@otherPartyNotLoggedIn": {}, "customReaction": "Reação personalizada", + "@customReaction": {}, + "moreEvents": "Mais eventos", + "@moreEvents": {}, + "declineInvitation": "Rejeitar convite", + "@declineInvitation": {}, + "noMessagesYet": "Nenhuma mensagem ainda", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Segure para gravar uma mensagem de voz.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Pausar", + "@pause": {}, + "resume": "Retomar", + "@resume": {}, + "newSubSpace": "Novo sub espaço", + "@newSubSpace": {}, + "moveToDifferentSpace": "Mover para espaço diferente", + "@moveToDifferentSpace": {}, + "moveUp": "Mover para cima", + "@moveUp": {}, + "moveDown": "Mover para baixo", + "@moveDown": {}, + "removeFromSpaceDescription": "A conversa será removida do espaço mas ainda aparecerá na sua lista de conversas.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} conversas", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Membro do espaço de {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Membro do espaço de {spaces} pode bater na porta", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Doar", + "@donate": {}, + "startedAPoll": "{username} iniciou uma enquete.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Enquete", + "@poll": {}, + "startPoll": "Abrir enquete", + "@startPoll": {}, + "endPoll": "Fechar enquete", + "@endPoll": {}, + "answersVisible": "Respostas visíveis", + "@answersVisible": {}, + "answersHidden": "Respostas ocultas", + "@answersHidden": {}, + "pollQuestion": "Questão da enquete", + "@pollQuestion": {}, + "answerOption": "Opção de resposta", + "@answerOption": {}, + "addAnswerOption": "Adicionar opção de resposta", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Permitir várias respostas", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "A enquete terminou", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Um voto} other{{count} votos}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "As respostas ficarão visíveis quando a enquete terminar", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Responder no tópico", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Uma resposta} other{{count} respostas}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Tópico", + "@thread": {}, + "backToMainChat": "Voltar à conversa principal", + "@backToMainChat": {}, + "saveChanges": "Salvar alterações", + "@saveChanges": {}, + "createSticker": "Criar figurinha ou emoji", + "@createSticker": {}, + "useAsSticker": "Usar como figurinha", + "@useAsSticker": {}, + "useAsEmoji": "Usar como emoji", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "O nome do pacote de figurinhas já existe", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Novo pacote de figurinhas", + "@newStickerPack": {}, + "stickerPackName": "Nome do pacote de figurinhas", + "@stickerPackName": {}, + "attribution": "Créditos", + "@attribution": {}, + "skipChatBackup": "Pular backup de conversas", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Tem certeza? Se não ativar o backup de conversas, você pode perder o acesso às suas mensagens se trocar de dispositivo.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Carregando mensagens", + "@loadingMessages": {}, + "setupChatBackup": "Configurar backup de conversas", + "@setupChatBackup": {}, + "changedTheChatDescription": "{username} alterou a descrição da conversa", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} alterou o nome da conversa", + "@changedTheChatName": {}, "writeAMessageLangCodes": "Digite em {l1} ou {l2}...", "requests": "Solicitações", "holdForInfo": "Clique e segure para informações da palavra.", @@ -3166,7 +3547,6 @@ "updateLanguage": "Meus idiomas", "whatLanguageYouWantToLearn": "Qual idioma você quer aprender?", "whatIsYourBaseLanguage": "Qual é o seu idioma base?", - "saveChanges": "Salvar alterações", "publicProfileTitle": "Permitir que meu perfil seja encontrado na busca", "publicProfileDesc": "Ao ativar, você permite que outros usuários encontrem seu perfil na barra de busca global e enviem solicitações para conversar. Neste momento, você pode escolher aceitar ou negar a solicitação.", "errorDisableIT": "Assistência de tradução está desativada.", @@ -4265,637 +4645,6 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_BR", - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@swipeRightToLeftToReply": { - "type": "String", - "placeholders": {} - }, - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" - }, - "participants": { - "type": "int" - } - } - }, - "@noMoreChatsFound": { - "type": "String", - "placeholders": {} - }, - "@noChatsFoundHere": { - "type": "String", - "placeholders": {} - }, - "@joinedChats": { - "type": "String", - "placeholders": {} - }, - "@unread": { - "type": "String", - "placeholders": {} - }, - "@space": { - "type": "String", - "placeholders": {} - }, - "@spaces": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@sendImages": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, - "@invitedBy": { - "type": "String", - "placeholders": { - "user": { - "type": "String" - } - } - }, - "@goToSpace": { - "type": "String", - "placeholders": { - "space": {} - } - }, - "@markAsUnread": { - "type": "String", - "placeholders": {} - }, - "@userLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@moderatorLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@adminLevel": { - "type": "String", - "placeholders": { - "level": { - "type": "int" - } - } - }, - "@changeGeneralChatSettings": { - "type": "String", - "placeholders": {} - }, - "@inviteOtherUsers": { - "type": "String", - "placeholders": {} - }, - "@changeTheChatPermissions": { - "type": "String", - "placeholders": {} - }, - "@changeTheVisibilityOfChatHistory": { - "type": "String", - "placeholders": {} - }, - "@changeTheCanonicalRoomAlias": { - "type": "String", - "placeholders": {} - }, - "@sendRoomNotifications": { - "type": "String", - "placeholders": {} - }, - "@changeTheDescriptionOfTheGroup": { - "type": "String", - "placeholders": {} - }, - "@chatPermissionsDescription": { - "type": "String", - "placeholders": {} - }, - "@updateInstalled": { - "type": "String", - "placeholders": { - "version": { - "type": "String" - } - } - }, - "@changelog": { - "type": "String", - "placeholders": {} - }, - "@sendCanceled": { - "type": "String", - "placeholders": {} - }, - "@loginWithMatrixId": { - "type": "String", - "placeholders": {} - }, - "@discoverHomeservers": { - "type": "String", - "placeholders": {} - }, - "@whatIsAHomeserver": { - "type": "String", - "placeholders": {} - }, - "@homeserverDescription": { - "type": "String", - "placeholders": {} - }, - "@doesNotSeemToBeAValidHomeserver": { - "type": "String", - "placeholders": {} - }, - "@calculatingFileSize": { - "type": "String", - "placeholders": {} - }, - "@prepareSendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachment": { - "type": "String", - "placeholders": {} - }, - "@generatingVideoThumbnail": { - "type": "String", - "placeholders": {} - }, - "@compressVideo": { - "type": "String", - "placeholders": {} - }, - "@sendingAttachmentCountOfCount": { - "type": "integer", - "placeholders": { - "index": { - "type": "int" - }, - "length": { - "type": "int" - } - } - }, - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, - "@oneOfYourDevicesIsNotVerified": { - "type": "String", - "placeholders": {} - }, - "@noticeChatBackupDeviceVerification": { - "type": "String", - "placeholders": {} - }, - "@continueText": { - "type": "String", - "placeholders": {} - }, - "@welcomeText": { - "type": "String", - "placeholders": {} - }, - "@blur": { - "type": "String", - "placeholders": {} - }, - "@opacity": { - "type": "String", - "placeholders": {} - }, - "@setWallpaper": { - "type": "String", - "placeholders": {} - }, - "@manageAccount": { - "type": "String", - "placeholders": {} - }, - "@noContactInformationProvided": { - "type": "String", - "placeholders": {} - }, - "@contactServerAdmin": { - "type": "String", - "placeholders": {} - }, - "@contactServerSecurity": { - "type": "String", - "placeholders": {} - }, - "@supportPage": { - "type": "String", - "placeholders": {} - }, - "@serverInformation": { - "type": "String", - "placeholders": {} - }, - "@name": { - "type": "String", - "placeholders": {} - }, - "@version": { - "type": "String", - "placeholders": {} - }, - "@website": { - "type": "String", - "placeholders": {} - }, - "@compress": { - "type": "String", - "placeholders": {} - }, - "@boldText": { - "type": "String", - "placeholders": {} - }, - "@italicText": { - "type": "String", - "placeholders": {} - }, - "@strikeThrough": { - "type": "String", - "placeholders": {} - }, - "@pleaseFillOut": { - "type": "String", - "placeholders": {} - }, - "@invalidUrl": { - "type": "String", - "placeholders": {} - }, - "@addLink": { - "type": "String", - "placeholders": {} - }, - "@unableToJoinChat": { - "type": "String", - "placeholders": {} - }, - "@previous": { - "type": "String", - "placeholders": {} - }, - "@otherPartyNotLoggedIn": { - "type": "String", - "placeholders": {} - }, - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@appWantsToUseForLoginDescription": { - "type": "String", - "placeholders": {} - }, - "@open": { - "type": "String", - "placeholders": {} - }, - "@waitingForServer": { - "type": "String", - "placeholders": {} - }, - "@appIntroduction": { - "type": "String", - "placeholders": {} - }, - "@newChatRequest": { - "type": "String", - "placeholders": {} - }, - "@contentNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@generalNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@roomNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@userSpecificNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@otherNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMaster": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMasterDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNotices": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNoticesDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMe": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEvent": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEventDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReaction": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReactionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEditsDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCall": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCallDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessage": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncrypted": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsi": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@allDevices": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@takeAPhoto": { - "type": "String", - "placeholders": {} - }, - "@recordAVideo": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@enterNewChat": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, - "@youHaveKnocked": { - "type": "String", - "placeholders": {} - }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -5071,10 +4820,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11423,4 +11168,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index 6dfb9eda2..561d6e68e 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -1974,7 +1974,6 @@ "report": "relatar", "signInWithPassword": "Iniciar sessão com palavra-passe", "pleaseTryAgainLaterOrChooseDifferentServer": "Por favor, tente novamente mais tarde ou escolha um servidor diferente.", - "signInWith": "Iniciar sessão com {provider}", "profileNotFound": "O utilizador não foi encontrado no servidor. Talvez haja um problema de ligação ou o utilizador não exista.", "setTheme": "Definir tema:", "setColorTheme": "Definir tema de cor:", @@ -4585,14 +4584,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5003,9 +4994,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12094,4 +12083,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 2b4912913..bb30b4d98 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -2583,7 +2583,6 @@ "noPublicLinkHasBeenCreatedYet": "Încă nu a fost creat un link public", "knock": "Bate la ușă", "hidePresences": "Ascunde lista de stare?", - "signInWith": "Autentifică-te cu {provider}", "profileNotFound": "Utilizatorul nu a fost găsit pe server. Poate există o problemă de conexiune sau utilizatorul nu există.", "setTheme": "Setează tema:", "setColorTheme": "Setează tema de culoare:", @@ -4274,14 +4273,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -4692,9 +4683,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -11800,4 +11789,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index da55bcfbc..99f992a87 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -218,7 +218,7 @@ } } }, - "changedTheChatNameTo": "{username} изменил(а) имя чата на: '{chatname}'", + "changedTheChatNameTo": "{username} изменил(а) имя чата на '{chatname}'", "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -239,7 +239,7 @@ } } }, - "changedTheDisplaynameTo": "{username} изменил(а) отображаемое имя на: '{displayname}'", + "changedTheDisplaynameTo": "{username} изменил(а) отображаемое имя на '{displayname}'", "@changedTheDisplaynameTo": { "type": "String", "placeholders": { @@ -260,7 +260,7 @@ } } }, - "changedTheGuestAccessRulesTo": "{username} изменил(а) правила гостевого доступа на: {rules}", + "changedTheGuestAccessRulesTo": "{username} изменил(а) правила гостевого доступа на {rules}", "@changedTheGuestAccessRulesTo": { "type": "String", "placeholders": { @@ -281,7 +281,7 @@ } } }, - "changedTheHistoryVisibilityTo": "{username} изменил(а) видимость истории на: {rules}", + "changedTheHistoryVisibilityTo": "{username} изменил(а) видимость истории на {rules}", "@changedTheHistoryVisibilityTo": { "type": "String", "placeholders": { @@ -302,7 +302,7 @@ } } }, - "changedTheJoinRulesTo": "{username} изменил(а) правила присоединения на: {joinRules}", + "changedTheJoinRulesTo": "{username} изменил(а) правила присоединения на {joinRules}", "@changedTheJoinRulesTo": { "type": "String", "placeholders": { @@ -381,7 +381,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "Резервная старых сообщений защищена ключом восстановления. Пожалуйста, не потеряйте его.", + "chatBackupDescription": "Ваши сообщения защищены ключом восстановления. Пожалуйста, не потеряйте его.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -1087,15 +1087,6 @@ "type": "String", "placeholders": {} }, - "loadCountMoreParticipants": "Загрузить еще {count} участника(ов)", - "@loadCountMoreParticipants": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, "loadingPleaseWait": "Загрузка... Пожалуйста, подождите.", "@loadingPleaseWait": { "type": "String", @@ -1879,16 +1870,7 @@ "type": "String", "placeholders": {} }, - "unreadChats": "{unreadCount, plural, other{{unreadCount} непрочитанных чата(ов)}}", - "@unreadChats": { - "type": "String", - "placeholders": { - "unreadCount": { - "type": "int" - } - } - }, - "userAndOthersAreTyping": "{username} и {count} других участников печатают…", + "userAndOthersAreTyping": "{username} и еще {count} печатают…", "@userAndOthersAreTyping": { "type": "String", "placeholders": { @@ -2328,14 +2310,6 @@ "@storeInAndroidKeystore": {}, "storeInAppleKeyChain": "Сохранить в Apple KeyChain", "@storeInAppleKeyChain": {}, - "countFiles": "{count} файлов", - "@countFiles": { - "placeholders": { - "count": { - "type": "int" - } - } - }, "user": "Пользователь", "@user": {}, "confirmMatrixId": "Пожалуйста, подтвердите Matrix ID, чтобы удалить свою учётную запись.", @@ -2437,7 +2411,7 @@ "@readUpToHere": {}, "commandHint_hug": "Отправить обнимашки", "@commandHint_hug": {}, - "cuddleContent": "{senderName} улыбнулся(-ась) Вам", + "cuddleContent": "{senderName} обнимает вас", "@cuddleContent": { "type": "String", "placeholders": { @@ -2497,15 +2471,6 @@ "@reopenChat": {}, "commandHint_googly": "Отправить выпученные глаза", "@commandHint_googly": {}, - "signInWith": "Войти с {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Это не картинка.", "@notAnImage": {}, "importNow": "Импортировать сейчас", @@ -2592,13 +2557,13 @@ "@setColorTheme": {}, "invite": "Пригласить", "@invite": {}, - "invitePrivateChat": "📨 Пригласить в приватный чат", + "invitePrivateChat": "📨 Вас пригласили в приватный чат", "@invitePrivateChat": {}, - "inviteGroupChat": "📨 Пригласить в групповой чат", + "inviteGroupChat": "📨 Вас пригласили в групповой чат", "@inviteGroupChat": {}, "invalidInput": "Недопустимый ввод!", "@invalidInput": {}, - "wrongPinEntered": "Введён неверный пин-код! Повторите попытку через {seconds} секунд...", + "wrongPinEntered": "Wrong pin entered! Try again in {seconds} seconds...", "@wrongPinEntered": { "type": "String", "placeholders": { @@ -2934,23 +2899,6 @@ "@commandHint_ignore": {}, "commandHint_unignore": "Не игнорировать данный matrix ID", "@commandHint_unignore": {}, - "unreadChatsInApp": "{appname}: {unread} непрочитанные чаты", - "@unreadChatsInApp": { - "type": "String", - "placeholders": { - "appname": { - "type": "String" - }, - "unread": { - "type": "String" - } - } - }, - "thereAreCountUsersBlocked": "Сейчас заблокировано {count} пользователей.", - "@thereAreCountUsersBlocked": { - "type": "String", - "count": {} - }, "restricted": "Запрещено", "@restricted": {}, "knockRestricted": "Стук запрещен", @@ -2970,7 +2918,7 @@ "@gallery": {}, "files": "Файлы", "@files": {}, - "swipeRightToLeftToReply": "Для ответа проведите с права на лево", + "swipeRightToLeftToReply": "Для ответа проведите справа налево", "@swipeRightToLeftToReply": {}, "userLevel": "{level} - Пользователь", "@userLevel": { @@ -3009,18 +2957,6 @@ "@inviteOtherUsers": {}, "changeTheVisibilityOfChatHistory": "Изменить видимость истории чата", "@changeTheVisibilityOfChatHistory": {}, - "countChatsAndCountParticipants": "{chats} чатов и {participants} участников", - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" - }, - "participants": { - "type": "int" - } - } - }, "unread": "Непрочитанные", "@unread": {}, "space": "Пространство", @@ -3061,7 +2997,7 @@ "@doesNotSeemToBeAValidHomeserver": {}, "noMoreChatsFound": "Больше чатов не обнаружено...", "@noMoreChatsFound": {}, - "alwaysUse24HourFormat": "нет", + "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -3097,7 +3033,7 @@ "@joinedChats": {}, "serverInformation": "Информация о сервере:", "@serverInformation": {}, - "sendingAttachmentCountOfCount": "Отправляю... {index} {length}...", + "sendingAttachmentCountOfCount": "Отправляется вложение {index} из {length}...", "@sendingAttachmentCountOfCount": { "type": "integer", "placeholders": { @@ -3132,17 +3068,10 @@ "@invalidUrl": {}, "addLink": "Добавить ссылку", "@addLink": {}, + "italicText": "Italic", + "@italicText": {}, "unableToJoinChat": "Невозможно присоединиться к чату. Возможно, другая сторона уже закончила разговор.", "@unableToJoinChat": {}, - "serverLimitReached": "Ограничения сервера. Ожидайте{seconds} секунд...", - "@serverLimitReached": { - "type": "integer", - "placeholders": { - "seconds": { - "type": "int" - } - } - }, "continueText": "Продолжить", "@continueText": {}, "blur": "Размытие:", @@ -3165,7 +3094,7 @@ "@version": {}, "website": "Сайт", "@website": {}, - "sendImages": "Отправить {count} изображений", + "sendImages": "Отправить {count} изображение", "@sendImages": { "type": "String", "placeholders": { @@ -3234,7 +3163,7 @@ "@userSpecificNotificationSettings": {}, "otherNotificationSettings": "Другие настройки уведомлений", "@otherNotificationSettings": {}, - "notificationRuleContainsUserNameDescription": "Уведомляет пользователя когда сообщение содержит его имя пользователя.", + "notificationRuleContainsUserNameDescription": "Уведомляет пользователя, когда сообщение содержит его имя пользователя.", "@notificationRuleContainsUserNameDescription": {}, "notificationRuleMasterDescription": "Перекрывает все другие правила и отключает все уведомления.", "@notificationRuleMasterDescription": {}, @@ -3272,48 +3201,312 @@ "@notificationRuleRoomOneToOne": {}, "setCustomPermissionLevel": "Установить уровень пользовательских разрешений", "@setCustomPermissionLevel": {}, - "setPermissionsLevelDescription": "Пожалуйста, выберите одну из предопределённых ролей ниже или введите пользовательский уровень разрешений от 0 до 100.", - "ignoreUser": "Игнорировать пользователя", + "setPermissionsLevelDescription": "Выберите одну из стандартных ролей ниже или введите произвольный уровень прав от 0 до 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Заглушить пользователя", + "@ignoreUser": {}, "normalUser": "Обычный пользователь", - "commandHint_roomupgrade": "Обновить эту комнату до указанной версии комнаты", - "checkList": "Список проверок", - "countInvited": "{count} приглашённых", + "@normalUser": {}, + "commandHint_roomupgrade": "Обновить комнату до указанной версии", + "@commandHint_roomupgrade": {}, "notificationRuleInviteForMeDescription": "Уведомляет пользователя, когда его приглашают в комнату.", - "notificationRuleMemberEvent": "Событие участника", - "notificationRuleIsUserMentionDescription": "Уведомляет пользователя, когда его прямо упоминают в сообщении.", - "notificationRuleContainsDisplayNameDescription": "Уведомляет пользователя, когда сообщение содержит его отображаемое имя.", - "notificationRuleIsRoomMentionDescription": "Уведомляет пользователя, когда есть упоминание комнаты.", - "notificationRuleRoomnotif": "Уведомление комнаты", + "@notificationRuleInviteForMeDescription": {}, + "unknownPushRule": "Неизвестное правило оповещения '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "checkList": "Контрольный список", + "@checkList": {}, + "changedTheChatDescription": "{username} изменил описание чата", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} изменил(а) имя чата", + "@changedTheChatName": {}, + "notificationRuleMemberEvent": "Мероприятие участника", + "@notificationRuleMemberEvent": {}, + "notificationRuleIsUserMentionDescription": "Уведомляет пользователя, когда он упоминается непосредственно в сообщении.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleContainsDisplayNameDescription": "Уведомляет пользователя, когда сообщение содержит его имя отображения.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsRoomMentionDescription": "Уведомляет пользователя о наличии упоминания о комнате.", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomnotif": "Уведомление о комнате", + "@notificationRuleRoomnotif": {}, "notificationRuleRoomnotifDescription": "Уведомляет пользователя, когда сообщение содержит '@room'.", - "notificationRuleTombstone": "Мемориальная метка", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleTombstone": "Надгробный камень", + "@notificationRuleTombstone": {}, "notificationRuleTombstoneDescription": "Уведомляет пользователя о сообщениях о деактивации комнаты.", + "@notificationRuleTombstoneDescription": {}, "notificationRuleRoomServerAcl": "ACL сервера комнаты", - "notificationRuleRoomServerAclDescription": "Подавляет уведомления о списках контроля доступа (ACL) сервера комнаты.", - "notificationRuleSuppressEdits": "Подавить редактирования", - "notificationRuleRoomOneToOneDescription": "Уведомляет пользователя о сообщениях в комнатах один на один.", - "notificationRuleMessageDescription": "Уведомляет пользователя о общих сообщениях.", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomServerAclDescription": "Удаляет уведомления о списках контроля доступа к серверу комнат (ACL).", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleSuppressEdits": "Подавление правки", + "@notificationRuleSuppressEdits": {}, + "notificationRuleRoomOneToOneDescription": "Уведомляет пользователя о сообщениях в разделах один на один.", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleMessageDescription": "Уведомляет пользователя об общих сообщениях.", + "@notificationRuleMessageDescription": {}, "notificationRuleEncryptedDescription": "Уведомляет пользователя о сообщениях в зашифрованных комнатах.", - "notificationRuleJitsi": "Jitsi", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsi": "Джици", + "@notificationRuleJitsi": {}, "notificationRuleJitsiDescription": "Уведомляет пользователя о событиях виджета Jitsi.", - "notificationRuleServerAcl": "Отключить события ACL сервера", - "notificationRuleServerAclDescription": "Отключает уведомления о событиях ACL сервера.", - "unknownPushRule": "Неизвестное правило push '{rule}'", - "sentVoiceMessage": "🎙️ {duration} - Голосовое сообщение от {sender}", - "deletePushRuleCanNotBeUndone": "Если вы удалите это уведомление, восстановить его будет невозможно.", + "@notificationRuleJitsiDescription": {}, + "notificationRuleServerAcl": "Запретить события ACL сервера", + "@notificationRuleServerAcl": {}, + "notificationRuleServerAclDescription": "Удаляет уведомления о событиях ACL сервера.", + "@notificationRuleServerAclDescription": {}, + "sentVoiceMessage": "️🎙️ {duration} - Голосовое сообщение от {sender}", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "Если вы удалите эту настройку уведомлений, это невозможно отменить.", + "@deletePushRuleCanNotBeUndone": {}, "shareKeysWith": "Поделиться ключами с...", - "shareKeysWithDescription": "Какие устройства должны быть доверенными, чтобы читать ваши сообщения в зашифрованных чатах?", - "crossVerifiedDevicesIfEnabled": "Перекрестно проверенные устройства, если включено", - "crossVerifiedDevices": "Перекрестно проверенные устройства", + "@shareKeysWith": {}, + "shareKeysWithDescription": "Каким устройствам следует доверять, чтобы они могли читать ваши сообщения в зашифрованных чатах?", + "@shareKeysWithDescription": {}, + "crossVerifiedDevicesIfEnabled": "Если включено, перекрестите проверенные устройства", + "@crossVerifiedDevicesIfEnabled": {}, + "crossVerifiedDevices": "Кросс-верифицированные устройства", + "@crossVerifiedDevices": {}, "verifiedDevicesOnly": "Только проверенные устройства", - "optionalMessage": "(Опционально) сообщение...", + "@verifiedDevicesOnly": {}, + "optionalMessage": "(Необязательно) сообщение...", + "@optionalMessage": {}, "notSupportedOnThisDevice": "Не поддерживается на этом устройстве", - "approve": "Одобрить", + "@notSupportedOnThisDevice": {}, + "approve": "Утверждать", + "@approve": {}, "youHaveKnocked": "Вы постучали", - "pleaseWaitUntilInvited": "Пожалуйста, подождите, пока кто-то из комнаты не пригласит вас.", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Пожалуйста, подождите, пока кто-нибудь из комнаты не пригласит вас.", + "@pleaseWaitUntilInvited": {}, "commandHint_logout": "Выйти из текущего устройства", - "commandHint_logoutall": "Выйти со всех активных устройств", - "displayNavigationRail": "Показать панель навигации на мобильном устройстве", + "@commandHint_logout": {}, + "commandHint_logoutall": "Выйти из всех активных устройств", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Показать навигационный рельс на мобильном устройстве", + "@displayNavigationRail": {}, "customReaction": "Пользовательская реакция", + "@customReaction": {}, + "moreEvents": "Больше событий", + "@moreEvents": {}, + "declineInvitation": "Отказаться от приглашения", + "@declineInvitation": {}, + "noMessagesYet": "Пока нет сообщений", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Нажмите длиннее, чтобы записать голосовое сообщение.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Пауза", + "@pause": {}, + "resume": "Резюмировать", + "@resume": {}, + "newSubSpace": "Новое подпространство", + "@newSubSpace": {}, + "moveToDifferentSpace": "Переместить в другое пространство", + "@moveToDifferentSpace": {}, + "moveUp": "Переместить вверх", + "@moveUp": {}, + "moveDown": "Переместить вниз", + "@moveDown": {}, + "removeFromSpaceDescription": "Чат будет удален из пространства, но все равно появится в вашем списке чатов.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} чатов", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Участник пространства из {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Участник пространства из {spaces} может постучать", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Пожертвовать", + "@donate": {}, + "startedAPoll": "{username} начал опрос.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Опрос", + "@poll": {}, + "startPoll": "Начать опрос", + "@startPoll": {}, + "endPoll": "Завершить опрос", + "@endPoll": {}, + "answersVisible": "Ответы видны", + "@answersVisible": {}, + "answersHidden": "Ответы скрыты", + "@answersHidden": {}, + "pollQuestion": "Вопрос по опросу", + "@pollQuestion": {}, + "answerOption": "Вариант ответа", + "@answerOption": {}, + "addAnswerOption": "Добавить вариант ответа", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Разрешить несколько ответов", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Опрос завершен", + "@pollHasBeenEnded": {}, + "answersWillBeVisibleWhenPollHasEnded": "Ответы будут видны после завершения опроса", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Ответить в потоке", + "@replyInThread": {}, + "thread": "Нить", + "@thread": {}, + "backToMainChat": "Вернуться в основной чат", + "@backToMainChat": {}, + "saveChanges": "Сохранить изменения", + "@saveChanges": {}, + "createSticker": "Создать стикер или эмоджи", + "@createSticker": {}, + "useAsSticker": "Использовать как стикер", + "@useAsSticker": {}, + "useAsEmoji": "Использовать в качестве эмоджи", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Имя набора стикеров уже существует", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Новая упаковка стикеров", + "@newStickerPack": {}, + "stickerPackName": "Имя упаковки стикеров", + "@stickerPackName": {}, + "attribution": "Атрибуция", + "@attribution": {}, + "skipChatBackup": "Пропустить резервную копию чата", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Вы уверены? Без включения резервного копирования чата вы можете потерять доступ к своим сообщениям, если переключите устройство.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Загрузка сообщений", + "@loadingMessages": {}, + "setupChatBackup": "Настроить резервную копию чата", + "@setupChatBackup": {}, + "loadCountMoreParticipants": "{count, plural, one{Загрузить еще # участника} few{Загрузить еще # участников} other{Загрузить еще # участников}}", + "@loadCountMoreParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "unreadChats": "{unreadCount, plural, one{# непрочитанный чат} few{# непрочитанных чата} other{# непрочитанных чатов}}", + "@unreadChats": { + "type": "String", + "placeholders": { + "unreadCount": { + "type": "int" + } + } + }, + "countFiles": "{count, plural, one{# файл} few{# файла} other{# файлов}}", + "@countFiles": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "unreadChatsInApp": "", + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "thereAreCountUsersBlocked": "Сейчас {count, plural, one{заблокирован # пользователь} few{заблокированы # пользователя} other{заблокированы # пользователей}}.", + "@thereAreCountUsersBlocked": { + "type": "String", + "count": {} + }, + "countChatsAndCountParticipants": "", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "serverLimitReached": "", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "countInvited": "{count, plural, one{Приглашён # участник} few{Приглашены # участника} other{Приглашены # участников}}", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "countVotes": "{count, plural, =1{Один голос} other{{count} голосов}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "countReplies": "{count, plural, =1{Один ответ} other{{count} ответов}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, "writeAMessageLangCodes": "Введите на {l1} или {l2}...", "requests": "Запросы", "holdForInfo": "Нажмите и удерживайте для получения информации о слове.", @@ -3355,7 +3548,6 @@ "updateLanguage": "Мои языки", "whatLanguageYouWantToLearn": "Какой язык вы хотите выучить?", "whatIsYourBaseLanguage": "Какой у вас основной язык?", - "saveChanges": "Сохранить изменения", "publicProfileTitle": "Разрешить находить мой профиль в поиске", "publicProfileDesc": "Включив это, вы позволяете другим пользователям находить ваш профиль в глобальной строке поиска и отправлять запросы на чат. В этот момент вы можете принять или отклонить запрос.", "errorDisableIT": "Помощь с переводом отключена.", @@ -4427,189 +4619,6 @@ "inviteYourFriends": "Пригласите своих друзей", "playWithAI": "Поиграть с ИИ пока что", "courseStartDesc": "Pangea Bot готов к работе в любое время!\n\n...но обучение лучше всего с друзьями!", - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEvent": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsi": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@deletePushRuleCanNotBeUndone": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWith": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, - "@youHaveKnocked": { - "type": "String", - "placeholders": {} - }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4785,10 +4794,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -10965,7 +10970,6 @@ "type": "String", "placeholders": {} }, - "italicText": "Курсивный текст", "exampleSentence": "Пример предложения", "reportToTeacher": "Кому вы хотите пожаловаться на это сообщение?", "reportMessageTitle": "{reportingUserId} пожаловался на сообщение от {reportedUserId} в чате {roomName}", @@ -10980,10 +10984,6 @@ "enableTTSToolDescription": "Позволяет приложению генерировать озвучивание текста на вашем целевом языке.", "yourUsername": "Ваше имя пользователя", "yourEmail": "Ваш email", - "@italicText": { - "type": "String", - "placeholders": {} - }, "joinSpaceOnboardingDesc": "У вас есть код приглашения или ссылка на публичный курс?", "welcomeUser": "Добро пожаловать {user}", "@joinSpaceOnboardingDesc": { @@ -11173,4 +11173,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index 42aacdf9a..15b18d377 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1787,7 +1787,6 @@ "report": "Nahlásiť", "signInWithPassword": "Prihlásiť sa s heslom", "pleaseTryAgainLaterOrChooseDifferentServer": "Skúste to prosím neskôr alebo vyberte iný server.", - "signInWith": "Prihlásiť sa s {provider}", "profileNotFound": "Používateľa sa nepodarilo nájsť na serveri. Možno je problém s pripojením alebo používateľ neexistuje.", "setTheme": "Nastaviť tému:", "setColorTheme": "Nastaviť farebnú tému:", @@ -4623,14 +4622,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5041,9 +5032,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12149,4 +12138,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index 3e3ea4c3b..8c9f59a8a 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -1117,7 +1117,6 @@ "report": "prijavi", "signInWithPassword": "Prijavi se z geslom", "pleaseTryAgainLaterOrChooseDifferentServer": "Poskusite znova kasneje ali izberite drug strežnik.", - "signInWith": "Prijavite se z {provider}", "profileNotFound": "Uporabnika ni bilo mogoče najti na strežniku. Morda je težava s povezavo ali uporabnik ne obstaja.", "setTheme": "Nastavi temo:", "setColorTheme": "Nastavi barvno temo:", @@ -4612,14 +4611,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5030,9 +5021,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12146,4 +12135,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index a05c0df09..2ee0646e4 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1963,8 +1963,11 @@ "type": "String", "placeholders": {} }, + "repeatPassword": "Ponovite lozinku", + "@repeatPassword": {}, + "commandHint_cuddle": "Pošalji zagrljaj", + "@commandHint_cuddle": {}, "alwaysUse24HourFormat": "лачно користи 24-часовни формат", - "repeatPassword": "Понови лозинку", "notAnImage": "Није слика.", "setCustomPermissionLevel": "Подеси прилагођени ниво дозвола", "setPermissionsLevelDescription": "Молимо изаберите једну од претходно дефинисаних улога испод или унесите прилагођени ниво дозвола између 0 и 100.", @@ -1983,7 +1986,6 @@ "allChats": "Сва ћаскања", "commandHint_roomupgrade": "Побољшај ову собу на датую верзију собе", "commandHint_googly": "Пошаљи неке гугли очи", - "commandHint_cuddle": "Pošalji zagrljaj", "commandHint_hug": "Pošalji zagrljaj", "googlyEyesContent": "{senderName} ti šalje čudne oči", "cuddleContent": "{senderName} te grli", @@ -2200,7 +2202,6 @@ "report": "prijavi", "signInWithPassword": "Prijavi se lozinkom", "pleaseTryAgainLaterOrChooseDifferentServer": "Molimo pokušajte ponovo kasnije ili odaberite drugi server.", - "signInWith": "Prijavi se sa {provider}", "profileNotFound": "Korisnik nije pronađen na serveru. Možda postoji problem sa konekcijom ili korisnik ne postoji.", "setTheme": "Postavi temu:", "setColorTheme": "Постави тему боја:", @@ -3546,10 +3547,6 @@ "type": "String", "placeholders": {} }, - "@repeatPassword": { - "type": "String", - "placeholders": {} - }, "@notAnImage": { "type": "String", "placeholders": {} @@ -3630,10 +3627,6 @@ "type": "String", "placeholders": {} }, - "@commandHint_cuddle": { - "type": "String", - "placeholders": {} - }, "@commandHint_hug": { "type": "String", "placeholders": {} @@ -4641,14 +4634,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5059,9 +5044,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12167,4 +12150,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index 7c0c741be..2f71f1bb4 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -2536,15 +2536,6 @@ } } }, - "signInWith": "Logga in med {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "optionalRedactReason": "(Frivilligt) Anledning till att ta bort det här meddelandet…", "@optionalRedactReason": {}, "archiveRoomDescription": "Den här chatten kommer flyttas till arkivet. Andra användare kommer kunna se att du har lämnat chatten.", @@ -4435,9 +4426,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -11543,4 +11532,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index b269b5882..72680277c 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -618,7 +618,7 @@ "type": "String", "placeholders": {} }, - "noEmotesFound": "உணர்ச்சிகள் எதுவும் காணப்படவில்லை. .", + "noEmotesFound": "உணர்ச்சிகள் எதுவும் காணப்படவில்லை. 😕", "@noEmotesFound": { "type": "String", "placeholders": {} @@ -774,7 +774,7 @@ } } }, - "unpin": "Unpin", + "unpin": "மூள்நீக்கு", "@unpin": { "type": "String", "placeholders": {} @@ -802,7 +802,7 @@ "type": "String", "placeholders": {} }, - "messageInfo": "செய்தி செய்தி", + "messageInfo": "செய்தி தகவல்", "@messageInfo": {}, "sentAFile": "📁 {username} கோப்பை அனுப்பியுள்ளார்", "@sentAFile": { @@ -1046,7 +1046,7 @@ } } }, - "placeCall": "அழைப்பு அழைப்பு", + "placeCall": "அழைப்பு இடு", "@placeCall": {}, "videoCallsBetaWarning": "வீடியோ அழைப்புகள் தற்போது பீட்டாவில் உள்ளன என்பதை நினைவில் கொள்க. அவர்கள் எதிர்பார்த்தபடி வேலை செய்யக்கூடாது அல்லது எல்லா தளங்களிலும் வேலை செய்யக்கூடாது.", "@videoCallsBetaWarning": {}, @@ -1662,7 +1662,7 @@ } } }, - "noChatsFoundHere": "இங்கே அரட்டைகள் எதுவும் காணப்படவில்லை. கீழே உள்ள பொத்தானைப் பயன்படுத்தி ஒருவருடன் புதிய அரட்டையைத் தொடங்கவும். .", + "noChatsFoundHere": "இங்கே அரட்டைகள் எதுவும் காணப்படவில்லை. கீழே உள்ள பொத்தானைப் பயன்படுத்தி ஒருவருடன் புதிய அரட்டையைத் தொடங்கவும். ⤵️", "@noChatsFoundHere": {}, "space": "இடைவெளி", "@space": {}, @@ -2148,7 +2148,7 @@ "type": "String", "placeholders": {} }, - "play": "Play {fileName}", + "play": "{fileName} இயக்கு", "@play": { "type": "String", "placeholders": { @@ -2604,20 +2604,11 @@ }, "pleaseTryAgainLaterOrChooseDifferentServer": "தயவுசெய்து பின்னர் மீண்டும் முயற்சிக்கவும் அல்லது வேறு சேவையகத்தைத் தேர்வுசெய்க.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "{provider} மூலம் உள்நுழையவும்", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "profileNotFound": "பயனரை சேவையகத்தில் காண முடியவில்லை. ஒரு இணைப்பு சிக்கல் இருக்கலாம் அல்லது பயனர் இல்லை.", "@profileNotFound": {}, - "inviteGroupChat": "Compect குழு அரட்டையை அழைக்கவும்", + "inviteGroupChat": "📨 குழு அரட்டை அழைப்பு", "@inviteGroupChat": {}, - "invitePrivateChat": "தனிப்பட்ட தனியார் அரட்டையை அழைக்கவும்", + "invitePrivateChat": "Sative தனியார் அரட்டை அழைப்பு", "@invitePrivateChat": {}, "invalidInput": "தவறான உள்ளீடு!", "@invalidInput": {}, @@ -2689,9 +2680,9 @@ "@formattedMessagesDescription": {}, "verifyOtherUser": "Poser மற்ற பயனரை சரிபார்க்கவும்", "@verifyOtherUser": {}, - "verifyOtherUserDescription": "நீங்கள் மற்றொரு பயனரைச் சரிபார்த்தால், நீங்கள் உண்மையில் யாருக்கு எழுதுகிறீர்கள் என்பது உங்களுக்குத் தெரியும் என்பதை நீங்கள் உறுதியாக நம்பலாம். .\n\n நீங்கள் ஒரு சரிபார்ப்பைத் தொடங்கும்போது, நீங்களும் மற்ற பயனரும் பயன்பாட்டில் ஒரு பாப்அப்பைக் காண்பீர்கள். நீங்கள் ஒருவருக்கொருவர் ஒப்பிட வேண்டிய தொடர்ச்சியான ஈமோசிகள் அல்லது எண்களைக் காண்பீர்கள்.\n\n இதைச் செய்வதற்கான சிறந்த வழி வீடியோ அழைப்பைச் சந்திப்பது அல்லது தொடங்குவது. .", + "verifyOtherUserDescription": "நீங்கள் மற்றொரு பயனரைச் சரிபார்த்தால், நீங்கள் உண்மையில் யாருக்கு எழுதுகிறீர்கள் என்பது உங்களுக்குத் தெரியும் என்பதை நீங்கள் உறுதியாக நம்பலாம். 💪\n\nநீங்கள் ஒரு சரிபார்ப்பைத் தொடங்கும்போது, நீங்களும் மற்ற பயனரும் பயன்பாட்டில் ஒரு பாப்அப்பைக் காண்பீர்கள். நீங்கள் ஒருவருக்கொருவர் ஒப்பிட வேண்டிய தொடர்ச்சியான ஈமோசிகள் அல்லது எண்களைக் காண்பீர்கள்.\n\nஇதைச் செய்வதற்கான சிறந்த வழி வீடியோ அழைப்பைச் சந்திப்பது அல்லது தொடங்குவது. 👭", "@verifyOtherUserDescription": {}, - "verifyOtherDeviceDescription": "நீங்கள் மற்றொரு சாதனத்தை சரிபார்க்கும்போது, அந்த சாதனங்கள் விசைகளை பரிமாறிக்கொள்ளலாம், உங்கள் ஒட்டுமொத்த பாதுகாப்பை அதிகரிக்கும். So நீங்கள் ஒரு சரிபார்ப்பைத் தொடங்கும்போது, இரண்டு சாதனங்களிலும் பயன்பாட்டில் ஒரு பாப்அப் தோன்றும். நீங்கள் ஒருவருக்கொருவர் ஒப்பிட வேண்டிய தொடர்ச்சியான ஈமோசிகள் அல்லது எண்களைக் காண்பீர்கள். நீங்கள் சரிபார்ப்பைத் தொடங்குவதற்கு முன்பு இரண்டு சாதனங்களையும் எளிதில் வைத்திருப்பது நல்லது. .", + "verifyOtherDeviceDescription": "நீங்கள் மற்றொரு சாதனத்தைச் சரிபார்க்கும்போது, அந்தச் சாதனங்கள் விசைகளைப் பரிமாறிக்கொள்ளலாம், உங்கள் ஒட்டுமொத்த பாதுகாப்பை அதிகரிக்கும். 💪 நீங்கள் ஒரு சரிபார்ப்பைத் தொடங்கும்போது, இரண்டு சாதனங்களிலும் பயன்பாட்டில் ஒரு பாப்அப் தோன்றும். நீங்கள் ஒருவருக்கொருவர் ஒப்பிட வேண்டிய தொடர்ச்சியான ஈமோசிகள் அல்லது எண்களைக் காண்பீர்கள். நீங்கள் சரிபார்ப்பைத் தொடங்குவதற்கு முன்பு இரண்டு சாதனங்களையும் எளிதில் வைத்திருப்பது நல்லது. 🤳", "@verifyOtherDeviceDescription": {}, "canceledKeyVerification": "{sender} ரத்து செய்யப்பட்ட விசை சரிபார்ப்பு", "@canceledKeyVerification": { @@ -3169,89 +3160,214 @@ "type": "String", "placeholders": {} }, - "setCustomPermissionLevel": "தனிப்பயன் அனுமதி நிலையை அமைக்கவும்", - "setPermissionsLevelDescription": "தயவுசெய்து கீழே ஒரு முன்பே நிர்ணயிக்கப்பட்ட பாத்திரத்தை தேர்ந்தெடுக்கவும் அல்லது 0 முதல் 100 வரை தனிப்பயன் அனுமதி நிலையை உள்ளிடவும்.", + "setCustomPermissionLevel": "தனிப்பயன் இசைவு அளவை அமைக்கவும்", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "தயவுசெய்து கீழே ஒரு முன் வரையறுக்கப்பட்ட பாத்திரத்தைத் தேர்வுசெய்க அல்லது 0 மற்றும் 100 க்கு இடையில் தனிப்பயன் இசைவு நிலையை உள்ளிடவும்.", + "@setPermissionsLevelDescription": {}, "ignoreUser": "பயனரை புறக்கணிக்கவும்", + "@ignoreUser": {}, "normalUser": "சாதாரண பயனர்", - "commandHint_roomupgrade": "இந்த அறையை கொடுக்கப்பட்ட அறை பதிப்புக்கு மேம்படுத்தவும்", - "checkList": "பரிசோதனை பட்டியல்", - "countInvited": "{count} அழைக்கப்பட்டவர்கள்", - "synchronizingPleaseWaitCounter": "செயலாக்கம் நடைபெற்று வருகிறது… ({percentage}%)", - "previous": "முன்னையது", - "otherPartyNotLoggedIn": "மற்ற பக்கம் தற்போது உள்நுழையவில்லை, ஆகையால் செய்திகள் பெற முடியாது!", - "appWantsToUseForLogin": "'{server}' ஐ உள்நுழைய பயன்படுத்தவும்", - "appWantsToUseForLoginDescription": "நீங்கள் இப்போது செயலி மற்றும் இணையதளத்திற்கு உங்களது தகவலை பகிர அனுமதிக்கின்றீர்கள்.", - "open": "திறக்கவும்", - "waitingForServer": "சேவையகத்திற்காக காத்திருக்கிறது...", - "appIntroduction": "FluffyChat உங்கள் நண்பர்களுடன் பல்வேறு செய்தியாளர்களில் பேச அனுமதிக்கிறது. மேலும் அறிய https://matrix.org இல் கற்றுக்கொள்ளவும் அல்லது *தொடரவும்* என்பதைக் கிளிக் செய்யவும்.", - "newChatRequest": "📩 புதிய உரையாடல் கோரிக்கை", + "@normalUser": {}, + "commandHint_roomupgrade": "கொடுக்கப்பட்ட அறை பதிப்பிற்கு இந்த அறையை மேம்படுத்தவும்", + "@commandHint_roomupgrade": {}, + "checkList": "சரிபார்ப்பு பட்டியல்", + "@checkList": {}, + "countInvited": "{count} அழைக்கப்பட்டது", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "synchronizingPleaseWaitCounter": " ஒத்திசைத்தல்… ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "previous": "முந்தைய", + "@previous": {}, + "otherPartyNotLoggedIn": "மற்ற கட்சி தற்போது உள்நுழைந்திருக்கவில்லை, எனவே செய்திகளைப் பெற முடியாது!", + "@otherPartyNotLoggedIn": {}, + "appWantsToUseForLogin": "உள்நுழைய '{server}' ஐப் பயன்படுத்தவும்", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appWantsToUseForLoginDescription": "உங்களைப் பற்றிய தகவல்களைப் பகிர பயன்பாடு மற்றும் வலைத்தளத்தை இதன்மூலம் அனுமதிக்கிறீர்கள்.", + "@appWantsToUseForLoginDescription": {}, + "open": "திற", + "@open": {}, + "waitingForServer": "சேவையகத்திற்காக காத்திருக்கிறது ...", + "@waitingForServer": {}, + "appIntroduction": "வெவ்வேறு தூதர்களில் உங்கள் நண்பர்களுடன் அரட்டையடிக்க உங்களை பஞ்சுபோன்றது உங்களை அனுமதிக்கிறது. Https://matrix.org இல் மேலும் அறிக அல்லது *தொடரவும் *தட்டவும்.", + "@appIntroduction": {}, + "newChatRequest": "அரட்டை கோரிக்கை", + "@newChatRequest": {}, "contentNotificationSettings": "உள்ளடக்க அறிவிப்பு அமைப்புகள்", - "generalNotificationSettings": "பொதுவான அறிவிப்பு அமைப்புகள்", + "@contentNotificationSettings": {}, + "generalNotificationSettings": "பொது அறிவிப்பு அமைப்புகள்", + "@generalNotificationSettings": {}, "roomNotificationSettings": "அறை அறிவிப்பு அமைப்புகள்", + "@roomNotificationSettings": {}, "userSpecificNotificationSettings": "பயனர் குறிப்பிட்ட அறிவிப்பு அமைப்புகள்", - "otherNotificationSettings": "மற்ற அறிவிப்பு அமைப்புகள்", - "notificationRuleContainsUserName": "பயனர் பெயரை உள்ளடக்கியது", - "notificationRuleContainsUserNameDescription": "ஒரு செய்தி அவர்களின் பயனர் பெயரை உள்ளடக்கிய போது பயனருக்கு அறிவிக்கிறது.", - "notificationRuleMaster": "அனைத்து அறிவிப்புகளையும் மௌனம்", - "notificationRuleMasterDescription": "அனைத்து விதிகளையும் மீறி அனைத்து அறிவிப்புகளையும் முடக்குகிறது.", - "notificationRuleSuppressNotices": "தானாக இயங்கும் செய்திகள் தடுக்கவும்", - "notificationRuleSuppressNoticesDescription": "போட்டிகள் போன்ற தானாக இயங்கும் கிளையண்டுகளிலிருந்து அறிவிப்புகளை தடுக்கிறது.", - "notificationRuleInviteForMe": "எனக்கான அழைப்பு", + "@userSpecificNotificationSettings": {}, + "otherNotificationSettings": "பிற அறிவிப்பு அமைப்புகள்", + "@otherNotificationSettings": {}, + "notificationRuleContainsUserName": "பயனர் பெயரைக் கொண்டுள்ளது", + "@notificationRuleContainsUserName": {}, + "notificationRuleContainsUserNameDescription": "ஒரு செய்தியில் அவற்றின் பயனர்பெயர் இருக்கும்போது பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleContainsUserNameDescription": {}, + "notificationRuleMaster": "அனைத்து அறிவிப்புகளையும் முடக்குகிறது", + "@notificationRuleMaster": {}, + "notificationRuleMasterDescription": "மற்ற எல்லா விதிகளையும் மீறி அனைத்து அறிவிப்புகளையும் முடக்குகிறது.", + "@notificationRuleMasterDescription": {}, + "notificationRuleSuppressNotices": "தானியங்கு செய்திகளை அடக்கவும்", + "@notificationRuleSuppressNotices": {}, + "notificationRuleSuppressNoticesDescription": "போட்ச் போன்ற தானியங்கி வாடிக்கையாளர்களிடமிருந்து அறிவிப்புகளை அடக்குகிறது.", + "@notificationRuleSuppressNoticesDescription": {}, + "notificationRuleInviteForMe": "எனக்கு அழைக்கவும்", + "@notificationRuleInviteForMe": {}, "notificationRuleInviteForMeDescription": "ஒரு அறைக்கு அழைக்கப்படும்போது பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleInviteForMeDescription": {}, "notificationRuleMemberEvent": "உறுப்பினர் நிகழ்வு", - "notificationRuleMemberEventDescription": "உறுப்பினர் நிகழ்வுகளுக்கான அறிவிப்புகளை தடுக்கிறது.", - "notificationRuleIsUserMention": "பயனர் குறிப்பிடல்", - "notificationRuleIsUserMentionDescription": "ஒரு செய்தியில் நேரடியாக குறிப்பிடப்பட்டால் பயனருக்கு அறிவிக்கிறது.", - "notificationRuleContainsDisplayName": "காண்பிப்பு பெயரை உள்ளடக்கியது", - "notificationRuleContainsDisplayNameDescription": "ஒரு செய்தியில் அவர்களின் காண்பிப்பு பெயர் உள்ளடக்கியால் பயனருக்கு அறிவிக்கிறது.", - "notificationRuleIsRoomMention": "அறை குறிப்பிடல்", - "notificationRuleIsRoomMentionDescription": "ஒரு அறை குறிப்பிடல் இருந்தால் பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleMemberEvent": {}, + "notificationRuleMemberEventDescription": "உறுப்பினர் நிகழ்வுகளுக்கான அறிவிப்புகளை அடக்குகிறது.", + "@notificationRuleMemberEventDescription": {}, + "notificationRuleIsUserMention": "பயனர் குறிப்பு", + "@notificationRuleIsUserMention": {}, + "notificationRuleIsUserMentionDescription": "ஒரு செய்தியில் நேரடியாக குறிப்பிடப்படும்போது பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleContainsDisplayName": "காட்சி பெயரைக் கொண்டுள்ளது", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleContainsDisplayNameDescription": "ஒரு செய்தியில் அவற்றின் காட்சி பெயர் இருக்கும்போது பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleIsRoomMention": "அறை குறிப்பு", + "@notificationRuleIsRoomMention": {}, + "notificationRuleIsRoomMentionDescription": "ஒரு அறை குறிப்பு இருக்கும்போது பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleIsRoomMentionDescription": {}, "notificationRuleRoomnotif": "அறை அறிவிப்பு", - "notificationRuleRoomnotifDescription": "ஒரு செய்தியில் '@room' உள்ளடக்கியால் பயனருக்கு அறிவிக்கிறது.", - "notificationRuleTombstone": "தொம்ப்ஸ்டோன்", - "notificationRuleTombstoneDescription": "பயனருக்கு அறை செயலிழக்கும் செய்திகளைக் குறித்து அறிவிக்கிறது.", - "notificationRuleReaction": "பிரதிக்ரியா", - "notificationRuleReactionDescription": "பிரதிக்ரியைகளுக்கான அறிவிப்புகளை தடுக்கிறது.", - "notificationRuleRoomServerAcl": "அறை சேவையக ACL", - "notificationRuleRoomServerAclDescription": "அறை சேவையக அணுகல் கட்டுப்பாட்டு பட்டியல்களுக்கு (ACL) அறிவிப்புகளை தடுக்கிறது.", - "notificationRuleSuppressEdits": "தொகுப்புகளை தடுக்கவும்", - "notificationRuleSuppressEditsDescription": "தொகுக்கப்பட்ட செய்திகளுக்கான அறிவிப்புகளை தடுக்கிறது.", - "notificationRuleCall": "அழைப்பு", - "notificationRuleCallDescription": "பயனருக்கு அழைப்புகள் பற்றி அறிவிக்கிறது.", - "notificationRuleEncryptedRoomOneToOne": "குறியாக்கப்பட்ட அறை ஒன்று-க்கு-ஒரு", - "notificationRuleEncryptedRoomOneToOneDescription": "குறியாக்கப்பட்ட ஒன்று-க்கு-ஒரு அறைகளில் உள்ள செய்திகளைக் குறித்து பயனருக்கு அறிவிக்கிறது.", - "notificationRuleRoomOneToOne": "அறை ஒன்று-க்கு-ஒரு", - "notificationRuleRoomOneToOneDescription": "ஒரு-க்கு-ஒரு அறைகளில் உள்ள செய்திகளைக் குறித்து பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleRoomnotif": {}, + "notificationRuleRoomnotifDescription": "ஒரு செய்தியில் '@ROOM' இருக்கும்போது பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleTombstone": "கல்லறை", + "@notificationRuleTombstone": {}, + "notificationRuleTombstoneDescription": "அறை செயலிழக்கச் செய்திகளைப் பற்றி பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleReaction": "எதிர்வினை", + "@notificationRuleReaction": {}, + "notificationRuleReactionDescription": "எதிர்வினைகளுக்கான அறிவிப்புகளை அடக்குகிறது.", + "@notificationRuleReactionDescription": {}, + "notificationRuleRoomServerAcl": "அறை சேவையகம் ACL", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomServerAclDescription": "அறை சேவையக அணுகல் கட்டுப்பாட்டு பட்டியல்களுக்கான (ACL) அறிவிப்புகளை அடக்குகிறது.", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleSuppressEdits": "திருத்தங்களை அடக்கவும்", + "@notificationRuleSuppressEdits": {}, + "notificationRuleSuppressEditsDescription": "திருத்தப்பட்ட செய்திகளுக்கான அறிவிப்புகளை அடக்குகிறது.", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleCall": "அழை", + "@notificationRuleCall": {}, + "notificationRuleCallDescription": "அழைப்புகள் பற்றி பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "மறைகுறியாக்கப்பட்ட அறை ஒன்றுக்கு ஒன்று", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "மறைகுறியாக்கப்பட்ட ஒன்றுக்கு ஒன்று அறைகளில் செய்திகளைப் பற்றி பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleRoomOneToOne": "அறை ஒன்று", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleRoomOneToOneDescription": "ஒருவருக்கு ஒன்று அறைகளில் செய்திகளைப் பற்றி பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleRoomOneToOneDescription": {}, "notificationRuleMessage": "செய்தி", - "notificationRuleMessageDescription": "பொது செய்திகளுக்கான அறிவிப்புகளை வழங்குகிறது.", + "@notificationRuleMessage": {}, + "notificationRuleMessageDescription": "பொதுவான செய்திகளைப் பற்றி பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleMessageDescription": {}, "notificationRuleEncrypted": "குறியாக்கப்பட்டது", - "notificationRuleEncryptedDescription": "குறியாக்கப்பட்ட அறைகளில் உள்ள செய்திகளைக் குறித்து பயனருக்கு அறிவிக்கிறது.", - "notificationRuleJitsi": "ஜிட்சி", - "notificationRuleJitsiDescription": "ஜிட்சி விகித்டு நிகழ்வுகள் பற்றி பயனருக்கு அறிவிக்கிறது.", - "notificationRuleServerAcl": "சேவையக ACL நிகழ்வுகளை தடுக்கவும்", - "notificationRuleServerAclDescription": "சேவையக ACL நிகழ்வுகளுக்கான அறிவிப்புகளை தடுக்கிறது.", - "unknownPushRule": "அறியப்படாத புஷ் விதி '{rule}'", - "sentVoiceMessage": "🎙️ {duration} - {sender} இல் இருந்து குரல் செய்தி", - "deletePushRuleCanNotBeUndone": "இந்த அறிவிப்பு அமைப்பை நீக்கினால், அதை மீட்டமைக்க முடியாது.", + "@notificationRuleEncrypted": {}, + "notificationRuleEncryptedDescription": "மறைகுறியாக்கப்பட்ட அறைகளில் செய்திகளைப் பற்றி பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsi": "சியோட்ச்", + "@notificationRuleJitsi": {}, + "notificationRuleJitsiDescription": "சிட்சி விட்செட் நிகழ்வுகளைப் பற்றி பயனருக்கு அறிவிக்கிறது.", + "@notificationRuleJitsiDescription": {}, + "notificationRuleServerAcl": "சேவையக ACL நிகழ்வுகளை அடக்கவும்", + "@notificationRuleServerAcl": {}, + "notificationRuleServerAclDescription": "சேவையக ACL நிகழ்வுகளுக்கான அறிவிப்புகளை அடக்குகிறது.", + "@notificationRuleServerAclDescription": {}, + "unknownPushRule": "அறியப்படாத புச் விதி '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "sentVoiceMessage": "🎙️{duration} - {sender} இலிருந்து குரல் செய்தி", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "இந்த அறிவிப்பு அமைப்பை நீங்கள் நீக்கினால், இதை செயல்தவிர்க்க முடியாது.", + "@deletePushRuleCanNotBeUndone": {}, "more": "மேலும்", - "shareKeysWith": "முக்கியங்களை பகிரவும்...", - "shareKeysWithDescription": "எந்த சாதனங்கள் நம்பிக்கையுடன் இருக்க வேண்டும், அவை உங்கள் குறியாக்கப்பட்ட உரையாடல்களில் உங்கள் செய்திகள் படிக்க முடியுமா?", - "allDevices": "அனைத்து சாதனங்கள்", - "crossVerifiedDevicesIfEnabled": "இணையம் சரிபார்க்கப்பட்ட சாதனங்கள் இயலுமையாக இருந்தால்", - "crossVerifiedDevices": "இணையம் சரிபார்க்கப்பட்ட சாதனங்கள்", - "verifiedDevicesOnly": "சரிபார்க்கப்பட்ட சாதனங்கள் மட்டும்", - "takeAPhoto": "படம் எடு", - "recordAVideo": "வீடியோ பதிவு செய்", - "optionalMessage": "(விருப்பமான) செய்தி...", + "@more": {}, + "shareKeysWith": "விசைகளைப் பகிரவும் ...", + "@shareKeysWith": {}, + "shareKeysWithDescription": "மறைகுறியாக்கப்பட்ட அரட்டைகளில் உங்கள் செய்திகளுடன் படிக்க எந்த சாதனங்களை நம்ப வேண்டும்?", + "@shareKeysWithDescription": {}, + "allDevices": "அனைத்து சாதனங்களும்", + "@allDevices": {}, + "crossVerifiedDevicesIfEnabled": "இயக்கப்பட்டால் சரிபார்க்கப்பட்ட சாதனங்களை குறுக்கு", + "@crossVerifiedDevicesIfEnabled": {}, + "crossVerifiedDevices": "குறுக்கு சரிபார்க்கப்பட்ட சாதனங்கள்", + "@crossVerifiedDevices": {}, + "verifiedDevicesOnly": "சரிபார்க்கப்பட்ட சாதனங்கள் மட்டுமே", + "@verifiedDevicesOnly": {}, + "takeAPhoto": "புகைப்படம் எடுக்கவும்", + "@takeAPhoto": {}, + "recordAVideo": "வீடியோவைப் பதிவுசெய்க", + "@recordAVideo": {}, + "optionalMessage": "(விரும்பினால்) செய்தி ...", + "@optionalMessage": {}, "notSupportedOnThisDevice": "இந்த சாதனத்தில் ஆதரிக்கப்படவில்லை", - "enterNewChat": "புதிய உரையாடலில் நுழையவும்", - "approve": "அனுமதி அளி", - "youHaveKnocked": "நீங்கள் தட்டினீர்கள்", - "pleaseWaitUntilInvited": "தயவுசெய்து காத்திருக்கவும், அறையில் இருந்து யாரும் உங்களை அழைக்கும்வரை.", - "commandHint_logout": "உங்கள் தற்போதைய சாதனத்தை வெளியேறவும்", - "commandHint_logoutall": "அனைத்து செயல்படும் சாதனங்களிலிருந்தும் வெளியேறவும்", - "displayNavigationRail": "மொபைலில் வழிசெலுத்தல் ரெய்லை காட்டவும்", + "@notSupportedOnThisDevice": {}, + "enterNewChat": "புதிய அரட்டையை உள்ளிடவும்", + "@enterNewChat": {}, + "approve": "ஒப்புதல்", + "@approve": {}, + "youHaveKnocked": "நீங்கள் தட்டிவிட்டீர்கள்", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "அறையில் இருந்து யாராவது உங்களை அழைக்கும் வரை தயவுசெய்து இப்போது காத்திருங்கள்.", + "@pleaseWaitUntilInvited": {}, + "commandHint_logout": "உங்கள் தற்போதைய சாதனத்தை பதிவு செய்க", + "@commandHint_logout": {}, + "commandHint_logoutall": "அனைத்து செயலில் உள்ள சாதனங்களையும் அனுப்புகிறது", + "@commandHint_logoutall": {}, + "displayNavigationRail": "மொபைலில் வழிசெலுத்தல் ரெயிலைக் காட்டு", + "@displayNavigationRail": {}, "customReaction": "தனிப்பயன் எதிர்வினை", + "@customReaction": {}, + "moreEvents": "மேலும் நிகழ்வுகள்", + "@moreEvents": {}, + "declineInvitation": "அழைப்பை நிராகரிக்கவும்", + "@declineInvitation": {}, "ignore": "தடுக்கும்", "ignoredUsers": "தடுக்கும் பயனர்கள்", "writeAMessageLangCodes": "{l1} அல்லது {l2} இல் எழுதவும்...", @@ -4382,361 +4498,6 @@ "playWithAI": "தற்காலிகமாக AI உடன் விளையாடவும்", "courseStartDesc": "பங்கேயா பாட்டி எப்போதும் தயாராக உள்ளது!\n\n...ஆனால் நண்பர்களுடன் கற்றல் சிறந்தது!", "@@locale": "ta", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, - "@previous": { - "type": "String", - "placeholders": {} - }, - "@otherPartyNotLoggedIn": { - "type": "String", - "placeholders": {} - }, - "@appWantsToUseForLogin": { - "type": "String", - "placeholders": { - "server": { - "type": "String" - } - } - }, - "@appWantsToUseForLoginDescription": { - "type": "String", - "placeholders": {} - }, - "@open": { - "type": "String", - "placeholders": {} - }, - "@waitingForServer": { - "type": "String", - "placeholders": {} - }, - "@appIntroduction": { - "type": "String", - "placeholders": {} - }, - "@newChatRequest": { - "type": "String", - "placeholders": {} - }, - "@contentNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@generalNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@roomNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@userSpecificNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@otherNotificationSettings": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsUserNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMaster": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMasterDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNotices": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressNoticesDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMe": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleInviteForMeDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEvent": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMemberEventDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsUserMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayName": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleContainsDisplayNameDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMention": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleIsRoomMentionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotif": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomnotifDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstone": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleTombstoneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReaction": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleReactionDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEdits": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleSuppressEditsDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCall": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleCallDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOne": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleRoomOneToOneDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessage": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleMessageDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncrypted": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleEncryptedDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsi": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleJitsiDescription": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAcl": { - "type": "String", - "placeholders": {} - }, - "@notificationRuleServerAclDescription": { - "type": "String", - "placeholders": {} - }, - "@unknownPushRule": { - "type": "String", - "placeholders": { - "rule": { - "type": "String" - } - } - }, - "@sentVoiceMessage": { - "type": "String", - "placeholders": { - "duration": { - "type": "String" - }, - "sender": { - "type": "String" - } - } - }, - "@deletePushRuleCanNotBeUndone": { - "type": "String", - "placeholders": {} - }, - "@more": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWith": { - "type": "String", - "placeholders": {} - }, - "@shareKeysWithDescription": { - "type": "String", - "placeholders": {} - }, - "@allDevices": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevicesIfEnabled": { - "type": "String", - "placeholders": {} - }, - "@crossVerifiedDevices": { - "type": "String", - "placeholders": {} - }, - "@verifiedDevicesOnly": { - "type": "String", - "placeholders": {} - }, - "@takeAPhoto": { - "type": "String", - "placeholders": {} - }, - "@recordAVideo": { - "type": "String", - "placeholders": {} - }, - "@optionalMessage": { - "type": "String", - "placeholders": {} - }, - "@notSupportedOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@enterNewChat": { - "type": "String", - "placeholders": {} - }, - "@approve": { - "type": "String", - "placeholders": {} - }, - "@youHaveKnocked": { - "type": "String", - "placeholders": {} - }, - "@pleaseWaitUntilInvited": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logout": { - "type": "String", - "placeholders": {} - }, - "@commandHint_logoutall": { - "type": "String", - "placeholders": {} - }, - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@ignore": { "type": "String", "placeholders": {} @@ -11289,4 +11050,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index f31433634..68c2a4747 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1,5 +1,5 @@ { - "alwaysUse24HourFormat": "తప్పుడు", + "alwaysUse24HourFormat": "", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -573,7 +573,6 @@ "report": "నివేదించండి", "signInWithPassword": "పాస్వర్డ్‌తో సైన్ ఇన్ చేయండి", "pleaseTryAgainLaterOrChooseDifferentServer": "దయచేసి తర్వాత ప్రయత్నించండి లేదా వేరే సర్వర్‌ను ఎంచుకోండి.", - "signInWith": "{provider} తో సైన్ ఇన్ చేయండి", "profileNotFound": "సర్వర్‌పై వినియోగదారు కనుగొనబడలేదు. కనెక్షన్ సమస్య ఉండవచ్చు లేదా వినియోగదారు ఉండకపోవచ్చు.", "setTheme": "థీమ్ సెట్ చేయండి:", "setColorTheme": "రంగు థీమ్ సెట్ చేయండి:", @@ -4623,14 +4622,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -5041,9 +5032,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12154,4 +12143,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index f1b74bf55..8f202a7bd 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -267,10 +267,7 @@ "type": "String", "placeholders": {} }, - "@jumpToLastReadMessage": { - "type": "String", - "placeholders": {} - }, + "@jumpToLastReadMessage": {}, "@allRooms": { "type": "String", "placeholders": {} @@ -283,14 +280,8 @@ "type": "String", "placeholders": {} }, - "@widgetVideo": { - "type": "String", - "placeholders": {} - }, - "@dismiss": { - "type": "String", - "placeholders": {} - }, + "@widgetVideo": {}, + "@dismiss": {}, "@unknownDevice": { "type": "String", "placeholders": {} @@ -303,10 +294,7 @@ "type": "String", "placeholders": {} }, - "@reportErrorDescription": { - "type": "String", - "placeholders": {} - }, + "@reportErrorDescription": {}, "@directChats": { "type": "String", "placeholders": {} @@ -323,10 +311,7 @@ } } }, - "@addAccount": { - "type": "String", - "placeholders": {} - }, + "@addAccount": {}, "@close": { "type": "String", "placeholders": {} @@ -335,10 +320,7 @@ "type": "String", "placeholders": {} }, - "@chatHasBeenAddedToThisSpace": { - "type": "String", - "placeholders": {} - }, + "@chatHasBeenAddedToThisSpace": {}, "@reply": { "type": "String", "placeholders": {} @@ -351,10 +333,7 @@ "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersion": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersion": {}, "@device": { "type": "String", "placeholders": {} @@ -367,10 +346,7 @@ "type": "String", "description": "Usage hint for the command /html" }, - "@widgetJitsi": { - "type": "String", - "placeholders": {} - }, + "@widgetJitsi": {}, "@youAreNoLongerParticipatingInThisChat": { "type": "String", "placeholders": {} @@ -379,26 +355,14 @@ "type": "String", "placeholders": {} }, - "@messageType": { - "type": "String", - "placeholders": {} - }, - "@indexedDbErrorLong": { - "type": "String", - "placeholders": {} - }, - "@oneClientLoggedOut": { - "type": "String", - "placeholders": {} - }, + "@messageType": {}, + "@indexedDbErrorLong": {}, + "@oneClientLoggedOut": {}, "@toggleMuted": { "type": "String", "placeholders": {} }, - "@unsupportedAndroidVersionLong": { - "type": "String", - "placeholders": {} - }, + "@unsupportedAndroidVersionLong": {}, "@kicked": { "type": "String", "placeholders": { @@ -439,14 +403,8 @@ "type": "String", "placeholders": {} }, - "@startFirstChat": { - "type": "String", - "placeholders": {} - }, - "@callingAccount": { - "type": "String", - "placeholders": {} - }, + "@startFirstChat": {}, + "@callingAccount": {}, "@requestPermission": { "type": "String", "placeholders": {} @@ -474,14 +432,8 @@ } } }, - "@setColorTheme": { - "type": "String", - "placeholders": {} - }, - "@nextAccount": { - "type": "String", - "placeholders": {} - }, + "@setColorTheme": {}, + "@nextAccount": {}, "@commandHint_create": { "type": "String", "description": "Usage hint for the command /create" @@ -498,18 +450,12 @@ "type": "String", "placeholders": {} }, - "@allSpaces": { - "type": "String", - "placeholders": {} - }, + "@allSpaces": {}, "@editDisplayname": { "type": "String", "placeholders": {} }, - "@user": { - "type": "String", - "placeholders": {} - }, + "@user": {}, "@roomVersion": { "type": "String", "placeholders": {} @@ -526,10 +472,7 @@ "type": "String", "placeholders": {} }, - "@youAcceptedTheInvitation": { - "type": "String", - "placeholders": {} - }, + "@youAcceptedTheInvitation": {}, "@banFromChat": { "type": "String", "placeholders": {} @@ -561,8 +504,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@userIsTyping": { "type": "String", @@ -584,18 +526,12 @@ } } }, - "@banUserDescription": { - "type": "String", - "placeholders": {} - }, + "@banUserDescription": {}, "@inviteContact": { "type": "String", "placeholders": {} }, - "@widgetEtherpad": { - "type": "String", - "placeholders": {} - }, + "@widgetEtherpad": {}, "@waitingPartnerAcceptRequest": { "type": "String", "placeholders": {} @@ -612,10 +548,7 @@ "type": "String", "placeholders": {} }, - "@removeDevicesDescription": { - "type": "String", - "placeholders": {} - }, + "@removeDevicesDescription": {}, "@changedTheChatDescriptionTo": { "type": "String", "placeholders": { @@ -639,10 +572,7 @@ "type": "String", "placeholders": {} }, - "@tryAgain": { - "type": "String", - "placeholders": {} - }, + "@tryAgain": {}, "@blocked": { "type": "String", "placeholders": {} @@ -652,8 +582,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@dateWithoutYear": { "type": "String", @@ -670,10 +599,7 @@ "type": "String", "placeholders": {} }, - "@unbanUserDescription": { - "type": "String", - "placeholders": {} - }, + "@unbanUserDescription": {}, "@userAndUserAreTyping": { "type": "String", "placeholders": { @@ -697,18 +623,9 @@ "type": "String", "placeholders": {} }, - "@youRejectedTheInvitation": { - "type": "String", - "placeholders": {} - }, - "@otherCallingPermissions": { - "type": "String", - "placeholders": {} - }, - "@messagesStyle": { - "type": "String", - "placeholders": {} - }, + "@youRejectedTheInvitation": {}, + "@otherCallingPermissions": {}, + "@messagesStyle": {}, "@couldNotDecryptMessage": { "type": "String", "placeholders": { @@ -721,30 +638,12 @@ "type": "String", "placeholders": {} }, - "@link": { - "type": "String", - "placeholders": {} - }, - "@widgetUrlError": { - "type": "String", - "placeholders": {} - }, - "@emailOrUsername": { - "type": "String", - "placeholders": {} - }, - "@newSpaceDescription": { - "type": "String", - "placeholders": {} - }, - "@chatDescription": { - "type": "String", - "placeholders": {} - }, - "@callingAccountDetails": { - "type": "String", - "placeholders": {} - }, + "@link": {}, + "@widgetUrlError": {}, + "@emailOrUsername": {}, + "@newSpaceDescription": {}, + "@chatDescription": {}, + "@callingAccountDetails": {}, "@next": { "type": "String", "placeholders": {} @@ -779,14 +678,8 @@ "type": "String", "placeholders": {} }, - "@enterSpace": { - "type": "String", - "placeholders": {} - }, - "@encryptThisChat": { - "type": "String", - "placeholders": {} - }, + "@enterSpace": {}, + "@encryptThisChat": {}, "@fileName": { "type": "String", "placeholders": {} @@ -795,10 +688,7 @@ "type": "String", "placeholders": {} }, - "@previousAccount": { - "type": "String", - "placeholders": {} - }, + "@previousAccount": {}, "@publicRooms": { "type": "String", "placeholders": {} @@ -819,14 +709,8 @@ "type": "String", "placeholders": {} }, - "@reopenChat": { - "type": "String", - "placeholders": {} - }, - "@pleaseEnterRecoveryKey": { - "type": "String", - "placeholders": {} - }, + "@reopenChat": {}, + "@pleaseEnterRecoveryKey": {}, "@create": { "type": "String", "placeholders": {} @@ -839,10 +723,7 @@ "type": "String", "placeholders": {} }, - "@widgetNameError": { - "type": "String", - "placeholders": {} - }, + "@widgetNameError": {}, "@inoffensive": { "type": "String", "placeholders": {} @@ -851,10 +732,7 @@ "type": "String", "placeholders": {} }, - "@addToBundle": { - "type": "String", - "placeholders": {} - }, + "@addToBundle": {}, "@reportMessage": { "type": "String", "placeholders": {} @@ -863,10 +741,7 @@ "type": "String", "placeholders": {} }, - "@addWidget": { - "type": "String", - "placeholders": {} - }, + "@addWidget": {}, "@removeAllOtherDevices": { "type": "String", "placeholders": {} @@ -880,13 +755,9 @@ "count": { "type": "int" } - }, - "type": "String" - }, - "@noKeyForThisMessage": { - "type": "String", - "placeholders": {} + } }, + "@noKeyForThisMessage": {}, "@enableEncryptionWarning": { "type": "String", "placeholders": {} @@ -910,10 +781,7 @@ "type": "String", "placeholders": {} }, - "@commandHint_markasgroup": { - "type": "String", - "placeholders": {} - }, + "@commandHint_markasgroup": {}, "@errorObtainingLocation": { "type": "String", "placeholders": { @@ -922,38 +790,20 @@ } } }, - "@hydrateTor": { - "type": "String", - "placeholders": {} - }, - "@pushNotificationsNotAvailable": { - "type": "String", - "placeholders": {} - }, + "@hydrateTor": {}, + "@pushNotificationsNotAvailable": {}, "@passwordRecovery": { "type": "String", "placeholders": {} }, - "@storeInAppleKeyChain": { - "type": "String", - "placeholders": {} - }, + "@storeInAppleKeyChain": {}, "@replaceRoomWithNewerVersion": { "type": "String", "placeholders": {} }, - "@hydrate": { - "type": "String", - "placeholders": {} - }, - "@invalidServerName": { - "type": "String", - "placeholders": {} - }, - "@chatPermissions": { - "type": "String", - "placeholders": {} - }, + "@hydrate": {}, + "@invalidServerName": {}, + "@chatPermissions": {}, "@voiceMessage": { "type": "String", "placeholders": {} @@ -970,14 +820,8 @@ } } }, - "@sender": { - "type": "String", - "placeholders": {} - }, - "@storeInAndroidKeystore": { - "type": "String", - "placeholders": {} - }, + "@sender": {}, + "@storeInAndroidKeystore": {}, "@hideRedactedEvents": { "type": "String", "placeholders": {} @@ -986,10 +830,7 @@ "type": "String", "placeholders": {} }, - "@signInWithPassword": { - "type": "String", - "placeholders": {} - }, + "@signInWithPassword": {}, "@ignoredUsers": { "type": "String", "placeholders": {} @@ -1025,10 +866,7 @@ "type": "String", "placeholders": {} }, - "@makeAdminDescription": { - "type": "String", - "placeholders": {} - }, + "@makeAdminDescription": {}, "@loadMore": { "type": "String", "placeholders": {} @@ -1065,26 +903,17 @@ "type": "String", "placeholders": {} }, - "@saveKeyManuallyDescription": { - "type": "String", - "placeholders": {} - }, + "@saveKeyManuallyDescription": {}, "@none": { "type": "String", "placeholders": {} }, - "@editBundlesForAccount": { - "type": "String", - "placeholders": {} - }, + "@editBundlesForAccount": {}, "@enableEncryption": { "type": "String", "placeholders": {} }, - "@whyIsThisMessageEncrypted": { - "type": "String", - "placeholders": {} - }, + "@whyIsThisMessageEncrypted": {}, "@unreadChats": { "type": "String", "placeholders": { @@ -1101,10 +930,7 @@ } } }, - "@setChatDescription": { - "type": "String", - "placeholders": {} - }, + "@setChatDescription": {}, "@userLeftTheChat": { "type": "String", "placeholders": { @@ -1125,18 +951,12 @@ "type": "String", "placeholders": {} }, - "@dehydrateWarning": { - "type": "String", - "placeholders": {} - }, + "@dehydrateWarning": {}, "@sendOriginal": { "type": "String", "placeholders": {} }, - "@noOtherDevicesFound": { - "type": "String", - "placeholders": {} - }, + "@noOtherDevicesFound": {}, "@whoIsAllowedToJoinThisGroup": { "type": "String", "placeholders": {} @@ -1153,14 +973,8 @@ } } }, - "@storeSecurlyOnThisDevice": { - "type": "String", - "placeholders": {} - }, - "@yourChatBackupHasBeenSetUp": { - "type": "String", - "placeholders": {} - }, + "@storeSecurlyOnThisDevice": {}, + "@yourChatBackupHasBeenSetUp": {}, "@chatBackup": { "type": "String", "placeholders": {} @@ -1177,10 +991,7 @@ "type": "String", "placeholders": {} }, - "@videoCallsBetaWarning": { - "type": "String", - "placeholders": {} - }, + "@videoCallsBetaWarning": {}, "@unmuteChat": { "type": "String", "placeholders": {} @@ -1225,14 +1036,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@username": { "type": "String", "placeholders": {} @@ -1245,18 +1048,8 @@ } } }, - "@fileIsTooBigForServer": { - "type": "String", - "placeholders": { - "max": { - "type": "String" - } - } - }, - "@homeserver": { - "type": "String", - "placeholders": {} - }, + "@fileIsTooBigForServer": {}, + "@homeserver": {}, "@people": { "type": "String", "placeholders": {} @@ -1292,22 +1085,13 @@ } } }, - "@callingPermissions": { - "type": "String", - "placeholders": {} - }, + "@callingPermissions": {}, "@newMessageInFluffyChat": { "type": "String", "placeholders": {} }, - "@readUpToHere": { - "type": "String", - "placeholders": {} - }, - "@start": { - "type": "String", - "placeholders": {} - }, + "@readUpToHere": {}, + "@start": {}, "@downloadFile": { "type": "String", "placeholders": {} @@ -1320,10 +1104,7 @@ "type": "String", "placeholders": {} }, - "@unlockOldMessages": { - "type": "String", - "placeholders": {} - }, + "@unlockOldMessages": {}, "@identity": { "type": "String", "placeholders": {} @@ -1367,10 +1148,7 @@ "type": "String", "placeholders": {} }, - "@optionalRedactReason": { - "type": "String", - "placeholders": {} - }, + "@optionalRedactReason": {}, "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -1395,10 +1173,7 @@ "type": "String", "placeholders": {} }, - "@dehydrate": { - "type": "String", - "placeholders": {} - }, + "@dehydrate": {}, "@locationPermissionDeniedNotice": { "type": "String", "placeholders": {} @@ -1433,10 +1208,7 @@ "type": "String", "placeholders": {} }, - "@archiveRoomDescription": { - "type": "String", - "placeholders": {} - }, + "@archiveRoomDescription": {}, "@changedTheChatNameTo": { "type": "String", "placeholders": { @@ -1475,10 +1247,7 @@ "type": "String", "placeholders": {} }, - "@placeCall": { - "type": "String", - "placeholders": {} - }, + "@placeCall": {}, "@removedBy": { "type": "String", "placeholders": { @@ -1511,18 +1280,12 @@ "type": "String", "placeholders": {} }, - "@experimentalVideoCalls": { - "type": "String", - "placeholders": {} - }, + "@experimentalVideoCalls": {}, "@openCamera": { "type": "String", "placeholders": {} }, - "@pleaseEnterRecoveryKeyDescription": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterRecoveryKeyDescription": {}, "@guestsAreForbidden": { "type": "String", "placeholders": {} @@ -1539,13 +1302,7 @@ "type": "String", "placeholders": {} }, - "@inviteContactToGroupQuestion": { - "type": "String", - "placeholders": { - "contact": {}, - "groupName": {} - } - }, + "@inviteContactToGroupQuestion": {}, "@emoteExists": { "type": "String", "placeholders": {} @@ -1570,8 +1327,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@group": { "type": "String", @@ -1585,18 +1341,12 @@ "type": "String", "placeholders": {} }, - "@appearOnTopDetails": { - "type": "String", - "placeholders": {} - }, + "@appearOnTopDetails": {}, "@roomHasBeenUpgraded": { "type": "String", "placeholders": {} }, - "@enterRoom": { - "type": "String", - "placeholders": {} - }, + "@enterRoom": {}, "@enableEmotesGlobally": { "type": "String", "placeholders": {} @@ -1617,10 +1367,7 @@ } } }, - "@reportUser": { - "type": "String", - "placeholders": {} - }, + "@reportUser": {}, "@sharedTheLocation": { "type": "String", "placeholders": { @@ -1648,10 +1395,7 @@ } } }, - "@confirmEventUnpin": { - "type": "String", - "placeholders": {} - }, + "@confirmEventUnpin": {}, "@badServerVersionsException": { "type": "String", "placeholders": { @@ -1668,8 +1412,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@kickedAndBanned": { "type": "String", @@ -1711,18 +1454,12 @@ }, "description": "State that {command} is not a valid /command." }, - "@redactMessageDescription": { - "type": "String", - "placeholders": {} - }, + "@redactMessageDescription": {}, "@rejoin": { "type": "String", "placeholders": {} }, - "@recoveryKey": { - "type": "String", - "placeholders": {} - }, + "@recoveryKey": {}, "@redactMessage": { "type": "String", "placeholders": {} @@ -1735,10 +1472,7 @@ "type": "String", "description": "Usage hint for the command /discardsession" }, - "@invalidInput": { - "type": "String", - "placeholders": {} - }, + "@invalidInput": {}, "@chooseAStrongPassword": { "type": "String", "placeholders": {} @@ -1747,10 +1481,7 @@ "type": "String", "placeholders": {} }, - "@dehydrateTorLong": { - "type": "String", - "placeholders": {} - }, + "@dehydrateTorLong": {}, "@yourPublicKey": { "type": "String", "placeholders": {} @@ -1786,14 +1517,8 @@ "type": "String", "placeholders": {} }, - "@doNotShowAgain": { - "type": "String", - "placeholders": {} - }, - "@report": { - "type": "String", - "placeholders": {} - }, + "@doNotShowAgain": {}, + "@report": {}, "@status": { "type": "String", "placeholders": {} @@ -1818,30 +1543,15 @@ "type": "String", "placeholders": {} }, - "@unverified": { - "type": "String", - "placeholders": {} - }, + "@unverified": {}, "@howOffensiveIsThisContent": { "type": "String", "placeholders": {} }, - "@serverRequiresEmail": { - "type": "String", - "placeholders": {} - }, - "@hideUnimportantStateEvents": { - "type": "String", - "placeholders": {} - }, - "@screenSharingTitle": { - "type": "String", - "placeholders": {} - }, - "@widgetCustom": { - "type": "String", - "placeholders": {} - }, + "@serverRequiresEmail": {}, + "@hideUnimportantStateEvents": {}, + "@screenSharingTitle": {}, + "@widgetCustom": {}, "@sentCallInformations": { "type": "String", "placeholders": { @@ -1850,17 +1560,13 @@ } } }, - "@addToSpaceDescription": { - "type": "String", - "placeholders": {} - }, + "@addToSpaceDescription": {}, "@youBannedUser": { "placeholders": { "user": { "type": "String" } - }, - "type": "String" + } }, "@theyDontMatch": { "type": "String", @@ -1907,21 +1613,11 @@ "user": { "type": "String" } - }, - "type": "String" - }, - "@publish": { - "type": "String", - "placeholders": {} - }, - "@openLinkInBrowser": { - "type": "String", - "placeholders": {} - }, - "@clearArchive": { - "type": "String", - "placeholders": {} + } }, + "@publish": {}, + "@openLinkInBrowser": {}, + "@clearArchive": {}, "@commandHint_react": { "type": "String", "description": "Usage hint for the command /react" @@ -1942,18 +1638,9 @@ "type": "String", "placeholders": {} }, - "@messageInfo": { - "type": "String", - "placeholders": {} - }, - "@disableEncryptionWarning": { - "type": "String", - "placeholders": {} - }, - "@directChat": { - "type": "String", - "placeholders": {} - }, + "@messageInfo": {}, + "@disableEncryptionWarning": {}, + "@directChat": {}, "@encryptionNotEnabled": { "type": "String", "placeholders": {} @@ -1970,34 +1657,19 @@ "type": "String", "placeholders": {} }, - "@inviteGroupChat": { - "type": "String", - "placeholders": {} - }, - "@appearOnTop": { - "type": "String", - "placeholders": {} - }, - "@invitePrivateChat": { - "type": "String", - "placeholders": {} - }, + "@inviteGroupChat": {}, + "@appearOnTop": {}, + "@invitePrivateChat": {}, "@verifyTitle": { "type": "String", "placeholders": {} }, - "@foregroundServiceRunning": { - "type": "String", - "placeholders": {} - }, + "@foregroundServiceRunning": {}, "@enterAnEmailAddress": { "type": "String", "placeholders": {} }, - "@voiceCall": { - "type": "String", - "placeholders": {} - }, + "@voiceCall": {}, "@commandHint_kick": { "type": "String", "description": "Usage hint for the command /kick" @@ -2034,18 +1706,12 @@ } } }, - "@noChatDescriptionYet": { - "type": "String", - "placeholders": {} - }, + "@noChatDescriptionYet": {}, "@defaultPermissionLevel": { "type": "String", "placeholders": {} }, - "@removeFromBundle": { - "type": "String", - "placeholders": {} - }, + "@removeFromBundle": {}, "@numUsersTyping": { "type": "String", "placeholders": { @@ -2062,10 +1728,7 @@ "type": "String", "placeholders": {} }, - "@learnMore": { - "type": "String", - "placeholders": {} - }, + "@learnMore": {}, "@iHaveClickedOnLink": { "type": "String", "placeholders": {} @@ -2074,38 +1737,17 @@ "type": "String", "placeholders": {} }, - "@users": { - "type": "String", - "placeholders": {} - }, - "@openGallery": { - "type": "String", - "placeholders": {} - }, - "@chatDescriptionHasBeenChanged": { - "type": "String", - "placeholders": {} - }, + "@users": {}, + "@openGallery": {}, + "@chatDescriptionHasBeenChanged": {}, "@search": { "type": "String", "placeholders": {} }, - "@newGroup": { - "type": "String", - "placeholders": {} - }, - "@bundleName": { - "type": "String", - "placeholders": {} - }, - "@dehydrateTor": { - "type": "String", - "placeholders": {} - }, - "@removeFromSpace": { - "type": "String", - "placeholders": {} - }, + "@newGroup": {}, + "@bundleName": {}, + "@dehydrateTor": {}, + "@removeFromSpace": {}, "@dateAndTimeOfDay": { "type": "String", "placeholders": { @@ -2129,10 +1771,7 @@ "type": "String", "placeholders": {} }, - "@roomUpgradeDescription": { - "type": "String", - "placeholders": {} - }, + "@roomUpgradeDescription": {}, "@commandHint_invite": { "type": "String", "description": "Usage hint for the command /invite" @@ -2148,18 +1787,12 @@ } } }, - "@scanQrCode": { - "type": "String", - "placeholders": {} - }, + "@scanQrCode": {}, "@logout": { "type": "String", "placeholders": {} }, - "@pleaseEnterANumber": { - "type": "String", - "placeholders": {} - }, + "@pleaseEnterANumber": {}, "@contactHasBeenInvitedToTheGroup": { "type": "String", "placeholders": {} @@ -2169,8 +1802,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@changedTheJoinRules": { "type": "String", @@ -2180,14 +1812,8 @@ } } }, - "@profileNotFound": { - "type": "String", - "placeholders": {} - }, - "@jump": { - "type": "String", - "placeholders": {} - }, + "@profileNotFound": {}, + "@jump": {}, "@groups": { "type": "String", "placeholders": {} @@ -2214,10 +1840,7 @@ } } }, - "@sorryThatsNotPossible": { - "type": "String", - "placeholders": {} - }, + "@sorryThatsNotPossible": {}, "@videoWithSize": { "type": "String", "placeholders": { @@ -2238,18 +1861,9 @@ } } }, - "@shareInviteLink": { - "type": "String", - "placeholders": {} - }, - "@commandHint_markasdm": { - "type": "String", - "placeholders": {} - }, - "@recoveryKeyLost": { - "type": "String", - "placeholders": {} - }, + "@shareInviteLink": {}, + "@commandHint_markasdm": {}, + "@recoveryKeyLost": {}, "@containsUserName": { "type": "String", "placeholders": {} @@ -2262,10 +1876,7 @@ "type": "String", "placeholders": {} }, - "@deviceKeys": { - "type": "String", - "placeholders": {} - }, + "@deviceKeys": {}, "@waitingPartnerNumbers": { "type": "String", "placeholders": {} @@ -2318,18 +1929,12 @@ "type": "String", "placeholders": {} }, - "@setTheme": { - "type": "String", - "placeholders": {} - }, + "@setTheme": {}, "@changeTheHomeserver": { "type": "String", "placeholders": {} }, - "@youJoinedTheChat": { - "type": "String", - "placeholders": {} - }, + "@youJoinedTheChat": {}, "@wallpaper": { "type": "String", "placeholders": {} @@ -2366,18 +1971,12 @@ "type": "String", "placeholders": {} }, - "@markAsRead": { - "type": "String", - "placeholders": {} - }, + "@markAsRead": {}, "@sendAudio": { "type": "String", "placeholders": {} }, - "@widgetName": { - "type": "String", - "placeholders": {} - }, + "@widgetName": {}, "@sentASticker": { "type": "String", "placeholders": { @@ -2386,10 +1985,7 @@ } } }, - "@errorAddingWidget": { - "type": "String", - "placeholders": {} - }, + "@errorAddingWidget": {}, "@commandHint_dm": { "type": "String", "description": "Usage hint for the command /dm" @@ -2415,8 +2011,7 @@ "user": { "type": "String" } - }, - "type": "String" + } }, "@deactivateAccountWarning": { "type": "String", @@ -2438,10 +2033,7 @@ "type": "String", "placeholders": {} }, - "@newSpace": { - "type": "String", - "placeholders": {} - }, + "@newSpace": {}, "@changePassword": { "type": "String", "placeholders": {} @@ -2458,10 +2050,7 @@ } } }, - "@emojis": { - "type": "String", - "placeholders": {} - }, + "@emojis": {}, "@pleaseEnterYourPin": { "type": "String", "placeholders": {} @@ -2474,14 +2063,8 @@ "type": "String", "placeholders": {} }, - "@pleaseTryAgainLaterOrChooseDifferentServer": { - "type": "String", - "placeholders": {} - }, - "@createGroup": { - "type": "String", - "placeholders": {} - }, + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "@createGroup": {}, "@privacy": { "type": "String", "placeholders": {} @@ -2494,14 +2077,8 @@ "type": "String", "placeholders": {} }, - "@hydrateTorLong": { - "type": "String", - "placeholders": {} - }, - "@time": { - "type": "String", - "placeholders": {} - }, + "@hydrateTorLong": {}, + "@time": {}, "@enterYourHomeserver": { "type": "String", "placeholders": {} @@ -2514,14 +2091,8 @@ "type": "String", "placeholders": {} }, - "@custom": { - "type": "String", - "placeholders": {} - }, - "@noBackupWarning": { - "type": "String", - "placeholders": {} - }, + "@custom": {}, + "@noBackupWarning": {}, "@fromJoining": { "type": "String", "placeholders": {} @@ -2534,18 +2105,9 @@ "type": "String", "placeholders": {} }, - "@storeInSecureStorageDescription": { - "type": "String", - "placeholders": {} - }, - "@openChat": { - "type": "String", - "placeholders": {} - }, - "@kickUserDescription": { - "type": "String", - "placeholders": {} - }, + "@storeInSecureStorageDescription": {}, + "@openChat": {}, + "@kickUserDescription": {}, "@sendAMessage": { "type": "String", "placeholders": {} @@ -2558,34 +2120,19 @@ "type": "String", "placeholders": {} }, - "@pinMessage": { - "type": "String", - "placeholders": {} - }, - "@screenSharingDetail": { - "type": "String", - "placeholders": {} - }, + "@pinMessage": {}, + "@screenSharingDetail": {}, "@muteChat": { "type": "String", "placeholders": {} }, - "@invite": { - "type": "String", - "placeholders": {} - }, - "@enableMultiAccounts": { - "type": "String", - "placeholders": {} - }, + "@invite": {}, + "@enableMultiAccounts": {}, "@emotePacks": { "type": "String", "placeholders": {} }, - "@indexedDbErrorTitle": { - "type": "String", - "placeholders": {} - }, + "@indexedDbErrorTitle": {}, "@endedTheCall": { "type": "String", "placeholders": { @@ -3109,7 +2656,6 @@ "report": "รายงาน", "signInWithPassword": "เข้าสู่ระบบด้วยรหัสผ่าน", "pleaseTryAgainLaterOrChooseDifferentServer": "โปรดลองอีกครั้งในภายหลังหรือเลือกเซิร์ฟเวอร์ที่แตกต่างกัน", - "signInWith": "เข้าสู่ระบบด้วย {provider}", "profileNotFound": "ไม่พบผู้ใช้บนเซิร์ฟเวอร์ อาจมีปัญหาในการเชื่อมต่อหรือผู้ใช้นั้นไม่มีอยู่จริง", "setTheme": "ตั้งธีม:", "setColorTheme": "ตั้งธีมสี:", @@ -5015,9 +4561,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12123,4 +11667,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 1ba922127..9afa85af9 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -2510,15 +2510,6 @@ "@signInWithPassword": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Lütfen daha sonra tekrar deneyin veya farklı bir sunucu seçin.", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "{provider} ile oturum aç", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Bir resim dosyası değil.", "@notAnImage": {}, "importNow": "Şimdi içe aktar", @@ -3178,14 +3169,36 @@ }, "welcomeText": "Hey Hey 👋 Karşınızda FluffyChat. https://matrix.org ile uyumlu herhangi bir homeserver'a giriş yapabilirsiniz. Ve herkesle konuşabilirsiniz. Bu koca bir merkeziyetsiz mesajlaşma ağı!", "@welcomeText": {}, - "setCustomPermissionLevel": "Özel izin seviyesi ayarla", - "setPermissionsLevelDescription": "Lütfen aşağıdaki önceden tanımlanmış rolden birini seçin veya 0 ile 100 arasında özel bir izin seviyesi girin.", - "ignoreUser": "Kullanıcıyı görmezden gel", + "setCustomPermissionLevel": "Özel izin düzeyi ayarla", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Lütfen aşağıdan önceden tanımlanmış bir rol seçin veya 0 ile 100 arasında bir özel izin seviyesi girin.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Kullanıcıyı yok say", + "@ignoreUser": {}, "normalUser": "Normal kullanıcı", - "commandHint_roomupgrade": "Bu odanın sürümünü verilen oda sürümüne yükselt", + "@normalUser": {}, + "commandHint_roomupgrade": "Bu odayı belirtilen oda sürümüne yükseltin", + "@commandHint_roomupgrade": {}, "checkList": "Kontrol listesi", + "@checkList": {}, "countInvited": "{count} davet edildi", - "synchronizingPleaseWaitCounter": "Senkronize ediliyor… ({percentage}%)", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "synchronizingPleaseWaitCounter": " Senkronize ediliyor… ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, "previous": "Önceki", "otherPartyNotLoggedIn": "Diğer taraf şu anda giriş yapmadı ve bu nedenle mesaj alamaz!", "appWantsToUseForLogin": "'{server}' kullanarak giriş yap", @@ -4388,46 +4401,6 @@ "inviteYourFriends": "Arkadaşlarınızı davet edin", "playWithAI": "Şimdilik yapay zeka ile oynayın", "courseStartDesc": "Pangea Bot her zaman hazır!\n\n...ama arkadaşlarınızla öğrenmek daha iyidir!", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, - "@checkList": { - "type": "String", - "placeholders": {} - }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, - "@synchronizingPleaseWaitCounter": { - "type": "String", - "placeholders": { - "percentage": { - "type": "String" - } - } - }, "@previous": { "type": "String", "placeholders": {} @@ -11287,4 +11260,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index ea9946ec9..478a0645a 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -782,7 +782,7 @@ "type": "String", "placeholders": {} }, - "noEmotesFound": "Емоджі не знайдено. 😕", + "noEmotesFound": "Емодзі не знайдено. 😕", "@noEmotesFound": { "type": "String", "placeholders": {} @@ -1247,7 +1247,7 @@ "type": "String", "placeholders": {} }, - "waitingPartnerEmoji": "Очікування прийняття емоджі партнером…", + "waitingPartnerEmoji": "Очікування прийняття емодзі партнером…", "@waitingPartnerEmoji": { "type": "String", "placeholders": {} @@ -1327,12 +1327,12 @@ "type": "String", "placeholders": {} }, - "enableEmotesGlobally": "Увімкнути пакунок емоджі глобально", + "enableEmotesGlobally": "Увімкнути набір емодзі глобально", "@enableEmotesGlobally": { "type": "String", "placeholders": {} }, - "emotePacks": "Набори емоджі для кімнати", + "emotePacks": "Набори емодзі для кімнати", "@emotePacks": { "type": "String", "placeholders": {} @@ -1545,7 +1545,7 @@ "type": "String", "placeholders": {} }, - "setCustomEmotes": "Установити користувацькі емоджі", + "setCustomEmotes": "Встановити власні емодзі", "@setCustomEmotes": { "type": "String", "placeholders": {} @@ -1736,7 +1736,7 @@ "type": "String", "placeholders": {} }, - "autoplayImages": "Автоматично відтворювати анімовані наліпки та емоджі", + "autoplayImages": "Автоматично відтворювати анімовані наліпки та емодзі", "@autoplayImages": { "type": "String", "placeholder": {} @@ -1820,7 +1820,7 @@ }, "chatHasBeenAddedToThisSpace": "Бесіду додано до цього простору", "@chatHasBeenAddedToThisSpace": {}, - "chatBackupDescription": "Ваші старі повідомлення захищені ключем відновлення. Переконайтеся, що ви не втратите його.", + "chatBackupDescription": "Ваші повідомлення захищені ключем відновлення. Переконайтеся, що ви не втратите його.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -2188,11 +2188,11 @@ } } }, - "emojis": "Емоджі", + "emojis": "Емодзі", "@emojis": {}, "pinMessage": "Прикріпити в кімнаті", "@pinMessage": {}, - "confirmEventUnpin": "Ви впевнені, що бажаєте назавжди відкріпите подію?", + "confirmEventUnpin": "Ви впевнені, що бажаєте назавжди відкріпити подію?", "@confirmEventUnpin": {}, "placeCall": "Здійснити виклик", "@placeCall": {}, @@ -2510,15 +2510,6 @@ "@pleaseTryAgainLaterOrChooseDifferentServer": {}, "signInWithPassword": "Увійти за допомогою пароля", "@signInWithPassword": {}, - "signInWith": "Увійти через {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Не файл зображення.", "@notAnImage": {}, "importNow": "Імпортувати зараз", @@ -2592,9 +2583,9 @@ "@redactMessageDescription": {}, "setChatDescription": "Налаштувати опис бесіди", "@setChatDescription": {}, - "inviteGroupChat": "📨 Запросити до групової бесіди", + "inviteGroupChat": "📨 Запрошення до групової бесіди", "@inviteGroupChat": {}, - "invitePrivateChat": "📨 Запросити до приватної бесіди", + "invitePrivateChat": "📨 Запрошення до приватної бесіди", "@invitePrivateChat": {}, "emoteKeyboardNoRecents": "Тут з'являться нещодавно використані смайлики...", "@emoteKeyboardNoRecents": { @@ -2879,9 +2870,9 @@ "@accessAndVisibilityDescription": {}, "calls": "Виклики", "@calls": {}, - "customEmojisAndStickers": "Власні емоджі та наліпки", + "customEmojisAndStickers": "Власні емодзі та наліпки", "@customEmojisAndStickers": {}, - "customEmojisAndStickersBody": "Додавайте або діліться власними емоджі або наліпками, які можна використовувати в будь-якій бесіді.", + "customEmojisAndStickersBody": "Додавайте або діліться власними емодзі або наліпками, які можна використовувати в будь-якій бесіді.", "@customEmojisAndStickersBody": {}, "createNewAddress": "Створити нову адресу", "@createNewAddress": {}, @@ -3022,7 +3013,7 @@ }, "markAsUnread": "Позначити непрочитаним", "@markAsUnread": {}, - "alwaysUse24HourFormat": "ні", + "alwaysUse24HourFormat": "вимкнено", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -3248,7 +3239,7 @@ "@notificationRuleMemberEvent": {}, "notificationRuleMemberEventDescription": "Забороняє сповіщення про події учасників.", "@notificationRuleMemberEventDescription": {}, - "notificationRuleSuppressNoticesDescription": "Забороняє сповіщення від автоматизованих клієнтів, таких як боти.", + "notificationRuleSuppressNoticesDescription": "Забороняє сповіщення від автоматизованих клієнтів, як-от боти.", "@notificationRuleSuppressNoticesDescription": {}, "notificationRuleIsUserMention": "Згадки користувачів", "@notificationRuleIsUserMention": {}, @@ -3351,7 +3342,7 @@ "@approve": {}, "youHaveKnocked": "Ви постукали", "@youHaveKnocked": {}, - "sentVoiceMessage": "🎙️ {duration} - {sender} - Голосове повідомлення від {sender}", + "sentVoiceMessage": "🎙️Голосове повідомлення від {sender} ({duration})", "@sentVoiceMessage": { "type": "String", "placeholders": { @@ -3378,8 +3369,144 @@ "@commandHint_logout": {}, "commandHint_logoutall": "Вийти на всіх активних пристроях", "@commandHint_logoutall": {}, - "displayNavigationRail": "Показати панель навігації на мобільному", - "customReaction": "Користувацька реакція", + "displayNavigationRail": "Показати навігаційну карту на мобільному", + "@displayNavigationRail": {}, + "customReaction": "Власні реакції", + "@customReaction": {}, + "moreEvents": "Інші події", + "@moreEvents": {}, + "declineInvitation": "Відхилити запрошення", + "@declineInvitation": {}, + "noMessagesYet": "Поки немає повідомлень", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Довге натискання, щоби записати голосове повідомлення.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Призупинити", + "@pause": {}, + "resume": "Продовжити", + "@resume": {}, + "newSubSpace": "Новий вкладений простір", + "@newSubSpace": {}, + "moveToDifferentSpace": "Перемістити в інший простір", + "@moveToDifferentSpace": {}, + "moveUp": "Перемістити вище", + "@moveUp": {}, + "moveDown": "Переместити нижче", + "@moveDown": {}, + "removeFromSpaceDescription": "Бесіду буде видалено з простору, та вона залишиться у вашому списку бесід.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} бесід", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "Учасник {spaces} просторів", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Учасник просторів {spaces} може постукати", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Задонатити", + "@donate": {}, + "startedAPoll": "Нове опитування від {username}.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "Опитування", + "@poll": {}, + "startPoll": "Розпочати опитування", + "@startPoll": {}, + "endPoll": "Завершити опитування", + "@endPoll": {}, + "answersVisible": "Публічні відповіді", + "@answersVisible": {}, + "answersHidden": "Приховані відповіді", + "@answersHidden": {}, + "pollQuestion": "Запитання", + "@pollQuestion": {}, + "answerOption": "Варіант відповіді", + "@answerOption": {}, + "addAnswerOption": "Додати варіант відповіді", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Дозволити декілька варіантів відповіді", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "Опитування завершилось", + "@pollHasBeenEnded": {}, + "skipChatBackupWarning": "Ви впевнені? Без резервного копіювання бесід ви можете втратити доступ до повідомлень, якщо ви зміните пристрій.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Завантажуються повідомлення", + "@loadingMessages": {}, + "setupChatBackup": "Налаштувати резервне копіювання бесід", + "@setupChatBackup": {}, + "replyInThread": "Відповісти у вітці", + "@replyInThread": {}, + "saveChanges": "Зберегти зміни", + "@saveChanges": {}, + "createSticker": "Створити наліпку чи емодзі", + "@createSticker": {}, + "useAsSticker": "Використати як наліпку", + "@useAsSticker": {}, + "useAsEmoji": "Використати як емодзі", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Набір наліпок з такою назвою вже є", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Новий набір наліпок", + "@newStickerPack": {}, + "stickerPackName": "Назва набору наліпок", + "@stickerPackName": {}, + "attribution": "Атрибуція", + "@attribution": {}, + "skipChatBackup": "Пропустити резервне копіювання бесід", + "@skipChatBackup": {}, + "thread": "Вітка", + "@thread": {}, + "backToMainChat": "Повернутись до основної бесіди", + "@backToMainChat": {}, + "answersWillBeVisibleWhenPollHasEnded": "Відповіді стануть публічними після завершення опитування", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "countVotes": "{count, plural, =1{Один голос} other{{count} голосів}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "countReplies": "{count, plural, =1{Одна відповідь} other{{count} відповідей}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "changedTheChatDescription": "{username} змінює опис бесіди", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} змінює назву бесіди", + "@changedTheChatName": {}, "writeAMessageLangCodes": "Введіть {l1} або {l2}...", "requests": "Запити", "holdForInfo": "Натисніть і утримуйте для інформації про слово.", @@ -3421,7 +3548,6 @@ "updateLanguage": "Мої мови", "whatLanguageYouWantToLearn": "Яку мову ви хочете вивчити?", "whatIsYourBaseLanguage": "Яка ваша базова мова?", - "saveChanges": "Зберегти зміни", "publicProfileTitle": "Дозволити знаходити мій профіль у пошуку", "publicProfileDesc": "Увімкнувши цю опцію, ви дозволяєте іншим користувачам знаходити ваш профіль у глобальній пошуковій стрічці та надсилати запити на чат. На цьому етапі ви можете прийняти або відхилити запит.", "errorDisableIT": "Допомога з перекладом вимкнена.", @@ -4507,14 +4633,6 @@ "inviteYourFriends": "Запросіть своїх друзів", "playWithAI": "Поки що грайте з ШІ", "courseStartDesc": "Пангей Бот готовий до роботи в будь-який час!\n\n...але навчання краще з друзями!", - "@displayNavigationRail": { - "type": "String", - "placeholders": {} - }, - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4690,10 +4808,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11059,4 +11173,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_uz.arb b/lib/l10n/intl_uz.arb new file mode 100644 index 000000000..dc6080d51 --- /dev/null +++ b/lib/l10n/intl_uz.arb @@ -0,0 +1,3497 @@ +{ + "repeatPassword": "Parolni takrorlang", + "@repeatPassword": {}, + "normalUser": "Oddiy foydalanuvchi", + "@normalUser": {}, + "alwaysUse24HourFormat": "true", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "notAnImage": "Rasm fayli emas.", + "@notAnImage": {}, + "setCustomPermissionLevel": "Maxsus ruxsatlar darajasini sozlash", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Quyidagi oldindan belgilangan rolni tanlang yoki 0-100 orasidagi maxsus ruxsatlar darajasini kiriting.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Foydalanuvchini e’tiborsiz qoldirish", + "@ignoreUser": {}, + "remove": "O‘chirish", + "@remove": { + "type": "String", + "placeholders": {} + }, + "importNow": "Hozir import qilish", + "@importNow": {}, + "importEmojis": "Emojilarni import qilish", + "@importEmojis": {}, + "importFromZipFile": ".zip faylidan import qilish", + "@importFromZipFile": {}, + "exportEmotePack": "Emotsiyalar to‘plamini .zip fayl ko‘rinishida eksport qilish", + "@exportEmotePack": {}, + "replace": "Almashtirmoq", + "@replace": {}, + "about": "Biz haqimizda", + "@about": {}, + "aboutHomeserver": "{homeserver} haqida", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "accept": "Qabul qilmoq", + "@accept": { + "type": "String", + "placeholders": {} + }, + "acceptedTheInvitation": "👍 {username} taklifni qabul qildi", + "@acceptedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "account": "Hisob", + "@account": { + "type": "String", + "placeholders": {} + }, + "activatedEndToEndEncryption": "🔐 {username} shifrlashni yakunlash uchun faollashtirdi", + "@activatedEndToEndEncryption": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "addEmail": "Email qo‘shish", + "@addEmail": { + "type": "String", + "placeholders": {} + }, + "confirmMatrixId": "Hisobingizni o‘chirish uchun Matrix ID hisobingizni tasdiqlang.", + "@confirmMatrixId": {}, + "supposedMxid": "Bu {mxid} bo‘lishi kerak", + "@supposedMxid": { + "type": "String", + "placeholders": { + "mxid": { + "type": "String" + } + } + }, + "addChatDescription": "Suhbat tavsifini kiriting...", + "@addChatDescription": {}, + "addToSpace": "Maydonga qo‘shish", + "@addToSpace": {}, + "admin": "Admin", + "@admin": { + "type": "String", + "placeholders": {} + }, + "alias": "taxallus", + "@alias": { + "type": "String", + "placeholders": {} + }, + "all": "Hammasi", + "@all": { + "type": "String", + "placeholders": {} + }, + "allChats": "Hamma suhbatlar", + "@allChats": { + "type": "String", + "placeholders": {} + }, + "commandHint_roomupgrade": "Bu guruhni berilgan guruh versiyasiga yangilang", + "@commandHint_roomupgrade": {}, + "commandHint_googly": "G‘ilay ko‘zlarini yuboring", + "@commandHint_googly": {}, + "commandHint_cuddle": "Erkalash yuborish", + "@commandHint_cuddle": {}, + "commandHint_hug": "Quchoqlash yuborish", + "@commandHint_hug": {}, + "googlyEyesContent": "{senderName} sizga gʻilay ko‘zlarini yubormoqda", + "@googlyEyesContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "cuddleContent": "{senderName} sizni erkalamoqda", + "@cuddleContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "hugContent": "{senderName} sizni quchoqlamoqda", + "@hugContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "answeredTheCall": "{senderName} chaqiruvga javob berdi", + "@answeredTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "anyoneCanJoin": "Har kim qo‘shilishi mumkin", + "@anyoneCanJoin": { + "type": "String", + "placeholders": {} + }, + "appLock": "Ilova qulfi", + "@appLock": { + "type": "String", + "placeholders": {} + }, + "appLockDescription": "PIN kod ishlatilmayotganida ilovani qulflash", + "@appLockDescription": {}, + "archive": "Arxiv", + "@archive": { + "type": "String", + "placeholders": {} + }, + "areGuestsAllowedToJoin": "Mehmon foydalanuvchilarga qo‘shilishga ruxsat berilganmi", + "@areGuestsAllowedToJoin": { + "type": "String", + "placeholders": {} + }, + "areYouSure": "Ishonchingiz komilmi?", + "@areYouSure": { + "type": "String", + "placeholders": {} + }, + "areYouSureYouWantToLogout": "Haqiqatan ham hisobingizdan chiqamoqchimisiz?", + "@areYouSureYouWantToLogout": { + "type": "String", + "placeholders": {} + }, + "askSSSSSign": "Narigi foydalanuvchini imzolash uchun xavfsiz do‘kon parol iborasi yoki tiklash kalitini kiriting.", + "@askSSSSSign": { + "type": "String", + "placeholders": {} + }, + "askVerificationRequest": "{username}dan ushbu tasdiqlash so‘rovi qabul qilinsinmi?", + "@askVerificationRequest": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "autoplayImages": "Animatsiyali stikerlar va emojilarni avtomatik ijro etish", + "@autoplayImages": { + "type": "String", + "placeholder": {} + }, + "sendTypingNotifications": "Yozish bildirishnomalarini yuborish", + "@sendTypingNotifications": {}, + "swipeRightToLeftToReply": "Javob berish uchun o‘ngdan chapga suring", + "@swipeRightToLeftToReply": {}, + "badServerLoginTypesException": "Homeserver quyidagi kirish turlarini qo'llab-quvvatlaydi:\n{serverVersions}\nLekin bu ilova faqat quyidagi turlarni qo'llab-quvvatlaydi:\n{supportedVersions}", + "@badServerLoginTypesException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "sendOnEnter": "Enterda yuborish", + "@sendOnEnter": {}, + "badServerVersionsException": "Homeserver quyidagi Spec versiyalarini qo'llab-quvvatlaydi:\n{serverVersions}\nLekin bu ilova faqat {supportedVersions} versiyalarini qo'llab-quvvatlaydi", + "@badServerVersionsException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "countChatsAndCountParticipants": "{chats} suhbatlar va {participants} ishtirokchilar", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noMoreChatsFound": "Boshqa chatlar topilmadi...", + "@noMoreChatsFound": {}, + "noChatsFoundHere": "Bu yerda hali chat topilmadi. Quyidagi tugmadan foydalanib, kimdir bilan yangi suhbat boshlang. ⤵️", + "@noChatsFoundHere": {}, + "joinedChats": "Qo'shilgan suhbatlar", + "@joinedChats": {}, + "unread": "Oʻqilmagan", + "@unread": {}, + "space": "Boʻshliq", + "@space": {}, + "spaces": "Boʻshliqlar", + "@spaces": {}, + "banFromChat": "Suhbatdan taqiqlash", + "@banFromChat": { + "type": "String", + "placeholders": {} + }, + "banned": "Taqiqlangan", + "@banned": { + "type": "String", + "placeholders": {} + }, + "bannedUser": "{username} taqiqladi {targetName}(ni)", + "@bannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "blockDevice": "Qurilmani bloklash", + "@blockDevice": { + "type": "String", + "placeholders": {} + }, + "blocked": "Bloklandi", + "@blocked": { + "type": "String", + "placeholders": {} + }, + "botMessages": "Bot xabarlari", + "@botMessages": { + "type": "String", + "placeholders": {} + }, + "cancel": "Bekor qilish", + "@cancel": { + "type": "String", + "placeholders": {} + }, + "cantOpenUri": "{uri} URIni ochib boʻlmadi", + "@cantOpenUri": { + "type": "String", + "placeholders": { + "uri": { + "type": "String" + } + } + }, + "changeDeviceName": "Qurilma nomini oʻzgartirish", + "@changeDeviceName": { + "type": "String", + "placeholders": {} + }, + "changedTheChatAvatar": "{username} suhbat avatarini oʻzgartirdi", + "@changedTheChatAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheChatDescriptionTo": "{username} suhbat tavsifini '{description}'ga oʻzgartirdi", + "@changedTheChatDescriptionTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "description": { + "type": "String" + } + } + }, + "changedTheChatNameTo": "{username} suhbat nomini: '{chatname}'ga oʻzgartirdi", + "@changedTheChatNameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "chatname": { + "type": "String" + } + } + }, + "changedTheChatPermissions": "{username} suhbat ruxsatnomalarini oʻzgartirdi", + "@changedTheChatPermissions": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheDisplaynameTo": "{username} oʻzining nomini '{displayname}'ga oʻzgartirdi", + "@changedTheDisplaynameTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "displayname": { + "type": "String" + } + } + }, + "changedTheGuestAccessRules": "{username} mehmon kirish qoidalarini oʻzgartirdi", + "@changedTheGuestAccessRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheGuestAccessRulesTo": "{username} mehmon kirish qoidalarini: {rules}ga oʻzgartirdi", + "@changedTheGuestAccessRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, + "changedTheHistoryVisibility": "{username} tarix koʻrinishini oʻzgartirdi", + "@changedTheHistoryVisibility": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheHistoryVisibilityTo": "{username} tarix koʻrinishini: {rules}ga oʻzgartirdi", + "@changedTheHistoryVisibilityTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "rules": { + "type": "String" + } + } + }, + "changedTheJoinRules": "{username} qoʻshilish qoidalarini oʻzgartirdi", + "@changedTheJoinRules": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheJoinRulesTo": "{username} qoʻshilish qoidalarini: {joinRules}ga oʻzgartirdi", + "@changedTheJoinRulesTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "joinRules": { + "type": "String" + } + } + }, + "changedTheProfileAvatar": "{username} avatarini oʻzgartirdi", + "@changedTheProfileAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changedTheRoomInvitationLink": "{username} taklif havolasini oʻzgartirdi", + "@changedTheRoomInvitationLink": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "changePassword": "Parolni o‘zgartirish", + "@changePassword": { + "type": "String", + "placeholders": {} + }, + "changeTheHomeserver": "Homeserverni almashtirish", + "@changeTheHomeserver": { + "type": "String", + "placeholders": {} + }, + "changeTheme": "Uslubingizni o‘zgartiring", + "@changeTheme": { + "type": "String", + "placeholders": {} + }, + "changeTheNameOfTheGroup": "Guruh nomini o‘zgartirish", + "@changeTheNameOfTheGroup": { + "type": "String", + "placeholders": {} + }, + "changeYourAvatar": "Avataringizni almashtiring", + "@changeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "channelCorruptedDecryptError": "Shifrlash buzilgan", + "@channelCorruptedDecryptError": { + "type": "String", + "placeholders": {} + }, + "chat": "Suhbat", + "@chat": { + "type": "String", + "placeholders": {} + }, + "yourChatBackupHasBeenSetUp": "Suhbat zaxirangiz sozlandi.", + "@yourChatBackupHasBeenSetUp": {}, + "chatBackup": "Suhbat zaxirasi", + "@chatBackup": { + "type": "String", + "placeholders": {} + }, + "chatBackupDescription": "Eski xabarlaringiz tiklash kaliti bilan himoyalangan. Uni yo‘qotib qo‘ymasligingizga ishonch hosil qiling.", + "@chatBackupDescription": { + "type": "String", + "placeholders": {} + }, + "chatDetails": "Suhbat tafsilotlari", + "@chatDetails": { + "type": "String", + "placeholders": {} + }, + "chatHasBeenAddedToThisSpace": "Suhbat bu maydonga kiritildi", + "@chatHasBeenAddedToThisSpace": {}, + "changedTheRoomAliases": "{username} xona taxalluslarini oʻzgartirdi", + "@changedTheRoomAliases": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "chooseAStrongPassword": "Kuchli parol tanlang", + "@chooseAStrongPassword": { + "type": "String", + "placeholders": {} + }, + "clearArchive": "Arxivni tozalash", + "@clearArchive": {}, + "close": "Yopish", + "@close": { + "type": "String", + "placeholders": {} + }, + "commandHint_markasdm": "Matrix identifikatorini berish uchun shaxsiy xabar guruhi sifatida belgilang", + "@commandHint_markasdm": {}, + "commandHint_markasgroup": "Guruh sifatida belgilash", + "@commandHint_markasgroup": {}, + "commandHint_ban": "Bu guruhdan berilgan foydalanuvchini bloklash", + "@commandHint_ban": { + "type": "String", + "description": "Usage hint for the command /ban" + }, + "commandHint_clearcache": "Kesh tozalash", + "@commandHint_clearcache": { + "type": "String", + "description": "Usage hint for the command /clearcache" + }, + "commandHint_create": "Boʻsh guruh suhbati yarating\nShifrlashni oʻchirish uchun --no-encryption dan foydalaning", + "@commandHint_create": { + "type": "String", + "description": "Usage hint for the command /create" + }, + "commandHint_discardsession": "Seansni bekor qilish", + "@commandHint_discardsession": { + "type": "String", + "description": "Usage hint for the command /discardsession" + }, + "commandHint_dm": "Jonli suhbatni boshlash\nShifrlashni o‘chirish uchun --no-encryption dan foydalaning", + "@commandHint_dm": { + "type": "String", + "description": "Usage hint for the command /dm" + }, + "commandHint_html": "HTML formatidagi matnni yuborish", + "@commandHint_html": { + "type": "String", + "description": "Usage hint for the command /html" + }, + "commandHint_invite": "Berilgan foydalanuvchini ushbu guruhga taklif qiling", + "@commandHint_invite": { + "type": "String", + "description": "Usage hint for the command /invite" + }, + "commandHint_join": "Berilgan guruhga qoʻshilish", + "@commandHint_join": { + "type": "String", + "description": "Usage hint for the command /join" + }, + "commandHint_kick": "Berilgan foydalanuvchini guruhdan oʻchirish", + "@commandHint_kick": { + "type": "String", + "description": "Usage hint for the command /kick" + }, + "commandHint_leave": "Guruhni tark etish", + "@commandHint_leave": { + "type": "String", + "description": "Usage hint for the command /leave" + }, + "commandHint_me": "Oʻzingizni tariflang", + "@commandHint_me": { + "type": "String", + "description": "Usage hint for the command /me" + }, + "commandHint_myroomavatar": "Bu guruh uchun rasmingizni sozlang (mxc-uri tomonidan)", + "@commandHint_myroomavatar": { + "type": "String", + "description": "Usage hint for the command /myroomavatar" + }, + "commandHint_myroomnick": "Bu guruh uchun displey nomini sozlang", + "@commandHint_myroomnick": { + "type": "String", + "description": "Usage hint for the command /myroomnick" + }, + "commandHint_op": "Berilgan foydalanuvchi quvvat darajasini oʻrnating (standart: 50)", + "@commandHint_op": { + "type": "String", + "description": "Usage hint for the command /op" + }, + "commandHint_plain": "Formatlanmagan matnni yuboring", + "@commandHint_plain": { + "type": "String", + "description": "Usage hint for the command /plain" + }, + "commandHint_react": "Javobni reaksiya sifatida yuboring", + "@commandHint_react": { + "type": "String", + "description": "Usage hint for the command /react" + }, + "commandHint_send": "Matn yuborish", + "@commandHint_send": { + "type": "String", + "description": "Usage hint for the command /send" + }, + "commandHint_unban": "Berilgan foydalanuvchini bu guruhdan blokdan chiqazish", + "@commandHint_unban": { + "type": "String", + "description": "Usage hint for the command /unban" + }, + "commandInvalid": "Buyruq yaroqsiz", + "@commandInvalid": { + "type": "String" + }, + "commandMissing": "{command} komanda emas.", + "@commandMissing": { + "type": "String", + "placeholders": { + "command": { + "type": "String" + } + }, + "description": "State that {command} is not a valid /command." + }, + "compareEmojiMatch": "Iltimos emojilarni taqqoslang", + "@compareEmojiMatch": { + "type": "String", + "placeholders": {} + }, + "compareNumbersMatch": "Iltimos raqamlarni taqqoslang", + "@compareNumbersMatch": { + "type": "String", + "placeholders": {} + }, + "configureChat": "Suhbatni sozlash", + "@configureChat": { + "type": "String", + "placeholders": {} + }, + "confirm": "Tasdiqlash", + "@confirm": { + "type": "String", + "placeholders": {} + }, + "connect": "Ulanish", + "@connect": { + "type": "String", + "placeholders": {} + }, + "contactHasBeenInvitedToTheGroup": "Kontakt guruhga taklif qilindi", + "@contactHasBeenInvitedToTheGroup": { + "type": "String", + "placeholders": {} + }, + "containsDisplayName": "Displey nomni oʻz ichiga oladi", + "@containsDisplayName": { + "type": "String", + "placeholders": {} + }, + "containsUserName": "Foydalanuvchi nomini oʻz ichiga oladi", + "@containsUserName": { + "type": "String", + "placeholders": {} + }, + "contentHasBeenReported": "Kontent server administratorlariga xabar qilindi", + "@contentHasBeenReported": { + "type": "String", + "placeholders": {} + }, + "chats": "Suhbatlar", + "@chats": { + "type": "String", + "placeholders": {} + }, + "copiedToClipboard": "Buferga nusxalandi", + "@copiedToClipboard": { + "type": "String", + "placeholders": {} + }, + "copy": "Nusxalash", + "@copy": { + "type": "String", + "placeholders": {} + }, + "copyToClipboard": "Buferga nusxalash", + "@copyToClipboard": { + "type": "String", + "placeholders": {} + }, + "couldNotDecryptMessage": "Xabarni shifrlab boʻlmadi: {error}", + "@couldNotDecryptMessage": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "checkList": "Tekshirish roʻyxati", + "@checkList": {}, + "countParticipants": "{count} qatnashuvchilar", + "@countParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "countInvited": "{count} taklif qilindi", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "create": "Yaratish", + "@create": { + "type": "String", + "placeholders": {} + }, + "createdTheChat": "💬 {username} suhbat yaratdi", + "@createdTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "createGroup": "Guruh yaratish", + "@createGroup": {}, + "createNewSpace": "Yangi maydon", + "@createNewSpace": { + "type": "String", + "placeholders": {} + }, + "currentlyActive": "Hozirda faol", + "@currentlyActive": { + "type": "String", + "placeholders": {} + }, + "darkTheme": "Qorongʻi", + "@darkTheme": { + "type": "String", + "placeholders": {} + }, + "dateAndTimeOfDay": "{date}, {timeOfDay}", + "@dateAndTimeOfDay": { + "type": "String", + "placeholders": { + "date": { + "type": "String" + }, + "timeOfDay": { + "type": "String" + } + } + }, + "dateWithoutYear": "{month}-{day}", + "@dateWithoutYear": { + "type": "String", + "placeholders": { + "month": { + "type": "String" + }, + "day": { + "type": "String" + } + } + }, + "dateWithYear": "{year}-{month}-{day}", + "@dateWithYear": { + "type": "String", + "placeholders": { + "year": { + "type": "String" + }, + "month": { + "type": "String" + }, + "day": { + "type": "String" + } + } + }, + "deactivateAccountWarning": "Bu sizning foydalanuvchi hisobingizni oʻchirib qoʻyadi. Buni qaytarib boʻlmaydi! Ishonchingiz komilmi?", + "@deactivateAccountWarning": { + "type": "String", + "placeholders": {} + }, + "defaultPermissionLevel": "Yangi foydalanuvchilar uchun standart ruxsat darajasi", + "@defaultPermissionLevel": { + "type": "String", + "placeholders": {} + }, + "delete": "Oʻchirish", + "@delete": { + "type": "String", + "placeholders": {} + }, + "deleteAccount": "Hisobni oʻchirish", + "@deleteAccount": { + "type": "String", + "placeholders": {} + }, + "deleteMessage": "Xabarni oʻchirish", + "@deleteMessage": { + "type": "String", + "placeholders": {} + }, + "device": "Qurilma", + "@device": { + "type": "String", + "placeholders": {} + }, + "deviceId": "Qurilma ID", + "@deviceId": { + "type": "String", + "placeholders": {} + }, + "devices": "Qurilmalar", + "@devices": { + "type": "String", + "placeholders": {} + }, + "directChats": "Shaxsiy suhbatlar", + "@directChats": { + "type": "String", + "placeholders": {} + }, + "allRooms": "Barcha guruh suhbatlar", + "@allRooms": { + "type": "String", + "placeholders": {} + }, + "emotePacks": "Guruh uchun Emote toʻplamlar", + "@emotePacks": { + "type": "String", + "placeholders": {} + }, + "emoteSettings": "Emote Sozlamalari", + "@emoteSettings": { + "type": "String", + "placeholders": {} + }, + "globalChatId": "Ommaviy suhbat IDʼsi", + "@globalChatId": {}, + "accessAndVisibility": "Kirish va koʻrinish", + "@accessAndVisibility": {}, + "accessAndVisibilityDescription": "Bu suhbatga kim qoʻshilishi mumkin va suhbatni qanday topish mumkin.", + "@accessAndVisibilityDescription": {}, + "calls": "Qoʻngʻiroqlar", + "@calls": {}, + "customEmojisAndStickers": "Maxsus emojilar va stikerlar", + "@customEmojisAndStickers": {}, + "customEmojisAndStickersBody": "Istalgan suhbatda ishlatilishi mumkin boʻlgan maxsus emojilar yoki stikerlarni qoʻshing yoki ulashing.", + "@customEmojisAndStickersBody": {}, + "emoteShortcode": "Emote qisqa kodi", + "@emoteShortcode": { + "type": "String", + "placeholders": {} + }, + "emoteWarnNeedToPick": "Siz emote qisqa kodi va rasmni tanlashingiz kerak!", + "@emoteWarnNeedToPick": { + "type": "String", + "placeholders": {} + }, + "emptyChat": "Boʻsh suhbat", + "@emptyChat": { + "type": "String", + "placeholders": {} + }, + "enableEmotesGlobally": "Emote paketini global miqyosda yoqish", + "@enableEmotesGlobally": { + "type": "String", + "placeholders": {} + }, + "enableEncryption": "Shifrlashni yoqish", + "@enableEncryption": { + "type": "String", + "placeholders": {} + }, + "enableEncryptionWarning": "Siz endi shifrlashni oʻchira olmaysiz. Ishonchingiz komilmi?", + "@enableEncryptionWarning": { + "type": "String", + "placeholders": {} + }, + "encrypted": "Shifrlangan", + "@encrypted": { + "type": "String", + "placeholders": {} + }, + "encryption": "Shifrlash", + "@encryption": { + "type": "String", + "placeholders": {} + }, + "fluffychat": "FluffyChat", + "@fluffychat": { + "type": "String", + "placeholders": {} + }, + "fontSize": "Shrift oʻlchami", + "@fontSize": { + "type": "String", + "placeholders": {} + }, + "forward": "Yuborvorish", + "@forward": { + "type": "String", + "placeholders": {} + }, + "fromJoining": "Qoʻshilishdan", + "@fromJoining": { + "type": "String", + "placeholders": {} + }, + "fromTheInvitation": "Taklifnomadan", + "@fromTheInvitation": { + "type": "String", + "placeholders": {} + }, + "goToTheNewRoom": "Yangi guruhga oʻtish", + "@goToTheNewRoom": { + "type": "String", + "placeholders": {} + }, + "group": "Guruh", + "@group": { + "type": "String", + "placeholders": {} + }, + "chatDescription": "Suhbat tavsifi", + "@chatDescription": {}, + "chatDescriptionHasBeenChanged": "Suhbat tavsifi oʻzgartirildi", + "@chatDescriptionHasBeenChanged": {}, + "groupIsPublic": "Guruh ommaviy", + "@groupIsPublic": { + "type": "String", + "placeholders": {} + }, + "groups": "Guruhlar", + "@groups": { + "type": "String", + "placeholders": {} + }, + "groupWith": "{displayname} bilan guruh", + "@groupWith": { + "type": "String", + "placeholders": { + "displayname": { + "type": "String" + } + } + }, + "guestsAreForbidden": "Mehmonlarga kirish taqiqlangan", + "@guestsAreForbidden": { + "type": "String", + "placeholders": {} + }, + "guestsCanJoin": "Mehmonlar qoʻshila oladi", + "@guestsCanJoin": { + "type": "String", + "placeholders": {} + }, + "hasWithdrawnTheInvitationFor": "{username} {targetName} uchun taklifnomani qaytarib oldi", + "@hasWithdrawnTheInvitationFor": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "help": "Yordam", + "@help": { + "type": "String", + "placeholders": {} + }, + "hideRedactedEvents": "Tahrirlangan tadbirlarni yashirish", + "@hideRedactedEvents": { + "type": "String", + "placeholders": {} + }, + "hideRedactedMessages": "Oʻchirilgan xabarlarni yashirish", + "@hideRedactedMessages": {}, + "hideRedactedMessagesBody": "Agar kimdir xabarni oʻchirsa, bu xabar endi suhbatda koʻrinmaydi.", + "@hideRedactedMessagesBody": {}, + "hideInvalidOrUnknownMessageFormats": "Notoʻgʻri yoki nomaʼlum xabar formatlarini yashirish", + "@hideInvalidOrUnknownMessageFormats": {}, + "howOffensiveIsThisContent": "Bu kontent qanchalik haqoratli?", + "@howOffensiveIsThisContent": { + "type": "String", + "placeholders": {} + }, + "id": "ID", + "@id": { + "type": "String", + "placeholders": {} + }, + "block": "Bloklash", + "@block": {}, + "blockedUsers": "Bloklangan foydalanuvchilar", + "@blockedUsers": {}, + "blockListDescription": "Sizni bezovta qilayotgan foydalanuvchilarni bloklashingiz mumkin. Shaxsiy bloklash roʻyxatingizdagi foydalanuvchilardan hech qanday xabar yoki guruhga taklifnomalarni qabul qila olmaysiz.", + "@blockListDescription": {}, + "blockUsername": "Foydalanuvchi nomini eʻtiborsiz qoldirish", + "@blockUsername": {}, + "iHaveClickedOnLink": "Men havolani bosdim", + "@iHaveClickedOnLink": { + "type": "String", + "placeholders": {} + }, + "incorrectPassphraseOrKey": "Notoʻgʻri parol yoki tiklash kaliti", + "@incorrectPassphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "inoffensive": "Zararsiz", + "@inoffensive": { + "type": "String", + "placeholders": {} + }, + "inviteContact": "Kontaktni taklif qilish", + "@inviteContact": { + "type": "String", + "placeholders": {} + }, + "inviteContactToGroupQuestion": "{contact} ni \"{groupName}\" suhbatiga taklif qilishni istaysizmi?", + "@inviteContactToGroupQuestion": {}, + "inviteContactToGroup": "Kontaktni {groupName} ga taklif qiling", + "@inviteContactToGroup": { + "type": "String", + "placeholders": { + "groupName": { + "type": "String" + } + } + }, + "noChatDescriptionYet": "Hali suhbat tavsifi yaratilmagan.", + "@noChatDescriptionYet": {}, + "tryAgain": "Qayta urinib koʻrish", + "@tryAgain": {}, + "invalidServerName": "Server nomi notoʻgʻri", + "@invalidServerName": {}, + "invited": "Taklif qilindi", + "@invited": { + "type": "String", + "placeholders": {} + }, + "redactMessageDescription": "Xabar ushbu suhbatdagi barcha ishtirokchilar uchun oʻchiriladi. Buni bekor qilib boʻlmaydi.", + "@redactMessageDescription": {}, + "optionalRedactReason": "(Ixtiyoriy) Ushbu xabarni oʻchirish sababi...", + "@optionalRedactReason": {}, + "invitedUser": "📩 {username} {targetName}ni taklif qildi", + "@invitedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "invitedUsersOnly": "Faqat taklif qilingan foydalanuvchilar", + "@invitedUsersOnly": { + "type": "String", + "placeholders": {} + }, + "inviteForMe": "Men uchun taklif qilish", + "@inviteForMe": { + "type": "String", + "placeholders": {} + }, + "inviteText": "{username} sizni FluffyChat’ga taklif qildi.\n1. fluffychat.im saytiga tashrif buyuring va ilovani oʻrnating.\n2. Roʻyxatdan oʻting yoki tizimga kiring.\n3. Taklif havolasini oching:\n{link}", + "@inviteText": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "link": { + "type": "String" + } + } + }, + "isTyping": "yozmoqda…", + "@isTyping": { + "type": "String", + "placeholders": {} + }, + "displaynameHasBeenChanged": "Displey nomi o‘zgartirildi", + "@displaynameHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "downloadFile": "Faylni yuklab olish", + "@downloadFile": { + "type": "String", + "placeholders": {} + }, + "edit": "Tahrirlash", + "@edit": { + "type": "String", + "placeholders": {} + }, + "editBlockedServers": "Bloklangan serverlarni tahrirlash", + "@editBlockedServers": { + "type": "String", + "placeholders": {} + }, + "chatPermissions": "Suhbat ruxsatlari", + "@chatPermissions": {}, + "editDisplayname": "Displey nomini tahrirlash", + "@editDisplayname": { + "type": "String", + "placeholders": {} + }, + "editRoomAliases": "Xona taxalluslarini tahrirlash", + "@editRoomAliases": { + "type": "String", + "placeholders": {} + }, + "editRoomAvatar": "Xona avatarini tahrirlash", + "@editRoomAvatar": { + "type": "String", + "placeholders": {} + }, + "emoteExists": "Emotsiya allaqachon mavjud!", + "@emoteExists": { + "type": "String", + "placeholders": {} + }, + "emoteInvalid": "Noto‘g‘ri emotsiya kodi!", + "@emoteInvalid": { + "type": "String", + "placeholders": {} + }, + "emoteKeyboardNoRecents": "Yaqinda ishlatilgan emotsiyalar shu yerda chiqadi...", + "@emoteKeyboardNoRecents": { + "type": "String", + "placeholders": {} + }, + "encryptionNotEnabled": "Shifrlash yoqilmagan", + "@encryptionNotEnabled": { + "type": "String", + "placeholders": {} + }, + "endedTheCall": "{senderName} chaqiruvni tugatdi", + "@endedTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "enterAnEmailAddress": "Email manzilini kiriting", + "@enterAnEmailAddress": { + "type": "String", + "placeholders": {} + }, + "homeserver": "Uy serveri", + "@homeserver": {}, + "enterYourHomeserver": "Uy serveriga kiring", + "@enterYourHomeserver": { + "type": "String", + "placeholders": {} + }, + "errorObtainingLocation": "Joylashuv axboroti olinmadi: {error}", + "@errorObtainingLocation": { + "type": "String", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "everythingReady": "Hammasi tayyor!", + "@everythingReady": { + "type": "String", + "placeholders": {} + }, + "extremeOffensive": "O‘ta haqoratomuz", + "@extremeOffensive": { + "type": "String", + "placeholders": {} + }, + "fileName": "Fayl nomi", + "@fileName": { + "type": "String", + "placeholders": {} + }, + "joinedTheChat": "👋 {username} suhbatga qoʻshildi", + "@joinedTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "joinRoom": "Guruhga qoʻshilish", + "@joinRoom": { + "type": "String", + "placeholders": {} + }, + "kicked": "👞 {username} {targetName}ni tepdi", + "@kicked": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "kickedAndBanned": "🙅 {username} {targetName}ni tepdi va blokladi", + "@kickedAndBanned": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "kickFromChat": "Suhbatdan tepish", + "@kickFromChat": { + "type": "String", + "placeholders": {} + }, + "lastActiveAgo": "Oxirgi faol: {localizedTimeShort}", + "@lastActiveAgo": { + "type": "String", + "placeholders": { + "localizedTimeShort": { + "type": "String" + } + } + }, + "leave": "Chiqish", + "@leave": { + "type": "String", + "placeholders": {} + }, + "leftTheChat": "Suhbatni tark etdi", + "@leftTheChat": { + "type": "String", + "placeholders": {} + }, + "license": "Litsenziya", + "@license": { + "type": "String", + "placeholders": {} + }, + "lightTheme": "Yorugʻlik", + "@lightTheme": { + "type": "String", + "placeholders": {} + }, + "loadCountMoreParticipants": "Yana {count} ishtirokchini yuklang", + "@loadCountMoreParticipants": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "dehydrate": "Sessiyani eksport qilish va qurilmani oʻchirish", + "@dehydrate": {}, + "dehydrateWarning": "Bu amalni bekor qilib boʻlmaydi. Zaxira faylini xavfsiz saqlang.", + "@dehydrateWarning": {}, + "dehydrateTor": "TOR foydalanuvchilari: Seansni eksport qilish", + "@dehydrateTor": {}, + "dehydrateTorLong": "TOR foydalanuvchilari uchun oynani yopishdan oldin seansni eksport qilish tavsiya etiladi.", + "@dehydrateTorLong": {}, + "hydrateTor": "TOR foydalanuvchilari: Seans eksportini import qilish", + "@hydrateTor": {}, + "hydrateTorLong": "Seansingizni oxirgi marta TOR’da eksport qildingizmi? Uni tezda import qiling va suhbatni davom ettiring.", + "@hydrateTorLong": {}, + "hydrate": "Zaxira faylidan tiklash", + "@hydrate": {}, + "loadingPleaseWait": "Yuklanmoqda… Iltimos, kuting.", + "@loadingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "loadMore": "Koʻproq yuklash…", + "@loadMore": { + "type": "String", + "placeholders": {} + }, + "locationDisabledNotice": "Joylashuv xizmatlari oʻchirib qoʻyilgan. Joylashuvingizni ulashish uchun ularni yoqing.", + "@locationDisabledNotice": { + "type": "String", + "placeholders": {} + }, + "locationPermissionDeniedNotice": "Joylashuvga ruxsat berilmadi. Iltimos, ularga joylashuvingizni ulashishga ruxsat bering.", + "@locationPermissionDeniedNotice": { + "type": "String", + "placeholders": {} + }, + "login": "Kirish", + "@login": { + "type": "String", + "placeholders": {} + }, + "logInTo": "{homeserver} ga kirish", + "@logInTo": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "logout": "Chiqish", + "@logout": { + "type": "String", + "placeholders": {} + }, + "memberChanges": "Aʼzo oʻzgarishlari", + "@memberChanges": { + "type": "String", + "placeholders": {} + }, + "mention": "Qayd etmoq", + "@mention": { + "type": "String", + "placeholders": {} + }, + "messages": "Xabarlar", + "@messages": { + "type": "String", + "placeholders": {} + }, + "messagesStyle": "Xabarlar:", + "@messagesStyle": {}, + "moderator": "Moderator", + "@moderator": { + "type": "String", + "placeholders": {} + }, + "muteChat": "Suhbatni ovozsizlantirish", + "@muteChat": { + "type": "String", + "placeholders": {} + }, + "needPantalaimonWarning": "Iltimos, hozircha Pantalaimon boshdan-oyoq shifrlashdan foydalanishi kerakligini yodda tuting.", + "@needPantalaimonWarning": { + "type": "String", + "placeholders": {} + }, + "newChat": "Yangi suhbat", + "@newChat": { + "type": "String", + "placeholders": {} + }, + "newMessageInFluffyChat": "💬 FluffyChat yangi xabarlar", + "@newMessageInFluffyChat": { + "type": "String", + "placeholders": {} + }, + "newVerificationRequest": "Yangi tasdiqlash so'rovi!", + "@newVerificationRequest": { + "type": "String", + "placeholders": {} + }, + "next": "Keyingi", + "@next": { + "type": "String", + "placeholders": {} + }, + "no": "Yoʻq", + "@no": { + "type": "String", + "placeholders": {} + }, + "noConnectionToTheServer": "Serverga ulanish yoʻq", + "@noConnectionToTheServer": { + "type": "String", + "placeholders": {} + }, + "noEmotesFound": "Hech qanday emoteʼlar topilmadi 😕", + "@noEmotesFound": { + "type": "String", + "placeholders": {} + }, + "noEncryptionForPublicRooms": "Shifrlashni faqat guruh endi hamma uchun ochiq bo'lmay qolgandan keyingina faollashtirishingiz mumkin.", + "@noEncryptionForPublicRooms": { + "type": "String", + "placeholders": {} + }, + "noGoogleServicesWarning": "Firebase Cloud Messaging qurilmangizda mavjud emasga o'xshaydi. Push-bildirishnomalarni olishda davom etish uchun ntfy-ni o'rnatishingizni tavsiya qilamiz. NTFY yoki boshqa Unified Push provayderi yordamida siz ma'lumotlar xavfsizligini ta'minlash orqali push-bildirishnomalarni olishingiz mumkin. Siz ntfy-ni PlayStore yoki F-Droid-dan yuklab olishingiz mumkin.", + "@noGoogleServicesWarning": { + "type": "String", + "placeholders": {} + }, + "noMatrixServer": "{server1} matrix serveri emas, buning o'rniga {server2} dan foydalanilsinmi?", + "@noMatrixServer": { + "type": "String", + "placeholders": { + "server1": { + "type": "String" + }, + "server2": { + "type": "String" + } + } + }, + "shareInviteLink": "Taklifnoma havolasini ulashish", + "@shareInviteLink": {}, + "scanQrCode": "QR kodini skanerlang", + "@scanQrCode": {}, + "none": "Hech biri", + "@none": { + "type": "String", + "placeholders": {} + }, + "noPasswordRecoveryDescription": "Siz hali parolingizni tiklash usulini qoʻshmadingiz.", + "@noPasswordRecoveryDescription": { + "type": "String", + "placeholders": {} + }, + "noPermission": "Ruxsat yoʻq", + "@noPermission": { + "type": "String", + "placeholders": {} + }, + "noRoomsFound": "Hech qanday guruhlar topilmadi…", + "@noRoomsFound": { + "type": "String", + "placeholders": {} + }, + "notifications": "Bildirishnomalar", + "@notifications": { + "type": "String", + "placeholders": {} + }, + "notificationsEnabledForThisAccount": "Ushbu hisob uchun bildirishnomalar yoqildi", + "@notificationsEnabledForThisAccount": { + "type": "String", + "placeholders": {} + }, + "numUsersTyping": "{count} foydalanuvchilar yozmoqda…", + "@numUsersTyping": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "obtainingLocation": "Joylashuv aniqlanmoqda…", + "@obtainingLocation": { + "type": "String", + "placeholders": {} + }, + "offensive": "Haqoratomuz", + "@offensive": { + "type": "String", + "placeholders": {} + }, + "offline": "Oflayn", + "@offline": { + "type": "String", + "placeholders": {} + }, + "ok": "Hop", + "@ok": { + "type": "String", + "placeholders": {} + }, + "online": "Onlayn", + "@online": { + "type": "String", + "placeholders": {} + }, + "onlineKeyBackupEnabled": "Onlayn kalit zaxira nusxasi yoqilgan", + "@onlineKeyBackupEnabled": { + "type": "String", + "placeholders": {} + }, + "oopsPushError": "Afsuski, push-bildirishnomalarni sozlashda xatolik yuz berdi.", + "@oopsPushError": { + "type": "String", + "placeholders": {} + }, + "oopsSomethingWentWrong": "Voy, nimadir notoʻgʻri ketdi…", + "@oopsSomethingWentWrong": { + "type": "String", + "placeholders": {} + }, + "openAppToReadMessages": "Xabarlarni oʻqish uchun ilovani oching", + "@openAppToReadMessages": { + "type": "String", + "placeholders": {} + }, + "openCamera": "Kamerani ochish", + "@openCamera": { + "type": "String", + "placeholders": {} + }, + "openVideoCamera": "Video uchun kamerani oching", + "@openVideoCamera": { + "type": "String", + "placeholders": {} + }, + "oneClientLoggedOut": "Mijozlaringizdan biri tizimdan chiqdi", + "@oneClientLoggedOut": {}, + "addAccount": "Hisob qoʻshish", + "@addAccount": {}, + "editBundlesForAccount": "Bu hisob uchun toʻplamlarni tahrirlash", + "@editBundlesForAccount": {}, + "addToBundle": "Toʻplamga qoʻshish", + "@addToBundle": {}, + "removeFromBundle": "Bu toʻplamdan oʻchirish", + "@removeFromBundle": {}, + "bundleName": "Toʻplam nomi", + "@bundleName": {}, + "enableMultiAccounts": "(BETA) Ushbu qurilmada bir nechta hisoblarni yoqish", + "@enableMultiAccounts": {}, + "openInMaps": "Xaritalarda ochish", + "@openInMaps": { + "type": "String", + "placeholders": {} + }, + "link": "Havola", + "@link": {}, + "serverRequiresEmail": "Ushbu server roʻyxatdan oʻtish uchun elektron pochta manzilingizni tasdiqlashi kerak.", + "@serverRequiresEmail": {}, + "or": "Yoki", + "@or": { + "type": "String", + "placeholders": {} + }, + "participant": "Qatnashuvchi", + "@participant": { + "type": "String", + "placeholders": {} + }, + "passphraseOrKey": "parol yoki tiklash kaliti", + "@passphraseOrKey": { + "type": "String", + "placeholders": {} + }, + "password": "Parol", + "@password": { + "type": "String", + "placeholders": {} + }, + "passwordForgotten": "Parol unitilgan", + "@passwordForgotten": { + "type": "String", + "placeholders": {} + }, + "passwordHasBeenChanged": "Parol oʻzgartirildi", + "@passwordHasBeenChanged": { + "type": "String", + "placeholders": {} + }, + "hideMemberChangesInPublicChats": "Ommaviy suhbatlarda aʼzolarga oʻzgartirishlarni yashirish", + "@hideMemberChangesInPublicChats": {}, + "hideMemberChangesInPublicChatsBody": "Oʻqish qulayligini oshirish uchun kimdir ochiq suhbatga qoʻshilsa yoki undan chiqsa, suhbat vaqt jadvalida koʻrsatilmasin.", + "@hideMemberChangesInPublicChatsBody": {}, + "overview": "Umumiy ma'lumot", + "@overview": {}, + "notifyMeFor": "Menga bildirishnoma yuborish", + "@notifyMeFor": {}, + "passwordRecoverySettings": "Parolni qayta tiklash sozlamalari", + "@passwordRecoverySettings": {}, + "passwordRecovery": "Parolni qayta tiklash", + "@passwordRecovery": { + "type": "String", + "placeholders": {} + }, + "people": "Odamlar", + "@people": { + "type": "String", + "placeholders": {} + }, + "pickImage": "Rasm tanlash", + "@pickImage": { + "type": "String", + "placeholders": {} + }, + "pin": "Toʻgʻnash", + "@pin": { + "type": "String", + "placeholders": {} + }, + "play": "{fileName}ni oʻynash", + "@play": { + "type": "String", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "pleaseChoose": "Iltimos tanlang", + "@pleaseChoose": { + "type": "String", + "placeholders": {} + }, + "pleaseChooseAPasscode": "Iltimos, kirish kodini tanlang", + "@pleaseChooseAPasscode": { + "type": "String", + "placeholders": {} + }, + "pleaseClickOnLink": "Iltimos, elektron pochtadagi havolani bosing va keyin davom eting.", + "@pleaseClickOnLink": { + "type": "String", + "placeholders": {} + }, + "pleaseEnter4Digits": "Ilova qulfini oʻchirish uchun 4 ta raqamni kiriting yoki boʻsh qoldiring.", + "@pleaseEnter4Digits": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterRecoveryKey": "Iltimos, tiklash kalitingizni kiriting:", + "@pleaseEnterRecoveryKey": {}, + "pleaseEnterYourPassword": "Iltimos parolingizni kiriting", + "@pleaseEnterYourPassword": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterYourPin": "Iltimos PIN kodingizni kiriting", + "@pleaseEnterYourPin": { + "type": "String", + "placeholders": {} + }, + "pleaseEnterYourUsername": "Iltimos foydalanuvchi nomini kiriting", + "@pleaseEnterYourUsername": { + "type": "String", + "placeholders": {} + }, + "pleaseFollowInstructionsOnWeb": "Iltimos, veb-saytdagi koʻrsatmalarga amal qiling va keyingisini bosing.", + "@pleaseFollowInstructionsOnWeb": { + "type": "String", + "placeholders": {} + }, + "privacy": "Maxfiylik", + "@privacy": { + "type": "String", + "placeholders": {} + }, + "publicRooms": "Ommaviy guruhlar", + "@publicRooms": { + "type": "String", + "placeholders": {} + }, + "pushRules": "Push qoidalari", + "@pushRules": { + "type": "String", + "placeholders": {} + }, + "reason": "Sabab", + "@reason": { + "type": "String", + "placeholders": {} + }, + "recording": "Yozilmoqda", + "@recording": { + "type": "String", + "placeholders": {} + }, + "redactedBy": "{username} tomonidan tahrirlangan", + "@redactedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "directChat": "Shaxsiy suhbat", + "@directChat": {}, + "redactedByBecause": "{username} tomonidan tahrirlandi, sababi: \"{reason}\"", + "@redactedByBecause": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "reason": { + "type": "String" + } + } + }, + "redactedAnEvent": "{username} tadbirni oʻchirib tashladi", + "@redactedAnEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "redactMessage": "Xabarni tahrirlash", + "@redactMessage": { + "type": "String", + "placeholders": {} + }, + "register": "Roʻyxatdan oʻtish", + "@register": { + "type": "String", + "placeholders": {} + }, + "reject": "Rad qilish", + "@reject": { + "type": "String", + "placeholders": {} + }, + "rejectedTheInvitation": "{username} taklifni rad qildi", + "@rejectedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "rejoin": "Qayta qoʻshilish", + "@rejoin": { + "type": "String", + "placeholders": {} + }, + "removeAllOtherDevices": "Qolgan barcha qurilmalarni oʻchirish", + "@removeAllOtherDevices": { + "type": "String", + "placeholders": {} + }, + "removedBy": "{username} tomonidan oʻchirildi", + "@removedBy": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "removeDevice": "Qurilmani oʻchirish", + "@removeDevice": { + "type": "String", + "placeholders": {} + }, + "unbanFromChat": "Suhbat blokidan chiqazish", + "@unbanFromChat": { + "type": "String", + "placeholders": {} + }, + "removeYourAvatar": "Avatarni oʻchirish", + "@removeYourAvatar": { + "type": "String", + "placeholders": {} + }, + "replaceRoomWithNewerVersion": "Guruhni yangiroq versiya bilan almashtirish", + "@replaceRoomWithNewerVersion": { + "type": "String", + "placeholders": {} + }, + "reply": "Javob yozish", + "@reply": { + "type": "String", + "placeholders": {} + }, + "reportMessage": "Xabar berish", + "@reportMessage": { + "type": "String", + "placeholders": {} + }, + "requestPermission": "Ruxsat soʻrash", + "@requestPermission": { + "type": "String", + "placeholders": {} + }, + "roomHasBeenUpgraded": "Xona takomillashtirildi", + "@roomHasBeenUpgraded": { + "type": "String", + "placeholders": {} + }, + "roomVersion": "Guruh versiyasi", + "@roomVersion": { + "type": "String", + "placeholders": {} + }, + "saveFile": "Fayl saqlash", + "@saveFile": { + "type": "String", + "placeholders": {} + }, + "search": "Qidiruv", + "@search": { + "type": "String", + "placeholders": {} + }, + "security": "Xavfsizlik", + "@security": { + "type": "String", + "placeholders": {} + }, + "recoveryKey": "Tiklash kaliti", + "@recoveryKey": {}, + "recoveryKeyLost": "Tiklash kaliti yo‘qolib qoldimi?", + "@recoveryKeyLost": {}, + "seenByUser": "{username} ko‘rgan", + "@seenByUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "send": "Yuborish", + "@send": { + "type": "String", + "placeholders": {} + }, + "sendAMessage": "Xabar yuborish", + "@sendAMessage": { + "type": "String", + "placeholders": {} + }, + "sendAsText": "Matn sifatida yuborish", + "@sendAsText": { + "type": "String" + }, + "sendAudio": "Audio yuborish", + "@sendAudio": { + "type": "String", + "placeholders": {} + }, + "sendFile": "Faylni yuborish", + "@sendFile": { + "type": "String", + "placeholders": {} + }, + "sendImage": "Rasm yuborish", + "@sendImage": { + "type": "String", + "placeholders": {} + }, + "sendImages": "{count} ta rasm yuborish", + "@sendImages": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "sendMessages": "Xabarlar yuborish", + "@sendMessages": { + "type": "String", + "placeholders": {} + }, + "sendOriginal": "Asl nusxani yuborish", + "@sendOriginal": { + "type": "String", + "placeholders": {} + }, + "sendSticker": "Stiker yuborish", + "@sendSticker": { + "type": "String", + "placeholders": {} + }, + "sendVideo": "Video yuborish", + "@sendVideo": { + "type": "String", + "placeholders": {} + }, + "sentAFile": "📁 {username} fayl yubordi", + "@sentAFile": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAnAudio": "🎤 {username} audio yubordi", + "@sentAnAudio": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAPicture": "️ 🖼️ {username} rasm yubordi", + "@sentAPicture": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentASticker": "😊 {username} stiker yubordi", + "@sentASticker": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentAVideo": "🎥 {username} video yubordi", + "@sentAVideo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "sentCallInformations": "{senderName} chaqiruv axborotini yubordi", + "@sentCallInformations": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "separateChatTypes": "To‘g‘ridan-to‘g‘ri suhbatlar va guruhlarni alohida ajratish", + "@separateChatTypes": { + "type": "String", + "placeholders": {} + }, + "setAsCanonicalAlias": "Asosiy taxallus sifatida belgilash", + "@setAsCanonicalAlias": { + "type": "String", + "placeholders": {} + }, + "setCustomEmotes": "Maxsus hissiyotlarni sozlash", + "@setCustomEmotes": { + "type": "String", + "placeholders": {} + }, + "setChatDescription": "Suhbat tavsifini sozlash", + "@setChatDescription": {}, + "setInvitationLink": "Taklif havolasini sozlash", + "@setInvitationLink": { + "type": "String", + "placeholders": {} + }, + "setPermissionsLevel": "Ruxsatlar darajasini belgilash", + "@setPermissionsLevel": { + "type": "String", + "placeholders": {} + }, + "setStatus": "Holatni sozlash", + "@setStatus": { + "type": "String", + "placeholders": {} + }, + "settings": "Sozlamalar", + "@settings": { + "type": "String", + "placeholders": {} + }, + "share": "Bo‘lishmoq", + "@share": { + "type": "String", + "placeholders": {} + }, + "sharedTheLocation": "{username} joylashuvini ulashdi", + "@sharedTheLocation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "shareLocation": "Joylashuvni ulashish", + "@shareLocation": { + "type": "String", + "placeholders": {} + }, + "showPassword": "Parolni ko‘rsatish", + "@showPassword": { + "type": "String", + "placeholders": {} + }, + "presenceStyle": "Mavjudlik:", + "@presenceStyle": { + "type": "String", + "placeholders": {} + }, + "presencesToggle": "Boshqa foydalanuvchilarning holat xabarlarini ko‘rsatish", + "@presencesToggle": { + "type": "String", + "placeholders": {} + }, + "singlesignon": "Yagona kirish", + "@singlesignon": { + "type": "String", + "placeholders": {} + }, + "skip": "Tashlab ketish", + "@skip": { + "type": "String", + "placeholders": {} + }, + "sourceCode": "Manba kodi", + "@sourceCode": { + "type": "String", + "placeholders": {} + }, + "spaceIsPublic": "Guruh ochiq", + "@spaceIsPublic": { + "type": "String", + "placeholders": {} + }, + "spaceName": "Guruh nomi", + "@spaceName": { + "type": "String", + "placeholders": {} + }, + "startedACall": "{senderName} chaqiruv boshladi", + "@startedACall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "startFirstChat": "Birinchi suhbatni boshlash", + "@startFirstChat": {}, + "status": "Holati", + "@status": { + "type": "String", + "placeholders": {} + }, + "statusExampleMessage": "Bugun ahvolingiz qalay?", + "@statusExampleMessage": { + "type": "String", + "placeholders": {} + }, + "submit": "Yuborish", + "@submit": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWait": "Sinxronlanmoqda... Iltimos, kuting.", + "@synchronizingPleaseWait": { + "type": "String", + "placeholders": {} + }, + "synchronizingPleaseWaitCounter": " Sinxronlanmoqda... ({percentage}%)", + "@synchronizingPleaseWaitCounter": { + "type": "String", + "placeholders": { + "percentage": { + "type": "String" + } + } + }, + "systemTheme": "Tizim", + "@systemTheme": { + "type": "String", + "placeholders": {} + }, + "theyDontMatch": "Ular mos emas", + "@theyDontMatch": { + "type": "String", + "placeholders": {} + }, + "theyMatch": "Ular mos keladi", + "@theyMatch": { + "type": "String", + "placeholders": {} + }, + "title": "FluffyChat", + "@title": { + "description": "Title for the application", + "type": "String", + "placeholders": {} + }, + "toggleFavorite": "Saralanganni almashtirish", + "@toggleFavorite": { + "type": "String", + "placeholders": {} + }, + "toggleMuted": "O‘chirib qo‘yish", + "@toggleMuted": { + "type": "String", + "placeholders": {} + }, + "toggleUnread": "O‘qilgan/O‘qilmaganni belgilash", + "@toggleUnread": { + "type": "String", + "placeholders": {} + }, + "tooManyRequestsWarning": "Talablar soni oshib ketdi. Keyinroq qayta urining!", + "@tooManyRequestsWarning": { + "type": "String", + "placeholders": {} + }, + "transferFromAnotherDevice": "Boshqa qurilmadan uzatish", + "@transferFromAnotherDevice": { + "type": "String", + "placeholders": {} + }, + "tryToSendAgain": "Qayta yuborishga urining", + "@tryToSendAgain": { + "type": "String", + "placeholders": {} + }, + "unavailable": "Mavjud emas", + "@unavailable": { + "type": "String", + "placeholders": {} + }, + "unbannedUser": "{username} {targetName}ni blokdan chiqardi", + "@unbannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "unblockDevice": "Qurilmani blokdan chiqarish", + "@unblockDevice": { + "type": "String", + "placeholders": {} + }, + "unknownDevice": "Notanish qurilma", + "@unknownDevice": { + "type": "String", + "placeholders": {} + }, + "unknownEncryptionAlgorithm": "Noma’lum shifrlash algoritmi", + "@unknownEncryptionAlgorithm": { + "type": "String", + "placeholders": {} + }, + "unknownEvent": "Noma’lum hodisa '{type}'", + "@unknownEvent": { + "type": "String", + "placeholders": { + "type": { + "type": "String" + } + } + }, + "unmuteChat": "Suhbatni ovozli qilish", + "@unmuteChat": { + "type": "String", + "placeholders": {} + }, + "unpin": "Olib tashlash", + "@unpin": { + "type": "String", + "placeholders": {} + }, + "unreadChats": "{unreadCount, plural, =1{1 ta oʻqilmagan suhbat} other{{unreadCount} ta o‘qilmagan chat}}", + "@unreadChats": { + "type": "String", + "placeholders": { + "unreadCount": { + "type": "int" + } + } + }, + "userAndOthersAreTyping": "{username} va yana {count} kishi yozmoqda…", + "@userAndOthersAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "userAndUserAreTyping": "{username} va {username2} yozmoqda…", + "@userAndUserAreTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "username2": { + "type": "String" + } + } + }, + "userIsTyping": "{username} yozmoqda…", + "@userIsTyping": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "userLeftTheChat": "🚪 {username} suhbatni tark etdi", + "@userLeftTheChat": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "username": "Foydalanuvchi nomi", + "@username": { + "type": "String", + "placeholders": {} + }, + "userSentUnknownEvent": "{username} {type} tadbirini yubordi", + "@userSentUnknownEvent": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "type": { + "type": "String" + } + } + }, + "unverified": "Tasdiqlanmagan", + "@unverified": {}, + "verified": "Tasdiqlangan", + "@verified": { + "type": "String", + "placeholders": {} + }, + "verify": "Tasdiqlash", + "@verify": { + "type": "String", + "placeholders": {} + }, + "verifyStart": "Tasdiqlashni boshlash", + "@verifyStart": { + "type": "String", + "placeholders": {} + }, + "verifySuccess": "Siz tasdiqladingiz!", + "@verifySuccess": { + "type": "String", + "placeholders": {} + }, + "verifyTitle": "Boshqa hisob tasdiqlanmoqda", + "@verifyTitle": { + "type": "String", + "placeholders": {} + }, + "videoCall": "Video chaqiruv", + "@videoCall": { + "type": "String", + "placeholders": {} + }, + "visibilityOfTheChatHistory": "Suhbat tarixining ko‘rinishi", + "@visibilityOfTheChatHistory": { + "type": "String", + "placeholders": {} + }, + "visibleForAllParticipants": "Barcha ishtirokchilarga ko‘rinadi", + "@visibleForAllParticipants": { + "type": "String", + "placeholders": {} + }, + "visibleForEveryone": "Hammaga ko‘rinadigan", + "@visibleForEveryone": { + "type": "String", + "placeholders": {} + }, + "voiceMessage": "Ovozli xabar", + "@voiceMessage": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerAcceptRequest": "Hamkor so‘rovni qabul qilishi kutilmoqda…", + "@waitingPartnerAcceptRequest": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerEmoji": "Hamkor emoji qabul qilishini kutmoqda…", + "@waitingPartnerEmoji": { + "type": "String", + "placeholders": {} + }, + "waitingPartnerNumbers": "Hamkor raqamlarni qabul qilishi kutilmoqda…", + "@waitingPartnerNumbers": { + "type": "String", + "placeholders": {} + }, + "wallpaper": "Fon rasmi:", + "@wallpaper": { + "type": "String", + "placeholders": {} + }, + "warning": "Ogohlantirish!", + "@warning": { + "type": "String", + "placeholders": {} + }, + "weSentYouAnEmail": "Sizga xat yubordik", + "@weSentYouAnEmail": { + "type": "String", + "placeholders": {} + }, + "whoCanPerformWhichAction": "Kim qaysi amalni bajarishi mumkin", + "@whoCanPerformWhichAction": { + "type": "String", + "placeholders": {} + }, + "whoIsAllowedToJoinThisGroup": "Bu guruhga kim qo‘shilishi mumkin", + "@whoIsAllowedToJoinThisGroup": { + "type": "String", + "placeholders": {} + }, + "whyDoYouWantToReportThis": "Nima uchun bu haqda xabar bermoqchisiz?", + "@whyDoYouWantToReportThis": { + "type": "String", + "placeholders": {} + }, + "wipeChatBackup": "Yangi tiklash kalitini yaratish uchun suhbat zaxirasi tozalansinmi?", + "@wipeChatBackup": { + "type": "String", + "placeholders": {} + }, + "withTheseAddressesRecoveryDescription": "Ushbu manzillar yordamida parolingizni tiklashingiz mumkin.", + "@withTheseAddressesRecoveryDescription": { + "type": "String", + "placeholders": {} + }, + "writeAMessage": "Xabar yozish…", + "@writeAMessage": { + "type": "String", + "placeholders": {} + }, + "yes": "Ha", + "@yes": { + "type": "String", + "placeholders": {} + }, + "you": "Siz", + "@you": { + "type": "String", + "placeholders": {} + }, + "youAreNoLongerParticipatingInThisChat": "Siz ortiq bu suhbatda qatnashmayapsiz", + "@youAreNoLongerParticipatingInThisChat": { + "type": "String", + "placeholders": {} + }, + "youHaveBeenBannedFromThisChat": "Bu suhbatdan bloklandingiz", + "@youHaveBeenBannedFromThisChat": { + "type": "String", + "placeholders": {} + }, + "yourPublicKey": "Ochiq kalitingiz", + "@yourPublicKey": { + "type": "String", + "placeholders": {} + }, + "messageInfo": "Xabar axboroti", + "@messageInfo": {}, + "time": "Vaqt", + "@time": {}, + "messageType": "Xabar turi", + "@messageType": {}, + "sender": "Yuboruvchi", + "@sender": {}, + "openGallery": "Galereyani ochish", + "@openGallery": {}, + "removeFromSpace": "Guruhdan olib tashlash", + "@removeFromSpace": {}, + "addToSpaceDescription": "Bu suhbatni unga kiritish uchun guruhni tanlang.", + "@addToSpaceDescription": {}, + "start": "Boshlash", + "@start": {}, + "usersMustKnock": "Foydalanuvchilar taqillatishi kerak", + "@usersMustKnock": {}, + "noOneCanJoin": "Hech kim qoʻshila olmaydi", + "@noOneCanJoin": {}, + "userWouldLikeToChangeTheChat": "{user} suhbatga qoʻshilmoqchi.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "noPublicLinkHasBeenCreatedYet": "Hech qanday ochiq havola yaratilmagan", + "@noPublicLinkHasBeenCreatedYet": {}, + "knock": "Taqillating", + "@knock": {}, + "users": "Foydalanuvchilar", + "@users": {}, + "unlockOldMessages": "Eski xabarlarni qulfdan chiqaring", + "@unlockOldMessages": {}, + "storeInSecureStorageDescription": "Qayta tiklash kalitini ushbu qurilmaning xavfsiz xotirasida saqlang.", + "@storeInSecureStorageDescription": {}, + "saveKeyManuallyDescription": "Tizim ulashish dialog oynasi yoki buferni ishga tushirish orqali ushbu kalitni qoʻlda saqlang.", + "@saveKeyManuallyDescription": {}, + "storeInAndroidKeystore": "Android KeyStoreʼda saqlang", + "@storeInAndroidKeystore": {}, + "storeInAppleKeyChain": "Apple KeyChainʼda saqlang", + "@storeInAppleKeyChain": {}, + "storeSecurlyOnThisDevice": "Ushbu qurilmada xavfsiz saqlang", + "@storeSecurlyOnThisDevice": {}, + "countFiles": "{count} fayllar", + "@countFiles": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "user": "Foydalanuvchi", + "@user": {}, + "custom": "Maxsus", + "@custom": {}, + "foregroundServiceRunning": "Bu bildirishnoma old plan xizmati ishlab turgan paytda paydo bo‘ladi.", + "@foregroundServiceRunning": {}, + "screenSharingTitle": "Ekranni ulashish", + "@screenSharingTitle": {}, + "screenSharingDetail": "Siz ekraningizni FuffyChat’da ulashmoqdasiz", + "@screenSharingDetail": {}, + "callingPermissions": "Qoʻngʻiroq qilish ruxsatlar", + "@callingPermissions": {}, + "callingAccount": "Qoʻngʻiroq qilishi hisobi", + "@callingAccount": {}, + "callingAccountDetails": "FluffyChat’ga mahalliy android terish ilovasidan foydalanishga ruxsat beradi.", + "@callingAccountDetails": {}, + "appearOnTop": "Teppada paydo boʻladi", + "@appearOnTop": {}, + "appearOnTopDetails": "Ilovaning yuqori qismida koʻrinishiga ruxsat beradi (agar sizda Fluffychat qoʻngʻiroq qiluvchi hisobi sifatida oʻrnatilgan boʻlsa, kerak emas)", + "@appearOnTopDetails": {}, + "otherCallingPermissions": "Mikrofon, kamera va boshqa FluffyChat ruxsatnomalari", + "@otherCallingPermissions": {}, + "whyIsThisMessageEncrypted": "Nima uchun bu xabarni oʻqib boʻlmaydi?", + "@whyIsThisMessageEncrypted": {}, + "noKeyForThisMessage": "Bu xabar siz ushbu qurilmada hisobingizga kirishdan oldin yuborilgan boʻlsa sodir boʻlishi mumkin.\n\nShuningdek, joʻnatuvchi qurilmangizni bloklagan yoki internet ulanishida biron bir muammo yuzaga kelgan boʻlishi mumkin.\n\nXabarni boshqa sessiyada oʻqiy olasizmi? Keyin xabarni undan uzatishingiz mumkin! Sozlamalar > Qurilmalar boʻlimiga oʻting va qurilmalaringiz bir-birini tasdiqlaganligiga ishonch hosil qiling. Keyingi safar xonani ochganingizda va ikkala sessiya ham oldinda boʻlganda, kalitlar avtomatik ravishda uzatiladi.\n\nTizimdan chiqishda yoki qurilmalarni almashtirishda kalitlarni yoʻqotishni xohlamaysizmi? Sozlamalarda suhbatning zaxira nusxasini yoqganingizga ishonch hosil qiling.", + "@noKeyForThisMessage": {}, + "newGroup": "Yangi guruh", + "@newGroup": {}, + "newSpace": "Yangi maydon", + "@newSpace": {}, + "enterSpace": "Maydonga kirish", + "@enterSpace": {}, + "enterRoom": "Guruhga kirish", + "@enterRoom": {}, + "allSpaces": "Barcha maydonlar", + "@allSpaces": {}, + "numChats": "{number} suhbatlar", + "@numChats": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "hideUnimportantStateEvents": "Muhim boʻlmagan shtat tadbirlarini yashirish", + "@hideUnimportantStateEvents": {}, + "hidePresences": "Holat roʻyxati yashirilsinmi?", + "@hidePresences": {}, + "doNotShowAgain": "Qaytib koʻrsatilmasin", + "@doNotShowAgain": {}, + "wasDirectChatDisplayName": "Boʻsh suhbat ({oldDisplayName} edi)", + "@wasDirectChatDisplayName": { + "type": "String", + "placeholders": { + "oldDisplayName": { + "type": "String" + } + } + }, + "newSpaceDescription": "Maydonlar sizga suhbatlaringizni birlashtirish va shaxsiy yoki ommaviy hamjamiyatlarni yaratish imkonini beradi.", + "@newSpaceDescription": {}, + "openChat": "Suhbatni ochish", + "@openChat": {}, + "indexedDbErrorLong": "Xabarlarni saqlash, afsuski, sukut bo'yicha maxfiy rejimda yoqilmagan.\nIltimos, tashrif buyuring\n- about:config\n- dom.indexedDB.privateBrowsing.enabled ga true berilgan\nAks holda, FluffyChat ni ishga tushirish mumkin emas.", + "@indexedDbErrorLong": {}, + "youJoinedTheChat": "Siz suhbatga qoʻshildingiz", + "@youJoinedTheChat": {}, + "encryptThisChat": "Bu suhbatni shifrlash", + "@encryptThisChat": {}, + "disableEncryptionWarning": "Xavfsizlik nuqtai nazaridan, agar u ilgari yoqilgan boʻlsa, suhbatda shifrlashni oʻchirib qoʻyolmaysiz.", + "@disableEncryptionWarning": {}, + "reopenChat": "Suhbatni qayta ochish", + "@reopenChat": {}, + "noBackupWarning": "Diqqat! Suhbatni zaxiralashni yoqmasangiz, shifrlangan xabarlaringizga kirish huquqini yoʻqotasiz. Tizimdan chiqishdan oldin chatni zaxiralashni yoqishingiz tavsiya etiladi.", + "@noBackupWarning": {}, + "inviteGroupChat": "📨 Guruh suhbatiga taklif", + "@inviteGroupChat": {}, + "invitePrivateChat": "📨 Shaxsiy suhbatga taklif", + "@invitePrivateChat": {}, + "archiveRoomDescription": "Suhbat arxivga koʻchiriladi. Boshqa foydalanuvchilar sizning suhbatdan chiqqaningizni koʻra oladilar.", + "@archiveRoomDescription": {}, + "roomUpgradeDescription": "Keyin suhbat yangi guruh versiyasi bilan qayta yaratiladi. Barcha ishtirokchilarga yangi suhbatga oʻtishlari kerakligi haqida xabar beriladi. Guruh versiyalari haqida koʻproq maʼlumotni https://spec.matrix.org/latest/rooms/ manzilida topishingiz mumkin", + "@roomUpgradeDescription": {}, + "banUserDescription": "Foydalanuvchi suhbatdan bloklanadi va blokdan chiqarilmaguncha suhbatga qayta kira olmaydi.", + "@banUserDescription": {}, + "unbanUserDescription": "Foydalanuvchi qayta suhbatga kira oladi agar ular urinib koʻrishsa.", + "@unbanUserDescription": {}, + "kickUserDescription": "Foydalanuvchi suhbatdan chiqarib yuboriladi, ammo taqiqlanmaydi. Ommaviy chatlarda foydalanuvchi istalgan vaqtda qayta qoʻshilishi mumkin.", + "@kickUserDescription": {}, + "chatCanBeDiscoveredViaSearchOnServer": "Suhbatni {server} saytidagi qidiruv orqali topish mumkin", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "searchChatsRooms": "Qidiruv #chats, @users...", + "@searchChatsRooms": {}, + "publicChatAddresses": "Ommaviy suhbat manzillari", + "@publicChatAddresses": {}, + "addChatOrSubSpace": "Suhbat yoki sub-maydon qoʻshing", + "@addChatOrSubSpace": {}, + "searchIn": "Suhbat \"{chat}\"da qidiring...", + "@searchIn": { + "type": "String", + "placeholders": { + "chat": { + "type": "String" + } + } + }, + "sendTypingNotificationsDescription": "Suhbatdagi boshqa ishtirokchilar siz yangi xabar yozayotganingizni koʻrishlari mumkin.", + "@sendTypingNotificationsDescription": {}, + "sendReadReceiptsDescription": "Suhbatdagi boshqa ishtirokchilar sizning xabarni qachon oʻqiganingizni koʻrishlari mumkin.", + "@sendReadReceiptsDescription": {}, + "unreadChatsInApp": "{appname}: {unread} ta oʻqilmagan suhbatlar", + "@unreadChatsInApp": { + "type": "String", + "placeholders": { + "appname": { + "type": "String" + }, + "unread": { + "type": "String" + } + } + }, + "changeGeneralChatSettings": "Umumiy suhbat sozlamalarini oʻzgartirish", + "@changeGeneralChatSettings": {}, + "inviteOtherUsers": "Boshqa foydalanuvchilarni bu suhbatga taklif qilish", + "@inviteOtherUsers": {}, + "changeTheChatPermissions": "Suhbat ruxsatnomalarini oʻzgartirish", + "@changeTheChatPermissions": {}, + "changeTheVisibilityOfChatHistory": "Suhbat tarix koʻrinishini oʻzgartirish", + "@changeTheVisibilityOfChatHistory": {}, + "changeTheCanonicalRoomAlias": "Asosiy umumiy suhbat manzilini oʻzgartirish", + "@changeTheCanonicalRoomAlias": {}, + "changeTheDescriptionOfTheGroup": "Suhbat tavsifini oʻzgartirish", + "@changeTheDescriptionOfTheGroup": {}, + "chatPermissionsDescription": "Ushbu suhbatda muayyan harakatlar uchun qaysi quvvat darajasi zarurligini aniqlang. 0, 50 va 100 quvvat darajalari odatda foydalanuvchilar, moderatorlar va administratorlarni ifodalaydi, ammo har qanday gradatsiya mumkin.", + "@chatPermissionsDescription": {}, + "noticeChatBackupDeviceVerification": "Eslatma: Barcha qurilmalaringizni suhbat zaxira nusxasiga ulaganingizda, ular avtomatik ravishda tasdiqlanadi.", + "@noticeChatBackupDeviceVerification": {}, + "welcomeText": "Hey Hey 👋 Bu FluffyChat. Siz https://matrix.org bilan mos keladigan istalgan uy serveriga kirishingiz mumkin. Va keyin istalgan kishi bilan suhbatlashishingiz mumkin. Bu ulkan markazlashtirilmagan xabar almashish tarmog'i!", + "@welcomeText": {}, + "unableToJoinChat": "Chatga qoʻshilib boʻlmadi. Ehtimol, boshqa tomon suhbatni allaqachon yopib qoʻygan.", + "@unableToJoinChat": {}, + "appIntroduction": "FluffyChat sizga turli messenjerlar orqali doʻstlaringiz bilan suhbatlashish imkonini beradi. Batafsil maʼlumotni https://matrix.org saytida oling yoki shunchaki *Davom etish* tugmasini bosing.", + "@appIntroduction": {}, + "newChatRequest": "📩 Yangi suhbat uchun soʻrov", + "@newChatRequest": {}, + "shareKeysWithDescription": "Shifrlangan suhbatlarda xabarlaringizni oʻqishlari uchun qaysi qurilmalarga ishonish kerak?", + "@shareKeysWithDescription": {}, + "enterNewChat": "Yangi suhbatga kirish", + "@enterNewChat": {}, + "removeFromSpaceDescription": "Suhbat maydondan olib tashlanadi, lekin hali ham suhbatlarlar ro‘yxatida chiqadi.", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} suhbatlar", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "backToMainChat": "Asosiy suhbatga qaytish", + "@backToMainChat": {}, + "pleaseEnterRecoveryKeyDescription": "Eski xabarlaringizni qulfdan chiqarish uchun, iltimos, avvalgi seansdan yaratilgan tiklash kalitingizni kiriting. Sizning tiklash kalitingiz parolingiz EMAS.", + "@pleaseEnterRecoveryKeyDescription": {}, + "publish": "Nashr qilish", + "@publish": {}, + "videoWithSize": "Video ({size})", + "@videoWithSize": { + "type": "String", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "markAsRead": "Oʻqilgan sifatida belgilash", + "@markAsRead": {}, + "reportUser": "Foydalanuvchi haqida xabar berish", + "@reportUser": {}, + "dismiss": "Rad qilmoq", + "@dismiss": {}, + "reactedWith": "{sender} {reaction} bilan reaksiya bildirdi", + "@reactedWith": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "reaction": { + "type": "String" + } + } + }, + "pinMessage": "Xonaga mahkamlash", + "@pinMessage": {}, + "confirmEventUnpin": "Tadbirni butunlay olib tashlashga ishonchingiz komilmi?", + "@confirmEventUnpin": {}, + "placeCall": "Qoʻngʻiroq qilish", + "@placeCall": {}, + "voiceCall": "Ovozli qoʻngʻiroq", + "@voiceCall": {}, + "unsupportedAndroidVersion": "Qoʻllab-quvvatlanmaydigan Android versiyasi", + "@unsupportedAndroidVersion": {}, + "unsupportedAndroidVersionLong": "Bu funksiya Androidning yangi versiyasini talab qiladi. Iltimos, yangilanishlar yoki Lineage OS qoʻllab-quvvatlashini tekshiring.", + "@unsupportedAndroidVersionLong": {}, + "videoCallsBetaWarning": "Iltimos, video qoʻngʻiroqlar hozirda beta-versiyada ekanligini unutmang. Ular kutilganidek ishlamasligi yoki barcha platformalarda umuman ishlamasligi mumkin.", + "@videoCallsBetaWarning": {}, + "experimentalVideoCalls": "Tajriba video qoʻngʻiroqlar", + "@experimentalVideoCalls": {}, + "emailOrUsername": "Elektron pochta yoki foydalanuvchi nomi", + "@emailOrUsername": {}, + "indexedDbErrorTitle": "Shaxsiy rejim bilan bogʻliq muammolar", + "@indexedDbErrorTitle": {}, + "switchToAccount": "{number} hisobiga oʻtish", + "@switchToAccount": { + "type": "number", + "placeholders": { + "number": { + "type": "String" + } + } + }, + "nextAccount": "Keyingi hisob", + "@nextAccount": {}, + "previousAccount": "Oldingi hisob", + "@previousAccount": {}, + "addWidget": "Vidjet qoʻshish", + "@addWidget": {}, + "widgetVideo": "Video", + "@widgetVideo": {}, + "sorryThatsNotPossible": "Kechirasiz... bu mumkin emas", + "@sorryThatsNotPossible": {}, + "deviceKeys": "Qurilma kalitlari:", + "@deviceKeys": {}, + "noOtherDevicesFound": "Boshqa qurilma topilmadi", + "@noOtherDevicesFound": {}, + "fileIsTooBigForServer": "Yuborish imkonsiz! Server faqat {max} hajmgacha bo‘lgan ilovalarni qo‘llab-quvvatlaydi.", + "@fileIsTooBigForServer": { + "type": "String", + "placeholders": { + "max": { + "type": "String" + } + } + }, + "fileHasBeenSavedAt": "Fayl {path}da saqlandi", + "@fileHasBeenSavedAt": { + "type": "String", + "placeholders": { + "path": { + "type": "String" + } + } + }, + "jumpToLastReadMessage": "Oxirgi o‘qilgan xabarga o‘tish", + "@jumpToLastReadMessage": {}, + "readUpToHere": "Bu yerga qadar o‘qish", + "@readUpToHere": {}, + "jump": "Sakrash", + "@jump": {}, + "openLinkInBrowser": "Havolani brauzerda ochish", + "@openLinkInBrowser": {}, + "reportErrorDescription": "😭 Voy yo‘q. Nimadir xato ketdi. Agar xohlasangiz, bu xato haqida dasturchilarga xabar berishingiz mumkin.", + "@reportErrorDescription": {}, + "report": "hisobot", + "@report": {}, + "signInWithPassword": "Parol bilan kirish", + "@signInWithPassword": {}, + "pleaseTryAgainLaterOrChooseDifferentServer": "Keyinroq qayta urining yoki boshqa serverni tanlang.", + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "profileNotFound": "Foydalanuvchi serverda topilmadi. Ehtimol, ulanishda muammo bor yoki foydalanuvchi mavjud emas.", + "@profileNotFound": {}, + "setTheme": "Mavzu tanlash:", + "@setTheme": {}, + "setColorTheme": "Rang mavzusini sozlash:", + "@setColorTheme": {}, + "invite": "Taklif qilish", + "@invite": {}, + "invalidInput": "Xato kiritildi!", + "@invalidInput": {}, + "wrongPinEntered": "PIN noto‘g‘ri kiritildi! {seconds} soniyadan keyin qayta urining...", + "@wrongPinEntered": { + "type": "String", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "pleaseEnterANumber": "0 dan katta son kiriting", + "@pleaseEnterANumber": {}, + "removeDevicesDescription": "Bu qurilmadan chiqarilasiz va ortiq xabarlarni qabul qila olmaysiz.", + "@removeDevicesDescription": {}, + "noUsersFoundWithQuery": "Afsuski, \"{query}\" soʻrovi bilan foydalanuvchi topilmadi. Iltimos, xato qilganingizni tekshiring.", + "@noUsersFoundWithQuery": { + "type": "String", + "placeholders": { + "query": { + "type": "String" + } + } + }, + "knocking": "Taqillatmoqda", + "@knocking": {}, + "nothingFound": "Hech nima topilmadi...", + "@nothingFound": {}, + "groupName": "Guruh nomi", + "@groupName": {}, + "createGroupAndInviteUsers": "Guruh yaratish va foydalanuvchilarni taklif qilish", + "@createGroupAndInviteUsers": {}, + "groupCanBeFoundViaSearch": "Guruh qidiruv orqali topilishi mumkin", + "@groupCanBeFoundViaSearch": {}, + "wrongRecoveryKey": "Kechirasiz... bu toʻgʻri tiklash kaliti emasga oʻxshaydi.", + "@wrongRecoveryKey": {}, + "startConversation": "Suhbat boshlash", + "@startConversation": {}, + "commandHint_sendraw": "Xom jsonni yuborish", + "@commandHint_sendraw": {}, + "databaseMigrationTitle": "Maʼlumotlar bazasi optimallashtirilgan", + "@databaseMigrationTitle": {}, + "databaseMigrationBody": "Iltimos, kuting. Bu biroz vaqt olishi mumkin.", + "@databaseMigrationBody": {}, + "leaveEmptyToClearStatus": "Holatingizni tozalash uchun boʻsh qoldiring.", + "@leaveEmptyToClearStatus": {}, + "select": "Tanlash", + "@select": {}, + "searchForUsers": "@users ni qidiring...", + "@searchForUsers": {}, + "pleaseEnterYourCurrentPassword": "Iltimos, joriy maxfiy soʻzingizni kiriting", + "@pleaseEnterYourCurrentPassword": {}, + "newPassword": "Yangi maxfiy soʻz", + "@newPassword": {}, + "pleaseChooseAStrongPassword": "Iltimos kuchli maxfiy soʻz tanlang", + "@pleaseChooseAStrongPassword": {}, + "passwordsDoNotMatch": "Maxfiy soʻzlar mos kelmadi", + "@passwordsDoNotMatch": {}, + "passwordIsWrong": "Siz kiritgan maxfiy soʻz xato", + "@passwordIsWrong": {}, + "publicLink": "Ommaviy havola", + "@publicLink": {}, + "createNewAddress": "Yangi manzil yarating", + "@createNewAddress": {}, + "joinSpace": "Maydonga qoʻshiling", + "@joinSpace": {}, + "publicSpaces": "Ommaviy maydonlar", + "@publicSpaces": {}, + "subspace": "Sub-maydonlar", + "@subspace": {}, + "decline": "Rad qilish", + "@decline": {}, + "thisDevice": "Ushbu qurilma:", + "@thisDevice": {}, + "initAppError": "Ilovani ishga tushirishda xatolik yuz berdi", + "@initAppError": {}, + "userRole": "Foydalanuvchi roli", + "@userRole": {}, + "minimumPowerLevel": "{level} minimal quvvat darajasidir.", + "@minimumPowerLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "String" + } + } + }, + "searchMore": "Koʻproq qidirish...", + "@searchMore": {}, + "gallery": "Galereya", + "@gallery": {}, + "files": "Fayllar", + "@files": {}, + "databaseBuildErrorBody": "SQlite maʼlumotlar bazasini yaratib boʻlmadi. Ilova hozircha eski maʼlumotlar bazasidan foydalanishga harakat qilmoqda. Iltimos, ushbu xato haqida {url} manzilidagi dasturchilarga xabar bering. Xato xabari: {error}", + "@databaseBuildErrorBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "sessionLostBody": "Seansingiz yoʻqoldi. Iltimos, ushbu xato haqida {url} manzilidagi dasturchilarga xabar bering. Xato xabari: {error}", + "@sessionLostBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "restoreSessionBody": "Ilova endi seansingizni zaxira nusxasidan tiklashga harakat qiladi. Iltimos, ushbu xato haqida {url} manzilidagi dasturchilarga xabar bering. Xato xabari: {error}", + "@restoreSessionBody": { + "type": "String", + "placeholders": { + "url": { + "type": "String" + }, + "error": { + "type": "String" + } + } + }, + "forwardMessageTo": "Xabarni {roomName}ga yoʻnaltirilsinmi?", + "@forwardMessageTo": { + "type": "String", + "placeholders": { + "roomName": { + "type": "String" + } + } + }, + "sendReadReceipts": "Oʻqilganlik haqida xabarnomalarni yuborish", + "@sendReadReceipts": {}, + "formattedMessages": "Formatlangan xabarlar", + "@formattedMessages": {}, + "formattedMessagesDescription": "Markdown yordamida qalin matn kabi boy xabar mazmunini koʻrsating.", + "@formattedMessagesDescription": {}, + "verifyOtherUser": "🔐 Boshqa foydalanuvchini tasdiqlang", + "@verifyOtherUser": {}, + "verifyOtherUserDescription": "Agar siz boshqa foydalanuvchini tasdiqlasangiz, aslida kimga yozayotganingizni bilishingizga amin boʻlishingiz mumkin. 💪\n\nTekshiruvni boshlaganingizda, siz va boshqa foydalanuvchi ilovada qalqib chiquvchi oynani koʻrasiz. Keyin u yerda siz bir-biringiz bilan taqqoslashingiz kerak boʻlgan bir qator emojilar yoki raqamlarni koʻrasiz.\n\nBuning eng yaxshi usuli - uchrashish yoki video qoʻngʻiroqni boshlash. 👭", + "@verifyOtherUserDescription": {}, + "verifyOtherDevice": "🔐 Boshqa qurilmani tasdiqlang", + "@verifyOtherDevice": {}, + "verifyOtherDeviceDescription": "Boshqa qurilmani tasdiqlaganingizda, bu qurilmalar kalitlarni almashishi mumkin, bu umumiy xavfsizligingizni oshiradi. 💪 Tasdiqlashni boshlaganingizda, ikkala qurilmada ham ilovada qalqib chiquvchi oyna paydo bo‘ladi. U yerda siz bir-biri bilan taqqoslashingiz kerak bo‘lgan emojilar yoki raqamlar qatorini ko‘rasiz. Tasdiqlashni boshlashdan oldin ikkala qurilma ham yoningizda bo‘lgani ma’qul. ✓", + "@verifyOtherDeviceDescription": {}, + "acceptedKeyVerification": "{sender} kalit tekshiruvini qabul qildi", + "@acceptedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "contentNotificationSettings": "Kontent bildirishnomasi sozlamalari", + "@contentNotificationSettings": {}, + "generalNotificationSettings": "Umumiy bildirishnoma sozlamalari", + "@generalNotificationSettings": {}, + "roomNotificationSettings": "Xona bildirishnomasi sozlamalari", + "@roomNotificationSettings": {}, + "userSpecificNotificationSettings": "Foydalanuvchiga xos bildirishnoma sozlamalari", + "@userSpecificNotificationSettings": {}, + "otherNotificationSettings": "Boshqa bildirishnoma sozlamalari", + "@otherNotificationSettings": {}, + "notificationRuleContainsUserName": "Foydalanuvchi nomini ichiga oladi", + "@notificationRuleContainsUserName": {}, + "notificationRuleContainsUserNameDescription": "Xabarda foydalanuvchi nomi mavjud bo‘lsa, foydalanuvchiga xabar beradi.", + "@notificationRuleContainsUserNameDescription": {}, + "notificationRuleMaster": "Barcha bildirishnomalarni ovozsiz qilish", + "@notificationRuleMaster": {}, + "notificationRuleMasterDescription": "Boshqa barcha qoidalarni bekor qiladi va barcha bildirishnomalarni faolsizlantiradi.", + "@notificationRuleMasterDescription": {}, + "notificationRuleSuppressNotices": "Avtomatlashtirilgan xabarlarni o‘chirish", + "@notificationRuleSuppressNotices": {}, + "notificationRuleInviteForMe": "Men uchun taklif qilish", + "@notificationRuleInviteForMe": {}, + "notificationRuleInviteForMeDescription": "Foydalanuvchi xonaga taklif qilinganda unga xabar beradi.", + "@notificationRuleInviteForMeDescription": {}, + "notificationRuleMemberEvent": "A’zo tadbirlari", + "@notificationRuleMemberEvent": {}, + "notificationRuleMemberEventDescription": "Obuna tadbirlari uchun bildirishnomalarni o‘chiradi.", + "@notificationRuleMemberEventDescription": {}, + "notificationRuleIsUserMention": "Foydalanuvchi zikri", + "@notificationRuleIsUserMention": {}, + "notificationRuleIsUserMentionDescription": "Foydalanuvchi xabarida to‘g‘ridan-to‘g‘ri tilga olinganida unga xabar beradi.", + "@notificationRuleIsUserMentionDescription": {}, + "notificationRuleContainsDisplayName": "Tarkibida displey nomi bor", + "@notificationRuleContainsDisplayName": {}, + "notificationRuleContainsDisplayNameDescription": "Xabarda foydalanuvchining displey nomi mavjudligi haqida foydalanuvchiga xabar beradi.", + "@notificationRuleContainsDisplayNameDescription": {}, + "notificationRuleSuppressNoticesDescription": "Botlar kabi avtomatlashtirilgan mijozlardan kelgan bildirishnomalarni to‘xtatadi.", + "@notificationRuleSuppressNoticesDescription": {}, + "notificationRuleIsRoomMention": "Xonaga eslatma", + "@notificationRuleIsRoomMention": {}, + "notificationRuleIsRoomMentionDescription": "Xona zikri mavjudligida foydalanuvchiga xabar beradi.", + "@notificationRuleIsRoomMentionDescription": {}, + "notificationRuleRoomnotif": "Xona bildirishnomasi", + "@notificationRuleRoomnotif": {}, + "notificationRuleRoomnotifDescription": "Xabar tarkibida @room bo‘lsa, foydalanuvchiga xabar beradi.", + "@notificationRuleRoomnotifDescription": {}, + "notificationRuleReaction": "Reaksiya", + "@notificationRuleReaction": {}, + "notificationRuleReactionDescription": "Munosabat bildirishnomalarini o‘chiradi.", + "@notificationRuleReactionDescription": {}, + "canceledKeyVerification": "{sender} kalit tekshiruvini bekor qildi", + "@canceledKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "completedKeyVerification": "{sender} kalitni tasdiqlashni yakunladi", + "@completedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "isReadyForKeyVerification": "{sender} kalitni tasdiqlash uchun tayyor", + "@isReadyForKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "requestedKeyVerification": "{sender} kalitni tasdiqlash talabini yubordi", + "@requestedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "startedKeyVerification": "{sender} kalit tekshiruvini so‘radi", + "@startedKeyVerification": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + } + } + }, + "transparent": "Shaffof", + "@transparent": {}, + "incomingMessages": "Kiruvchi xabarlar", + "@incomingMessages": {}, + "stickers": "Stikerlar", + "@stickers": {}, + "discover": "Kashf etish", + "@discover": {}, + "commandHint_ignore": "Berilgan matriks ID e’tiborga olinmasin", + "@commandHint_ignore": {}, + "commandHint_unignore": "Berilgan matriks IDni e’tiborsiz qoldirish", + "@commandHint_unignore": {}, + "noDatabaseEncryption": "Bu platformada ma’lumotlar bazasini shifrlash ishlamaydi", + "@noDatabaseEncryption": {}, + "thereAreCountUsersBlocked": "Hozirda {count} ta foydalanuvchi bloklangan.", + "@thereAreCountUsersBlocked": { + "type": "String", + "count": {} + }, + "restricted": "Cheklangan", + "@restricted": {}, + "knockRestricted": "Taqillatish cheklangan", + "@knockRestricted": {}, + "goToSpace": "Maydonga o‘tish: {space}", + "@goToSpace": { + "type": "String", + "space": {} + }, + "markAsUnread": "Ochilmagan deb belgilash", + "@markAsUnread": {}, + "userLevel": "{level} - Foydalanuvchi", + "@userLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "moderatorLevel": "{level} - Moderator", + "@moderatorLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "adminLevel": "{level} - Admin", + "@adminLevel": { + "type": "String", + "placeholders": { + "level": { + "type": "int" + } + } + }, + "sendRoomNotifications": "@room bildirishnomalarini yuborish", + "@sendRoomNotifications": {}, + "updateInstalled": "🎉 {version} versiyasiga yangilandi!", + "@updateInstalled": { + "type": "String", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "changelog": "O‘zgarishlar jurnali", + "@changelog": {}, + "sendCanceled": "Yuborish bekor qilindi", + "@sendCanceled": {}, + "loginWithMatrixId": "Matriks-ID bilan kirish", + "@loginWithMatrixId": {}, + "discoverHomeservers": "Uy serverlarini kashf eting", + "@discoverHomeservers": {}, + "whatIsAHomeserver": "Uy serveri nima?", + "@whatIsAHomeserver": {}, + "homeserverDescription": "Barcha ma’lumotlaringiz xuddi elektron pochta provayderi kabi homeserverda saqlanadi. Siz qaysi uy serveridan foydalanishni tanlashingiz mumkin, shu bilan birga siz hamma bilan muloqot qilishingiz mumkin. Batafsil: https://matrix.org.", + "@homeserverDescription": {}, + "doesNotSeemToBeAValidHomeserver": "Uy serveri mos emasga o‘xshaydi. URL xato kiritilganmi?", + "@doesNotSeemToBeAValidHomeserver": {}, + "calculatingFileSize": "Fayl hajmi hisoblanmoqda...", + "@calculatingFileSize": {}, + "prepareSendingAttachment": "Yuborish uchun biriktirmani tayyorlang...", + "@prepareSendingAttachment": {}, + "sendingAttachment": "Biriktirish yuborilmoqda...", + "@sendingAttachment": {}, + "generatingVideoThumbnail": "Video eskizi yaratilmoqda...", + "@generatingVideoThumbnail": {}, + "compressVideo": "Video siqilmoqda...", + "@compressVideo": {}, + "sendingAttachmentCountOfCount": "Biriktirma yuborilmoqda: {index} of {length}...", + "@sendingAttachmentCountOfCount": { + "type": "integer", + "placeholders": { + "index": { + "type": "int" + }, + "length": { + "type": "int" + } + } + }, + "serverLimitReached": "Server limiti tugadi! {seconds} soniya kutilmoqda...", + "@serverLimitReached": { + "type": "integer", + "placeholders": { + "seconds": { + "type": "int" + } + } + }, + "oneOfYourDevicesIsNotVerified": "Qurilmalaringizdan biri tasdiqlanmagan", + "@oneOfYourDevicesIsNotVerified": {}, + "continueText": "Davom ettirish", + "@continueText": {}, + "blur": "Xiralashtirish:", + "@blur": {}, + "opacity": "Noaniqlik:", + "@opacity": {}, + "setWallpaper": "Fon rasmini sozlash", + "@setWallpaper": {}, + "manageAccount": "Hisobni boshqarish", + "@manageAccount": {}, + "noContactInformationProvided": "Server hech qanday yaroqli kontakt axborotini taqdim etmaydi", + "@noContactInformationProvided": {}, + "contactServerAdmin": "Server administratori bilan bog‘lanish", + "@contactServerAdmin": {}, + "contactServerSecurity": "Aloqa serveri xavfsizligi", + "@contactServerSecurity": {}, + "supportPage": "Yordam sahifasi", + "@supportPage": {}, + "serverInformation": "Server haqida ma’lumot:", + "@serverInformation": {}, + "name": "Nomi", + "@name": {}, + "version": "Versiya", + "@version": {}, + "website": "Sayt", + "@website": {}, + "compress": "Siqmoq", + "@compress": {}, + "boldText": "Qalin matn", + "@boldText": {}, + "italicText": "Qiya matn", + "@italicText": {}, + "strikeThrough": "O‘tish joyi", + "@strikeThrough": {}, + "pleaseFillOut": "Iltimos, to‘ldiring", + "@pleaseFillOut": {}, + "invalidUrl": "Yaroqsiz url", + "@invalidUrl": {}, + "addLink": "Havola kiritish", + "@addLink": {}, + "previous": "Avvalgi", + "@previous": {}, + "otherPartyNotLoggedIn": "Narigi tomon hozirda hisobingizga kirmagan va shuning uchun xabarlarni qabul qila olmaydi!", + "@otherPartyNotLoggedIn": {}, + "appWantsToUseForLogin": "Hisobga kirish '{server}' ishlating", + "@appWantsToUseForLogin": { + "type": "String", + "placeholders": { + "server": { + "type": "String" + } + } + }, + "appWantsToUseForLoginDescription": "Siz bu bilan ilova va veb-saytga siz haqingizdagi axborotni ulashishga ruxsat berasiz.", + "@appWantsToUseForLoginDescription": {}, + "open": "Ochish", + "@open": {}, + "waitingForServer": "Server kutilmoqda...", + "@waitingForServer": {}, + "notificationRuleRoomServerAcl": "Guruh serveri ACL", + "@notificationRuleRoomServerAcl": {}, + "notificationRuleRoomServerAclDescription": "Guruh serveriga kirishni boshqarish ro‘yxatlari (ACL) uchun bildirishnomalarni bostiradi.", + "@notificationRuleRoomServerAclDescription": {}, + "notificationRuleSuppressEdits": "Tahrirlarni bostirish", + "@notificationRuleSuppressEdits": {}, + "notificationRuleSuppressEditsDescription": "Tahrirlangan xabarlar uchun bildirishnomalarni o‘chiradi.", + "@notificationRuleSuppressEditsDescription": {}, + "notificationRuleCall": "Chaqiruv", + "@notificationRuleCall": {}, + "notificationRuleCallDescription": "Chaqiruvlar haqida foydalanuvchiga xabar beradi.", + "@notificationRuleCallDescription": {}, + "notificationRuleEncryptedRoomOneToOne": "Shifrlangan birga-bir guruh", + "@notificationRuleEncryptedRoomOneToOne": {}, + "notificationRuleEncryptedRoomOneToOneDescription": "Shifrlangan birga-bir guruhlardagi xabarlar haqida foydalanuvchiga xabar beradi.", + "@notificationRuleEncryptedRoomOneToOneDescription": {}, + "notificationRuleTombstoneDescription": "Xonani faolsizlantirish xabarlari haqida foydalanuvchiga xabar beradi.", + "@notificationRuleTombstoneDescription": {}, + "notificationRuleRoomOneToOne": "Birga-bir guruh", + "@notificationRuleRoomOneToOne": {}, + "notificationRuleRoomOneToOneDescription": "Birga-bir guruhlardagidagi xabarlar haqida foydalanuvchiga xabar beradi.", + "@notificationRuleRoomOneToOneDescription": {}, + "notificationRuleMessage": "Xabar", + "@notificationRuleMessage": {}, + "notificationRuleMessageDescription": "Foydalanuvchiga umumiy xabarlar haqida xabar beradi.", + "@notificationRuleMessageDescription": {}, + "notificationRuleEncrypted": "Shifrlangan", + "@notificationRuleEncrypted": {}, + "notificationRuleEncryptedDescription": "Shifrlangan guruhlardagi xabarlar haqida foydalanuvchiga xabar beradi.", + "@notificationRuleEncryptedDescription": {}, + "notificationRuleJitsi": "Jitsi", + "@notificationRuleJitsi": {}, + "notificationRuleJitsiDescription": "Jitsi vidjet hodisalari haqida foydalanuvchiga xabar beradi.", + "@notificationRuleJitsiDescription": {}, + "notificationRuleServerAcl": "Server ACL hodisalarini bostirish", + "@notificationRuleServerAcl": {}, + "notificationRuleServerAclDescription": "Server ACL hodisalari uchun bildirishnomalarni o‘chiradi.", + "@notificationRuleServerAclDescription": {}, + "unknownPushRule": "Noma’lum push qoidasi '{rule}'", + "@unknownPushRule": { + "type": "String", + "placeholders": { + "rule": { + "type": "String" + } + } + }, + "sentVoiceMessage": "️ 🎙️{duration} - {sender}dan ovozli xabar", + "@sentVoiceMessage": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "duration": { + "type": "String" + } + } + }, + "deletePushRuleCanNotBeUndone": "Agar ushbu bildirishnoma sozlamasini o‘chirib tashlasangiz, buni bekor qilib bo‘lmaydi.", + "@deletePushRuleCanNotBeUndone": {}, + "more": "Yana", + "@more": {}, + "shareKeysWith": "Kalitlarni ulashish...", + "@shareKeysWith": {}, + "allDevices": "Barcha qurilmalar", + "@allDevices": {}, + "crossVerifiedDevicesIfEnabled": "Agar yoqilgan bo‘lsa, tasdiqlangan qurilmalarni kesib o‘tish", + "@crossVerifiedDevicesIfEnabled": {}, + "crossVerifiedDevices": "O‘zaro tekshirilgan qurilmalar", + "@crossVerifiedDevices": {}, + "verifiedDevicesOnly": "Faqat tasdiqlangan qurilmalar", + "@verifiedDevicesOnly": {}, + "takeAPhoto": "Suratga olish", + "@takeAPhoto": {}, + "recordAVideo": "Video yozib olish", + "@recordAVideo": {}, + "optionalMessage": "(Ixtiyoriy) xabar...", + "@optionalMessage": {}, + "notSupportedOnThisDevice": "Bu qurilmada ishlamaydi", + "@notSupportedOnThisDevice": {}, + "approve": "Tasdiqlash", + "@approve": {}, + "youHaveKnocked": "Siz taqillatdingiz", + "@youHaveKnocked": {}, + "pleaseWaitUntilInvited": "Iltimos, hozir kutib turing, xonadan kimdir sizni taklif qilguncha.", + "@pleaseWaitUntilInvited": {}, + "commandHint_logout": "Joriy qurilmadan chiqish", + "@commandHint_logout": {}, + "commandHint_logoutall": "Barcha faol qurilmalardan chiqish", + "@commandHint_logoutall": {}, + "displayNavigationRail": "Mobilda navigatsiya temir yo‘lini ko‘rsatish", + "@displayNavigationRail": {}, + "customReaction": "Maxsus reaksiya", + "@customReaction": {}, + "moreEvents": "Boshqa hodisalar", + "@moreEvents": {}, + "declineInvitation": "Taklifni rad etish", + "@declineInvitation": {}, + "noMessagesYet": "Hozircha xabarlar yo‘q", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "Ovozli xabarni yozib olish uchun uzoq bosing.", + "@longPressToRecordVoiceMessage": {}, + "pause": "Pauza", + "@pause": {}, + "resume": "Davom etish", + "@resume": {}, + "newSubSpace": "Yangi quyi maydon", + "@newSubSpace": {}, + "moveToDifferentSpace": "Boshqa maydonga o‘tish", + "@moveToDifferentSpace": {}, + "moveUp": "Yuqoriga surish", + "@moveUp": {}, + "moveDown": "Pastga surish", + "@moveDown": {}, + "spaceMemberOf": "{spaces} maydoni a’zosi", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "{spaces} maydoni a’zosi eshikni taqillatishi mumkin", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "Xayriya qilmoq", + "@donate": {}, + "startedAPoll": "{username} so‘rovnoma boshladi.", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "So‘rov", + "@poll": {}, + "startPoll": "So‘rovni boshlash", + "@startPoll": {}, + "endPoll": "So‘rovnomani yakunlash", + "@endPoll": {}, + "answersVisible": "Javoblar ko‘rinadi", + "@answersVisible": {}, + "answersHidden": "Javoblar berkitildi", + "@answersHidden": {}, + "pollQuestion": "So‘rovnoma savoli", + "@pollQuestion": {}, + "answerOption": "Javob varianti", + "@answerOption": {}, + "addAnswerOption": "Javob variantini kiritish", + "@addAnswerOption": {}, + "allowMultipleAnswers": "Bir nechta javobga ruxsat berish", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "So‘rovnoma yakunlandi", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{Bir ovoz} other{{count} ta ovoz}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "So‘rovnoma tugaganida javoblar chiqadi", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "replyInThread": "Sahifada javob berish", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{Bitta javob} other{{count} ta javob}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "thread": "Sahifa", + "@thread": {}, + "widgetEtherpad": "Matnli qayd", + "@widgetEtherpad": {}, + "widgetJitsi": "Jitsi Meet", + "@widgetJitsi": {}, + "widgetCustom": "Maxsus", + "@widgetCustom": {}, + "widgetName": "Nomi", + "@widgetName": {}, + "widgetUrlError": "Bu yaroqli URL emas.", + "@widgetUrlError": {}, + "widgetNameError": "Iltimos, displey nomini kiriting.", + "@widgetNameError": {}, + "errorAddingWidget": "Vidjet kiritilmadi.", + "@errorAddingWidget": {}, + "youRejectedTheInvitation": "Taklifni rad etdingiz", + "@youRejectedTheInvitation": {}, + "youAcceptedTheInvitation": "👍 Taklifni qabul qildingiz", + "@youAcceptedTheInvitation": {}, + "youBannedUser": "Siz {user}ni blokladingiz", + "@youBannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youHaveWithdrawnTheInvitationFor": "Siz {user} uchun taklifnomani bekor qildingiz", + "@youHaveWithdrawnTheInvitationFor": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youInvitedToBy": "📩 Sizni quyidagi havola orqali taklif qilishdi:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": { + "type": "String" + } + } + }, + "youInvitedBy": "📩 Sizni {user} taklif qildi", + "@youInvitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "invitedBy": "📩 {user} taklif qilgan", + "@invitedBy": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youInvitedUser": "📩 Siz {user}ni taklif qildingiz", + "@youInvitedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youKicked": "👞 Siz {user}ni chiqarib yubordingiz", + "@youKicked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youKickedAndBanned": "🙅 Siz {user}ni chiqardingiz va blokladingiz", + "@youKickedAndBanned": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "youUnbannedUser": "{user}ni blokdan chiqardingiz", + "@youUnbannedUser": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "hasKnocked": "🚪 {user} taqillatdi", + "@hasKnocked": { + "placeholders": { + "user": { + "type": "String" + } + } + }, + "makeAdminDescription": "Bu foydalanuvchini admini qilsangiz, uni bekor qila olmasligingiz mumkin, chunki u siz bilan bir xil ruxsatlarga ega bo‘ladi.", + "@makeAdminDescription": {}, + "pushNotificationsNotAvailable": "Push-bildirishnomalar mavjud emas", + "@pushNotificationsNotAvailable": {}, + "learnMore": "Batafsil", + "@learnMore": {}, + "yourGlobalUserIdIs": "Global foydalanuvchi ID raqamingiz: ", + "@yourGlobalUserIdIs": {}, + "notificationRuleTombstone": "Qabrtosh", + "@notificationRuleTombstone": {}, + "identity": "Shaxs", + "@identity": { + "type": "String", + "placeholders": {} + }, + "emojis": "Emojilar", + "@emojis": {}, + "changedTheChatDescription": "{username} chat tavsifini o‘zgartirdi", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} chat nomini o‘zgartirdi", + "@changedTheChatName": {}, + "saveChanges": "O‘zgarishlarni saqlash", + "@saveChanges": {}, + "createSticker": "Stiker yoki emoji yaratish", + "@createSticker": {}, + "useAsSticker": "Stiker sifatida ishlatish", + "@useAsSticker": {}, + "useAsEmoji": "Emoji sifatida ishlatish", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "Stiker paketi nomi allaqachon mavjud", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "Yangi stikerlar paketi", + "@newStickerPack": {}, + "stickerPackName": "Stiker paketi nomi", + "@stickerPackName": {}, + "attribution": "Atributsiya", + "@attribution": {}, + "skipChatBackup": "Chat zaxirasini tashlab ketish", + "@skipChatBackup": {}, + "skipChatBackupWarning": "Ishonchingiz komilmi? Chat zaxirasini yoqmasdan qurilmangizni almashtirsangiz, xabarlaringizga kira olmay qolishingiz mumkin.", + "@skipChatBackupWarning": {}, + "loadingMessages": "Xabarlar yuklanmoqda", + "@loadingMessages": {}, + "setupChatBackup": "Chat zaxirasini sozlash", + "@setupChatBackup": {} +} diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index f11eeaae4..1f101bb6b 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -445,7 +445,7 @@ "type": "String", "placeholders": {} }, - "badServerVersionsException": "Máy chủ nhà hỗ trợ Spec phiên bản:\n{serverVerions}\nNhưng ứng dụng này chỉ hỗ trợ {supportedVersions}", + "badServerVersionsException": "Máy chủ hỗ trợ Spec phiên bản:\n{serverVerions}\nNhưng ứng dụng này chỉ hỗ trợ {supportedVersions}", "@badServerVersionsException": { "type": "String", "placeholders": { @@ -601,15 +601,6 @@ "@noOtherDevicesFound": {}, "fileIsTooBigForServer": "Máy chủ báo cáo rằng tệp tin quá lớn để gửi.", "@fileIsTooBigForServer": {}, - "signInWith": "Đăng nhập với {provider}", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "notAnImage": "Không phải tệp ảnh.", "@notAnImage": {}, "importNow": "Nhập vào", @@ -719,7 +710,7 @@ } } }, - "alwaysUse24HourFormat": "Không", + "alwaysUse24HourFormat": "không", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -732,9 +723,19 @@ } } }, - "commandHint_googly": "Gửi cặp mắt theo dõi", - "commandHint_cuddle": "Gửi một cái âu yếm", - "googlyEyesContent": "{senderName} gửi bạn cặp mắt theo dõi", + "setCustomPermissionLevel": "Cài mức phân quyền", + "@setCustomPermissionLevel": {}, + "setPermissionsLevelDescription": "Vui lòng chọn vai trò được xác định trước bên dưới hoặc nhập mức quyền tùy chỉnh từ 0 đến 100.", + "@setPermissionsLevelDescription": {}, + "ignoreUser": "Chặn người dùng", + "@ignoreUser": {}, + "normalUser": "Người dùng thường", + "@normalUser": {}, + "commandHint_roomupgrade": "Nâng cấp phòng lên phiên bản mặc định", + "@commandHint_roomupgrade": {}, + "commandHint_cuddle": "Gửi cái ôm", + "@commandHint_cuddle": {}, + "googlyEyesContent": "{senderName} gửi nháy mắt", "@googlyEyesContent": { "type": "String", "placeholders": { @@ -743,7 +744,7 @@ } } }, - "cuddleContent": "{senderName} âu yếm bạn", + "cuddleContent": "{senderName} ôm nào", "@cuddleContent": { "type": "String", "placeholders": { @@ -752,11 +753,56 @@ } } }, - "askSSSSSign": "Để có thể ký cho người khác, vui lòng nhập mật khẩu kho lưu trữ an toàn hoặc khóa phục hồi của bạn.", - "autoplayImages": "Tự động phát những biểu tượng cảm xúc và nhãn dán động", - "sendTypingNotifications": "Gửi thông báo đang gõ", - "swipeRightToLeftToReply": "Quét từ phải sang trái để trả lời", - "sendOnEnter": "Gửi khi nhấn Enter", + "askSSSSSign": "Để có thể ký tên cho người khác, vui lòng nhập mật khẩu lưu trữ an toàn hoặc khóa khôi phục của bạn.", + "@askSSSSSign": { + "type": "String", + "placeholders": {} + }, + "autoplayImages": "Tự động chạy nhãn dán hình động và biểu tượng cảm xúc", + "@autoplayImages": { + "type": "String", + "placeholder": {} + }, + "sendTypingNotifications": "Gửi thông báo đang nhập", + "@sendTypingNotifications": {}, + "swipeRightToLeftToReply": "Gạt từ phải sang trái để trả lời", + "@swipeRightToLeftToReply": {}, + "sendOnEnter": "Gửi bằng Enter", + "@sendOnEnter": {}, + "continueText": "Tiếp", + "@continueText": {}, + "videoWithSize": "Video ({size})", + "@videoWithSize": { + "type": "String", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "markAsRead": "Đánh dấu đã đọc", + "@markAsRead": {}, + "reportUser": "Báo cáo người dùng", + "@reportUser": {}, + "dismiss": "Bỏ", + "@dismiss": {}, + "reactedWith": "{sender} bày tỏ {reaction}", + "@reactedWith": { + "type": "String", + "placeholders": { + "sender": { + "type": "String" + }, + "reaction": { + "type": "String" + } + } + }, + "pinMessage": "Ghim phòng", + "@pinMessage": {}, + "confirmEventUnpin": "Bạn có muốn bỏ ghim sự kiện?", + "@confirmEventUnpin": {}, + "commandHint_googly": "Gửi cặp mắt theo dõi", "countChatsAndCountParticipants": "{chats} cuộc trò chuyện và {participants} thành viên", "@countChatsAndCountParticipants": { "type": "String", @@ -1549,33 +1595,7 @@ "start": "Bắt đầu", "pleaseEnterRecoveryKeyDescription": "Để mở khóa tin nhắn cũ, hãy nhập khóa khôi phục đã tạo ở phiên trước. Khóa khôi phục KHÔNG phải là mật khẩu.", "publish": "Công khai", - "videoWithSize": "Video ({size})", - "@videoWithSize": { - "type": "String", - "placeholders": { - "size": { - "type": "String" - } - } - }, "openChat": "Mở trò chuyện", - "markAsRead": "Đánh dấu đã đọc", - "reportUser": "Báo cáo người dùng", - "dismiss": "Bỏ qua", - "reactedWith": "{sender} đã phản ứng với {reaction}", - "@reactedWith": { - "type": "String", - "placeholders": { - "sender": { - "type": "String" - }, - "reaction": { - "type": "String" - } - } - }, - "pinMessage": "Ghim vào phòng", - "confirmEventUnpin": "Bạn có chắc muốn bỏ ghim sự kiện này không?", "emojis": "Biểu tượng cảm xúc", "placeCall": "Gọi", "voiceCall": "Gọi thoại", @@ -2456,7 +2476,6 @@ } }, "l1TranslationBody": "Tin nhắn trong ngôn ngữ gốc sẽ không được dịch.", - "continueText": "Tiếp tục", "deleteSubscriptionWarningTitle": "Bạn đang có gói đăng ký hoạt động", "deleteSubscriptionWarningBody": "Việc xóa tài khoản sẽ không hủy gói đăng ký của bạn.", "manageSubscription": "Quản lý gói đăng ký", @@ -3202,11 +3221,6 @@ } } }, - "setCustomPermissionLevel": "Đặt cấp độ quyền tùy chỉnh", - "setPermissionsLevelDescription": "Vui lòng chọn một vai trò đã định nghĩa sẵn bên dưới hoặc nhập một cấp độ quyền tùy chỉnh từ 0 đến 100.", - "ignoreUser": "Bỏ qua người dùng", - "normalUser": "Người dùng bình thường", - "commandHint_roomupgrade": "Nâng cấp phòng này lên phiên bản phòng đã cho", "enterNewChat": "Tham gia trò chuyện mới", "completeActivitiesToUnlock": "Hoàn thành ít nhất một hoạt động để mở khóa bản dịch!", "constructUseGaDesc": "Hỗ trợ ngữ pháp", @@ -3263,26 +3277,6 @@ "shareInviteCode": "Chia sẻ mã mời: {code}", "leaderboard": "Bảng xếp hạng", "skipForNow": "Bỏ qua tạm thời", - "@setCustomPermissionLevel": { - "type": "String", - "placeholders": {} - }, - "@setPermissionsLevelDescription": { - "type": "String", - "placeholders": {} - }, - "@ignoreUser": { - "type": "String", - "placeholders": {} - }, - "@normalUser": { - "type": "String", - "placeholders": {} - }, - "@commandHint_roomupgrade": { - "type": "String", - "placeholders": {} - }, "@enterNewChat": { "type": "String", "placeholders": {} @@ -6643,4 +6637,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index 5d2d0d348..bb5132e76 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -496,7 +496,6 @@ "report": "報告", "signInWithPassword": "用密碼登入", "pleaseTryAgainLaterOrChooseDifferentServer": "請稍後再試或選擇不同的伺服器。", - "signInWith": "用 {provider} 登入", "profileNotFound": "在伺服器上找不到該用戶。可能是連線問題或用戶不存在。", "setTheme": "設定主題:", "setColorTheme": "設定色彩主題:", @@ -4224,14 +4223,6 @@ "type": "String", "placeholders": {} }, - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "@profileNotFound": { "type": "String", "placeholders": {} @@ -4642,9 +4633,7 @@ }, "@thereAreCountUsersBlocked": { "type": "String", - "placeholders": { - "count": {} - } + "count": {} }, "@restricted": { "type": "String", @@ -12156,4 +12145,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_yue_Hant.arb.backup b/lib/l10n/intl_yue_Hant.arb.backup index 58241d12c..cce50f372 100644 --- a/lib/l10n/intl_yue_Hant.arb.backup +++ b/lib/l10n/intl_yue_Hant.arb.backup @@ -1,337 +1,337 @@ { - "@@locale": "yue", - "normalUser": "正常用家", - "@normalUser": {}, - "areYouSureYouWantToLogout": "係咪確定要 log out?", - "@areYouSureYouWantToLogout": { - "type": "String", - "placeholders": {} - }, - "areGuestsAllowedToJoin": "畀唔畀陌生人 Join", - "@areGuestsAllowedToJoin": { - "type": "String", - "placeholders": {} - }, - "askSSSSSign": "要向其他人簽名,請輸入你 Keep 好咗嘅密碼或者恢復密鑰。", - "@askSSSSSign": { - "type": "String", - "placeholders": {} - }, - "badServerLoginTypesException": "呢個 Homeserver 支持嘅登錄類型有:\n{serverVersions}\n但係呢個 App 淨係支援:\n{supportedVersions}", - "@badServerLoginTypesException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "cantOpenUri": "打唔開嘅 URI {uri}", - "@cantOpenUri": { - "type": "String", - "placeholders": { - "uri": { - "type": "String" - } - } - }, - "badServerVersionsException": "呢個 Homeserver 支持以下 Spec 版本:\n{serverVersions}\n但係個 App 淨係支持 {supoortedVersions} 版本", - "@badServerVersionsException": { - "type": "String", - "placeholders": { - "serverVersions": { - "type": "String" - }, - "supportedVersions": { - "type": "String" - } - } - }, - "banFromChat": "喺傾偈入面 Ban 咗佢", - "@banFromChat": { - "type": "String", - "placeholders": {} - }, - "noChatsFoundHere": "暫時未有偈傾。撳下面粒掣同人開始傾偈 ⤵️", - "@noChatsFoundHere": {}, - "bannedUser": "{username} Ban 咗 {targetName}", - "@bannedUser": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "targetName": { - "type": "String" - } - } - }, - "changedTheChatDescriptionTo": "{username} 改咗呢個偈嘅介紹: 「'{description}'」", - "@changedTheChatDescriptionTo": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - }, - "description": { - "type": "String" - } - } - }, - "addEmail": "加 Email", - "@addEmail": { - "type": "String", - "placeholders": {} - }, - "alwaysUse24HourFormat": "false", - "@alwaysUse24HourFormat": { - "description": "Set to true to always display time of day in 24 hour format." - }, - "importEmojis": "導入 Emoji", - "@importEmojis": {}, - "hugContent": "{senderName} 抱咗你", - "@hugContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "commandHint_hug": "Send 個 Hug", - "@commandHint_hug": {}, - "anyoneCanJoin": "任何人都可以 Join", - "@anyoneCanJoin": { - "type": "String", - "placeholders": {} - }, - "repeatPassword": "輸入多一次密碼", - "@repeatPassword": {}, - "notAnImage": "唔係圖檔。", - "@notAnImage": {}, - "remove": "刪走", - "@remove": { - "type": "String", - "placeholders": {} - }, - "importNow": "即刻導入", - "@importNow": {}, - "exportEmotePack": "將表情符號導出成 .zip 檔案", - "@exportEmotePack": {}, - "replace": "換走", - "@replace": {}, - "about": "關於", - "@about": {}, - "aboutHomeserver": "關於{homeserver}", - "@aboutHomeserver": { - "type": "String", - "placeholders": { - "homeserver": { - "type": "String" - } - } - }, - "accept": "同意", - "@accept": { - "type": "String", - "placeholders": {} - }, - "acceptedTheInvitation": "👍 {username} 同意咗邀请", - "@acceptedTheInvitation": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "account": "Account", - "@account": { - "type": "String", - "placeholders": {} - }, - "activatedEndToEndEncryption": "🔐 {username} 開咗點對點加密", - "@activatedEndToEndEncryption": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "supposedMxid": "呢度應該係 {mxid}", - "@supposedMxid": { - "type": "String", - "placeholders": { - "mxid": { - "type": "String" - } - } - }, - "addChatDescription": "講下關於呢個偈係傾嘅乜嘢……", - "@addChatDescription": {}, - "addToSpace": "加落去空間嗰度", - "@addToSpace": {}, - "admin": "Admin", - "@admin": { - "type": "String", - "placeholders": {} - }, - "alias": "花名", - "@alias": { - "type": "String", - "placeholders": {} - }, - "all": "全部", - "@all": { - "type": "String", - "placeholders": {} - }, - "allChats": "所有傾嘅偈仔", - "@allChats": { - "type": "String", - "placeholders": {} - }, - "commandHint_roomupgrade": "將呢間房升級到指定版本", - "@commandHint_roomupgrade": {}, - "commandHint_googly": "送啲古靈精怪表情過去", - "@commandHint_googly": {}, - "commandHint_cuddle": "Send 個攬攬", - "@commandHint_cuddle": {}, - "googlyEyesContent": "{senderName} Send 咗你一個咕嚕眼", - "@googlyEyesContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "cuddleContent": "{senderName} 攬咗你", - "@cuddleContent": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "answeredTheCall": "{senderName} 聽咗你嘅電話", - "@answeredTheCall": { - "type": "String", - "placeholders": { - "senderName": { - "type": "String" - } - } - }, - "appLock": "App 鎖", - "@appLock": { - "type": "String", - "placeholders": {} - }, - "appLockDescription": "無人用嘅時候用密碼鎖住個App", - "@appLockDescription": {}, - "archive": "存檔", - "@archive": { - "type": "String", - "placeholders": {} - }, - "areYouSure": "咪住先?", - "@areYouSure": { - "type": "String", - "placeholders": {} - }, - "askVerificationRequest": "係咪要 Accept 來自 {username} 嘅驗證申請?", - "@askVerificationRequest": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "autoplayImages": "自動播放動畫貼紙同表情", - "@autoplayImages": { - "type": "String", - "placeholder": {} - }, - "sendTypingNotifications": "通知人地打緊字", - "@sendTypingNotifications": {}, - "swipeRightToLeftToReply": "向左滑嚟回覆", - "@swipeRightToLeftToReply": {}, - "sendOnEnter": "撳 Enter 即 Send", - "@sendOnEnter": {}, - "countChatsAndCountParticipants": "{chats} 間房同 {participants} 條友", - "@countChatsAndCountParticipants": { - "type": "String", - "placeholders": { - "chats": { - "type": "int" - }, - "participants": { - "type": "int" - } - } - }, - "noMoreChatsFound": "搵唔到更多偈傾啦…", - "@noMoreChatsFound": {}, - "joinedChats": "入咗嘅房間", - "@joinedChats": {}, - "unread": "未讀", - "@unread": {}, - "space": "空間", - "@space": {}, - "spaces": "空間", - "@spaces": {}, - "banned": "Block 咗", - "@banned": { - "type": "String", - "placeholders": {} - }, - "blockDevice": "Block 咗嘅裝置", - "@blockDevice": { - "type": "String", - "placeholders": {} - }, - "blocked": "Block 咗", - "@blocked": { - "type": "String", - "placeholders": {} - }, - "botMessages": "機械人訊息", - "@botMessages": { - "type": "String", - "placeholders": {} - }, - "cancel": "取消", - "@cancel": { - "type": "String", - "placeholders": {} - }, - "changeDeviceName": "改裝置名", - "@changeDeviceName": { - "type": "String", - "placeholders": {} - }, - "changedTheChatAvatar": "{username}轉咗個大頭貼", - "@changedTheChatAvatar": { - "type": "String", - "placeholders": { - "username": { - "type": "String" - } - } - }, - "confirmMatrixId": "Confirm 你嘅 Matrix ID ,我哋先至可以刪除你嘅 Account。", - "@confirmMatrixId": {}, - "setCustomPermissionLevel": "自訂權限級別", - "@setCustomPermissionLevel": {}, - "importFromZipFile": "喺 .zip 檔案導入", - "@importFromZipFile": {}, - "setPermissionsLevelDescription": "請喺下面選擇一個預定義嘅角色,或輸入介乎0同100之間嘅自定義權限級別。", - "@setPermissionsLevelDescription": {} -} \ No newline at end of file + "@@locale": "yue", + "normalUser": "正常用家", + "@normalUser": {}, + "areYouSureYouWantToLogout": "係咪確定要 log out?", + "@areYouSureYouWantToLogout": { + "type": "String", + "placeholders": {} + }, + "areGuestsAllowedToJoin": "畀唔畀陌生人 Join", + "@areGuestsAllowedToJoin": { + "type": "String", + "placeholders": {} + }, + "askSSSSSign": "要向其他人簽名,請輸入你 Keep 好咗嘅密碼或者恢復密鑰。", + "@askSSSSSign": { + "type": "String", + "placeholders": {} + }, + "badServerLoginTypesException": "呢個 Homeserver 支持嘅登錄類型有:\n{serverVersions}\n但係呢個 App 淨係支援:\n{supportedVersions}", + "@badServerLoginTypesException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "cantOpenUri": "打唔開嘅 URI {uri}", + "@cantOpenUri": { + "type": "String", + "placeholders": { + "uri": { + "type": "String" + } + } + }, + "badServerVersionsException": "呢個 Homeserver 支持以下 Spec 版本:\n{serverVersions}\n但係個 App 淨係支持 {supoortedVersions} 版本", + "@badServerVersionsException": { + "type": "String", + "placeholders": { + "serverVersions": { + "type": "String" + }, + "supportedVersions": { + "type": "String" + } + } + }, + "banFromChat": "喺傾偈入面 Ban 咗佢", + "@banFromChat": { + "type": "String", + "placeholders": {} + }, + "noChatsFoundHere": "暫時未有偈傾。撳下面粒掣同人開始傾偈 ⤵️", + "@noChatsFoundHere": {}, + "bannedUser": "{username} Ban 咗 {targetName}", + "@bannedUser": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "targetName": { + "type": "String" + } + } + }, + "changedTheChatDescriptionTo": "{username} 改咗呢個偈嘅介紹: 「'{description}'」", + "@changedTheChatDescriptionTo": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + }, + "description": { + "type": "String" + } + } + }, + "addEmail": "加 Email", + "@addEmail": { + "type": "String", + "placeholders": {} + }, + "alwaysUse24HourFormat": "false", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + }, + "importEmojis": "導入 Emoji", + "@importEmojis": {}, + "hugContent": "{senderName} 抱咗你", + "@hugContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "commandHint_hug": "Send 個 Hug", + "@commandHint_hug": {}, + "anyoneCanJoin": "任何人都可以 Join", + "@anyoneCanJoin": { + "type": "String", + "placeholders": {} + }, + "repeatPassword": "輸入多一次密碼", + "@repeatPassword": {}, + "notAnImage": "唔係圖檔。", + "@notAnImage": {}, + "remove": "刪走", + "@remove": { + "type": "String", + "placeholders": {} + }, + "importNow": "即刻導入", + "@importNow": {}, + "exportEmotePack": "將表情符號導出成 .zip 檔案", + "@exportEmotePack": {}, + "replace": "換走", + "@replace": {}, + "about": "關於", + "@about": {}, + "aboutHomeserver": "關於{homeserver}", + "@aboutHomeserver": { + "type": "String", + "placeholders": { + "homeserver": { + "type": "String" + } + } + }, + "accept": "同意", + "@accept": { + "type": "String", + "placeholders": {} + }, + "acceptedTheInvitation": "👍 {username} 同意咗邀请", + "@acceptedTheInvitation": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "account": "Account", + "@account": { + "type": "String", + "placeholders": {} + }, + "activatedEndToEndEncryption": "🔐 {username} 開咗點對點加密", + "@activatedEndToEndEncryption": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "supposedMxid": "呢度應該係 {mxid}", + "@supposedMxid": { + "type": "String", + "placeholders": { + "mxid": { + "type": "String" + } + } + }, + "addChatDescription": "講下關於呢個偈係傾嘅乜嘢……", + "@addChatDescription": {}, + "addToSpace": "加落去空間嗰度", + "@addToSpace": {}, + "admin": "Admin", + "@admin": { + "type": "String", + "placeholders": {} + }, + "alias": "花名", + "@alias": { + "type": "String", + "placeholders": {} + }, + "all": "全部", + "@all": { + "type": "String", + "placeholders": {} + }, + "allChats": "所有傾嘅偈仔", + "@allChats": { + "type": "String", + "placeholders": {} + }, + "commandHint_roomupgrade": "將呢間房升級到指定版本", + "@commandHint_roomupgrade": {}, + "commandHint_googly": "送啲古靈精怪表情過去", + "@commandHint_googly": {}, + "commandHint_cuddle": "Send 個攬攬", + "@commandHint_cuddle": {}, + "googlyEyesContent": "{senderName} Send 咗你一個咕嚕眼", + "@googlyEyesContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "cuddleContent": "{senderName} 攬咗你", + "@cuddleContent": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "answeredTheCall": "{senderName} 聽咗你嘅電話", + "@answeredTheCall": { + "type": "String", + "placeholders": { + "senderName": { + "type": "String" + } + } + }, + "appLock": "App 鎖", + "@appLock": { + "type": "String", + "placeholders": {} + }, + "appLockDescription": "無人用嘅時候用密碼鎖住個App", + "@appLockDescription": {}, + "archive": "存檔", + "@archive": { + "type": "String", + "placeholders": {} + }, + "areYouSure": "咪住先?", + "@areYouSure": { + "type": "String", + "placeholders": {} + }, + "askVerificationRequest": "係咪要 Accept 來自 {username} 嘅驗證申請?", + "@askVerificationRequest": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "autoplayImages": "自動播放動畫貼紙同表情", + "@autoplayImages": { + "type": "String", + "placeholder": {} + }, + "sendTypingNotifications": "通知人地打緊字", + "@sendTypingNotifications": {}, + "swipeRightToLeftToReply": "向左滑嚟回覆", + "@swipeRightToLeftToReply": {}, + "sendOnEnter": "撳 Enter 即 Send", + "@sendOnEnter": {}, + "countChatsAndCountParticipants": "{chats} 間房同 {participants} 條友", + "@countChatsAndCountParticipants": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + }, + "participants": { + "type": "int" + } + } + }, + "noMoreChatsFound": "搵唔到更多偈傾啦…", + "@noMoreChatsFound": {}, + "joinedChats": "入咗嘅房間", + "@joinedChats": {}, + "unread": "未讀", + "@unread": {}, + "space": "空間", + "@space": {}, + "spaces": "空間", + "@spaces": {}, + "banned": "Block 咗", + "@banned": { + "type": "String", + "placeholders": {} + }, + "blockDevice": "Block 咗嘅裝置", + "@blockDevice": { + "type": "String", + "placeholders": {} + }, + "blocked": "Block 咗", + "@blocked": { + "type": "String", + "placeholders": {} + }, + "botMessages": "機械人訊息", + "@botMessages": { + "type": "String", + "placeholders": {} + }, + "cancel": "取消", + "@cancel": { + "type": "String", + "placeholders": {} + }, + "changeDeviceName": "改裝置名", + "@changeDeviceName": { + "type": "String", + "placeholders": {} + }, + "changedTheChatAvatar": "{username}轉咗個大頭貼", + "@changedTheChatAvatar": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "confirmMatrixId": "Confirm 你嘅 Matrix ID ,我哋先至可以刪除你嘅 Account。", + "@confirmMatrixId": {}, + "setCustomPermissionLevel": "自訂權限級別", + "@setCustomPermissionLevel": {}, + "importFromZipFile": "喺 .zip 檔案導入", + "@importFromZipFile": {}, + "setPermissionsLevelDescription": "請喺下面選擇一個預定義嘅角色,或輸入介乎0同100之間嘅自定義權限級別。", + "@setPermissionsLevelDescription": {} +} diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 1f2424e65..4ed07cf1f 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -88,7 +88,7 @@ "type": "String", "placeholders": {} }, - "areYouSureYouWantToLogout": "你确定要注销吗?", + "areYouSureYouWantToLogout": "你确定要退出登录吗?", "@areYouSureYouWantToLogout": { "type": "String", "placeholders": {} @@ -362,7 +362,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "你的旧消息受恢复密钥保护。请确保你不会丢失它。", + "chatBackupDescription": "你的消息受恢复密钥保护。请确保你不会丢失它。", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -1087,7 +1087,7 @@ } } }, - "logout": "注销", + "logout": "退出登录", "@logout": { "type": "String", "placeholders": {} @@ -1216,7 +1216,7 @@ "type": "String", "placeholders": {} }, - "ok": "好", + "ok": "确认", "@ok": { "type": "String", "placeholders": {} @@ -2375,7 +2375,7 @@ "@commandHint_markasdm": {}, "whyIsThisMessageEncrypted": "为什么此消息不可读?", "@whyIsThisMessageEncrypted": {}, - "noKeyForThisMessage": "如果消息是在你在此设备上登录账户前发送的,就可能发生这种情况。\n\n也有可能是发送者屏蔽了你的设备或网络连接出了问题。\n\n你能在另一个会话中读取消息吗?如果是的话,你可以从它那里传递信息!点击设置 > 设备,并确保你的设备已经相互验证。当你下次打开聊天室,且两个会话都在前台,密钥就会自动传输。\n\n你不想在注销或切换设备时丢失密钥?请确保在设置中启用了聊天备份。", + "noKeyForThisMessage": "如果消息是在你在此设备上登录账户前发送的,就可能发生这种情况。\n\n也有可能是发送者屏蔽了你的设备或网络连接出了问题。\n\n你能在另一个会话中读取消息吗?如果是的话,你可以从它那里传递信息!点击设置 > 设备,并确保你的设备已经相互验证。当你下次打开聊天室,且两个会话都在前台,密钥就会自动传输。\n\n你不想在退出登录或切换设备时丢失密钥?请确保在设置中启用了聊天备份。", "@noKeyForThisMessage": {}, "newGroup": "新群组", "@newGroup": {}, @@ -2486,15 +2486,6 @@ "@jump": {}, "openLinkInBrowser": "在浏览器中打开链接", "@openLinkInBrowser": {}, - "signInWith": "使用 {provider} 登录", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "allRooms": "所有群聊", "@allRooms": { "type": "String", @@ -2511,7 +2502,7 @@ }, "reportErrorDescription": "😭 哦不。出了点差错。如果你愿意,可以向开发人员报告此错误。", "@reportErrorDescription": {}, - "noBackupWarning": "警告!如果不启用聊天备份,你将无法访问加密消息。强烈建议在注销前先启用聊天备份。", + "noBackupWarning": "警告!如果不启用聊天备份,你将无法访问加密消息。强烈建议在退出登录前先启用聊天备份。", "@noBackupWarning": {}, "signInWithPassword": "使用密码登录", "@signInWithPassword": {}, @@ -2592,9 +2583,9 @@ } } }, - "inviteGroupChat": "📨 邀请至群聊", + "inviteGroupChat": "📨 群聊邀请", "@inviteGroupChat": {}, - "invitePrivateChat": "📨 邀请至私聊", + "invitePrivateChat": "📨 私聊邀请", "@invitePrivateChat": {}, "emoteKeyboardNoRecents": "最近使用过的表情会出现在这里...", "@emoteKeyboardNoRecents": { @@ -2972,7 +2963,7 @@ "@restricted": {}, "swipeRightToLeftToReply": "从右向左滑动进行回复", "@swipeRightToLeftToReply": {}, - "alwaysUse24HourFormat": "false", + "alwaysUse24HourFormat": "否", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." }, @@ -3380,7 +3371,142 @@ "@commandHint_logoutall": {}, "displayNavigationRail": "在移动设备上显示导航栏", "@displayNavigationRail": {}, - "customReaction": "自定义反应", + "customReaction": "自定义回应", + "@customReaction": {}, + "moreEvents": "更多事件", + "@moreEvents": {}, + "declineInvitation": "拒绝邀请", + "@declineInvitation": {}, + "noMessagesYet": "尚无消息", + "@noMessagesYet": {}, + "longPressToRecordVoiceMessage": "长按录制语音消息。", + "@longPressToRecordVoiceMessage": {}, + "pause": "暂停", + "@pause": {}, + "resume": "继续", + "@resume": {}, + "newSubSpace": "新建子空间", + "@newSubSpace": {}, + "moveToDifferentSpace": "移动到别的空间", + "@moveToDifferentSpace": {}, + "moveUp": "上移", + "@moveUp": {}, + "moveDown": "下移", + "@moveDown": {}, + "removeFromSpaceDescription": "将从空间移除该聊天,但仍出现在聊天列表中。", + "@removeFromSpaceDescription": {}, + "countChats": "{chats} 个聊天", + "@countChats": { + "type": "String", + "placeholders": { + "chats": { + "type": "int" + } + } + }, + "spaceMemberOf": "{spaces} 的空间成员", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "{spaces} 的空间成员可以敲门", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "donate": "捐赠", + "@donate": {}, + "startedAPoll": "{username} 启动了投票。", + "@startedAPoll": { + "type": "String", + "placeholders": { + "username": { + "type": "String" + } + } + }, + "poll": "投票", + "@poll": {}, + "startPoll": "启动投票", + "@startPoll": {}, + "endPoll": "结束投票", + "@endPoll": {}, + "answersVisible": "结果可见", + "@answersVisible": {}, + "answersHidden": "结果隐藏", + "@answersHidden": {}, + "pollQuestion": "投票问题", + "@pollQuestion": {}, + "answerOption": "结果选项", + "@answerOption": {}, + "addAnswerOption": "添加结果选项", + "@addAnswerOption": {}, + "allowMultipleAnswers": "允许多个结果", + "@allowMultipleAnswers": {}, + "pollHasBeenEnded": "投票已结束", + "@pollHasBeenEnded": {}, + "countVotes": "{count, plural, =1{票} other{{count} 票}}", + "@countVotes": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "answersWillBeVisibleWhenPollHasEnded": "投票结束后将显示结果", + "@answersWillBeVisibleWhenPollHasEnded": {}, + "thread": "嘟文串", + "@thread": {}, + "replyInThread": "在嘟文串中回复", + "@replyInThread": {}, + "countReplies": "{count, plural, =1{则回复} other{{count} 则回复}}", + "@countReplies": { + "type": "int", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "backToMainChat": "返回主聊天", + "@backToMainChat": {}, + "saveChanges": "保存更改", + "@saveChanges": {}, + "createSticker": "创建贴纸或表情图片", + "@createSticker": {}, + "useAsSticker": "用作贴纸", + "@useAsSticker": {}, + "useAsEmoji": "用作表情图片", + "@useAsEmoji": {}, + "stickerPackNameAlreadyExists": "贴纸包名已存在", + "@stickerPackNameAlreadyExists": {}, + "newStickerPack": "新建贴纸包", + "@newStickerPack": {}, + "stickerPackName": "贴纸包名", + "@stickerPackName": {}, + "attribution": "作者", + "@attribution": {}, + "skipChatBackup": "跳过聊天备份", + "@skipChatBackup": {}, + "skipChatBackupWarning": "确定吗?不开启聊天备份,如果切换设备可能无法访问消息。", + "@skipChatBackupWarning": {}, + "loadingMessages": "加载消息中", + "@loadingMessages": {}, + "setupChatBackup": "设置聊天备份", + "@setupChatBackup": {}, + "changedTheChatDescription": "{username} 更改了聊天描述", + "@changedTheChatDescription": {}, + "changedTheChatName": "{username} 更改了聊天名", + "@changedTheChatName": {}, "writeAMessageLangCodes": "输入 {l1} 或 {l2}...", "requests": "请求", "holdForInfo": "点击并按住获取单词信息。", @@ -3422,7 +3548,6 @@ "updateLanguage": "我的语言", "whatLanguageYouWantToLearn": "你想学习哪种语言?", "whatIsYourBaseLanguage": "你的基础语言是什么?", - "saveChanges": "保存更改", "publicProfileTitle": "允许在搜索中找到我的资料", "publicProfileDesc": "开启后,其他用户可以在全球搜索栏中找到你的资料并向你发起聊天请求。此时,你可以选择接受或拒绝请求。", "errorDisableIT": "翻译协助已关闭。", @@ -4508,10 +4633,6 @@ "inviteYourFriends": "邀请你的朋友", "playWithAI": "暂时与AI玩玩", "courseStartDesc": "Pangea 机器人随时准备开始!\n\n……但和朋友一起学习会更好!", - "@customReaction": { - "type": "String", - "placeholders": {} - }, "@writeAMessageLangCodes": { "type": "String", "placeholders": { @@ -4687,10 +4808,6 @@ "type": "String", "placeholders": {} }, - "@saveChanges": { - "type": "String", - "placeholders": {} - }, "@publicProfileTitle": { "type": "String", "placeholders": {} @@ -11056,4 +11173,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index d40146897..f80efa3cd 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1055,7 +1055,7 @@ "type": "String", "placeholders": {} }, - "noGoogleServicesWarning": "您手機上沒有安裝 Google 服務框架。這或許對於保護您的隱私而言是個好事!但為了收到 FluffyChat 的推播通知,我們建議您使用 https://microg.org 或 https://unifiedpush.org。", + "noGoogleServicesWarning": "未能在你的裝置找到 Firebase Cloud Messaging(FCM). 如果想要收到通知消息的推送,我們建議安裝 ntfy。在有 ntfy 或其他 Unified Push 應用,便能在資料安全的情況下收到通知的推送。你可以在 Play store 或 F-Droid 下載並安裝 ntfy。", "@noGoogleServicesWarning": { "type": "String", "placeholders": {} @@ -2942,15 +2942,6 @@ "@newSpaceDescription": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "請稍後再試,或選擇不同的伺服器。", "@pleaseTryAgainLaterOrChooseDifferentServer": {}, - "signInWith": "使用 {provider} 登入", - "@signInWith": { - "type": "String", - "placeholders": { - "provider": { - "type": "String" - } - } - }, "invalidInput": "無效的輸入!", "@invalidInput": {}, "verifyOtherUser": "🔐 驗證其他使用者", @@ -3210,7 +3201,7 @@ "@version": {}, "unableToJoinChat": "無法加入聊天室。對話可能以被其他方結束。", "@unableToJoinChat": {}, - "appWantsToUseForLogin": "使用伺服器「{server} 」登入", + "appWantsToUseForLogin": "使用「{server} 」伺服器登入", "@appWantsToUseForLogin": { "type": "String", "placeholders": { @@ -3350,8 +3341,16 @@ "@pleaseWaitUntilInvited": {}, "notificationRuleIsUserMentionDescription": "被@時通知他們。", "@notificationRuleIsUserMentionDescription": {}, + "countInvited": "已邀請{count}位", + "@countInvited": { + "type": "String", + "placeholders": { + "count": { + "type": "int" + } + } + }, "checkList": "清單", - "countInvited": "{count} 被邀請", "sentVoiceMessage": "🎙️ {duration} - 來自 {sender} 的語音訊息", "commandHint_logout": "登出您的當前設備", "commandHint_logoutall": "登出所有活動設備", @@ -4501,14 +4500,6 @@ "type": "String", "placeholders": {} }, - "@countInvited": { - "type": "String", - "placeholders": { - "count": { - "type": "int" - } - } - }, "@sentVoiceMessage": { "type": "String", "placeholders": { @@ -11063,4 +11054,4 @@ "type": "String", "placeholders": {} } -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index ac55d22a4..ce48ce4f0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,6 @@ +import 'dart:isolate'; +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -16,14 +19,15 @@ import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/utils/client_manager.dart'; +import 'package:fluffychat/utils/notification_background_handler.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'config/setting_keys.dart'; import 'utils/background_push.dart'; import 'widgets/fluffy_chat_app.dart'; -void main() async { - Logs().i('Welcome to ${AppConfig.applicationName} <3'); +ReceivePort? mainIsolateReceivePort; +void main() async { // #Pangea try { await dotenv.load(fileName: ".env"); @@ -50,17 +54,29 @@ void main() async { await Future.wait(initFutures); // Pangea# + if (PlatformInfos.isAndroid) { + final port = mainIsolateReceivePort = ReceivePort(); + IsolateNameServer.removePortNameMapping(AppConfig.mainIsolatePortName); + IsolateNameServer.registerPortWithName( + port.sendPort, + AppConfig.mainIsolatePortName, + ); + await waitForPushIsolateDone(); + } + // Our background push shared isolate accesses flutter-internal things very early in the startup proccess // To make sure that the parts of flutter needed are started up already, we need to ensure that the // widget bindings are initialized already. WidgetsFlutterBinding.ensureInitialized(); + final store = await AppSettings.init(); + Logs().i('Welcome to ${AppSettings.applicationName.value} <3'); + // #Pangea // await vod.init(wasmPath: './assets/assets/vodozemac/'); // Pangea# Logs().nativeColors = !PlatformInfos.isIOS; - final store = await SharedPreferences.getInstance(); final clients = await ClientManager.getClients(store: store); // If the app starts in detached mode, we assume that it is in @@ -80,14 +96,14 @@ void main() async { // To start the flutter engine afterwards we add an custom observer. WidgetsBinding.instance.addObserver(AppStarter(clients, store)); Logs().i( - '${AppConfig.applicationName} started in background-fetch mode. No GUI will be created unless the app is no longer detached.', + '${AppSettings.applicationName.value} started in background-fetch mode. No GUI will be created unless the app is no longer detached.', ); return; } // Started in foreground mode. Logs().i( - '${AppConfig.applicationName} started in foreground mode. Rendering GUI...', + '${AppSettings.applicationName.value} started in foreground mode. Rendering GUI...', ); await startGui(clients, store); } @@ -98,8 +114,9 @@ Future startGui(List clients, SharedPreferences store) async { String? pin; if (PlatformInfos.isMobile) { try { - pin = - await const FlutterSecureStorage().read(key: SettingKeys.appLockKey); + pin = await const FlutterSecureStorage().read( + key: 'chat.fluffy.app_lock', + ); } catch (e, s) { Logs().d('Unable to read PIN from Secure storage', e, s); } @@ -152,7 +169,7 @@ class AppStarter with WidgetsBindingObserver { if (state == AppLifecycleState.detached) return; Logs().i( - '${AppConfig.applicationName} switches from the detached background-fetch mode to ${state.name} mode. Rendering GUI...', + '${AppSettings.applicationName.value} switches from the detached background-fetch mode to ${state.name} mode. Rendering GUI...', ); // Switching to foreground mode needs to reenable send online sync presence. for (final client in clients) { diff --git a/lib/pages/archive/archive.dart b/lib/pages/archive/archive.dart index c5bd5e6b0..550175505 100644 --- a/lib/pages/archive/archive.dart +++ b/lib/pages/archive/archive.dart @@ -58,8 +58,10 @@ class ArchiveController extends State { } await showFutureLoadingDialog( context: context, - future: () async { + futureWithProgress: (onProgress) async { + final count = archive.length; while (archive.isNotEmpty) { + onProgress(1 - (archive.length / count)); Logs().v('Forget room ${archive.last.getLocalizedDisplayname()}'); await archive.last.forget(); archive.removeLast(); diff --git a/lib/pages/archive/archive_view.dart b/lib/pages/archive/archive_view.dart index f9446c26f..5ef4176f2 100644 --- a/lib/pages/archive/archive_view.dart +++ b/lib/pages/archive/archive_view.dart @@ -60,8 +60,9 @@ class ArchiveView extends StatelessWidget { itemBuilder: (BuildContext context, int i) => ChatListItem( controller.archive[i], onForget: () => controller.forgetRoomAction(i), - onTap: () => context - .go('/rooms/archive/${controller.archive[i].id}'), + onTap: () => context.go( + '/rooms/archive/${controller.archive[i].id}', + ), ), ); } diff --git a/lib/pages/bootstrap/bootstrap_dialog.dart b/lib/pages/bootstrap/bootstrap_dialog.dart index a303c3530..e7fa87032 100644 --- a/lib/pages/bootstrap/bootstrap_dialog.dart +++ b/lib/pages/bootstrap/bootstrap_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:go_router/go_router.dart'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; @@ -10,25 +11,17 @@ import 'package:fluffychat/utils/error_reporter.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/sync_status_localization.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; -import '../../utils/adaptive_bottom_sheet.dart'; +import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import '../key_verification/key_verification_dialog.dart'; class BootstrapDialog extends StatefulWidget { final bool wipe; - final Client client; - const BootstrapDialog({ - super.key, - this.wipe = false, - required this.client, - }); - - Future show(BuildContext context) => showAdaptiveBottomSheet( - context: context, - builder: (context) => this, - ); + const BootstrapDialog({super.key, this.wipe = false}); @override BootstrapDialogState createState() => BootstrapDialogState(); @@ -38,7 +31,7 @@ class BootstrapDialogState extends State { final TextEditingController _recoveryKeyTextEditingController = TextEditingController(); - late Bootstrap bootstrap; + Bootstrap? bootstrap; String? _recoveryKeyInputError; @@ -54,7 +47,7 @@ class BootstrapDialogState extends State { bool? _wipe; String get _secureStorageKey => - 'ssss_recovery_key_${bootstrap.client.userID}'; + 'ssss_recovery_key_${bootstrap!.client.userID}'; bool get _supportsSecureStorage => PlatformInfos.isMobile || PlatformInfos.isDesktop; @@ -69,18 +62,66 @@ class BootstrapDialogState extends State { return L10n.of(context).storeSecurlyOnThisDevice; } + late final Client client; + @override void initState() { - _createBootstrap(widget.wipe); super.initState(); + client = Matrix.of(context).client; + _createBootstrap(widget.wipe); + } + + void _cancelAction() async { + final consent = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).skipChatBackup, + message: L10n.of(context).skipChatBackupWarning, + okLabel: L10n.of(context).skip, + isDestructive: true, + ); + if (consent != OkCancelResult.ok) return; + if (!mounted) return; + _goBackAction(false); + } + + void _goBackAction(bool success) { + if (success) _decryptLastEvents(); + + context.canPop() ? context.pop(success) : context.go('/rooms'); + } + + void _decryptLastEvents() async { + for (final room in client.rooms) { + final event = room.lastEvent; + if (event != null && + event.type == EventTypes.Encrypted && + event.messageType == MessageTypes.BadEncrypted && + event.content['can_request_session'] == true) { + final sessionId = event.content.tryGet('session_id'); + final senderKey = event.content.tryGet('sender_key'); + if (sessionId != null && senderKey != null) { + room.client.encryption?.keyManager.maybeAutoRequest( + room.id, + sessionId, + senderKey, + ); + } + } + } } void _createBootstrap(bool wipe) async { + await client.roomsLoading; + await client.accountDataLoading; + await client.userDeviceKeysLoading; + while (client.prevBatch == null) { + await client.onSync.stream.first; + } + await client.updateUserDeviceKeys(); _wipe = wipe; titleText = null; _recoveryKeyStored = false; - bootstrap = - widget.client.encryption!.bootstrap(onUpdate: (_) => setState(() {})); + bootstrap = client.encryption!.bootstrap(onUpdate: (_) => setState(() {})); final key = await const FlutterSecureStorage().read(key: _secureStorageKey); if (key == null) return; _recoveryKeyTextEditingController.text = key; @@ -89,28 +130,52 @@ class BootstrapDialogState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final bootstrap = this.bootstrap; + if (bootstrap == null) { + return LoginScaffold( + appBar: AppBar( + centerTitle: true, + leading: CloseButton(onPressed: _cancelAction), + title: Text(L10n.of(context).loadingMessages), + ), + body: Center( + child: StreamBuilder( + stream: client.onSyncStatus.stream, + builder: (context, snapshot) { + final status = snapshot.data; + return Column( + mainAxisAlignment: .center, + children: [ + CircularProgressIndicator.adaptive(value: status?.progress), + if (status != null) Text(status.calcLocalizedString(context)), + ], + ); + }, + ), + ), + ); + } + _wipe ??= widget.wipe; final buttons = []; - Widget body = const CircularProgressIndicator.adaptive(); + Widget body = const Center(child: CircularProgressIndicator.adaptive()); titleText = L10n.of(context).loadingPleaseWait; if (bootstrap.newSsssKey?.recoveryKey != null && _recoveryKeyStored == false) { final key = bootstrap.newSsssKey!.recoveryKey; titleText = L10n.of(context).recoveryKey; - return Scaffold( + return LoginScaffold( appBar: AppBar( centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ), + leading: CloseButton(onPressed: _cancelAction), title: Text(L10n.of(context).recoveryKey), ), body: Center( child: ConstrainedBox( - constraints: - const BoxConstraints(maxWidth: FluffyThemes.columnWidth * 1.5), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 1.5, + ), child: ListView( padding: const EdgeInsets.all(16.0), children: [ @@ -125,10 +190,7 @@ class BootstrapDialogState extends State { ), subtitle: Text(L10n.of(context).chatBackupDescription), ), - const Divider( - height: 32, - thickness: 1, - ), + const Divider(height: 32, thickness: 1), TextField( minLines: 2, maxLines: 4, @@ -152,8 +214,9 @@ class BootstrapDialogState extends State { }); }, title: Text(_getSecureStorageLocalizedName()), - subtitle: - Text(L10n.of(context).storeInSecureStorageDescription), + subtitle: Text( + L10n.of(context).storeInSecureStorageDescription, + ), ), const SizedBox(height: 16), CheckboxListTile.adaptive( @@ -173,16 +236,16 @@ class BootstrapDialogState extends State { label: Text(L10n.of(context).next), onPressed: (_recoveryKeyCopied || _storeInSecureStorage == true) - ? () { - if (_storeInSecureStorage == true) { - const FlutterSecureStorage().write( - key: _secureStorageKey, - value: key, - ); - } - setState(() => _recoveryKeyStored = true); - } - : null, + ? () { + if (_storeInSecureStorage == true) { + const FlutterSecureStorage().write( + key: _secureStorageKey, + value: key, + ); + } + setState(() => _recoveryKeyStored = true); + } + : null, ), ], ), @@ -220,14 +283,11 @@ class BootstrapDialogState extends State { break; case BootstrapState.openExistingSsss: _recoveryKeyStored = true; - return Scaffold( + return LoginScaffold( appBar: AppBar( centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ), - title: Text(L10n.of(context).chatBackup), + leading: CloseButton(onPressed: _cancelAction), + title: Text(L10n.of(context).setupChatBackup), ), body: Center( child: ConstrainedBox( @@ -238,8 +298,9 @@ class BootstrapDialogState extends State { padding: const EdgeInsets.all(16.0), children: [ ListTile( - contentPadding: - const EdgeInsets.symmetric(horizontal: 8.0), + contentPadding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), trailing: Icon( Icons.info_outlined, color: theme.colorScheme.primary, @@ -303,28 +364,23 @@ class BootstrapDialogState extends State { Logs().v( 'Cross signing is already enabled. Try to self-sign', ); - try { - await bootstrap - .client.encryption!.crossSigning - .selfSign(recoveryKey: key); - Logs().d('Successful selfsigned'); - } catch (e, s) { - Logs().e( - 'Unable to self sign with recovery key after successfully open existing SSSS', - e, - s, - ); - } + await bootstrap + .client + .encryption! + .crossSigning + .selfSign(recoveryKey: key); + Logs().d('Successful selfsigned'); } } on InvalidPassphraseException catch (e) { setState( - () => _recoveryKeyInputError = - e.toLocalizedString(context), + () => _recoveryKeyInputError = e + .toLocalizedString(context), ); } on FormatException catch (_) { setState( - () => _recoveryKeyInputError = - L10n.of(context).wrongRecoveryKey, + () => _recoveryKeyInputError = L10n.of( + context, + ).wrongRecoveryKey, ); } catch (e, s) { ErrorReporter( @@ -332,8 +388,8 @@ class BootstrapDialogState extends State { 'Unable to open SSSS with recovery key', ).onErrorCallback(e, s); setState( - () => _recoveryKeyInputError = - e.toLocalizedString(context), + () => _recoveryKeyInputError = e + .toLocalizedString(context), ); } finally { setState( @@ -363,8 +419,9 @@ class BootstrapDialogState extends State { final consent = await showOkCancelAlertDialog( context: context, title: L10n.of(context).verifyOtherDevice, - message: L10n.of(context) - .verifyOtherDeviceDescription, + message: L10n.of( + context, + ).verifyOtherDeviceDescription, okLabel: L10n.of(context).ok, cancelLabel: L10n.of(context).cancel, ); @@ -373,16 +430,39 @@ class BootstrapDialogState extends State { context: context, delay: false, future: () async { - await widget.client.updateUserDeviceKeys(); - return widget.client - .userDeviceKeys[widget.client.userID!]! + await client.updateUserDeviceKeys(); + return client.userDeviceKeys[client.userID!]! .startVerification(); }, ); if (req.error != null) return; - await KeyVerificationDialog(request: req.result!) - .show(context); - Navigator.of(context, rootNavigator: false).pop(); + final success = await KeyVerificationDialog( + request: req.result!, + ).show(context); + if (success != true) return; + if (!mounted) return; + + final result = await showFutureLoadingDialog( + context: context, + future: () async { + final allCached = + await client.encryption!.keyManager + .isCached() && + await client.encryption!.crossSigning + .isCached(); + if (!allCached) { + await client + .encryption! + .ssss + .onSecretStored + .stream + .first; + } + return; + }, + ); + if (!mounted) return; + if (!result.isError) _goBackAction(true); }, ), const SizedBox(height: 16), @@ -446,8 +526,7 @@ class BootstrapDialogState extends State { body = const Icon(Icons.error_outline, color: Colors.red, size: 80); buttons.add( ElevatedButton( - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(false), + onPressed: () => _goBackAction(false), child: Text(L10n.of(context).close), ), ); @@ -455,7 +534,7 @@ class BootstrapDialogState extends State { case BootstrapState.done: titleText = L10n.of(context).everythingReady; body = Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon( Icons.check_circle_rounded, @@ -472,8 +551,7 @@ class BootstrapDialogState extends State { ); buttons.add( ElevatedButton( - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(false), + onPressed: () => _goBackAction(true), child: Text(L10n.of(context).close), ), ); @@ -481,27 +559,18 @@ class BootstrapDialogState extends State { } } - return Scaffold( + return LoginScaffold( appBar: AppBar( - leading: Center( - child: CloseButton( - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(true), - ), - ), + leading: CloseButton(onPressed: _cancelAction), title: Text(titleText ?? L10n.of(context).loadingPleaseWait), ), body: Center( child: Padding( padding: const EdgeInsets.all(20.0), child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - body, - const SizedBox(height: 8), - ...buttons, - ], + mainAxisSize: .min, + crossAxisAlignment: .stretch, + children: [body, const SizedBox(height: 8), ...buttons], ), ), ), diff --git a/lib/pages/chat/add_widget_tile_view.dart b/lib/pages/chat/add_widget_tile_view.dart index 9877ac576..c69968d6a 100644 --- a/lib/pages/chat/add_widget_tile_view.dart +++ b/lib/pages/chat/add_widget_tile_view.dart @@ -19,20 +19,21 @@ class AddWidgetTileView extends StatelessWidget { CupertinoSegmentedControl( groupValue: controller.widgetType, padding: const EdgeInsets.all(8), - children: { - 'm.etherpad': Text(L10n.of(context).widgetEtherpad), - 'm.jitsi': Text(L10n.of(context).widgetJitsi), - 'm.video': Text(L10n.of(context).widgetVideo), - 'm.custom': Text(L10n.of(context).widgetCustom), - }.map( - (key, value) => MapEntry( - key, - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: value, + children: + { + 'm.etherpad': Text(L10n.of(context).widgetEtherpad), + 'm.jitsi': Text(L10n.of(context).widgetJitsi), + 'm.video': Text(L10n.of(context).widgetVideo), + 'm.custom': Text(L10n.of(context).widgetCustom), + }.map( + (key, value) => MapEntry( + key, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: value, + ), + ), ), - ), - ), onValueChanged: controller.setWidgetType, ), Padding( diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 452e2a0a0..9708d8066 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -8,6 +8,7 @@ import 'package:flutter/services.dart'; import 'package:collection/collection.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:just_audio/just_audio.dart'; @@ -15,17 +16,14 @@ import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:universal_html/html.dart' as html; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat_view.dart'; import 'package:fluffychat/pages/chat/event_info_dialog.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; -import 'package:fluffychat/pages/chat/recording_dialog.dart'; +import 'package:fluffychat/pages/chat/start_poll_bottom_sheet.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_chat_controller.dart'; @@ -73,6 +71,7 @@ import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_reques import 'package:fluffychat/pangea/toolbar/message_practice/message_practice_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/tokens_util.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/error_reporter.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; @@ -203,6 +202,8 @@ class ChatController extends State Timeline? timeline; + String? activeThreadId; + late final String readMarkerEventId; String get roomId => widget.room.id; @@ -210,7 +211,6 @@ class ChatController extends State final AutoScrollController scrollController = AutoScrollController(); late final FocusNode inputFocus; - StreamSubscription? onFocusSub; Timer? typingCoolDown; Timer? typingTimeout; @@ -218,9 +218,9 @@ class ChatController extends State // #Pangea // bool dragging = false; - // void onDragEntered(_) => setState(() => dragging = true); + // void onDragEntered(dynamic _) => setState(() => dragging = true); - // void onDragExited(_) => setState(() => dragging = false); + // void onDragExited(dynamic _) => setState(() => dragging = false); // void onDragDone(DropDoneDetails details) async { // setState(() => dragging = false); @@ -232,6 +232,8 @@ class ChatController extends State // files: details.files, // room: room, // outerContext: context, + // threadRootEventId: activeThreadId, + // threadLastEventId: threadLastEventId, // ), // ); // } @@ -247,7 +249,8 @@ class ChatController extends State MessageTypes.File, }.contains(selectedEvents.single.messageType); - void saveSelectedEvent(context) => selectedEvents.single.saveFile(context); + void saveSelectedEvent(BuildContext context) => + selectedEvents.single.saveFile(context); List selectedEvents = []; @@ -275,6 +278,25 @@ class ChatController extends State bool showEmojiPicker = false; + String? get threadLastEventId { + final threadId = activeThreadId; + if (threadId == null) return null; + return timeline?.events + .filterByVisibleInGui(threadId: threadId) + .firstOrNull + ?.eventId; + } + + void enterThread(String eventId) => setState(() { + activeThreadId = eventId; + selectedEvents.clear(); + }); + + void closeThread() => setState(() { + activeThreadId = null; + selectedEvents.clear(); + }); + void recreateChat() async { final room = this.room; final userId = room.directChatMatrixID; @@ -302,7 +324,7 @@ class ChatController extends State } // #Pangea - // void requestHistory([_]) async { + // void requestHistory([dynamic _]) async { Future requestHistory() async { if (timeline == null) return; if (!timeline!.canRequestHistory) return; @@ -315,9 +337,25 @@ class ChatController extends State final timeline = this.timeline; if (timeline == null) return; Logs().v('Requesting future...'); - final mostRecentEventId = timeline.events.first.eventId; + + final mostRecentEvent = timeline.events.filterByVisibleInGui().firstOrNull; + await timeline.requestFuture(historyCount: _loadHistoryCount); - setReadMarker(eventId: mostRecentEventId); + + if (mostRecentEvent != null) { + setReadMarker(eventId: mostRecentEvent.eventId); + WidgetsBinding.instance.addPostFrameCallback((_) { + final index = timeline.events.filterByVisibleInGui().indexOf( + mostRecentEvent, + ); + if (index >= 0) { + scrollController.scrollToIndex( + index, + preferPosition: AutoScrollPosition.begin, + ); + } + }); + } } void _updateScrollController() { @@ -334,15 +372,10 @@ class ChatController extends State // setReadMarker(); // } // Pangea# - - if (scrollController.position.pixels == 0 || - scrollController.position.pixels == 64) { - requestFuture(); - } } void _loadDraft() async { - final prefs = await SharedPreferences.getInstance(); + final prefs = Matrix.of(context).store; final draft = prefs.getString('draft_$roomId'); if (draft != null && draft.isNotEmpty) { // #Pangea @@ -352,7 +385,7 @@ class ChatController extends State } } - void _shareItems([_]) { + void _shareItems([dynamic _]) { final shareItems = widget.shareItems; if (shareItems == null || shareItems.isEmpty) return; if (!room.otherPartyCanReceiveMessages) { @@ -363,9 +396,7 @@ class ChatController extends State closeIconColor: theme.colorScheme.onErrorContainer, content: Text( L10n.of(context).otherPartyNotLoggedIn, - style: TextStyle( - color: theme.colorScheme.onErrorContainer, - ), + style: TextStyle(color: theme.colorScheme.onErrorContainer), ), showCloseIcon: true, ), @@ -388,6 +419,8 @@ class ChatController extends State files: files, room: room, outerContext: context, + threadRootEventId: activeThreadId, + threadLastEventId: threadLastEventId, ), ); } @@ -395,7 +428,7 @@ class ChatController extends State KeyEventResult _customEnterKeyHandling(FocusNode node, KeyEvent evt) { if (!HardwareKeyboard.instance.isShiftPressed && evt.logicalKey.keyLabel == 'Enter' && - (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile)) { + AppSettings.sendOnEnter.value) { if (evt is KeyDownEvent) { // #Pangea // send(); @@ -404,11 +437,9 @@ class ChatController extends State } return KeyEventResult.handled; } else if (evt.logicalKey.keyLabel == 'Enter' && evt is KeyDownEvent) { - final currentLineNum = sendController.text - .substring( - 0, - sendController.selection.baseOffset, - ) + final currentLineNum = + sendController.text + .substring(0, sendController.selection.baseOffset) .split('\n') .length - 1; @@ -451,17 +482,19 @@ class ChatController extends State WidgetsBinding.instance.addPostFrameCallback(_shareItems); super.initState(); _displayChatDetailsColumn = ValueNotifier( - AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store), + AppSettings.displayChatDetailsColumn.value, ); sendingClient = Matrix.of(context).client; - readMarkerEventId = room.hasNewMessages ? room.fullyRead : ''; + final lastEventThreadId = + room.lastEvent?.relationshipType == RelationshipTypes.thread + ? room.lastEvent?.relationshipEventId + : null; + readMarkerEventId = room.hasNewMessages + ? lastEventThreadId ?? room.fullyRead + : ''; WidgetsBinding.instance.addObserver(this); _tryLoadTimeline(); - if (kIsWeb) { - onFocusSub = html.window.onFocus.listen((_) => setReadMarker()); - } - // #Pangea _pangeaInit(); // Pangea# @@ -469,11 +502,7 @@ class ChatController extends State // #Pangea void _onLevelUp(LevelUpdate update) { - LevelUpUtil.showLevelUpDialog( - update.newLevel, - update.prevLevel, - context, - ); + LevelUpUtil.showLevelUpDialog(update.newLevel, update.prevLevel, context); } void _onUnlockConstructs(Set constructs) { @@ -521,10 +550,7 @@ class ChatController extends State matrix.audioPlayer!.setFilePath(file.path); } else { matrix.audioPlayer!.setAudioSource( - BytesAudioSource( - audioFile.bytes, - audioFile.mimeType, - ), + BytesAudioSource(audioFile.bytes, audioFile.mimeType), ); } @@ -538,11 +564,13 @@ class ChatController extends State _levelSubscription = updater.levelUpdateStream.stream.listen(_onLevelUp); - _constructsSubscription = - updater.unlockedConstructsStream.stream.listen(_onUnlockConstructs); + _constructsSubscription = updater.unlockedConstructsStream.stream.listen( + _onUnlockConstructs, + ); - _tokensSubscription = - updater.newConstructsStream.stream.listen(_onTokenUpdate); + _tokensSubscription = updater.newConstructsStream.stream.listen( + _onTokenUpdate, + ); _botAudioSubscription = room.client.onSync.stream.listen(_botAudioListener); @@ -555,31 +583,62 @@ class ChatController extends State if (!mounted) return; LanguageService.showDialogOnEmptyLanguage( context, - () => () => setState(() {}), + () => + () => setState(() {}), ); }); } // Pangea# + final Set expandedEventIds = {}; + + void expandEventsFrom(Event event, bool expand) { + final events = timeline!.events.filterByVisibleInGui( + threadId: activeThreadId, + ); + final start = events.indexOf(event); + setState(() { + for (var i = start; i < events.length; i++) { + final event = events[i]; + if (!event.isCollapsedState) return; + if (expand) { + expandedEventIds.add(event.eventId); + } else { + expandedEventIds.remove(event.eventId); + } + } + }); + } + void _tryLoadTimeline() async { final initialEventId = widget.eventId; loadTimelineFuture = _getTimeline(); try { await loadTimelineFuture; - if (initialEventId != null) scrollToEventId(initialEventId); + // We launched the chat with a given initial event ID: + if (initialEventId != null) { + scrollToEventId(initialEventId); + return; + } var readMarkerEventIndex = readMarkerEventId.isEmpty ? -1 : timeline!.events - .filterByVisibleInGui(exceptionEventId: readMarkerEventId) - .indexWhere((e) => e.eventId == readMarkerEventId); + .filterByVisibleInGui( + exceptionEventId: readMarkerEventId, + threadId: activeThreadId, + ) + .indexWhere((e) => e.eventId == readMarkerEventId); // Read marker is existing but not found in first events. Try a single // requestHistory call before opening timeline on event context: if (readMarkerEventId.isNotEmpty && readMarkerEventIndex == -1) { await timeline?.requestHistory(historyCount: _loadHistoryCount); readMarkerEventIndex = timeline!.events - .filterByVisibleInGui(exceptionEventId: readMarkerEventId) + .filterByVisibleInGui( + exceptionEventId: readMarkerEventId, + threadId: activeThreadId, + ) .indexWhere((e) => e.eventId == readMarkerEventId); } @@ -604,12 +663,12 @@ class ChatController extends State String? scrollUpBannerEventId; void discardScrollUpBannerEventId() => setState(() { - scrollUpBannerEventId = null; - }); + scrollUpBannerEventId = null; + }); void _showScrollUpMaterialBanner(String eventId) => setState(() { - scrollUpBannerEventId = eventId; - }); + scrollUpBannerEventId = eventId; + }); void updateView() { if (!mounted) return; @@ -626,22 +685,15 @@ class ChatController extends State void onInsert(int i) { // setState will be called by updateView() anyway - animateInEventIndex = i; + if (timeline?.allowNewEvent == true) animateInEventIndex = i; } // #Pangea List get visibleEvents => - timeline?.events - .where( - (x) => x.isVisibleInGui, - ) - .toList() ?? - []; + timeline?.events.where((x) => x.isVisibleInGui).toList() ?? []; // Pangea# - Future _getTimeline({ - String? eventContextId, - }) async { + Future _getTimeline({String? eventContextId}) async { await Matrix.of(context).client.roomsLoading; await Matrix.of(context).client.accountDataLoading; if (eventContextId != null && @@ -733,17 +785,7 @@ class ChatController extends State } // Do not send read markers when app is not in foreground - // #Pangea - try { - // Pangea# - if (kIsWeb && !Matrix.of(context).webHasFocus) return; - // #Pangea - } catch (err) { - return; - } - // Pangea# - if (!kIsWeb && - WidgetsBinding.instance.lifecycleState != AppLifecycleState.resumed) { + if (WidgetsBinding.instance.lifecycleState != AppLifecycleState.resumed) { return; } @@ -754,30 +796,30 @@ class ChatController extends State // ignore: unawaited_futures _setReadMarkerFuture = timeline .setReadMarker( - eventId: eventId, - public: AppConfig.sendPublicReadReceipts, - ) - .then((_) { - _setReadMarkerFuture = null; - }) + eventId: eventId, + public: AppSettings.sendPublicReadReceipts.value, + ) // #Pangea + // .then((_) { + // _setReadMarkerFuture = null; + // }); + .then((_) { + _setReadMarkerFuture = null; + }) .catchError((e, s) { - ErrorHandler.logError( - e: PangeaWarningError("Failed to set read marker: $e"), - s: s, - data: { - 'eventId': eventId, - 'roomId': roomId, - }, - ); - Sentry.captureException( - e, - stackTrace: s, - withScope: (scope) { - scope.setTag('where', 'setReadMarker'); - }, - ); - }); + ErrorHandler.logError( + e: PangeaWarningError("Failed to set read marker: $e"), + s: s, + data: {'eventId': eventId, 'roomId': roomId}, + ); + Sentry.captureException( + e, + stackTrace: s, + withScope: (scope) { + scope.setTag('where', 'setReadMarker'); + }, + ); + }); // Pangea# if (eventId == null || eventId == timeline.room.lastEvent?.eventId) { Matrix.of(context).backgroundPush?.cancelNotification(roomId); @@ -790,9 +832,6 @@ class ChatController extends State timeline = null; // #Pangea // inputFocus.removeListener(_inputFocusListener); - // Pangea# - onFocusSub?.cancel(); - // #Pangea WidgetsBinding.instance.removeObserver(this); _storeInputTimeoutTimer?.cancel(); _displayChatDetailsColumn.dispose(); @@ -848,34 +887,34 @@ class ChatController extends State void setSendingClient(Client c) { // #Pangea - // // first cancel typing with the old sending client - // if (currentlyTyping) { - // // no need to have the setting typing to false be blocking - // typingCoolDown?.cancel(); - // typingCoolDown = null; - // room.setTyping(false); - // currentlyTyping = false; - // } - // // then cancel the old timeline - // // fixes bug with read reciepts and quick switching - // loadTimelineFuture = _getTimeline(eventContextId: room.fullyRead).onError( - // ErrorReporter( - // context, - // 'Unable to load timeline after changing sending Client', - // ).onErrorCallback, - // ); - - // // then set the new sending client - // setState(() => sendingClient = c); + // // first cancel typing with the old sending client + // if (currentlyTyping) { + // // no need to have the setting typing to false be blocking + // typingCoolDown?.cancel(); + // typingCoolDown = null; + // room.setTyping(false); + // currentlyTyping = false; // } + // // then cancel the old timeline + // // fixes bug with read reciepts and quick switching + // loadTimelineFuture = _getTimeline(eventContextId: room.fullyRead).onError( + // ErrorReporter( + // context, + // 'Unable to load timeline after changing sending Client', + // ).onErrorCallback, + // ); - // void setActiveClient(Client c) { - // setState(() { - // Matrix.of(context).setActiveClient(c); - // }); + // // then set the new sending client + // setState(() => sendingClient = c); // Pangea# } + // #Pangea + // void setActiveClient(Client c) => setState(() { + // Matrix.of(context).setActiveClient(c); + // }); + // Pangea# + // #Pangea Event? pangeaEditingEvent; void clearEditingEvent() { @@ -898,9 +937,7 @@ class ChatController extends State } // Future send() async { - // Original send function gets the tx id within the matrix lib, - // but for choero, the tx id is generated before the message send. - // Also, adding PangeaMessageData + // if (sendController.text.trim().isEmpty) return; Future send() async { final message = sendController.text; final edit = editEvent.value; @@ -918,10 +955,9 @@ class ChatController extends State choreographer.clear(); if (message.trim().isEmpty) return; - // if (sendController.text.trim().isEmpty) return; // Pangea# _storeInputTimeoutTimer?.cancel(); - final prefs = await SharedPreferences.getInstance(); + final prefs = Matrix.of(context).store; prefs.remove('draft_$roomId'); var parseCommands = true; @@ -950,8 +986,9 @@ class ChatController extends State // room.sendTextEvent( // sendController.text, // inReplyTo: replyEvent, - // editEventId: editEvent?.eventId, + // editEventId: editEvent.eventId, // parseCommands: parseCommands, + // threadRootEventId: activeThreadId, // ); // If the message and the sendController text don't match, it's possible // that there was a delay in tokenization before send, and the user started @@ -968,51 +1005,70 @@ class ChatController extends State room .pangeaSendTextEvent( - message, - inReplyTo: reply, - editEventId: edit?.eventId, - parseCommands: parseCommands, - originalWritten: content.originalWritten, - tokensSent: content.tokensSent, - tokensWritten: content.tokensWritten, - choreo: content.choreo, - txid: tempEventId, - ) - .then( - (String? msgEventId) async { - // #Pangea - // There's a listen in my_analytics_controller that decides when to auto-update - // analytics based on when / how many messages the logged in user send. This - // stream sends the data for newly sent messages. - _sendMessageAnalytics( - msgEventId, - originalSent: PangeaRepresentation( - langCode: content.tokensSent?.detections?.firstOrNull?.langCode ?? - LanguageKeys.unknownLanguage, - text: message, - originalSent: true, - originalWritten: false, - ), + message, + inReplyTo: reply, + editEventId: edit?.eventId, + parseCommands: parseCommands, + originalWritten: content.originalWritten, tokensSent: content.tokensSent, + tokensWritten: content.tokensWritten, choreo: content.choreo, - ); - - if (previousEdit != null) { - pangeaEditingEvent = previousEdit; - } - - final spaceCode = room.classCode; - if (spaceCode != null) { - GoogleAnalytics.sendMessage( - room.id, - spaceCode, + txid: tempEventId, + threadRootEventId: activeThreadId, + ) + .then((String? msgEventId) async { + // #Pangea + // There's a listen in my_analytics_controller that decides when to auto-update + // analytics based on when / how many messages the logged in user send. This + // stream sends the data for newly sent messages. + _sendMessageAnalytics( + msgEventId, + originalSent: PangeaRepresentation( + langCode: + content.tokensSent?.detections?.firstOrNull?.langCode ?? + LanguageKeys.unknownLanguage, + text: message, + originalSent: true, + originalWritten: false, + ), + tokensSent: content.tokensSent, + choreo: content.choreo, ); - } - if (msgEventId == null) { + if (previousEdit != null) { + pangeaEditingEvent = previousEdit; + } + + final spaceCode = room.classCode; + if (spaceCode != null) { + GoogleAnalytics.sendMessage(room.id, spaceCode); + } + + if (msgEventId == null) { + ErrorHandler.logError( + e: Exception('msgEventId is null'), + s: StackTrace.current, + data: { + 'roomId': roomId, + 'text': message, + 'inReplyTo': reply?.eventId, + 'editEventId': edit?.eventId, + }, + ); + return; + } + }) + .catchError((err, s) { + if (err is EventTooLarge) { + showAdaptiveDialog( + context: context, + builder: (context) => const EventTooLargeDialog(), + ); + return; + } ErrorHandler.logError( - e: Exception('msgEventId is null'), - s: StackTrace.current, + e: err, + s: s, data: { 'roomId': roomId, 'text': message, @@ -1020,29 +1076,7 @@ class ChatController extends State 'editEventId': edit?.eventId, }, ); - return; - } - }, - ).catchError((err, s) { - if (err is EventTooLarge) { - showAdaptiveDialog( - context: context, - builder: (context) => const EventTooLargeDialog(), - ); - return; - } - ErrorHandler.logError( - e: err, - s: s, - data: { - 'roomId': roomId, - 'text': message, - 'inReplyTo': reply?.eventId, - 'editEventId': edit?.eventId, - }, - ); - }); - // #Pangea + }); // sendController.value = TextEditingValue( // text: pendingText, // selection: const TextSelection.collapsed(offset: 0), @@ -1058,12 +1092,8 @@ class ChatController extends State // Pangea# } - void sendFileAction({FileSelectorType type = FileSelectorType.any}) async { - final files = await selectFiles( - context, - allowMultiple: true, - type: type, - ); + void sendFileAction({FileType type = FileType.any}) async { + final files = await selectFiles(context, allowMultiple: true, type: type); if (files.isEmpty) return; await showAdaptiveDialog( context: context, @@ -1071,6 +1101,8 @@ class ChatController extends State files: files, room: room, outerContext: context, + threadRootEventId: activeThreadId, + threadLastEventId: threadLastEventId, ), ); } @@ -1083,6 +1115,8 @@ class ChatController extends State files: [XFile.fromData(image)], room: room, outerContext: context, + threadRootEventId: activeThreadId, + threadLastEventId: threadLastEventId, ), ); } @@ -1099,6 +1133,8 @@ class ChatController extends State files: [file], room: room, outerContext: context, + threadRootEventId: activeThreadId, + threadLastEventId: threadLastEventId, ), ); } @@ -1118,17 +1154,20 @@ class ChatController extends State files: [file], room: room, outerContext: context, + threadRootEventId: activeThreadId, + threadLastEventId: threadLastEventId, ), ); } - void voiceMessageAction() async { + Future onVoiceMessageSend( + String path, + int duration, + List waveform, + String? fileName, + ) async { // #Pangea stopMediaStream.add(null); - // Pangea# - room.client.getConfig(); // Preload server file configuration. - - final scaffoldMessenger = ScaffoldMessenger.of(context); if (PlatformInfos.isAndroid) { final info = await DeviceInfoPlugin().androidInfo; if (info.version.sdkInt < 19) { @@ -1141,17 +1180,9 @@ class ChatController extends State return; } } - - // #Pangea - // if (await AudioRecorder().hasPermission() == false) return; // Pangea# - final result = await showDialog( - context: context, - barrierDismissible: false, - builder: (c) => const RecordingDialog(), - ); - if (result == null) return; - final audioFile = XFile(result.path); + final scaffoldMessenger = ScaffoldMessenger.of(context); + final audioFile = XFile(path); final bytesResult = await showFutureLoadingDialog( context: context, @@ -1162,30 +1193,31 @@ class ChatController extends State final file = MatrixAudioFile( bytes: bytes, - name: result.fileName ?? audioFile.path, + name: fileName ?? audioFile.path, ); // #Pangea + // setState(() { + // replyEvent = null; + // }); final reply = replyEvent.value; replyEvent.value = null; // Pangea# - await room + room .sendFileEvent( file, // #Pangea // inReplyTo: replyEvent, inReplyTo: reply, // Pangea# + threadRootEventId: activeThreadId, extraContent: { - 'info': { - ...file.info, - 'duration': result.duration, - }, + 'info': {...file.info, 'duration': duration}, 'org.matrix.msc3245.voice': {}, 'org.matrix.msc1767.audio': { - 'duration': result.duration, - 'waveform': result.waveform, + 'duration': duration, + 'waveform': waveform, }, // #Pangea 'speaker_l1': pangeaController.userController.userL1Code, @@ -1199,36 +1231,21 @@ class ChatController extends State ErrorHandler.logError( e: e, s: s, - data: { - 'roomId': roomId, - 'file': file.name, - 'duration': result.duration, - 'waveform': result.waveform, - }, + data: {'roomId': roomId, 'file': file.name}, ); scaffoldMessenger.showSnackBar( - SnackBar( - content: Text( - (e as Object).toLocalizedString(context), - ), - ), + SnackBar(content: Text((e as Object).toLocalizedString(context))), ); return null; }); - // ).catchError((e) { + // .catchError((e) { // scaffoldMessenger.showSnackBar( - // SnackBar( - // content: Text( - // (e as Object).toLocalizedString(context), - // ), - // ), + // SnackBar(content: Text((e as Object).toLocalizedString(context))), // ); // return null; // }); - // setState(() { - // replyEvent = null; - // }); // Pangea# + return; } void hideEmojiPicker() { @@ -1268,7 +1285,9 @@ class ChatController extends State } for (final event in selectedEvents) { if (copyString.isNotEmpty) copyString += '\n\n'; - copyString += event.getDisplayEvent(timeline!).calcLocalizedBodyFallback( + copyString += event + .getDisplayEvent(timeline!) + .calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)), withSenderNamePrefix: true, ); @@ -1299,14 +1318,8 @@ class ChatController extends State value: -100, label: L10n.of(context).extremeOffensive, ), - AdaptiveModalAction( - value: -50, - label: L10n.of(context).offensive, - ), - AdaptiveModalAction( - value: 0, - label: L10n.of(context).inoffensive, - ), + AdaptiveModalAction(value: -50, label: L10n.of(context).offensive), + AdaptiveModalAction(value: 0, label: L10n.of(context).inoffensive), ], ); if (score == null) return; @@ -1321,11 +1334,11 @@ class ChatController extends State final result = await showFutureLoadingDialog( context: context, future: () => Matrix.of(context).client.reportEvent( - event.roomId!, - event.eventId, - reason: reason, - score: score, - ), + event.roomId!, + event.eventId, + reason: reason, + score: score, + ), ); if (result.error != null) return; // #Pangea @@ -1370,9 +1383,9 @@ class ChatController extends State message: L10n.of(context).redactMessageDescription, isDestructive: true, hintText: L10n.of(context).optionalRedactReason, - // #Pangea maxLength: 255, - // Pangea# + maxLines: 3, + minLines: 1, okLabel: L10n.of(context).remove, cancelLabel: L10n.of(context).cancel, // #Pangea @@ -1388,22 +1401,14 @@ class ChatController extends State } // Pangea# final reason = reasonInput.isEmpty ? null : reasonInput; - for (final event in selectedEvents) { - await showFutureLoadingDialog( - context: context, - future: () async { + await showFutureLoadingDialog( + context: context, + futureWithProgress: (onProgress) async { + final count = selectedEvents.length; + for (final (i, event) in selectedEvents.indexed) { + onProgress(i / count); if (event.status.isSent) { if (event.canRedact) { - // #Pangea - // https://github.com/pangeachat/client/issues/3353 - if (room.pinnedEventIds.contains(event.eventId) && - room.canChangeStateEvent(EventTypes.RoomPinnedEvents)) { - final pinnedEvents = room.pinnedEventIds - .where((e) => e != event.eventId) - .toList(); - await room.setPinnedEvents(pinnedEvents); - } - // Pangea# await event.redactEvent(reason: reason); } else { final client = currentRoomBundle.firstWhere( @@ -1414,16 +1419,17 @@ class ChatController extends State return; } final room = client.getRoomById(roomId)!; - await Event.fromJson(event.toJson(), room).redactEvent( - reason: reason, - ); + await Event.fromJson( + event.toJson(), + room, + ).redactEvent(reason: reason); } } else { await event.cancelSend(); } - }, - ); - } + } + }, + ); // #Pangea // setState(() { // showEmojiPicker = false; @@ -1456,7 +1462,8 @@ class ChatController extends State if (isArchived || !room.canChangeStateEvent(EventTypes.RoomPinnedEvents) || selectedEvents.length != 1 || - !selectedEvents.single.status.isSent) { + !selectedEvents.single.status.isSent || + activeThreadId != null) { return false; } return true; @@ -1468,8 +1475,9 @@ class ChatController extends State !selectedEvents.first.status.isSent) { return false; } - return currentRoomBundle - .any((cl) => selectedEvents.first.senderId == cl!.userID); + return currentRoomBundle.any( + (cl) => selectedEvents.first.senderId == cl!.userID, + ); } void forwardEventsAction() async { @@ -1477,9 +1485,9 @@ class ChatController extends State final timeline = this.timeline; if (timeline == null) return; - final forwardEvents = List.from(selectedEvents) - .map((event) => event.getDisplayEvent(timeline)) - .toList(); + final forwardEvents = List.from( + selectedEvents, + ).map((event) => event.getDisplayEvent(timeline)).toList(); await showScaffoldDialog( context: context, @@ -1489,10 +1497,11 @@ class ChatController extends State // .map((event) => ContentShareItem(event.content)) // .toList(), .map((event) { - final content = Map.from(event.content); - content.remove("m.relates_to"); - return ContentShareItem(content); - }).toList(), + final content = Map.from(event.content); + content.remove("m.relates_to"); + return ContentShareItem(content); + }) + .toList(), // Pangea# ), ); @@ -1545,26 +1554,29 @@ class ChatController extends State inputFocus.requestFocus(); } - void scrollToEventId( - String eventId, { - bool highlightEvent = true, - }) async { - final foundEvent = - timeline!.events.firstWhereOrNull((event) => event.eventId == eventId); + void scrollToEventId(String eventId, {bool highlightEvent = true}) async { + final foundEvent = timeline!.events.firstWhereOrNull( + (event) => event.eventId == eventId, + ); final eventIndex = foundEvent == null ? -1 : timeline!.events - .filterByVisibleInGui(exceptionEventId: eventId) - .indexOf(foundEvent); + .filterByVisibleInGui( + exceptionEventId: eventId, + threadId: activeThreadId, + ) + .indexOf(foundEvent); if (eventIndex == -1) { setState(() { timeline = null; _scrolledUp = false; loadTimelineFuture = _getTimeline(eventContextId: eventId).onError( - ErrorReporter(context, 'Unable to load timeline after scroll to ID') - .onErrorCallback, + ErrorReporter( + context, + 'Unable to load timeline after scroll to ID', + ).onErrorCallback, ); }); await loadTimelineFuture; @@ -1592,8 +1604,10 @@ class ChatController extends State timeline = null; _scrolledUp = false; loadTimelineFuture = _getTimeline().onError( - ErrorReporter(context, 'Unable to load timeline after scroll down') - .onErrorCallback, + ErrorReporter( + context, + 'Unable to load timeline after scroll down', + ).onErrorCallback, ); }); await loadTimelineFuture; @@ -1601,7 +1615,7 @@ class ChatController extends State scrollController.jumpTo(0); } - void onEmojiSelected(_, Emoji? emoji) { + void onEmojiSelected(dynamic _, Emoji? emoji) { typeEmoji(emoji); onInputBarChanged(sendController.text); } @@ -1641,9 +1655,15 @@ class ChatController extends State // #Pangea // void clearSelectedEvents() => setState(() { - // selectedEvents.clear(); - // showEmojiPicker = false; - // }); + // selectedEvents.clear(); + // showEmojiPicker = false; + // }); + + // void clearSingleSelectedEvent() { + // if (selectedEvents.length <= 1) { + // clearSelectedEvents(); + // } + // } void clearSelectedEvents() { if (!mounted) return; if (!_isToolbarOpen && selectedEvents.isEmpty) return; @@ -1662,13 +1682,6 @@ class ChatController extends State selectedEvents.add(event); }); } - - // #Pangea - // void clearSingleSelectedEvent() { - // if (selectedEvents.length <= 1) { - // clearSelectedEvents(); - // } - // } // Pangea# void editSelectedEventAction() { @@ -1684,63 +1697,68 @@ class ChatController extends State // setState(() { // pendingText = sendController.text; // editEvent = selectedEvents.first; - // sendController.text = - // editEvent!.getDisplayEvent(timeline!).calcLocalizedBodyFallback( - // MatrixLocals(L10n.of(context)), - // withSenderNamePrefix: false, - // hideReply: true, - // ); + // sendController.text = editEvent + // .getDisplayEvent(timeline!) + // .calcLocalizedBodyFallback( + // MatrixLocals(L10n.of(context)), + // withSenderNamePrefix: false, + // hideReply: true, + // ); // selectedEvents.clear(); // }); pendingText = sendController.text; editEvent.value = selectedEvents.first; - sendController.text = - editEvent.value!.getDisplayEvent(timeline!).calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)), - withSenderNamePrefix: false, - hideReply: true, - ); + sendController.text = editEvent.value! + .getDisplayEvent(timeline!) + .calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)), + withSenderNamePrefix: false, + hideReply: true, + ); clearSelectedEvents(); // Pangea# inputFocus.requestFocus(); } void goToNewRoomAction() async { - final newRoomId = room - .getState(EventTypes.RoomTombstone)! - .parsedTombstoneContent - .replacementRoom; final result = await showFutureLoadingDialog( context: context, - future: () => room.client.joinRoom( - room - .getState(EventTypes.RoomTombstone)! - .parsedTombstoneContent - .replacementRoom, - via: [newRoomId.domain!], - ), + future: () async { + final users = await room.requestParticipants( + [Membership.join, Membership.leave], + true, + false, + ); + users.sort((a, b) => a.powerLevel.compareTo(b.powerLevel)); + final via = users + .map((user) => user.id.domain) + .whereType() + .toSet() + .take(10) + .toList(); + return room.client.joinRoom( + room + .getState(EventTypes.RoomTombstone)! + .parsedTombstoneContent + .replacementRoom, + via: via, + ); + }, ); if (result.error != null) return; if (!mounted) return; context.go('/rooms/${result.result!}'); - await showFutureLoadingDialog( - context: context, - future: room.leave, - ); + await showFutureLoadingDialog(context: context, future: room.leave); } // #Pangea // void onSelectMessage(Event event) { // if (!event.redacted) { // if (selectedEvents.contains(event)) { - // setState( - // () => selectedEvents.remove(event), - // ); + // setState(() => selectedEvents.remove(event)); // } else { - // setState( - // () => selectedEvents.add(event), - // ); + // setState(() => selectedEvents.add(event)); // } // selectedEvents.sort( // (a, b) => a.originServerTs.compareTo(b.originServerTs), @@ -1769,41 +1787,50 @@ class ChatController extends State // #Pangea // void onInputBarSubmitted(String _) { + // send(); + // FocusScope.of(context).requestFocus(inputFocus); + // } Future onInputBarSubmitted() async { - // send(); if (MatrixState.pangeaController.subscriptionController.shouldShowPaywall) { PaywallCard.show(context, ChoreoConstants.inputTransformTargetKey); return; } await onRequestWritingAssistance(manual: false, autosend: true); - // FocusScope.of(context).requestFocus(inputFocus); - // Pangea# } + // Pangea# - void onAddPopupMenuButtonSelected(String choice) { - room.client.getConfig(); // Preload server file configuration. + void onAddPopupMenuButtonSelected(AddPopupMenuActions choice) { + room.client.getConfig(); - if (choice == 'file') { - sendFileAction(); - } - if (choice == 'image') { - sendFileAction(type: FileSelectorType.images); - } - if (choice == 'video') { - sendFileAction(type: FileSelectorType.videos); - } - if (choice == 'camera') { - openCameraAction(); - } - if (choice == 'camera-video') { - openVideoCameraAction(); - } - if (choice == 'location') { - sendLocationAction(); + switch (choice) { + case AddPopupMenuActions.image: + sendFileAction(type: FileType.image); + return; + case AddPopupMenuActions.video: + sendFileAction(type: FileType.video); + return; + case AddPopupMenuActions.file: + sendFileAction(); + return; + case AddPopupMenuActions.poll: + showAdaptiveBottomSheet( + context: context, + builder: (context) => StartPollBottomSheet(room: room), + ); + return; + case AddPopupMenuActions.photoCamera: + openCameraAction(); + return; + case AddPopupMenuActions.videoCamera: + openVideoCameraAction(); + return; + case AddPopupMenuActions.location: + sendLocationAction(); + return; } } - unpinEvent(String eventId) async { + void unpinEvent(String eventId) async { final response = await showOkCancelAlertDialog( context: context, title: L10n.of(context).unpin, @@ -1835,7 +1862,8 @@ class ChatController extends State // Pangea# final pinnedEventIds = room.pinnedEventIds; final selectedEventIds = selectedEvents.map((e) => e.eventId).toSet(); - final unpin = selectedEventIds.length == 1 && + final unpin = + selectedEventIds.length == 1 && pinnedEventIds.contains(selectedEventIds.single); if (unpin) { // #Pangea @@ -1868,14 +1896,10 @@ class ChatController extends State // _inputTextIsEmpty = text.isEmpty; // }); // } - // if (_inputTextIsEmpty.value != text.isEmpty) { - // _inputTextIsEmpty.value = text.isEmpty; - // } // Pangea# - _storeInputTimeoutTimer?.cancel(); _storeInputTimeoutTimer = Timer(_storeInputTimeout, () async { - final prefs = await SharedPreferences.getInstance(); + final prefs = Matrix.of(context).store; await prefs.setString('draft_$roomId', text); }); // #Pangea @@ -1894,7 +1918,7 @@ class ChatController extends State // } // } // Pangea# - if (AppConfig.sendTypingNotifications) { + if (AppSettings.sendTypingNotifications.value) { typingCoolDown?.cancel(); typingCoolDown = Timer(const Duration(seconds: 2), () { typingCoolDown = null; @@ -1970,25 +1994,25 @@ class ChatController extends State try { await voipPlugin!.voip.inviteToCall(room, callType); } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(e.toLocalizedString(context))), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } } void cancelReplyEventAction() => setState(() { - // #Pangea - // sendController.text = pendingText; - sendController.setSystemText(pendingText, EditTypeEnum.other); - // Pangea# - pendingText = ''; - // #Pangea - // replyEvent = null; - // editEvent = null; - replyEvent.value = null; - editEvent.value = null; - // Pangea# - }); + // #Pangea + // sendController.text = pendingText; + sendController.setSystemText(pendingText, EditTypeEnum.other); + // Pangea# + pendingText = ''; + // #Pangea + // replyEvent = null; + // editEvent = null; + replyEvent.value = null; + editEvent.value = null; + // Pangea# + }); // #Pangea ValueNotifier depressMessageButton = ValueNotifier(false); @@ -2009,10 +2033,7 @@ class ChatController extends State event.senderId == BotName.byEnvironment && !event.redacted, ); - if (candidate?.hasAggregatedEvents( - timeline!, - RelationshipTypes.edit, - ) == + if (candidate?.hasAggregatedEvents(timeline!, RelationshipTypes.edit) == true) { return null; } @@ -2071,8 +2092,8 @@ class ChatController extends State final delay = keyboardOpen ? const Duration(milliseconds: 500) : isButton - ? const Duration(milliseconds: 200) - : null; + ? const Duration(milliseconds: 200) + : null; if (isButton) { depressMessageButton.value = true; @@ -2172,9 +2193,7 @@ class ChatController extends State ErrorHandler.logError( e: Exception('eventID null in voiceMessageAction'), s: StackTrace.current, - data: { - 'roomId': roomId, - }, + data: {'roomId': roomId}, ); return; } @@ -2184,9 +2203,7 @@ class ChatController extends State ErrorHandler.logError( e: Exception('Event not found after sending voice message'), s: StackTrace.current, - data: { - 'roomId': roomId, - }, + data: {'roomId': roomId}, ); return; } @@ -2209,18 +2226,14 @@ class ChatController extends State if (constructs.isEmpty) return; _showAnalyticsFeedback(constructs, eventId); - Matrix.of(context).analyticsDataService.updateService.addAnalytics( - eventId, - constructs, - ); + Matrix.of( + context, + ).analyticsDataService.updateService.addAnalytics(eventId, constructs); } catch (e, s) { ErrorHandler.logError( e: e, s: s, - data: { - 'roomId': roomId, - 'eventId': eventId, - }, + data: {'roomId': roomId, 'eventId': eventId}, ); } } @@ -2291,13 +2304,12 @@ class ChatController extends State context: context, future: () async { clearSelectedEvents(); - await MatrixState.pangeaController.userController.updateProfile( - (profile) { - profile.userSettings.targetLanguage = target; - return profile; - }, - waitForDataInSync: true, - ); + await MatrixState.pangeaController.userController.updateProfile(( + profile, + ) { + profile.userSettings.targetLanguage = target; + return profile; + }, waitForDataInSync: true); }, ); if (resp.isError) return; @@ -2361,8 +2373,9 @@ class ChatController extends State child: MessageAnalyticsFeedback( newGrammarConstructs: newGrammarConstructs, newVocabConstructs: newVocabConstructs, - close: () => MatrixState.pAnyState - .closeOverlay("msg_analytics_feedback_$eventId"), + close: () => MatrixState.pAnyState.closeOverlay( + "msg_analytics_feedback_$eventId", + ), ), transformTargetId: eventId, ignorePointer: true, @@ -2388,12 +2401,8 @@ class ChatController extends State inputFocus.unfocus(); activityController.toggleShowDropdown(); - if (!AppConfig.showedActivityMenu) { - AppConfig.showedActivityMenu = true; - Matrix.of(context).store.setBool( - SettingKeys.showedActivityMenu, - AppConfig.showedActivityMenu, - ); + if (!InstructionsEnum.showedActivityMenu.isToggledOff) { + InstructionsEnum.showedActivityMenu.setToggledOff(true); } } @@ -2415,10 +2424,9 @@ class ChatController extends State if (result.isError) return; final r = Matrix.of(context).client.getRoomById(widget.room.id); if (r != null && r.membership != Membership.leave) { - await Matrix.of(context).client.waitForRoomInSync( - widget.room.id, - leave: true, - ); + await Matrix.of( + context, + ).client.waitForRoomInSync(widget.room.id, leave: true); } NavigationUtil.goToSpaceRoute(null, [], context); @@ -2438,18 +2446,13 @@ class ChatController extends State clearSelectedEvents(); await showFutureLoadingDialog( context: context, - future: () => room.sendEvent( - { - "m.relates_to": { - "rel_type": PangeaEventTypes.regenerationRequest, - "event_id": eventId, - }, - PangeaEventTypes.regenerationRequest: { - "reason": reason, - }, + future: () => room.sendEvent({ + "m.relates_to": { + "rel_type": PangeaEventTypes.regenerationRequest, + "event_id": eventId, }, - type: PangeaEventTypes.regenerationRequest, - ), + PangeaEventTypes.regenerationRequest: {"reason": reason}, + }, type: PangeaEventTypes.regenerationRequest), ); } // Pangea# @@ -2458,7 +2461,6 @@ class ChatController extends State void toggleDisplayChatDetailsColumn() async { await AppSettings.displayChatDetailsColumn.setItem( - Matrix.of(context).store, !_displayChatDetailsColumn.value, ); _displayChatDetailsColumn.value = !_displayChatDetailsColumn.value; @@ -2471,46 +2473,36 @@ class ChatController extends State room: room, builder: (context, participants) { if (!room.participantListComplete && participants.loading) { - return const Center( - child: CircularProgressIndicator.adaptive(), - ); + return const Center(child: CircularProgressIndicator.adaptive()); } // Pangea# final theme = Theme.of(context); return Row( children: [ - Expanded( - child: ChatView(this), - ), + Expanded(child: ChatView(this)), ValueListenableBuilder( valueListenable: _displayChatDetailsColumn, builder: (context, displayChatDetailsColumn, _) => !FluffyThemes.isThreeColumnMode(context) || - room.membership != Membership.join || - !displayChatDetailsColumn - ? const SizedBox( - height: double.infinity, - width: 0, - ) - : Container( - width: FluffyThemes.columnWidth, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - border: Border( - left: BorderSide( - width: 1, - color: theme.dividerColor, - ), - ), - ), - child: ChatDetails( - roomId: roomId, - embeddedCloseButton: IconButton( - icon: const Icon(Icons.close), - onPressed: toggleDisplayChatDetailsColumn, - ), - ), + room.membership != Membership.join || + !displayChatDetailsColumn + ? const SizedBox(height: double.infinity, width: 0) + : Container( + width: FluffyThemes.columnWidth, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + border: Border( + left: BorderSide(width: 1, color: theme.dividerColor), ), + ), + child: ChatDetails( + roomId: roomId, + embeddedCloseButton: IconButton( + icon: const Icon(Icons.close), + onPressed: toggleDisplayChatDetailsColumn, + ), + ), + ), ), ], ); @@ -2518,3 +2510,13 @@ class ChatController extends State ); } } + +enum AddPopupMenuActions { + image, + video, + file, + poll, + photoCamera, + videoCamera, + location, +} diff --git a/lib/pages/chat/chat_app_bar_list_tile.dart b/lib/pages/chat/chat_app_bar_list_tile.dart index a1fc21b43..0f038a89e 100644 --- a/lib/pages/chat/chat_app_bar_list_tile.dart +++ b/lib/pages/chat/chat_app_bar_list_tile.dart @@ -31,7 +31,7 @@ class ChatAppBarListTile extends StatelessWidget { onTap: onTap, child: Row( children: [ - if (leading != null) leading, + ?leading, Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), @@ -56,7 +56,7 @@ class ChatAppBarListTile extends StatelessWidget { ), ), ), - if (trailing != null) trailing, + ?trailing, ], ), ), diff --git a/lib/pages/chat/chat_app_bar_title.dart b/lib/pages/chat/chat_app_bar_title.dart index 714f7ac00..e8fbdeaea 100644 --- a/lib/pages/chat/chat_app_bar_title.dart +++ b/lib/pages/chat/chat_app_bar_title.dart @@ -36,14 +36,10 @@ class ChatAppBarTitle extends StatelessWidget { onTap: controller.isArchived ? null : () => FluffyThemes.isThreeColumnMode(context) - ? controller.toggleDisplayChatDetailsColumn() - // #Pangea - // : context.go('/rooms/${room.id}/details'), - : NavigationUtil.goToSpaceRoute( - room.id, - ['details'], - context, - ), + ? controller.toggleDisplayChatDetailsColumn() + // #Pangea + // : context.go('/rooms/${room.id}/details'), + : NavigationUtil.goToSpaceRoute(room.id, ['details'], context), // Pangea# child: Row( children: [ @@ -63,22 +59,22 @@ class ChatAppBarTitle extends StatelessWidget { const SizedBox(width: 12), Expanded( child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: .start, children: [ Text( room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))), maxLines: 1, overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 16, - ), + style: const TextStyle(fontSize: 16), ), StreamBuilder( stream: room.client.onSyncStatus.stream, builder: (context, snapshot) { - final status = room.client.onSyncStatus.value ?? + final status = + room.client.onSyncStatus.value ?? const SyncStatusUpdate(SyncStatus.waitingForResponse); - final hide = FluffyThemes.isColumnMode(context) || + final hide = + FluffyThemes.isColumnMode(context) || (room.client.onSync.value != null && status.status != SyncStatus.error && room.client.prevBatch != null); @@ -90,8 +86,10 @@ class ChatAppBarTitle extends StatelessWidget { builder: (context, presence) { final lastActiveTimestamp = presence?.lastActiveTimestamp; - final style = - Theme.of(context).textTheme.bodySmall; + final style = TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ); if (presence?.currentlyActive == true) { return Text( L10n.of(context).currentlyActive, @@ -101,8 +99,9 @@ class ChatAppBarTitle extends StatelessWidget { if (lastActiveTimestamp != null) { return Text( L10n.of(context).lastActiveAgo( - lastActiveTimestamp - .localizedTimeShort(context), + lastActiveTimestamp.localizedTimeShort( + context, + ), ), style: style, ); diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart index 734d082e4..4a7076f5a 100644 --- a/lib/pages/chat/chat_emoji_picker.dart +++ b/lib/pages/chat/chat_emoji_picker.dart @@ -19,7 +19,7 @@ class ChatEmojiPicker extends StatelessWidget { clipBehavior: Clip.hardEdge, decoration: const BoxDecoration(), height: controller.showEmojiPicker - ? MediaQuery.of(context).size.height / 2 + ? MediaQuery.sizeOf(context).height / 2 : 0, child: controller.showEmojiPicker ? DefaultTabController( @@ -44,6 +44,7 @@ class ChatEmojiPicker extends StatelessWidget { onEmojiSelected: controller.onEmojiSelected, onBackspacePressed: controller.emojiPickerBackspace, config: Config( + locale: Localizations.localeOf(context), emojiViewConfig: EmojiViewConfig( noRecents: const NoRecent(), backgroundColor: @@ -53,20 +54,22 @@ class ChatEmojiPicker extends StatelessWidget { // #Pangea // enabled: false, showBackspaceButton: false, - backgroundColor: Theme.of(context) - .colorScheme - .surfaceContainer, - buttonColor: Theme.of(context) - .colorScheme - .surfaceContainer, - buttonIconColor: - Theme.of(context).colorScheme.onSurface, + backgroundColor: Theme.of( + context, + ).colorScheme.surfaceContainer, + buttonColor: Theme.of( + context, + ).colorScheme.surfaceContainer, + buttonIconColor: Theme.of( + context, + ).colorScheme.onSurface, // Pangea# ), categoryViewConfig: CategoryViewConfig( backspaceColor: theme.colorScheme.primary, - iconColor: - theme.colorScheme.primary.withAlpha(128), + iconColor: theme.colorScheme.primary.withAlpha( + 128, + ), iconColorSelected: theme.colorScheme.primary, indicatorColor: theme.colorScheme.primary, backgroundColor: theme.colorScheme.surface, @@ -103,6 +106,8 @@ class ChatEmojiPicker extends StatelessWidget { // 'url': sticker.url.toString(), // }, // type: EventTypes.Sticker, + // threadRootEventId: controller.activeThreadId, + // threadLastEventId: controller.threadLastEventId, // ); // controller.hideEmojiPicker(); // }, diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 990469e8f..92576db1a 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -1,9 +1,11 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:collection/collection.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/message.dart'; import 'package:fluffychat/pages/chat/seen_by_row.dart'; @@ -16,10 +18,7 @@ import 'package:fluffychat/utils/platform_infos.dart'; class ChatEventList extends StatelessWidget { final ChatController controller; - const ChatEventList({ - super.key, - required this.controller, - }); + const ChatEventList({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -30,14 +29,13 @@ class ChatEventList extends StatelessWidget { } final theme = Theme.of(context); - final colors = [ - theme.secondaryBubbleColor, - theme.bubbleColor, - ]; + final colors = [theme.secondaryBubbleColor, theme.bubbleColor]; final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - final events = timeline.events.filterByVisibleInGui(); + final events = timeline.events.filterByVisibleInGui( + threadId: controller.activeThreadId, + ); final animateInEventIndex = controller.animateInEventIndex; // create a map of eventId --> index to greatly improve performance of @@ -79,21 +77,21 @@ class ChatEventList extends StatelessWidget { (BuildContext context, int i) { // Footer to display typing indicator and read receipts: if (i == 0) { - if (timeline.isRequestingFuture) { - return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ); - } if (timeline.canRequestFuture) { return Center( - child: IconButton( - onPressed: controller.requestFuture, - icon: const Icon(Icons.refresh_outlined), + child: TextButton.icon( + onPressed: timeline.isRequestingFuture + ? null + : controller.requestFuture, + icon: timeline.isRequestingFuture + ? CircularProgressIndicator.adaptive(strokeWidth: 2) + : const Icon(Icons.arrow_downward_outlined), + label: Text(L10n.of(context).loadMore), ), ); } return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ SeenByRow(controller), TypingIndicators(controller), @@ -106,33 +104,39 @@ class ChatEventList extends StatelessWidget { // if (i == events.length + 1) { if (i == events.length + 2) { // Pangea# - if (timeline.isRequestingHistory) { - return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ); + if (controller.activeThreadId != null) { + return const SizedBox.shrink(); } - if (timeline.canRequestHistory) { - return Builder( - builder: (context) { + return Builder( + builder: (context) { + final visibleIndex = timeline.events.lastIndexWhere( + (event) => + !event.isCollapsedState && event.isVisibleInGui, + ); + if (visibleIndex > timeline.events.length - 50) { // #Pangea - // WidgetsBinding.instance - // .addPostFrameCallback(controller.requestHistory); + // WidgetsBinding.instance.addPostFrameCallback( + // controller.requestHistory, + // ); WidgetsBinding.instance.addPostFrameCallback( (_) => controller.requestHistory(), ); // Pangea# - return Center( - child: IconButton( - onPressed: controller.requestHistory, - icon: const Icon(Icons.refresh_outlined), - ), - ); - }, - ); - } - return const SizedBox.shrink(); + } + return Center( + child: TextButton.icon( + onPressed: timeline.isRequestingHistory + ? null + : controller.requestHistory, + icon: timeline.isRequestingHistory + ? CircularProgressIndicator.adaptive(strokeWidth: 2) + : const Icon(Icons.arrow_upward_outlined), + label: Text(L10n.of(context).loadMore), + ), + ); + }, + ); } - // #Pangea if (i == 1) { return ActivityUserSummaries(controller: controller); @@ -146,10 +150,24 @@ class ChatEventList extends StatelessWidget { // The message at this index: final event = events[i]; - final animateIn = animateInEventIndex != null && + final animateIn = + animateInEventIndex != null && timeline.events.length > animateInEventIndex && event == timeline.events[animateInEventIndex]; + final nextEvent = i + 1 < events.length ? events[i + 1] : null; + final previousEvent = i > 0 ? events[i - 1] : null; + + // Collapsed state event + final canExpand = + event.isCollapsedState && + nextEvent?.isCollapsedState == true && + previousEvent?.isCollapsedState != true; + final isCollapsed = + event.isCollapsedState && + previousEvent?.isCollapsedState == true && + !controller.expandedEventIds.contains(event.eventId); + return AutoScrollTag( key: ValueKey(event.eventId), index: i, @@ -181,20 +199,31 @@ class ChatEventList extends StatelessWidget { isButton: event.eventId == controller.buttonEventID, canRefresh: event.eventId == controller.refreshEventID, // Pangea# - selected: controller.selectedEvents - .any((e) => e.eventId == event.eventId), + selected: controller.selectedEvents.any( + (e) => e.eventId == event.eventId, + ), singleSelected: controller.selectedEvents.singleOrNull?.eventId == - event.eventId, + event.eventId, onEdit: () => controller.editSelectedEventAction(), timeline: timeline, displayReadMarker: i > 0 && controller.readMarkerEventId == event.eventId, - nextEvent: i + 1 < events.length ? events[i + 1] : null, - previousEvent: i > 0 ? events[i - 1] : null, + nextEvent: nextEvent, + previousEvent: previousEvent, wallpaperMode: hasWallpaper, scrollController: controller.scrollController, colors: colors, + isCollapsed: isCollapsed, + enterThread: controller.activeThreadId == null + ? controller.enterThread + : null, + onExpand: canExpand + ? () => controller.expandEventsFrom( + event, + !controller.expandedEventIds.contains(event.eventId), + ) + : null, ), ); }, diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 97dbc47b3..68258f251 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -1,10 +1,13 @@ // import 'package:flutter/material.dart'; // import 'package:animations/animations.dart'; +// import 'package:emoji_picker_flutter/locales/default_emoji_set_locale.dart'; // import 'package:matrix/matrix.dart'; -// import 'package:fluffychat/config/app_config.dart'; +// import 'package:fluffychat/config/setting_keys.dart'; // import 'package:fluffychat/l10n/l10n.dart'; +// import 'package:fluffychat/pages/chat/recording_input_row.dart'; +// import 'package:fluffychat/pages/chat/recording_view_model.dart'; // import 'package:fluffychat/utils/other_party_can_receive.dart'; // import 'package:fluffychat/utils/platform_infos.dart'; // import 'package:fluffychat/widgets/avatar.dart'; @@ -41,296 +44,356 @@ // foregroundColor: theme.colorScheme.onTertiaryContainer, // ); -// return Row( -// crossAxisAlignment: CrossAxisAlignment.end, -// mainAxisAlignment: MainAxisAlignment.spaceBetween, -// children: controller.selectMode -// ? [ -// if (controller.selectedEvents -// .every((event) => event.status == EventStatus.error)) -// SizedBox( -// height: height, -// child: TextButton( -// style: TextButton.styleFrom( -// foregroundColor: Colors.orange, +// return RecordingViewModel( +// builder: (context, recordingViewModel) { +// if (recordingViewModel.isRecording) { +// return RecordingInputRow( +// state: recordingViewModel, +// onSend: controller.onVoiceMessageSend, +// ); +// } +// return Row( +// crossAxisAlignment: .end, +// mainAxisAlignment: .spaceBetween, +// children: controller.selectMode +// ? [ +// if (controller.selectedEvents.every( +// (event) => event.status == EventStatus.error, +// )) +// SizedBox( +// height: height, +// child: TextButton( +// style: TextButton.styleFrom( +// foregroundColor: theme.colorScheme.error, +// ), +// onPressed: controller.deleteErrorEventsAction, +// child: Row( +// children: [ +// const Icon(Icons.delete_forever_outlined), +// Text(L10n.of(context).delete), +// ], +// ), +// ), +// ) +// else +// SizedBox( +// height: height, +// child: TextButton( +// style: selectedTextButtonStyle, +// onPressed: controller.forwardEventsAction, +// child: Row( +// children: [ +// const Icon(Icons.keyboard_arrow_left_outlined), +// Text(L10n.of(context).forward), +// ], +// ), +// ), // ), -// onPressed: controller.deleteErrorEventsAction, -// child: Row( -// children: [ -// const Icon(Icons.delete), -// Text(L10n.of(context).delete), +// controller.selectedEvents.length == 1 +// ? controller.selectedEvents.first +// .getDisplayEvent(controller.timeline!) +// .status +// .isSent +// ? SizedBox( +// height: height, +// child: TextButton( +// style: selectedTextButtonStyle, +// onPressed: controller.replyAction, +// child: Row( +// children: [ +// Text(L10n.of(context).reply), +// const Icon(Icons.keyboard_arrow_right), +// ], +// ), +// ), +// ) +// : SizedBox( +// height: height, +// child: TextButton( +// style: selectedTextButtonStyle, +// onPressed: controller.sendAgainAction, +// child: Row( +// children: [ +// Text(L10n.of(context).tryToSendAgain), +// const SizedBox(width: 4), +// const Icon(Icons.send_outlined, size: 16), +// ], +// ), +// ), +// ) +// : const SizedBox.shrink(), +// ] +// : [ +// const SizedBox(width: 4), +// AnimatedContainer( +// duration: FluffyThemes.animationDuration, +// curve: FluffyThemes.animationCurve, +// width: controller.sendController.text.isNotEmpty +// ? 0 +// : height, +// height: height, +// alignment: Alignment.center, +// decoration: const BoxDecoration(), +// clipBehavior: Clip.hardEdge, +// child: PopupMenuButton( +// useRootNavigator: true, +// icon: const Icon(Icons.add_circle_outline), +// iconColor: theme.colorScheme.onPrimaryContainer, +// onSelected: controller.onAddPopupMenuButtonSelected, +// itemBuilder: (BuildContext context) => [ +// if (PlatformInfos.isMobile) +// PopupMenuItem( +// value: AddPopupMenuActions.location, +// child: ListTile( +// leading: CircleAvatar( +// backgroundColor: +// theme.colorScheme.onPrimaryContainer, +// foregroundColor: +// theme.colorScheme.primaryContainer, +// child: const Icon(Icons.gps_fixed_outlined), +// ), +// title: Text(L10n.of(context).shareLocation), +// contentPadding: const EdgeInsets.all(0), +// ), +// ), +// PopupMenuItem( +// value: AddPopupMenuActions.poll, +// child: ListTile( +// leading: CircleAvatar( +// backgroundColor: +// theme.colorScheme.onPrimaryContainer, +// foregroundColor: +// theme.colorScheme.primaryContainer, +// child: const Icon(Icons.poll_outlined), +// ), +// title: Text(L10n.of(context).startPoll), +// contentPadding: const EdgeInsets.all(0), +// ), +// ), +// PopupMenuItem( +// value: AddPopupMenuActions.image, +// child: ListTile( +// leading: CircleAvatar( +// backgroundColor: +// theme.colorScheme.onPrimaryContainer, +// foregroundColor: +// theme.colorScheme.primaryContainer, +// child: const Icon(Icons.photo_outlined), +// ), +// title: Text(L10n.of(context).sendImage), +// contentPadding: const EdgeInsets.all(0), +// ), +// ), +// PopupMenuItem( +// value: AddPopupMenuActions.video, +// child: ListTile( +// leading: CircleAvatar( +// backgroundColor: +// theme.colorScheme.onPrimaryContainer, +// foregroundColor: +// theme.colorScheme.primaryContainer, +// child: const Icon( +// Icons.video_camera_back_outlined, +// ), +// ), +// title: Text(L10n.of(context).sendVideo), +// contentPadding: const EdgeInsets.all(0), +// ), +// ), +// PopupMenuItem( +// value: AddPopupMenuActions.file, +// child: ListTile( +// leading: CircleAvatar( +// backgroundColor: +// theme.colorScheme.onPrimaryContainer, +// foregroundColor: +// theme.colorScheme.primaryContainer, +// child: const Icon(Icons.attachment_outlined), +// ), +// title: Text(L10n.of(context).sendFile), +// contentPadding: const EdgeInsets.all(0), +// ), +// ), // ], // ), // ), -// ) -// else -// SizedBox( -// height: height, -// child: TextButton( -// style: selectedTextButtonStyle, -// onPressed: controller.forwardEventsAction, -// child: Row( -// children: [ -// const Icon(Icons.keyboard_arrow_left_outlined), -// Text(L10n.of(context).forward), -// ], -// ), -// ), -// ), -// controller.selectedEvents.length == 1 -// ? controller.selectedEvents.first -// .getDisplayEvent(controller.timeline!) -// .status -// .isSent -// ? SizedBox( -// height: height, -// child: TextButton( -// style: selectedTextButtonStyle, -// onPressed: controller.replyAction, -// child: Row( -// children: [ -// Text(L10n.of(context).reply), -// const Icon(Icons.keyboard_arrow_right), -// ], +// if (PlatformInfos.isMobile) +// AnimatedContainer( +// duration: FluffyThemes.animationDuration, +// curve: FluffyThemes.animationCurve, +// width: controller.sendController.text.isNotEmpty +// ? 0 +// : height, +// height: height, +// alignment: Alignment.center, +// decoration: const BoxDecoration(), +// clipBehavior: Clip.hardEdge, +// child: PopupMenuButton( +// useRootNavigator: true, +// icon: const Icon(Icons.camera_alt_outlined), +// onSelected: controller.onAddPopupMenuButtonSelected, +// iconColor: theme.colorScheme.onPrimaryContainer, +// itemBuilder: (context) => [ +// PopupMenuItem( +// value: AddPopupMenuActions.videoCamera, +// child: ListTile( +// leading: CircleAvatar( +// backgroundColor: +// theme.colorScheme.onPrimaryContainer, +// foregroundColor: +// theme.colorScheme.primaryContainer, +// child: const Icon(Icons.videocam_outlined), +// ), +// title: Text(L10n.of(context).recordAVideo), +// contentPadding: const EdgeInsets.all(0), // ), // ), -// ) -// : SizedBox( -// height: height, -// child: TextButton( -// style: selectedTextButtonStyle, -// onPressed: controller.sendAgainAction, -// child: Row( -// children: [ -// Text(L10n.of(context).tryToSendAgain), -// const SizedBox(width: 4), -// const Icon(Icons.send_outlined, size: 16), -// ], +// PopupMenuItem( +// value: AddPopupMenuActions.photoCamera, +// child: ListTile( +// leading: CircleAvatar( +// backgroundColor: +// theme.colorScheme.onPrimaryContainer, +// foregroundColor: +// theme.colorScheme.primaryContainer, +// child: const Icon(Icons.camera_alt_outlined), +// ), +// title: Text(L10n.of(context).takeAPhoto), +// contentPadding: const EdgeInsets.all(0), // ), // ), -// ) -// : const SizedBox.shrink(), -// ] -// : [ -// const SizedBox(width: 4), -// AnimatedContainer( -// duration: FluffyThemes.animationDuration, -// curve: FluffyThemes.animationCurve, -// width: controller.sendController.text.isNotEmpty ? 0 : height, -// height: height, -// alignment: Alignment.center, -// decoration: const BoxDecoration(), -// clipBehavior: Clip.hardEdge, -// child: PopupMenuButton( -// useRootNavigator: true, -// icon: const Icon(Icons.add_circle_outline), -// iconColor: theme.colorScheme.onPrimaryContainer, -// onSelected: controller.onAddPopupMenuButtonSelected, -// itemBuilder: (BuildContext context) => -// >[ -// if (PlatformInfos.isMobile) -// PopupMenuItem( -// value: 'location', -// child: ListTile( -// leading: CircleAvatar( -// backgroundColor: -// theme.colorScheme.onPrimaryContainer, -// foregroundColor: theme.colorScheme.primaryContainer, -// child: const Icon(Icons.gps_fixed_outlined), -// ), -// title: Text(L10n.of(context).shareLocation), -// contentPadding: const EdgeInsets.all(0), -// ), -// ), -// PopupMenuItem( -// value: 'image', -// child: ListTile( -// leading: CircleAvatar( -// backgroundColor: theme.colorScheme.onPrimaryContainer, -// foregroundColor: theme.colorScheme.primaryContainer, -// child: const Icon(Icons.photo_outlined), -// ), -// title: Text(L10n.of(context).sendImage), -// contentPadding: const EdgeInsets.all(0), +// ], // ), // ), -// PopupMenuItem( -// value: 'video', -// child: ListTile( -// leading: CircleAvatar( -// backgroundColor: theme.colorScheme.onPrimaryContainer, -// foregroundColor: theme.colorScheme.primaryContainer, -// child: const Icon(Icons.video_camera_back_outlined), -// ), -// title: Text(L10n.of(context).sendVideo), -// contentPadding: const EdgeInsets.all(0), -// ), -// ), -// PopupMenuItem( -// value: 'file', -// child: ListTile( -// leading: CircleAvatar( -// backgroundColor: theme.colorScheme.onPrimaryContainer, -// foregroundColor: theme.colorScheme.primaryContainer, -// child: const Icon(Icons.attachment_outlined), -// ), -// title: Text(L10n.of(context).sendFile), -// contentPadding: const EdgeInsets.all(0), -// ), -// ), -// ], -// ), -// ), -// if (PlatformInfos.isMobile) -// AnimatedContainer( -// duration: FluffyThemes.animationDuration, -// curve: FluffyThemes.animationCurve, -// width: controller.sendController.text.isNotEmpty ? 0 : height, -// height: height, -// alignment: Alignment.center, -// decoration: const BoxDecoration(), -// clipBehavior: Clip.hardEdge, -// child: PopupMenuButton( -// useRootNavigator: true, -// icon: const Icon(Icons.camera_alt_outlined), -// onSelected: controller.onAddPopupMenuButtonSelected, -// iconColor: theme.colorScheme.onPrimaryContainer, -// itemBuilder: (context) => [ -// PopupMenuItem( -// value: 'camera-video', -// child: ListTile( -// leading: CircleAvatar( -// backgroundColor: -// theme.colorScheme.onPrimaryContainer, -// foregroundColor: theme.colorScheme.primaryContainer, -// child: const Icon(Icons.videocam_outlined), -// ), -// title: Text(L10n.of(context).recordAVideo), -// contentPadding: const EdgeInsets.all(0), +// Container( +// height: height, +// width: height, +// alignment: Alignment.center, +// child: IconButton( +// tooltip: L10n.of(context).emojis, +// color: theme.colorScheme.onPrimaryContainer, +// icon: PageTransitionSwitcher( +// transitionBuilder: +// ( +// Widget child, +// Animation primaryAnimation, +// Animation secondaryAnimation, +// ) { +// return SharedAxisTransition( +// animation: primaryAnimation, +// secondaryAnimation: secondaryAnimation, +// transitionType: SharedAxisTransitionType.scaled, +// fillColor: Colors.transparent, +// child: child, +// ); +// }, +// child: Icon( +// controller.showEmojiPicker +// ? Icons.keyboard +// : Icons.add_reaction_outlined, +// key: ValueKey(controller.showEmojiPicker), // ), // ), -// PopupMenuItem( -// value: 'camera', -// child: ListTile( -// leading: CircleAvatar( -// backgroundColor: -// theme.colorScheme.onPrimaryContainer, -// foregroundColor: theme.colorScheme.primaryContainer, -// child: const Icon(Icons.camera_alt_outlined), -// ), -// title: Text(L10n.of(context).takeAPhoto), -// contentPadding: const EdgeInsets.all(0), -// ), -// ), -// ], -// ), -// ), -// Container( -// height: height, -// width: height, -// alignment: Alignment.center, -// child: IconButton( -// tooltip: L10n.of(context).emojis, -// color: theme.colorScheme.onPrimaryContainer, -// icon: PageTransitionSwitcher( -// transitionBuilder: ( -// Widget child, -// Animation primaryAnimation, -// Animation secondaryAnimation, -// ) { -// return SharedAxisTransition( -// animation: primaryAnimation, -// secondaryAnimation: secondaryAnimation, -// transitionType: SharedAxisTransitionType.scaled, -// fillColor: Colors.transparent, -// child: child, -// ); -// }, -// child: Icon( -// controller.showEmojiPicker -// ? Icons.keyboard -// : Icons.add_reaction_outlined, -// key: ValueKey(controller.showEmojiPicker), +// onPressed: controller.emojiPickerAction, // ), // ), -// onPressed: controller.emojiPickerAction, -// ), -// ), -// if (Matrix.of(context).isMultiAccount && -// Matrix.of(context).hasComplexBundles && -// Matrix.of(context).currentBundle!.length > 1) -// Container( -// width: height, -// height: height, -// alignment: Alignment.center, -// child: _ChatAccountPicker(controller), -// ), -// Expanded( -// child: Padding( -// padding: const EdgeInsets.symmetric(vertical: 0.0), -// child: InputBar( -// room: controller.room, -// minLines: 1, -// maxLines: 8, -// autofocus: !PlatformInfos.isMobile, -// keyboardType: TextInputType.multiline, -// textInputAction: -// AppConfig.sendOnEnter == true && PlatformInfos.isMobile +// if (Matrix.of(context).isMultiAccount && +// Matrix.of(context).hasComplexBundles && +// Matrix.of(context).currentBundle!.length > 1) +// Container( +// height: height, +// width: height, +// alignment: Alignment.center, +// child: _ChatAccountPicker(controller), +// ), +// Expanded( +// child: Padding( +// padding: const EdgeInsets.symmetric(vertical: 0.0), +// child: InputBar( +// room: controller.room, +// minLines: 1, +// maxLines: 8, +// autofocus: !PlatformInfos.isMobile, +// keyboardType: TextInputType.multiline, +// textInputAction: +// AppSettings.sendOnEnter.value == true && +// PlatformInfos.isMobile // ? TextInputAction.send // : null, -// onSubmitted: controller.onInputBarSubmitted, -// onSubmitImage: controller.sendImageFromClipBoard, -// focusNode: controller.inputFocus, -// controller: controller.sendController, -// decoration: InputDecoration( -// contentPadding: const EdgeInsets.only( -// left: 6.0, -// right: 6.0, -// bottom: 6.0, -// top: 3.0, +// onSubmitted: controller.onInputBarSubmitted, +// onSubmitImage: controller.sendImageFromClipBoard, +// focusNode: controller.inputFocus, +// controller: controller.sendController, +// decoration: InputDecoration( +// contentPadding: const EdgeInsets.only( +// left: 6.0, +// right: 6.0, +// bottom: 6.0, +// top: 3.0, +// ), +// counter: const SizedBox.shrink(), +// hintText: L10n.of(context).writeAMessage, +// hintMaxLines: 1, +// border: InputBorder.none, +// enabledBorder: InputBorder.none, +// filled: false, +// ), +// onChanged: controller.onInputBarChanged, +// suggestionEmojis: +// getDefaultEmojiLocale( +// AppSettings.emojiSuggestionLocale.value.isNotEmpty +// ? Locale( +// AppSettings.emojiSuggestionLocale.value, +// ) +// : Localizations.localeOf(context), +// ).fold( +// [], +// (emojis, category) => +// emojis..addAll(category.emoji), +// ), // ), -// hintText: L10n.of(context).writeAMessage, -// hintMaxLines: 1, -// border: InputBorder.none, -// enabledBorder: InputBorder.none, -// filled: false, // ), -// onChanged: controller.onInputBarChanged, // ), -// ), -// ), -// Container( -// height: height, -// width: height, -// alignment: Alignment.center, -// child: PlatformInfos.platformCanRecord && -// controller.sendController.text.isEmpty -// ? FloatingActionButton.small( -// tooltip: L10n.of(context).voiceMessage, -// onPressed: controller.voiceMessageAction, -// elevation: 0, -// heroTag: null, -// shape: RoundedRectangleBorder( -// borderRadius: BorderRadius.circular(height), -// ), -// backgroundColor: theme.bubbleColor, -// foregroundColor: theme.onBubbleColor, -// child: const Icon(Icons.mic_none_outlined), -// ) -// : FloatingActionButton.small( -// tooltip: L10n.of(context).send, -// onPressed: controller.send, -// elevation: 0, -// heroTag: null, -// shape: RoundedRectangleBorder( -// borderRadius: BorderRadius.circular(height), -// ), -// backgroundColor: theme.bubbleColor, -// foregroundColor: theme.onBubbleColor, -// child: const Icon(Icons.send_outlined), -// ), -// ), -// ], +// Container( +// height: height, +// width: height, +// alignment: Alignment.center, +// child: +// PlatformInfos.platformCanRecord && +// controller.sendController.text.isEmpty +// ? IconButton( +// tooltip: L10n.of(context).voiceMessage, +// onPressed: () => +// ScaffoldMessenger.of(context).showSnackBar( +// SnackBar( +// content: Text( +// L10n.of( +// context, +// ).longPressToRecordVoiceMessage, +// ), +// ), +// ), +// onLongPress: () => recordingViewModel +// .startRecording(controller.room), +// style: IconButton.styleFrom( +// backgroundColor: theme.bubbleColor, +// foregroundColor: theme.onBubbleColor, +// ), +// icon: const Icon(Icons.mic_none_outlined), +// ) +// : IconButton( +// tooltip: L10n.of(context).send, +// onPressed: controller.send, +// style: IconButton.styleFrom( +// backgroundColor: theme.bubbleColor, +// foregroundColor: theme.onBubbleColor, +// ), +// icon: const Icon(Icons.send_outlined), +// ), +// ), +// ], +// ); +// }, // ); // } // } @@ -341,9 +404,9 @@ // const _ChatAccountPicker(this.controller); // void _popupMenuButtonSelected(String mxid, BuildContext context) { -// final client = Matrix.of(context) -// .currentBundle! -// .firstWhere((cl) => cl!.userID == mxid, orElse: () => null); +// final client = Matrix.of( +// context, +// ).currentBundle!.firstWhere((cl) => cl!.userID == mxid, orElse: () => null); // if (client == null) { // Logs().w('Attempted to switch to a non-existing client $mxid'); // return; @@ -363,14 +426,15 @@ // onSelected: (mxid) => _popupMenuButtonSelected(mxid, context), // itemBuilder: (BuildContext context) => clients // .map( -// (client) => PopupMenuItem( +// (client) => PopupMenuItem( // value: client!.userID, // child: FutureBuilder( // future: client.fetchOwnProfile(), // builder: (context, snapshot) => ListTile( // leading: Avatar( // mxContent: snapshot.data?.avatarUrl, -// name: snapshot.data?.displayName ?? +// name: +// snapshot.data?.displayName ?? // client.userID!.localpart, // size: 20, // ), @@ -383,7 +447,8 @@ // .toList(), // child: Avatar( // mxContent: snapshot.data?.avatarUrl, -// name: snapshot.data?.displayName ?? +// name: +// snapshot.data?.displayName ?? // Matrix.of(context).client.userID!.localpart, // size: 20, // ), diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index c698dacbf..f9665e0aa 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -54,17 +54,21 @@ class ChatView extends StatelessWidget { // tooltip: L10n.of(context).edit, // onPressed: controller.editSelectedEventAction, // ), + // if (controller.selectedEvents.length == 1 && + // controller.activeThreadId == null && + // controller.room.canSendDefaultMessages) + // IconButton( + // icon: const Icon(Icons.message_outlined), + // tooltip: L10n.of(context).replyInThread, + // onPressed: () => controller.enterThread( + // controller.selectedEvents.single.eventId, + // ), + // ), // IconButton( // icon: const Icon(Icons.copy_outlined), - // tooltip: L10n.of(context).copy, + // tooltip: L10n.of(context).copyToClipboard, // onPressed: controller.copyEventsAction, // ), - // if (controller.canPinSelectedEvents) - // IconButton( - // icon: const Icon(Icons.push_pin_outlined), - // onPressed: controller.pinEvent, - // tooltip: L10n.of(context).pinMessage, - // ), // if (controller.canRedactSelectedEvents) // IconButton( // icon: const Icon(Icons.delete_outlined), @@ -86,12 +90,25 @@ class ChatView extends StatelessWidget { // } // }, // itemBuilder: (context) => [ + // if (controller.canPinSelectedEvents) + // PopupMenuItem( + // onTap: controller.pinEvent, + // value: null, + // child: Row( + // mainAxisSize: .min, + // children: [ + // const Icon(Icons.push_pin_outlined), + // const SizedBox(width: 12), + // Text(L10n.of(context).pinMessage), + // ], + // ), + // ), // if (controller.canSaveSelectedEvent) // PopupMenuItem( // onTap: () => controller.saveSelectedEvent(context), // value: null, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // const Icon(Icons.download_outlined), // const SizedBox(width: 12), @@ -102,7 +119,7 @@ class ChatView extends StatelessWidget { // PopupMenuItem( // value: _EventContextAction.info, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // const Icon(Icons.info_outlined), // const SizedBox(width: 12), @@ -114,12 +131,9 @@ class ChatView extends StatelessWidget { // PopupMenuItem( // value: _EventContextAction.report, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ - // const Icon( - // Icons.shield_outlined, - // color: Colors.red, - // ), + // const Icon(Icons.shield_outlined, color: Colors.red), // const SizedBox(width: 12), // Text(L10n.of(context).reportMessage), // ], @@ -130,7 +144,7 @@ class ChatView extends StatelessWidget { // ]; // } else if (!controller.room.isArchived) { // return [ - // if (AppConfig.experimentalVoip && + // if (AppSettings.experimentalVoip.value && // Matrix.of(context).voipPlugin != null && // controller.room.isDirectChat) // IconButton( @@ -150,10 +164,7 @@ class ChatView extends StatelessWidget { if (controller.room.showActivityChatUI) { return [ ActivityMenuButton(controller: controller), - ActivitySessionPopupMenu( - controller.room, - onLeave: controller.onLeave, - ), + ActivitySessionPopupMenu(controller.room, onLeave: controller.onLeave), ]; } @@ -162,11 +173,9 @@ class ChatView extends StatelessWidget { icon: const Icon(Icons.search_outlined), tooltip: L10n.of(context).search, onPressed: () { - NavigationUtil.goToSpaceRoute( - controller.room.id, - ['search'], - context, - ); + NavigationUtil.goToSpaceRoute(controller.room.id, [ + 'search', + ], context); }, ), IconButton( @@ -174,17 +183,11 @@ class ChatView extends StatelessWidget { tooltip: L10n.of(context).chatDetails, onPressed: () { if (GoRouterState.of(context).uri.path.endsWith('/details')) { - NavigationUtil.goToSpaceRoute( - controller.room.id, - [], - context, - ); + NavigationUtil.goToSpaceRoute(controller.room.id, [], context); } else { - NavigationUtil.goToSpaceRoute( - controller.room.id, - ['details'], - context, - ); + NavigationUtil.goToSpaceRoute(controller.room.id, [ + 'details', + ], context); } }, ), @@ -208,13 +211,18 @@ class ChatView extends StatelessWidget { final accountConfig = Matrix.of(context).client.applicationAccountConfig; return PopScope( - canPop: controller.selectedEvents.isEmpty && !controller.showEmojiPicker, + canPop: + controller.selectedEvents.isEmpty && + !controller.showEmojiPicker && + controller.activeThreadId == null, onPopInvokedWithResult: (pop, _) async { if (pop) return; if (controller.selectedEvents.isNotEmpty) { controller.clearSelectedEvents(); } else if (controller.showEmojiPicker) { controller.emojiPickerAction(); + } else if (controller.activeThreadId != null) { + controller.closeThread(); } }, child: StreamBuilder( @@ -235,10 +243,15 @@ class ChatView extends StatelessWidget { } // Pangea# var appbarBottomHeight = 0.0; - if (controller.room.pinnedEventIds.isNotEmpty) { + final activeThreadId = controller.activeThreadId; + if (activeThreadId != null) { appbarBottomHeight += ChatAppBarListTile.fixedHeight; } - if (scrollUpBannerEventId != null) { + if (controller.room.pinnedEventIds.isNotEmpty && + activeThreadId == null) { + appbarBottomHeight += ChatAppBarListTile.fixedHeight; + } + if (scrollUpBannerEventId != null && activeThreadId == null) { appbarBottomHeight += ChatAppBarListTile.fixedHeight; } return Scaffold( @@ -250,7 +263,9 @@ class ChatView extends StatelessWidget { // : theme.colorScheme.onTertiaryContainer, // ), // backgroundColor: controller.selectedEvents.isEmpty - // ? null + // ? controller.activeThreadId != null + // ? theme.colorScheme.secondaryContainer + // : null // : theme.colorScheme.tertiaryContainer, // Pangea# automaticallyImplyLeading: false, @@ -261,49 +276,74 @@ class ChatView extends StatelessWidget { tooltip: L10n.of(context).close, color: theme.colorScheme.onTertiaryContainer, ) + : activeThreadId != null + ? IconButton( + icon: const Icon(Icons.close), + onPressed: controller.closeThread, + tooltip: L10n.of(context).backToMainChat, + color: theme.colorScheme.onSecondaryContainer, + ) // #Pangea : controller.widget.backButton != null - ? controller.widget.backButton! - // : FluffyThemes.isColumnMode(context) - // ? null - // Pangea# - : StreamBuilder( - stream: - Matrix.of(context).client.onSync.stream.where( - (syncUpdate) => syncUpdate.hasRoomUpdate, - ), - // #Pangea - // builder: (context, _) => UnreadRoomsBadge( - // filter: (r) => r.id != controller.roomId, - // badgePosition: - // BadgePosition.topEnd(end: 8, top: 4), - // child: const Center(child: BackButton()), - // ), - builder: (context, _) => Center( - child: SizedBox( - height: kToolbarHeight, - child: UnreadRoomsBadge( - filter: (r) => r.id != controller.roomId, - badgePosition: BadgePosition.topEnd( - end: 8, - top: 9, - ), - child: const Center(child: BackButton()), - ), + ? controller.widget.backButton! + // : FluffyThemes.isColumnMode(context) + // ? null + // Pangea# + : StreamBuilder( + stream: Matrix.of(context).client.onSync.stream.where( + (syncUpdate) => syncUpdate.hasRoomUpdate, + ), + // #Pangea + // builder: (context, _) => UnreadRoomsBadge( + // filter: (r) => r.id != controller.roomId, + // badgePosition: BadgePosition.topEnd(end: 8, top: 4), + // child: const Center(child: BackButton()), + // ), + builder: (context, _) => Center( + child: SizedBox( + height: kToolbarHeight, + child: UnreadRoomsBadge( + filter: (r) => r.id != controller.roomId, + badgePosition: BadgePosition.topEnd( + end: 8, + top: 9, ), + child: const Center(child: BackButton()), ), - // Pangea# ), + ), + // Pangea# + ), titleSpacing: FluffyThemes.isColumnMode(context) ? 24 : 0, title: ChatAppBarTitle(controller), actions: _appBarActions(context), bottom: PreferredSize( preferredSize: Size.fromHeight(appbarBottomHeight), child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ PinnedEvents(controller), - if (scrollUpBannerEventId != null) + if (activeThreadId != null) + SizedBox( + height: ChatAppBarListTile.fixedHeight, + child: Center( + child: TextButton.icon( + onPressed: () => + controller.scrollToEventId(activeThreadId), + icon: const Icon(Icons.message), + label: Text(L10n.of(context).replyInThread), + style: TextButton.styleFrom( + foregroundColor: + theme.colorScheme.onSecondaryContainer, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ), + ), + if (scrollUpBannerEventId != null && + activeThreadId == null) ChatAppBarListTile( leading: IconButton( color: theme.colorScheme.onSurfaceVariant, @@ -317,9 +357,7 @@ class ChatView extends StatelessWidget { title: L10n.of(context).jumpToLastReadMessage, trailing: TextButton( onPressed: () { - controller.scrollToEventId( - scrollUpBannerEventId, - ); + controller.scrollToEventId(scrollUpBannerEventId); controller.discardScrollUpBannerEventId(); }, child: Text(L10n.of(context).jump), @@ -330,9 +368,8 @@ class ChatView extends StatelessWidget { ), ), // #Pangea - // floatingActionButtonLocation: - // FloatingActionButtonLocation.miniCenterFloat, - // floatingActionButton: controller.showScrollDownButton && + // floatingActionButton: + // controller.showScrollDownButton && // controller.selectedEvents.isEmpty // ? Padding( // padding: const EdgeInsets.only(bottom: 56.0), @@ -372,7 +409,8 @@ class ChatView extends StatelessWidget { sigmaX: accountConfig.wallpaperBlur ?? 0.0, sigmaY: accountConfig.wallpaperBlur ?? 0.0, ), - child: controller.room.activityPlan!.imageURL! + child: + controller.room.activityPlan!.imageURL! .toString() .startsWith('mxc') ? MxcImage( @@ -381,7 +419,9 @@ class ChatView extends StatelessWidget { height: MediaQuery.sizeOf(context).height, width: MediaQuery.sizeOf(context).width, cacheKey: controller - .room.activityPlan!.imageURL + .room + .activityPlan! + .imageURL .toString(), isThumbnail: false, ) @@ -391,8 +431,8 @@ class ChatView extends StatelessWidget { fit: BoxFit.cover, height: MediaQuery.sizeOf(context).height, width: MediaQuery.sizeOf(context).width, - headers: controller - .room.activityPlan!.imageURL + headers: + controller.room.activityPlan!.imageURL .toString() .contains(Environment.cmsApi) ? { @@ -455,10 +495,7 @@ class ChatView extends StatelessWidget { ), // #Pangea // if (controller.showScrollDownButton) - // Divider( - // height: 1, - // color: theme.dividerColor, - // ), + // Divider(height: 1, color: theme.dividerColor), ListenableBuilder( listenable: controller.scrollController, builder: (context, _) { @@ -488,67 +525,60 @@ class ChatView extends StatelessWidget { // #Pangea // else if (controller.room.canSendDefaultMessages && // controller.room.membership == Membership.join) - // Container( - // margin: EdgeInsets.all(bottomSheetPadding), - // constraints: const BoxConstraints( - // maxWidth: FluffyThemes.maxTimelineWidth, - // ), - // alignment: Alignment.center, - // child: Material( - // clipBehavior: Clip.hardEdge, - // color: controller.selectedEvents.isNotEmpty - // ? theme.colorScheme.tertiaryContainer - // : theme.colorScheme.surfaceContainerHigh, - // borderRadius: const BorderRadius.all( - // Radius.circular(24), + // Container( + // margin: EdgeInsets.all(bottomSheetPadding), + // constraints: const BoxConstraints( + // maxWidth: FluffyThemes.maxTimelineWidth, // ), - // child: controller.room.isAbandonedDMRoom == true - // ? Row( - // mainAxisAlignment: - // MainAxisAlignment.spaceEvenly, - // children: [ - // TextButton.icon( - // style: TextButton.styleFrom( - // padding: const EdgeInsets.all( - // 16, + // alignment: Alignment.center, + // child: Material( + // clipBehavior: Clip.hardEdge, + // color: controller.selectedEvents.isNotEmpty + // ? theme.colorScheme.tertiaryContainer + // : theme.colorScheme.surfaceContainerHigh, + // borderRadius: const BorderRadius.all( + // Radius.circular(24), + // ), + // child: controller.room.isAbandonedDMRoom == true + // ? Row( + // mainAxisAlignment: .spaceEvenly, + // children: [ + // TextButton.icon( + // style: TextButton.styleFrom( + // padding: const EdgeInsets.all(16), + // foregroundColor: + // theme.colorScheme.error, // ), - // foregroundColor: - // theme.colorScheme.error, + // icon: const Icon( + // Icons.archive_outlined, + // ), + // onPressed: controller.leaveChat, + // label: Text(L10n.of(context).leave), // ), - // icon: const Icon( - // Icons.archive_outlined, - // ), - // onPressed: controller.leaveChat, - // label: Text( - // L10n.of(context).leave, - // ), - // ), - // TextButton.icon( - // style: TextButton.styleFrom( - // padding: const EdgeInsets.all( - // 16, + // TextButton.icon( + // style: TextButton.styleFrom( + // padding: const EdgeInsets.all(16), + // ), + // icon: const Icon( + // Icons.forum_outlined, + // ), + // onPressed: controller.recreateChat, + // label: Text( + // L10n.of(context).reopenChat, // ), // ), - // icon: const Icon( - // Icons.forum_outlined, - // ), - // onPressed: controller.recreateChat, - // label: Text( - // L10n.of(context).reopenChat, - // ), - // ), - // ], - // ) - // : Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // ReplyDisplay(controller), - // ChatInputRow(controller), - // ChatEmojiPicker(controller), - // ], - // ), + // ], + // ) + // : Column( + // mainAxisSize: .min, + // children: [ + // ReplyDisplay(controller), + // ChatInputRow(controller), + // ChatEmojiPicker(controller), + // ], + // ), + // ), // ), - // ) else if (controller.room.canSendDefaultMessages && controller.room.membership == Membership.join && (controller.room.activityPlan == null || @@ -565,9 +595,7 @@ class ChatView extends StatelessWidget { controller: controller, ), if (controller.room.isActivityFinished) - LoadActivitySummaryWidget( - room: controller.room, - ), + LoadActivitySummaryWidget(room: controller.room), // Pangea# ], ), @@ -578,7 +606,7 @@ class ChatView extends StatelessWidget { ValueListenableBuilder( valueListenable: controller.activityController.hasRainedConfetti, - builder: (context, hasRained, __) { + builder: (context, hasRained, _) { return hasRained ? const SizedBox() : StarRainWidget( @@ -593,10 +621,7 @@ class ChatView extends StatelessWidget { // Container( // color: theme.scaffoldBackgroundColor.withAlpha(230), // alignment: Alignment.center, - // child: const Icon( - // Icons.upload_outlined, - // size: 100, - // ), + // child: const Icon(Icons.upload_outlined, size: 100), // ), // Pangea# ], diff --git a/lib/pages/chat/encryption_button.dart b/lib/pages/chat/encryption_button.dart index 652d9f1cc..594ec4e4b 100644 --- a/lib/pages/chat/encryption_button.dart +++ b/lib/pages/chat/encryption_button.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:badges/badges.dart' as b; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; @@ -12,13 +13,13 @@ class EncryptionButton extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); return StreamBuilder( - stream: Matrix.of(context) - .client - .onSync - .stream - .where((s) => s.deviceLists != null), + stream: Matrix.of( + context, + ).client.onSync.stream.where((s) => s.deviceLists != null), builder: (context, snapshot) { + final shouldBeEncrypted = room.joinRules != JoinRules.public; return FutureBuilder( future: room.encrypted ? room.calcEncryptionHealthState() @@ -27,16 +28,31 @@ class EncryptionButton extends StatelessWidget { tooltip: room.encrypted ? L10n.of(context).encrypted : L10n.of(context).encryptionNotEnabled, - icon: Icon( - room.encrypted ? Icons.lock_outlined : Icons.lock_open_outlined, - size: 20, - color: room.joinRules != JoinRules.public && !room.encrypted - ? Colors.red - : room.joinRules != JoinRules.public && - snapshot.data == - EncryptionHealthState.unverifiedDevices - ? Colors.orange - : null, + icon: b.Badge( + badgeAnimation: const b.BadgeAnimation.fade(), + showBadge: + snapshot.data == EncryptionHealthState.unverifiedDevices, + badgeStyle: b.BadgeStyle( + badgeColor: theme.colorScheme.error, + elevation: 4, + ), + badgeContent: Text( + '!', + style: TextStyle( + fontSize: 9, + color: theme.colorScheme.onError, + fontWeight: FontWeight.bold, + ), + ), + child: Icon( + room.encrypted + ? Icons.lock_outlined + : Icons.no_encryption_outlined, + size: 20, + color: (shouldBeEncrypted && !room.encrypted) + ? theme.colorScheme.error + : theme.colorScheme.onSurface, + ), ), onPressed: () => context.go('/rooms/${room.id}/encryption'), ), diff --git a/lib/pages/chat/event_info_dialog.dart b/lib/pages/chat/event_info_dialog.dart index 558f13b43..e3b458998 100644 --- a/lib/pages/chat/event_info_dialog.dart +++ b/lib/pages/chat/event_info_dialog.dart @@ -13,21 +13,16 @@ import 'package:fluffychat/widgets/avatar.dart'; extension EventInfoDialogExtension on Event { void showInfoDialog(BuildContext context) => showAdaptiveBottomSheet( - context: context, - builder: (context) => - EventInfoDialog(l10n: L10n.of(context), event: this), - ); + context: context, + builder: (context) => EventInfoDialog(l10n: L10n.of(context), event: this), + ); } class EventInfoDialog extends StatelessWidget { final Event event; final L10n l10n; - const EventInfoDialog({ - required this.event, - required this.l10n, - super.key, - }); + const EventInfoDialog({required this.event, required this.l10n, super.key}); String prettyJson(MatrixEvent event) { const decoder = JsonDecoder(); @@ -75,15 +70,9 @@ class EventInfoDialog extends StatelessWidget { trailing: IconButton( icon: const Icon(Icons.copy), onPressed: () { - Clipboard.setData( - ClipboardData( - text: prettyJson(event), - ), - ); + Clipboard.setData(ClipboardData(text: prettyJson(event))); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(L10n.of(context).copiedToClipboard), - ), + SnackBar(content: Text(L10n.of(context).copiedToClipboard)), ); }, ), @@ -99,9 +88,7 @@ class EventInfoDialog extends StatelessWidget { scrollDirection: Axis.horizontal, child: SelectableText( prettyJson(MatrixEvent.fromJson(event.toJson())), - style: TextStyle( - color: theme.colorScheme.onSurface, - ), + style: TextStyle(color: theme.colorScheme.onSurface), ), ), ), @@ -118,9 +105,7 @@ class EventInfoDialog extends StatelessWidget { scrollDirection: Axis.horizontal, child: SelectableText( prettyJson(originalSource), - style: TextStyle( - color: theme.colorScheme.onSurface, - ), + style: TextStyle(color: theme.colorScheme.onSurface), ), ), ), diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index b10397e23..f8e32b05b 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -65,6 +65,7 @@ class AudioPlayerState extends State { static const double buttonSize = 36; AudioPlayerStatus status = AudioPlayerStatus.notDownloaded; + double? _downloadProgress; late final MatrixState matrix; List? _waveform; @@ -79,8 +80,10 @@ class AudioPlayerState extends State { @override void dispose() { super.dispose(); + // #Pangea // final audioPlayer = matrix.voiceMessageEventId.value != widget.event.eventId final audioPlayer = matrix.voiceMessageEventId.value != widget.eventId + // Pangea# ? null : matrix.audioPlayer; if (audioPlayer != null) { @@ -111,16 +114,10 @@ class AudioPlayerState extends State { // stream: audioPlayer.positionStream.asBroadcastStream(), // builder: (context, _) => GestureDetector( // onTap: () => FluffyChatApp.router.go( - // // #Pangea - // // '/rooms/${widget.event.room.id}?event=${widget.event.eventId}', - // '/rooms/${widget.roomId}?event=${widget.eventId}', - // // Pangea# + // '/rooms/${widget.event.room.id}?event=${widget.event.eventId}', // ), // child: Text( - // // #Pangea - // // '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event.senderFromMemoryOrFallback.calcDisplayname()}', - // '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event?.senderFromMemoryOrFallback.calcDisplayname() ?? widget.senderId}', - // // Pangea# + // '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event.senderFromMemoryOrFallback.calcDisplayname()}', // maxLines: 1, // overflow: TextOverflow.ellipsis, // ), @@ -131,12 +128,13 @@ class AudioPlayerState extends State { // onPressed: () { // audioPlayer.pause(); // audioPlayer.dispose(); - // matrix.voiceMessageEventId.value = - // matrix.audioPlayer = null; + // matrix.voiceMessageEventId.value = matrix.audioPlayer = + // null; // WidgetsBinding.instance.addPostFrameCallback((_) { - // ScaffoldMessenger.of(matrix.context) - // .clearMaterialBanners(); + // ScaffoldMessenger.of( + // matrix.context, + // ).clearMaterialBanners(); // }); // }, // icon: const Icon(Icons.close_outlined), @@ -151,8 +149,8 @@ class AudioPlayerState extends State { audioPlayer.pause(); audioPlayer.dispose(); matrix.voiceMessageEventId.value = matrix.audioPlayer = null; - matrix.voiceMessageEventId.removeListener(_onPlayerChange); // #Pangea + matrix.voiceMessageEventId.removeListener(_onPlayerChange); _onAudioStateChanged?.cancel(); // Pangea# } @@ -162,21 +160,20 @@ class AudioPlayerState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(matrix.context).clearMaterialBanners(); }); - final currentPlayer = // #Pangea // matrix.voiceMessageEventId.value != widget.event.eventId matrix.voiceMessageEventId.value != widget.eventId - // Pangea# - ? null - : matrix.audioPlayer; - + // Pangea# + ? null + : matrix.audioPlayer; if (currentPlayer != null) { // #Pangea currentPlayer.setSpeed(playbackSpeed); _onAudioStateChanged?.cancel(); - _onAudioStateChanged = - matrix.audioPlayer!.playerStateStream.listen((state) { + _onAudioStateChanged = matrix.audioPlayer!.playerStateStream.listen(( + state, + ) { if (state.processingState == ProcessingState.completed) { matrix.audioPlayer!.stop(); matrix.audioPlayer!.seek(Duration.zero); @@ -206,9 +203,26 @@ class AudioPlayerState extends State { setState(() => status = AudioPlayerStatus.downloading); try { // #Pangea - // matrixFile = await widget.event.downloadAndDecryptAttachment(); - matrixFile = await widget.event?.downloadAndDecryptAttachment(); - // Pangea# + // final fileSize = widget.event.content + final fileSize = widget.event?.content + // Pangea# + .tryGetMap('info') + ?.tryGet('size'); + // #Pangea + // matrixFile = await widget.event.downloadAndDecryptAttachment( + matrixFile = await widget.event?.downloadAndDecryptAttachment( + // Pangea# + onDownloadProgress: fileSize != null && fileSize > 0 + ? (progress) { + final progressPercentage = progress / fileSize; + setState(() { + _downloadProgress = progressPercentage < 1 + ? progressPercentage + : null; + }); + } + : null, + ); // #Pangea // if (!kIsWeb) { @@ -250,11 +264,9 @@ class AudioPlayerState extends State { }); } catch (e, s) { Logs().v('Could not download audio file', e, s); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(e.toLocalizedString(context)), - ), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); rethrow; } if (!context.mounted) return; @@ -269,8 +281,9 @@ class AudioPlayerState extends State { // #Pangea audioPlayer.setSpeed(playbackSpeed); _onAudioStateChanged?.cancel(); - _onAudioStateChanged = - matrix.audioPlayer!.playerStateStream.listen((state) { + _onAudioStateChanged = matrix.audioPlayer!.playerStateStream.listen(( + state, + ) { if (state.processingState == ProcessingState.completed) { matrix.audioPlayer!.stop(); matrix.audioPlayer!.seek(Duration.zero); @@ -305,9 +318,8 @@ class AudioPlayerState extends State { // Pangea# audioPlayer.play().onError( - ErrorReporter(context, 'Unable to play audio message') - .onErrorCallback, - ); + ErrorReporter(context, 'Unable to play audio message').onErrorCallback, + ); } void _toggleSpeed() async { @@ -366,11 +378,12 @@ class AudioPlayerState extends State { List? _getWaveform() { // #Pangea - final eventWaveForm = widget.matrixFile?.waveform ?? + final eventWaveForm = + widget.matrixFile?.waveform ?? widget.event?.content .tryGetMap('org.matrix.msc1767.audio') ?.tryGetList('waveform'); - // final eventWaveForm = widget.event?.content + // final eventWaveForm = widget.event.content // .tryGetMap('org.matrix.msc1767.audio') // ?.tryGetList('waveform'); // Pangea# @@ -395,8 +408,9 @@ class AudioPlayerState extends State { void _onPlayerChange() { if (matrix.audioPlayer == null) return; _onAudioStateChanged?.cancel(); - _onAudioStateChanged = - matrix.audioPlayer?.playerStateStream.listen((state) { + _onAudioStateChanged = matrix.audioPlayer?.playerStateStream.listen(( + state, + ) { if (state.processingState == ProcessingState.completed) { matrix.audioPlayer?.stop(); matrix.audioPlayer?.seek(Duration.zero); @@ -409,8 +423,10 @@ class AudioPlayerState extends State { void initState() { super.initState(); matrix = Matrix.of(context); + // #Pangea WidgetsBinding.instance.addPostFrameCallback((_) => _onPlayerChange()); matrix.voiceMessageEventId.addListener(_onPlayerChange); + // Pangea# _waveform = _getWaveform(); // #Pangea @@ -451,10 +467,12 @@ class AudioPlayerState extends State { valueListenable: matrix.voiceMessageEventId, builder: (context, eventId, _) { // #Pangea - // final audioPlayer = - // eventId != widget.event.eventId ? null : matrix.audioPlayer; - final audioPlayer = - eventId != widget.eventId ? null : matrix.audioPlayer; + // final audioPlayer = eventId != widget.event.eventId + // ? null + // : matrix.audioPlayer; + final audioPlayer = eventId != widget.eventId + ? null + : matrix.audioPlayer; // Pangea# // #Pangea @@ -481,7 +499,8 @@ class AudioPlayerState extends State { audioPlayer?.position.inMilliseconds.toDouble() ?? 0.0; if (currentPosition > maxPosition) currentPosition = maxPosition; - final wavePosition = (currentPosition / maxPosition) * + final wavePosition = + (currentPosition / maxPosition) * AudioPlayerWidget.wavesCount; final statusText = audioPlayer == null @@ -490,15 +509,15 @@ class AudioPlayerState extends State { return Padding( padding: const EdgeInsets.all(12.0), child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: .min, + crossAxisAlignment: .start, children: [ ConstrainedBox( constraints: const BoxConstraints( maxWidth: FluffyThemes.columnWidth, ), child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ SizedBox( width: buttonSize, @@ -507,6 +526,7 @@ class AudioPlayerState extends State { ? CircularProgressIndicator( strokeWidth: 2, color: widget.color, + value: _downloadProgress, ) : InkWell( borderRadius: BorderRadius.circular(64), @@ -542,9 +562,11 @@ class AudioPlayerState extends State { ), child: Row( children: [ - for (var i = 0; - i < AudioPlayerWidget.wavesCount; - i++) + for ( + var i = 0; + i < AudioPlayerWidget.wavesCount; + i++ + ) Expanded( child: Container( height: 32, @@ -552,13 +574,14 @@ class AudioPlayerState extends State { child: Container( margin: const EdgeInsets.symmetric( - horizontal: 1, - ), + horizontal: 1, + ), decoration: BoxDecoration( color: i < wavePosition ? widget.color - : widget.color - .withAlpha(128), + : widget.color.withAlpha( + 128, + ), borderRadius: BorderRadius.circular(64), ), @@ -574,11 +597,13 @@ class AudioPlayerState extends State { height: 32, child: Slider( // #Pangea - // thumbColor: widget.event.senderId == + // thumbColor: + // widget.event.senderId == // widget.event.room.client.userID - // ? theme.colorScheme.onPrimary - // : theme.colorScheme.primary, - thumbColor: widget.senderId == + // ? theme.colorScheme.onPrimary + // : theme.colorScheme.primary, + thumbColor: + widget.senderId == Matrix.of(context).client.userID ? widget.color : theme.colorScheme.onSurface, @@ -595,13 +620,13 @@ class AudioPlayerState extends State { onChanged: !widget.enableClicks ? null : (position) => audioPlayer == null - ? _onButtonTap() - : audioPlayer.seek( - Duration( - milliseconds: - position.round(), + ? _onButtonTap() + : audioPlayer.seek( + Duration( + milliseconds: position + .round(), + ), ), - ), // onChanged: (position) => audioPlayer == null // ? _onButtonTap() // : audioPlayer.seek( @@ -629,21 +654,20 @@ class AudioPlayerState extends State { // ), Text( statusText, - style: TextStyle( - color: widget.color, - fontSize: 12, - ), + style: TextStyle(color: widget.color, fontSize: 12), ), // Pangea# const SizedBox(width: 8), // #Pangea Material( color: widget.color.withAlpha(64), - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), child: InkWell( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), onTap: !widget.enableClicks ? null : _toggleSpeed, child: SizedBox( width: 32, @@ -670,11 +694,13 @@ class AudioPlayerState extends State { // ), // secondChild: Material( // color: widget.color.withAlpha(64), - // borderRadius: - // BorderRadius.circular(AppConfig.borderRadius), + // borderRadius: BorderRadius.circular( + // AppConfig.borderRadius, + // ), // child: InkWell( - // borderRadius: - // BorderRadius.circular(AppConfig.borderRadius), + // borderRadius: BorderRadius.circular( + // AppConfig.borderRadius, + // ), // onTap: _toggleSpeed, // child: SizedBox( // width: 32, @@ -710,8 +736,9 @@ class AudioPlayerState extends State { ), child: Linkify( text: fileDescription, - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), style: TextStyle( color: widget.color, fontSize: widget.fontSize, @@ -791,4 +818,5 @@ class BytesAudioSource extends StreamAudioSource { ); } } + // Pangea# diff --git a/lib/pages/chat/events/cute_events.dart b/lib/pages/chat/events/cute_events.dart index bb675e641..604537711 100644 --- a/lib/pages/chat/events/cute_events.dart +++ b/lib/pages/chat/events/cute_events.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; class CuteContent extends StatefulWidget { @@ -21,7 +21,7 @@ class _CuteContentState extends State { @override void initState() { - if (AppConfig.autoplayImages && !_isOverlayShown) { + if (AppSettings.autoplayImages.value && !_isOverlayShown) { addOverlay(); } super.initState(); @@ -37,13 +37,10 @@ class _CuteContentState extends State { return GestureDetector( onTap: addOverlay, child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: .min, + mainAxisAlignment: .center, children: [ - Text( - widget.event.text, - style: const TextStyle(fontSize: 150), - ), + Text(widget.event.text, style: const TextStyle(fontSize: 150)), if (label != null) Text(label), ], ), @@ -69,7 +66,7 @@ class _CuteContentState extends State { Overlay.of(context).insert(overlay); } - generateLabel(User? user) { + String? generateLabel(User? user) { switch (widget.event.content['cute_type']) { case 'googly_eyes': return L10n.of(context).googlyEyesContent( @@ -90,6 +87,7 @@ class _CuteContentState extends State { '', ); } + return null; } } @@ -111,10 +109,7 @@ class _CuteEventOverlayState extends State with TickerProviderStateMixin { final List items = List.generate( 50, - (index) => Size( - Random().nextDouble(), - 4 + (Random().nextDouble() * 4), - ), + (index) => Size(Random().nextDouble(), 4 + (Random().nextDouble() * 4)), ); AnimationController? controller; @@ -149,14 +144,13 @@ class _CuteEventOverlayState extends State .map( (position) => Positioned( left: position.width * width, - bottom: (height * + bottom: + (height * .25 * position.height * (controller?.value ?? 0)) - _CuteOverlayContent.size, - child: _CuteOverlayContent( - emoji: widget.emoji, - ), + child: _CuteOverlayContent(emoji: widget.emoji), ), ) .toList(), @@ -185,10 +179,7 @@ class _CuteOverlayContent extends StatelessWidget { Widget build(BuildContext context) { return SizedBox.square( dimension: size, - child: Text( - emoji, - style: const TextStyle(fontSize: 48), - ), + child: Text(emoji, style: const TextStyle(fontSize: 48)), ); } } diff --git a/lib/pages/chat/events/emoji_burst.dart b/lib/pages/chat/events/emoji_burst.dart index 27a96c891..f15773281 100644 --- a/lib/pages/chat/events/emoji_burst.dart +++ b/lib/pages/chat/events/emoji_burst.dart @@ -22,10 +22,7 @@ class BurstPainter extends CustomPainter { final List particles; final double progress; - BurstPainter({ - required this.particles, - required this.progress, - }); + BurstPainter({required this.particles, required this.progress}); @override void paint(Canvas canvas, Size size) { diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index d09a7b7dd..67dc32f1c 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -2,9 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; -import 'package:flutter_highlighter/flutter_highlighter.dart'; -import 'package:flutter_highlighter/themes/shades-of-purple.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:highlight/highlight.dart' show highlight; import 'package:html/dom.dart' as dom; import 'package:html/parser.dart' as parser; import 'package:matrix/matrix.dart'; @@ -23,6 +22,7 @@ import 'package:fluffychat/pangea/toolbar/reading_assistance/token_emoji_button. import 'package:fluffychat/pangea/toolbar/reading_assistance/token_rendering_util.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/tokens_util.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/underline_text_widget.dart'; +import 'package:fluffychat/utils/code_highlight_theme.dart'; import 'package:fluffychat/utils/event_checkbox_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -125,14 +125,14 @@ class HtmlMessage extends StatelessWidget { 'rt', 'html', 'body', - // Workaround for https://github.com/krille-chan/fluffychat/issues/507 - 'tg-forward', // #Pangea 'token', 'nontoken', // Pangea# }; + static const Set ignoredHtmlTags = {'mx-reply'}; + /// We add line breaks before these tags: static const Set blockHtmlTags = { 'p', @@ -157,20 +157,17 @@ class HtmlMessage extends StatelessWidget { }; // #Pangea - List? get tokens => - pangeaMessageEvent?.messageDisplayRepresentation?.tokens - ?.where( - (t) => - !["SYM"].contains(t.pos) && - !t.lemma.text.contains(RegExp(r'[0-9]')), - ) - .toList(); + List? get tokens => pangeaMessageEvent + ?.messageDisplayRepresentation + ?.tokens + ?.where( + (t) => + !["SYM"].contains(t.pos) && + !t.lemma.text.contains(RegExp(r'[0-9]')), + ) + .toList(); - PangeaToken? getToken( - String text, - int offset, - int length, - ) => + PangeaToken? getToken(String text, int offset, int length) => tokens?.firstWhereOrNull( (token) => token.text.offset == offset && token.text.length == length, ); @@ -204,11 +201,9 @@ class HtmlMessage extends StatelessWidget { replyTagIndex, ); if (closingReplyTagIndex != -1) { - result.replaceRange( - replyTagIndex, - closingReplyTagIndex + 1, - [result.sublist(replyTagIndex, closingReplyTagIndex + 1).join()], - ); + result.replaceRange(replyTagIndex, closingReplyTagIndex + 1, [ + result.sublist(replyTagIndex, closingReplyTagIndex + 1).join(), + ]); } } @@ -240,10 +235,10 @@ class HtmlMessage extends StatelessWidget { } final int tokenLength = tokenSpanText.characters.length; - final before = - result[substringIndex].characters.take(tokenIndex).toString(); - final after = result[substringIndex] - .characters + final before = result[substringIndex].characters + .take(tokenIndex) + .toString(); + final after = result[substringIndex].characters .skip(tokenIndex + tokenLength) .toString(); @@ -368,21 +363,40 @@ class HtmlMessage extends StatelessWidget { ]; } + InlineSpan _renderCodeBlockNode(dom.Node node) { + if (node is! dom.Element) { + return TextSpan(text: node.text); + } + final style = + atomOneDarkTheme[node.className.split('-').last] ?? + atomOneDarkTheme['root']; + + return TextSpan( + children: node.nodes.map(_renderCodeBlockNode).toList(), + style: style, + ); + } + /// Transforms a Node to an InlineSpan. + // #Pangea + // InlineSpan _renderHtml(dom.Node node, BuildContext context, {int depth = 1}) { InlineSpan _renderHtml( dom.Node node, - // #Pangea - // BuildContext context, { BuildContext context, TextStyle textStyle, { - // Pangea# int depth = 1, }) { + // Pangea# // We must not render elements nested more than 100 elements deep: if (depth >= 100) return const TextSpan(); - // This is a text node, so we render it as text: - if (node is! dom.Element) { + if (node is dom.Element && + ignoredHtmlTags.contains(node.localName?.toLowerCase())) { + return const TextSpan(); + } + + // This is a text node or not permitted node, so we render it as text: + if (node is! dom.Element || !allowedHtmlTags.contains(node.localName)) { var text = node.text ?? ''; // Single linebreak nodes between Elements are ignored: if (text == '\n') text = ''; @@ -395,13 +409,11 @@ class HtmlMessage extends StatelessWidget { ); } - // We must not render tags which are not in the allow list: - if (!allowedHtmlTags.contains(node.localName)) return const TextSpan(); - // #Pangea double fontSize = this.fontSize; if (readingAssistanceMode == ReadingAssistanceMode.practiceMode) { - fontSize = (overlayController != null && overlayController!.maxWidth > 600 + fontSize = + (overlayController != null && overlayController!.maxWidth > 600 ? Theme.of(context).textTheme.titleLarge?.fontSize : Theme.of(context).textTheme.bodyLarge?.fontSize) ?? this.fontSize; @@ -409,13 +421,13 @@ class HtmlMessage extends StatelessWidget { final existingStyle = pangeaMessageEvent != null ? textStyle - .merge( - AppConfig.messageTextStyle( - pangeaMessageEvent!.event, - textColor, - ), - ) - .copyWith(fontSize: fontSize) + .merge( + AppConfig.messageTextStyle( + pangeaMessageEvent!.event, + textColor, + ), + ) + .copyWith(fontSize: fontSize) : textStyle.copyWith(fontSize: fontSize); final renderer = TokenRenderingUtil(); @@ -426,8 +438,8 @@ class HtmlMessage extends StatelessWidget { final newTokens = pangeaMessageEvent != null && !pangeaMessageEvent!.ownMessage - ? TokensUtil.getNewTokensByEvent(pangeaMessageEvent!) - : []; + ? TokensUtil.getNewTokensByEvent(pangeaMessageEvent!) + : []; // Pangea# switch (node.localName) { @@ -448,7 +460,8 @@ class HtmlMessage extends StatelessWidget { : false; final isNew = token != null && newTokens.contains(token.text); - final isFirstNewToken = isNew && + final isFirstNewToken = + isNew && controller.buttonEventID == event.eventId && newTokens.first == token.text; final showShimmer = @@ -466,8 +479,8 @@ class HtmlMessage extends StatelessWidget { WidgetSpan( alignment: readingAssistanceMode == ReadingAssistanceMode.practiceMode - ? PlaceholderAlignment.bottom - : PlaceholderAlignment.middle, + ? PlaceholderAlignment.bottom + : PlaceholderAlignment.middle, child: Column( children: [ if (token != null && overlayController != null) @@ -494,10 +507,10 @@ class HtmlMessage extends StatelessWidget { CompositedTransformTarget( link: token != null ? MatrixState.pAnyState - .layerLinkAndKey( - "message-token-${token.text.uniqueKey}-${event.eventId}", - ) - .link + .layerLinkAndKey( + "message-token-${token.text.uniqueKey}-${event.eventId}", + ) + .link : LayerLink(), child: MouseRegion( cursor: SystemMouseCursors.click, @@ -519,14 +532,15 @@ class HtmlMessage extends StatelessWidget { pangeaMessageEvent?.textDirection, underlineColor: TokenRenderingUtil.underlineColor( - underlineColor, - selected: selected, - highlighted: highlighted, - isNew: isNew, - practiceMode: readingAssistanceMode == - ReadingAssistanceMode.practiceMode, - hovered: hovered, - ), + underlineColor, + selected: selected, + highlighted: highlighted, + isNew: isNew, + practiceMode: + readingAssistanceMode == + ReadingAssistanceMode.practiceMode, + hovered: hovered, + ), ), ); }, @@ -546,8 +560,10 @@ class HtmlMessage extends StatelessWidget { ), curve: Curves.easeOut, child: SizedBox( - height: overlayController! - .practiceController.practiceMode != + height: + overlayController! + .practiceController + .practiceMode != MessagePracticeMode.noneSelected ? 4.0 : 0.0, @@ -576,7 +592,7 @@ class HtmlMessage extends StatelessWidget { final user = room.unsafeGetUserFromMemoryOrFallback(matrixId); return WidgetSpan( // #Pangea - alignment: PlaceholderAlignment.middle, + alignment: .middle, // Pangea# child: MatrixPill( key: Key('user_pill_$matrixId'), @@ -598,7 +614,7 @@ class HtmlMessage extends StatelessWidget { : this.room.client.getRoomByAlias(matrixId); return WidgetSpan( // #Pangea - alignment: PlaceholderAlignment.middle, + alignment: .middle, // Pangea# child: MatrixPill( name: room?.getLocalizedDisplayname() ?? matrixId, @@ -629,9 +645,7 @@ class HtmlMessage extends StatelessWidget { node.nodes, context, // #Pangea - textStyle.merge( - linkStyle.copyWith(height: 1.25), - ), + textStyle.merge(linkStyle.copyWith(height: 1.25)), // Pangea# depth: depth, ), @@ -651,9 +665,9 @@ class HtmlMessage extends StatelessWidget { final isCheckbox = node.className == 'task-list-item'; final checkboxIndex = isCheckbox ? node.rootElement - .getElementsByClassName('task-list-item') - .indexOf(node) + - 1 + .getElementsByClassName('task-list-item') + .indexOf(node) + + 1 : null; final checkedByReaction = !isCheckbox ? null @@ -673,23 +687,21 @@ class HtmlMessage extends StatelessWidget { // Pangea# TextSpan( children: [ - if (node.parent?.localName == 'ul') - // #Pangea - // const TextSpan(text: '• '), - TextSpan( - text: '• ', - style: existingStyle, - ), - // Pangea# - if (node.parent?.localName == 'ol') - TextSpan( - text: - '${(node.parent?.nodes.whereType().toList().indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ', + if (!isCheckbox) ...[ + if (node.parent?.localName == 'ul') // #Pangea - // style: textStyle, - style: existingStyle, - // Pangea# - ), + // const TextSpan(text: '• '), + TextSpan(text: '• ', style: existingStyle), + // Pangea# + if (node.parent?.localName == 'ol') + TextSpan( + text: + '${(node.parent?.nodes.whereType().toList().indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ', + // #Pangea + style: existingStyle, + // Pangea# + ), + ], if (node.className == 'task-list-item') WidgetSpan( child: Padding( @@ -702,7 +714,8 @@ class HtmlMessage extends StatelessWidget { activeColor: textColor.withAlpha(64), value: staticallyChecked || checkedByReaction != null, - onChanged: eventId == null || + onChanged: + eventId == null || checkboxIndex == null || staticallyChecked || !room.canSendDefaultMessages || @@ -711,28 +724,29 @@ class HtmlMessage extends StatelessWidget { room.client.userID) ? null : (_) => showFutureLoadingDialog( - context: context, - future: () => checkedByReaction != null - ? room.redactEvent( - checkedByReaction.eventId, - ) - : room.checkCheckbox( - eventId, - checkboxIndex, - ), - ), + context: context, + future: () => checkedByReaction != null + ? room.redactEvent( + checkedByReaction.eventId, + ) + : room.checkCheckbox( + eventId, + checkboxIndex, + ), + ), ), ), ), ), + // #Pangea + // ..._renderWithLineBreaks(node.nodes, context, depth: depth), ..._renderWithLineBreaks( node.nodes, context, - // #Pangea textStyle, - // Pangea# depth: depth, ), + // Pangea# ], style: TextStyle(fontSize: fontSize, color: textColor), ), @@ -744,12 +758,7 @@ class HtmlMessage extends StatelessWidget { child: Container( padding: const EdgeInsets.only(left: 8.0), decoration: BoxDecoration( - border: Border( - left: BorderSide( - color: textColor, - width: 5, - ), - ), + border: Border(left: BorderSide(color: textColor, width: 5)), ), child: Text.rich( // #Pangea @@ -775,31 +784,37 @@ class HtmlMessage extends StatelessWidget { ); case 'code': final isInline = node.parent?.localName != 'pre'; + final lang = + node.className + .split(' ') + .singleWhereOrNull( + (className) => className.startsWith('language-'), + ) + ?.split('language-') + .last ?? + 'md'; + final highlightedHtml = highlight + .parse(node.text, language: lang) + .toHtml(); + final element = parser.parse(highlightedHtml).body; + if (element == null) { + return const TextSpan(text: 'Unable to render code block!'); + } + return WidgetSpan( child: Material( - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(4), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: HighlightView( - node.text, - language: node.className - .split(' ') - .singleWhereOrNull( - (className) => className.startsWith('language-'), - ) - ?.split('language-') - .last ?? - 'md', - theme: shadesOfPurpleTheme, - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: isInline ? 0 : 8, - ), - textStyle: TextStyle( - fontSize: fontSize, - fontFamily: 'RobotoMono', - ), + color: atomOneBackgroundColor, + shape: RoundedRectangleBorder( + side: const BorderSide(color: hightlightTextColor), + borderRadius: BorderRadius.circular(4), + ), + child: Padding( + padding: isInline + ? const EdgeInsets.symmetric(horizontal: 4.0) + : const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + child: Text.rich( + TextSpan(children: [_renderCodeBlockNode(element)]), + selectionColor: hightlightTextColor.withAlpha(128), ), ), ), @@ -866,10 +881,7 @@ class HtmlMessage extends StatelessWidget { node, context, textStyle.merge( - TextStyle( - fontSize: fontSize, - color: textColor, - ), + TextStyle(fontSize: fontSize, color: textColor), ), depth: depth, ), @@ -886,10 +898,7 @@ class HtmlMessage extends StatelessWidget { ), ], ), - style: TextStyle( - fontSize: fontSize, - color: textColor, - ), + style: TextStyle(fontSize: fontSize, color: textColor), ), ), ), @@ -935,16 +944,13 @@ class HtmlMessage extends StatelessWidget { default: // #Pangea final style = switch (node.localName) { - 'body' => TextStyle( - fontSize: fontSize, - color: textColor, - ), + 'body' => TextStyle(fontSize: fontSize, color: textColor), 'a' => linkStyle, 'strong' => const TextStyle(fontWeight: FontWeight.bold), 'em' || 'i' => const TextStyle(fontStyle: FontStyle.italic), - 'del' || - 'strikethrough' => - const TextStyle(decoration: TextDecoration.lineThrough), + 'del' || 'strikethrough' => const TextStyle( + decoration: TextDecoration.lineThrough, + ), 'u' => const TextStyle(decoration: TextDecoration.underline), 'h1' => TextStyle(fontSize: fontSize * 1.6, height: 2), 'h2' => TextStyle(fontSize: fontSize * 1.5, height: 2), @@ -953,11 +959,12 @@ class HtmlMessage extends StatelessWidget { 'h5' => TextStyle(fontSize: fontSize * 1.2, height: 1.75), 'h6' => TextStyle(fontSize: fontSize * 1.1, height: 1.5), 'span' => TextStyle( - color: node.attributes['color']?.hexToColor ?? - node.attributes['data-mx-color']?.hexToColor ?? - textColor, - backgroundColor: node.attributes['data-mx-bg-color']?.hexToColor, - ), + color: + node.attributes['color']?.hexToColor ?? + node.attributes['data-mx-color']?.hexToColor ?? + textColor, + backgroundColor: node.attributes['data-mx-bg-color']?.hexToColor, + ), 'sup' => const TextStyle(fontFeatures: [FontFeature.superscripts()]), 'sub' => const TextStyle(fontFeatures: [FontFeature.subscripts()]), _ => null, @@ -998,9 +1005,9 @@ class HtmlMessage extends StatelessWidget { child: SizedBox( height: overlayController!.practiceController.practiceMode != - MessagePracticeMode.noneSelected - ? 4.0 - : 0.0, + MessagePracticeMode.noneSelected + ? 4.0 + : 0.0, width: 0, ), ), @@ -1010,17 +1017,13 @@ class HtmlMessage extends StatelessWidget { ); // return TextSpan( // style: switch (node.localName) { - // 'body' => TextStyle( - // fontSize: fontSize, - // color: textColor, - // ), + // 'body' => TextStyle(fontSize: fontSize, color: textColor), // 'a' => linkStyle, // 'strong' => const TextStyle(fontWeight: FontWeight.bold), // 'em' || 'i' => const TextStyle(fontStyle: FontStyle.italic), - // 'del' || - // 's' || - // 'strikethrough' => - // const TextStyle(decoration: TextDecoration.lineThrough), + // 'del' || 's' || 'strikethrough' => const TextStyle( + // decoration: TextDecoration.lineThrough, + // ), // 'u' => const TextStyle(decoration: TextDecoration.underline), // 'h1' => TextStyle(fontSize: fontSize * 1.6, height: 2), // 'h2' => TextStyle(fontSize: fontSize * 1.5, height: 2), @@ -1029,25 +1032,19 @@ class HtmlMessage extends StatelessWidget { // 'h5' => TextStyle(fontSize: fontSize * 1.2, height: 1.75), // 'h6' => TextStyle(fontSize: fontSize * 1.1, height: 1.5), // 'span' => TextStyle( - // color: node.attributes['color']?.hexToColor ?? - // node.attributes['data-mx-color']?.hexToColor ?? - // textColor, - // backgroundColor: - // node.attributes['data-mx-bg-color']?.hexToColor, - // ), + // color: + // node.attributes['color']?.hexToColor ?? + // node.attributes['data-mx-color']?.hexToColor ?? + // textColor, + // backgroundColor: node.attributes['data-mx-bg-color']?.hexToColor, + // ), // 'sup' => const TextStyle( - // fontFeatures: [FontFeature.superscripts()], - // ), - // 'sub' => const TextStyle( - // fontFeatures: [FontFeature.subscripts()], - // ), + // fontFeatures: [FontFeature.superscripts()], + // ), + // 'sub' => const TextStyle(fontFeatures: [FontFeature.subscripts()]), // _ => null, // }, - // children: _renderWithLineBreaks( - // node.nodes, - // context, - // depth: depth, - // ), + // children: _renderWithLineBreaks(node.nodes, context, depth: depth), // ); // Pangea# } @@ -1059,12 +1056,10 @@ class HtmlMessage extends StatelessWidget { // final element = parser.parse(html).body ?? dom.Element.html(''); // return Text.rich( // _renderHtml(element, context), - // style: TextStyle( - // fontSize: fontSize, - // color: textColor, - // ), - // maxLines: limitHeight ? 64 : null,Add commentMore actions + // style: TextStyle(fontSize: fontSize, color: textColor), + // maxLines: limitHeight ? 64 : null, // overflow: TextOverflow.fade, + // selectionColor: textColor.withAlpha(128) // ); final parsed = parser.parse(_addTokenTags()).body ?? dom.Element.html(''); return GestureDetector( @@ -1083,17 +1078,12 @@ class HtmlMessage extends StatelessWidget { _renderHtml( parsed, context, - TextStyle( - fontSize: fontSize, - color: textColor, - ), - ), - style: TextStyle( - fontSize: fontSize, - color: textColor, + TextStyle(fontSize: fontSize, color: textColor), ), + style: TextStyle(fontSize: fontSize, color: textColor), maxLines: limitHeight ? 64 : null, overflow: TextOverflow.fade, + selectionColor: textColor.withAlpha(128), ), ); } @@ -1156,26 +1146,27 @@ class MatrixPill extends StatelessWidget { ], ), ), - // child: Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // Avatar( - // mxContent: avatar, - // name: name, - // size: 16, - // ), - // const SizedBox(width: 6), - // Text( - // name, - // style: TextStyle( - // color: color, - // decorationColor: color, - // decoration: TextDecoration.underline, - // fontSize: fontSize, - // height: 1.25, + // child: Text.rich( + // TextSpan( + // children: [ + // WidgetSpan( + // child: Padding( + // padding: const EdgeInsets.only(right: 4.0), + // child: Avatar(mxContent: avatar, name: name, size: 16), + // ), // ), - // ), - // ], + // TextSpan( + // text: name, + // style: TextStyle( + // color: color, + // decorationColor: color, + // decoration: TextDecoration.underline, + // fontSize: fontSize, + // height: 1.25, + // ), + // ), + // ], + // ), // ), // Pangea# ); diff --git a/lib/pages/chat/events/image_bubble.dart b/lib/pages/chat/events/image_bubble.dart index f056366b1..e1c6d934c 100644 --- a/lib/pages/chat/events/image_bubble.dart +++ b/lib/pages/chat/events/image_bubble.dart @@ -5,6 +5,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/url_launcher.dart'; @@ -46,8 +47,8 @@ class ImageBubble extends StatelessWidget { Widget _buildPlaceholder(BuildContext context) { final String blurHashString = event.infoMap['xyz.amorgan.blurhash'] is String - ? event.infoMap['xyz.amorgan.blurhash'] - : 'LEHV6nWB2yk8pyo0adR*.7kCMdnj'; + ? event.infoMap['xyz.amorgan.blurhash'] + : 'LEHV6nWB2yk8pyo0adR*.7kCMdnj'; return SizedBox( width: width, height: height, @@ -68,11 +69,8 @@ class ImageBubble extends StatelessWidget { if (!tapToView) return; showDialog( context: context, - builder: (_) => ImageViewer( - event, - timeline: timeline, - outerContext: context, - ), + builder: (_) => + ImageViewer(event, timeline: timeline, outerContext: context), ); } @@ -94,7 +92,7 @@ class ImageBubble extends StatelessWidget { } return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, spacing: 8, children: [ Material( @@ -114,7 +112,8 @@ class ImageBubble extends StatelessWidget { child: Hero( tag: event.eventId, // #Pangea - child: event.content['url'] is String && + child: + event.content['url'] is String && !(event.content['url'] as String).startsWith('mxc') ? CachedNetworkImage( imageUrl: event.content['url'] as String, @@ -153,23 +152,22 @@ class ImageBubble extends StatelessWidget { SizedBox( width: width, child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Linkify( text: fileDescription, textScaleFactor: MediaQuery.textScalerOf(context).scale(1), style: TextStyle( color: textColor, fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: linkColor, fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index d4ee1d5c1..56bef9695 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -5,22 +5,24 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:swipe_to_action/swipe_to_action.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/pangea_message_reactions.dart'; -import 'package:fluffychat/pages/chat/events/room_creation_state_event.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_summary_widget.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart'; import 'package:fluffychat/pangea/chat/extensions/custom_room_display_extension.dart'; +import 'package:fluffychat/pangea/chat/widgets/room_creation_state_event.dart'; import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/file_description.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -41,6 +43,7 @@ class Message extends StatelessWidget { final void Function() onSwipe; final void Function() onMention; final void Function() onEdit; + final void Function(String eventId)? enterThread; final bool longPressSelect; final bool selected; final bool singleSelected; @@ -51,6 +54,8 @@ class Message extends StatelessWidget { final bool wallpaperMode; final ScrollController scrollController; final List colors; + final void Function()? onExpand; + final bool isCollapsed; // #Pangea final ChatController controller; final bool isButton; @@ -78,6 +83,9 @@ class Message extends StatelessWidget { required this.onMention, required this.scrollController, required this.colors, + this.onExpand, + required this.enterThread, + this.isCollapsed = false, // #Pangea required this.controller, this.isButton = false, @@ -125,25 +133,23 @@ class Message extends StatelessWidget { EventTypes.Sticker, EventTypes.Encrypted, EventTypes.CallInvite, + PollEventContent.startType, }.contains(event.type)) { if (event.type.startsWith('m.call.')) { return const SizedBox.shrink(); } + // #Pangea if (event.type == EventTypes.RoomCreate) { - // #Pangea - // return RoomCreationStateEvent(event: event); return event.room.isActivitySession ? const SizedBox() : RoomCreationStateEvent(event: event); - // Pangea# } - // #Pangea if (event.type == PangeaEventTypes.activityPlan && event.room.activityPlan != null) { return ValueListenableBuilder( valueListenable: controller.activityController.showInstructions, - builder: (context, show, __) { + builder: (context, show, _) { return ActivitySummary( inChat: true, activity: event.room.activityPlan!, @@ -168,8 +174,7 @@ class Message extends StatelessWidget { return ActivityRolesEvent(event: event); } // Pangea# - - return StateMessage(event); + return StateMessage(event, onExpand: onExpand, isCollapsed: isCollapsed); } if (event.type == EventTypes.Message && @@ -181,11 +186,13 @@ class Message extends StatelessWidget { final ownMessage = event.senderId == client.userID; final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft; - var color = theme.colorScheme.surfaceContainerHighest; - final displayTime = event.type == EventTypes.RoomCreate || + var color = theme.colorScheme.surfaceContainerHigh; + final displayTime = + event.type == EventTypes.RoomCreate || nextEvent == null || !event.originServerTs.sameEnvironment(nextEvent!.originServerTs); - final nextEventSameSender = nextEvent != null && + final nextEventSameSender = + nextEvent != null && { EventTypes.Message, EventTypes.Sticker, @@ -194,7 +201,8 @@ class Message extends StatelessWidget { nextEvent!.senderId == event.senderId && !displayTime; - final previousEventSameSender = previousEvent != null && + final previousEventSameSender = + previousEvent != null && { EventTypes.Message, EventTypes.Sticker, @@ -204,26 +212,28 @@ class Message extends StatelessWidget { previousEvent!.originServerTs.sameEnvironment(event.originServerTs); // #Pangea - // final textColor = - // ownMessage ? theme.onBubbleColor : theme.colorScheme.onSurface; + // final textColor = ownMessage + // ? theme.onBubbleColor + // : theme.colorScheme.onSurface; final textColor = ownMessage ? ThemeData.dark().colorScheme.onPrimary : theme.colorScheme.onSurface; // final linkColor = ownMessage // ? theme.brightness == Brightness.light - // ? theme.colorScheme.primaryFixed - // : theme.colorScheme.onTertiaryContainer + // ? theme.colorScheme.primaryFixed + // : theme.colorScheme.onTertiaryContainer // : theme.colorScheme.primary; final linkColor = theme.brightness == Brightness.light ? theme.colorScheme.primary : ownMessage - ? theme.colorScheme.onPrimary - : theme.colorScheme.onSurface; + ? theme.colorScheme.onPrimary + : theme.colorScheme.onSurface; // Pangea# - final rowMainAxisAlignment = - ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start; + final rowMainAxisAlignment = ownMessage + ? MainAxisAlignment.end + : MainAxisAlignment.start; final displayEvent = event.getDisplayEvent(timeline); const hardCorner = Radius.circular(4); @@ -231,12 +241,15 @@ class Message extends StatelessWidget { final borderRadius = BorderRadius.only( topLeft: !ownMessage && nextEventSameSender ? hardCorner : roundedCorner, topRight: ownMessage && nextEventSameSender ? hardCorner : roundedCorner, - bottomLeft: - !ownMessage && previousEventSameSender ? hardCorner : roundedCorner, - bottomRight: - ownMessage && previousEventSameSender ? hardCorner : roundedCorner, + bottomLeft: !ownMessage && previousEventSameSender + ? hardCorner + : roundedCorner, + bottomRight: ownMessage && previousEventSameSender + ? hardCorner + : roundedCorner, ); - final noBubble = ({ + final noBubble = + ({ MessageTypes.Video, MessageTypes.Image, MessageTypes.Sticker, @@ -251,8 +264,9 @@ class Message extends StatelessWidget { if (ownMessage) { // #Pangea - // color = - // displayEvent.status.isError ? Colors.redAccent : theme.bubbleColor; + // color = displayEvent.status.isError + // ? Colors.redAccent + // : theme.bubbleColor; color = displayEvent.status.isError ? Colors.redAccent : Color.alphaBlend( @@ -269,10 +283,7 @@ class Message extends StatelessWidget { if (singleSelected) { sentReactions.addAll( event - .aggregatedEvents( - timeline, - RelationshipTypes.reaction, - ) + .aggregatedEvents(timeline, RelationshipTypes.reaction) .where( (event) => event.senderId == event.room.client.userID && @@ -287,24 +298,31 @@ class Message extends StatelessWidget { ); } - final showReceiptsRow = - event.hasAggregatedEvents(timeline, RelationshipTypes.reaction); + final showReceiptsRow = event.hasAggregatedEvents( + timeline, + RelationshipTypes.reaction, + ); + + final threadChildren = event.aggregatedEvents( + timeline, + RelationshipTypes.thread, + ); // #Pangea // final showReactionPicker = // singleSelected && event.room.canSendDefaultMessages; // Pangea# + final enterThread = this.enterThread; + return Center( child: Swipeable( key: ValueKey(event.eventId), background: const Padding( padding: EdgeInsets.symmetric(horizontal: 12.0), - child: Center( - child: Icon(Icons.check_outlined), - ), + child: Center(child: Icon(Icons.check_outlined)), ), - direction: AppConfig.swipeRightToLeftToReply + direction: AppSettings.swipeRightToLeftToReply.value ? SwipeDirection.endToStart : SwipeDirection.startToEnd, onSwipe: (_) => onSwipe(), @@ -319,9 +337,8 @@ class Message extends StatelessWidget { bottom: previousEventSameSender ? 1.0 : 4.0, ), child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - ownMessage ? CrossAxisAlignment.end : CrossAxisAlignment.start, + mainAxisSize: .min, + crossAxisAlignment: ownMessage ? .end : .start, children: [ // #Pangea // if (displayTime || selected) @@ -335,8 +352,9 @@ class Message extends StatelessWidget { child: Padding( padding: const EdgeInsets.only(top: 4.0), child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius * 2), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius * 2, + ), color: theme.colorScheme.surface.withAlpha(128), child: Padding( padding: const EdgeInsets.symmetric( @@ -346,7 +364,7 @@ class Message extends StatelessWidget { child: Text( event.originServerTs.localizedTime(context), style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, + fontSize: 12 * AppSettings.fontSizeFactor.value, fontWeight: FontWeight.bold, color: theme.colorScheme.secondary, ), @@ -361,10 +379,7 @@ class Message extends StatelessWidget { if (animateIn && resetAnimateIn != null) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { animateIn = false; - // #Pangea - // setState(resetAnimateIn); - if (context.mounted) setState(resetAnimateIn); - // Pangea# + setState(resetAnimateIn); }); } return AnimatedSize( @@ -406,13 +421,13 @@ class Message extends StatelessWidget { ), color: selected || highlightMarker ? theme.colorScheme.secondaryContainer - .withAlpha(128) + .withAlpha(128) : Colors.transparent, ), ), ), Row( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: .start, mainAxisAlignment: rowMainAxisAlignment, children: [ // #Pangea @@ -440,18 +455,17 @@ class Message extends StatelessWidget { child: SizedBox( width: 16, height: 16, - child: event.status == - EventStatus.error + child: + event.status == EventStatus.error ? const Icon( Icons.error, color: Colors.red, ) : event.fileSendingStatus != null - ? const CircularProgressIndicator - .adaptive( - strokeWidth: 1, - ) - : null, + ? const CircularProgressIndicator.adaptive( + strokeWidth: 1, + ) + : null, ), ), ) @@ -459,20 +473,21 @@ class Message extends StatelessWidget { FutureBuilder( future: event.fetchSenderUser(), builder: (context, snapshot) { - final user = snapshot.data ?? + final user = + snapshot.data ?? event.senderFromMemoryOrFallback; return Avatar( mxContent: user.avatarUrl, name: user.calcDisplayname(), onTap: () => showMemberActionsPopupMenu( - context: context, - user: user, - onMention: onMention, - // #Pangea - room: controller.room, - // Pangea# - ), + context: context, + user: user, + onMention: onMention, + // #Pangea + room: controller.room, + // Pangea# + ), presenceUserId: user.stateKey, presenceBackgroundColor: wallpaperMode ? Colors.transparent @@ -480,23 +495,22 @@ class Message extends StatelessWidget { // #Pangea miniIcon: user.id == BotName.byEnvironment - ? BotSettingsLanguageIcon( - user: user, - ) - : null, + ? BotSettingsLanguageIcon( + user: user, + ) + : null, presenceOffset: user.id == BotName.byEnvironment - ? const Offset(0, 0) - : null, + ? const Offset(0, 0) + : null, // Pangea# ); }, ), Expanded( child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + crossAxisAlignment: .start, + mainAxisSize: .min, children: [ if (!nextEventSameSender) Padding( @@ -504,16 +518,16 @@ class Message extends StatelessWidget { left: 8.0, bottom: 4, ), - child: ownMessage || + child: + ownMessage || event.room.isDirectChat ? const SizedBox(height: 12) : FutureBuilder( - future: - event.fetchSenderUser(), - builder: - (context, snapshot) { - final displayname = snapshot - .data + future: event + .fetchSenderUser(), + builder: (context, snapshot) { + final displayname = + snapshot.data ?.calcDisplayname() ?? event .senderFromMemoryOrFallback @@ -523,38 +537,39 @@ class Message extends StatelessWidget { // displayname, controller.room .senderDisplayName( - snapshot.data ?? - event - .senderFromMemoryOrFallback, - ), + snapshot.data ?? + event + .senderFromMemoryOrFallback, + ), // Pangea# style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, - color: (theme.brightness == + color: + (theme.brightness == Brightness .light ? displayname - .color + .color : displayname - .lightColorText), + .lightColorText), shadows: !wallpaperMode - ? null - : [ - const Shadow( - offset: - Offset( + ? null + : [ + const Shadow( + offset: + Offset( 0.0, 0.0, ), - blurRadius: - 3, - color: Colors - .black, - ), - ], + blurRadius: + 3, + color: Colors + .black, + ), + ], ), maxLines: 1, overflow: TextOverflow @@ -565,8 +580,9 @@ class Message extends StatelessWidget { ), Container( alignment: alignment, - padding: - const EdgeInsets.only(left: 8), + padding: const EdgeInsets.only( + left: 8, + ), child: GestureDetector( // #Pangea onTap: () => @@ -584,25 +600,50 @@ class Message extends StatelessWidget { opacity: animateIn ? 0 : event.messageType == - MessageTypes - .BadEncrypted || - event.status.isSending - ? 0.5 - : 1, + MessageTypes + .BadEncrypted || + event.status.isSending + ? 0.5 + : 1, duration: FluffyThemes .animationDuration, curve: FluffyThemes.animationCurve, - child: - // #Pangea - SelectionContainer.disabled( + // #Pangea + child: SelectionContainer.disabled( child: MouseRegion( cursor: SystemMouseCursors.click, child: ValueListenableBuilder( valueListenable: controller .depressMessageButton, + builder: + ( + context, + depressed, + child, + ) => PressableButton( + buttonHeight: 5, + depressed: + !isButton || + depressed, + borderRadius: + borderRadius, + onPressed: () { + showToolbar( + pangeaMessageEvent, + ); + }, + color: color, + visible: + isButton && + !noBubble, + builder: + (context, _, _) => + child!, + ), + // Pangea# child: Container( decoration: BoxDecoration( color: noBubble @@ -614,240 +655,195 @@ class Message extends StatelessWidget { clipBehavior: Clip.antiAlias, // #Pangea - child: - CompositedTransformTarget( + child: CompositedTransformTarget( link: MatrixState .pAnyState .layerLinkAndKey( event.eventId, ) .link, - // child: BubbleBackground( - // colors: colors, - // ignore: noBubble || !ownMessage, - // scrollController: scrollController, // Pangea# - child: Container( + child: BubbleBackground( + colors: colors, // #Pangea - key: MatrixState - .pAnyState - .layerLinkAndKey( - event.eventId, - ) - .key, + // ignore: noBubble || + // !ownMessage || + // MediaQuery + // .highContrastOf( + // context, + // ), + ignore: true, // Pangea# - decoration: - BoxDecoration( - borderRadius: - BorderRadius - .circular( - AppConfig - .borderRadius, + scrollController: + scrollController, + child: Container( + // #Pangea + key: MatrixState + .pAnyState + .layerLinkAndKey( + event.eventId, + ) + .key, + // Pangea# + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular( + AppConfig + .borderRadius, + ), ), - ), - constraints: - const BoxConstraints( - maxWidth: FluffyThemes - .columnWidth * - 1.5, - ), - child: Column( - mainAxisSize: - MainAxisSize - .min, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - if ({ - RelationshipTypes - .reply, - RelationshipTypes - .thread, - }.contains( - event - .relationshipType, - )) - FutureBuilder< - Event?>( - future: event - .getReplyEvent( - timeline, - ), - builder: ( - BuildContext - context, - snapshot, - ) { - final replyEvent = snapshot - .hasData - ? snapshot - .data! - : Event( - eventId: - event.relationshipEventId!, - content: { - 'msgtype': 'm.text', - 'body': '...', - }, - // #Pangea - // senderId: event - // .senderId, - senderId: - "", - // Pangea# - type: - 'm.room.message', - room: - event.room, - status: - EventStatus.sent, - originServerTs: - DateTime.now(), - ); - return Padding( - padding: - const EdgeInsets - .only( - left: - 16, - right: - 16, - top: 8, - ), - child: - Material( - color: Colors - .transparent, - borderRadius: - ReplyContent.borderRadius, - child: - InkWell( - borderRadius: - ReplyContent.borderRadius, - onTap: () => - scrollToEventId( - replyEvent.eventId, - ), - child: - AbsorbPointer( - child: - ReplyContent( - replyEvent, - ownMessage: ownMessage, - timeline: timeline, - ), - ), - ), - ), - ); - }, + constraints: + const BoxConstraints( + maxWidth: + FluffyThemes + .columnWidth * + 1.5, ), - MessageContent( - displayEvent, - textColor: - textColor, - linkColor: - linkColor, - onInfoTab: - onInfoTab, - borderRadius: - borderRadius, - timeline: - timeline, - selected: - selected, - // #Pangea - pangeaMessageEvent: - pangeaMessageEvent, - controller: - controller, - nextEvent: - nextEvent, - prevEvent: - previousEvent, - // Pangea# - ), - if (event - .hasAggregatedEvents( - timeline, - RelationshipTypes - .edit, - )) - Padding( - padding: - const EdgeInsets - .only( - bottom: 8.0, - left: 16.0, - right: 16.0, - ), - child: Row( - mainAxisSize: - MainAxisSize - .min, - spacing: - 4.0, - children: [ - Icon( - Icons - .edit_outlined, - color: textColor - .withAlpha( - 164, + child: Column( + mainAxisSize: + .min, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + if (event.inReplyToEventId( + includingFallback: + false, + ) != + null) + FutureBuilder< + Event? + >( + future: event + .getReplyEvent( + timeline, ), - size: - 14, - ), - Text( - displayEvent - .originServerTs - .localizedTimeShort( + builder: + ( + BuildContext context, - ), - style: - TextStyle( - color: - textColor.withAlpha( + snapshot, + ) { + final replyEvent = + snapshot.hasData + ? snapshot.data! + : Event( + eventId: + event.inReplyToEventId() ?? + '\$fake_event_id', + content: { + 'msgtype': 'm.text', + 'body': '...', + }, + senderId: event.senderId, + type: 'm.room.message', + room: event.room, + status: EventStatus.sent, + originServerTs: DateTime.now(), + ); + return Padding( + padding: const EdgeInsets.only( + left: 16, + right: 16, + top: 8, + ), + child: Material( + color: Colors.transparent, + borderRadius: ReplyContent.borderRadius, + child: InkWell( + borderRadius: ReplyContent.borderRadius, + onTap: () => scrollToEventId( + replyEvent.eventId, + ), + child: AbsorbPointer( + child: ReplyContent( + replyEvent, + ownMessage: ownMessage, + timeline: timeline, + ), + ), + ), + ), + ); + }, + ), + MessageContent( + displayEvent, + textColor: + textColor, + linkColor: + linkColor, + onInfoTab: + onInfoTab, + borderRadius: + borderRadius, + timeline: + timeline, + selected: + selected, + // #Pangea + pangeaMessageEvent: + pangeaMessageEvent, + controller: + controller, + nextEvent: + nextEvent, + prevEvent: + previousEvent, + // Pangea# + ), + if (event + .hasAggregatedEvents( + timeline, + RelationshipTypes + .edit, + )) + Padding( + padding: const EdgeInsets.only( + bottom: + 8.0, + left: + 16.0, + right: + 16.0, + ), + child: Row( + mainAxisSize: + MainAxisSize + .min, + spacing: + 4.0, + children: [ + Icon( + Icons + .edit_outlined, + color: textColor.withAlpha( 164, ), - fontSize: - 11, + size: + 14, ), - ), - ], + Text( + displayEvent.originServerTs.localizedTimeShort( + context, + ), + style: TextStyle( + color: textColor.withAlpha( + 164, + ), + fontSize: + 11, + ), + ), + ], + ), ), - ), - ], + ], + ), ), ), ), ), - // #Pangea - builder: ( - context, - depressed, - child, - ) => - PressableButton( - buttonHeight: 5, - depressed: !isButton || - depressed, - borderRadius: - borderRadius, - onPressed: () { - showToolbar( - pangeaMessageEvent, - ); - }, - color: color, - visible: - isButton && !noBubble, - builder: - (context, _, __) => - child!, - ), - // Pangea# ), ), ), @@ -867,46 +863,43 @@ class Message extends StatelessWidget { // ? Padding( // padding: // const EdgeInsets.all( - // 4.0, - // ), + // 4.0, + // ), // child: Material( // elevation: 4, // borderRadius: // BorderRadius.circular( - // AppConfig.borderRadius, - // ), + // AppConfig + // .borderRadius, + // ), // shadowColor: theme - // .colorScheme.surface + // .colorScheme + // .surface // .withAlpha(128), - // child: - // SingleChildScrollView( + // child: SingleChildScrollView( // scrollDirection: // Axis.horizontal, // child: Row( - // mainAxisSize: - // MainAxisSize.min, + // mainAxisSize: .min, // children: [ - // ...AppConfig - // .defaultReactions - // .map( - // (emoji) => - // IconButton( + // ...AppConfig.defaultReactions.map( + // ( + // emoji, + // ) => IconButton( // padding: // EdgeInsets // .zero, // icon: Center( - // child: - // Opacity( - // opacity: sentReactions - // .contains( - // emoji, - // ) + // child: Opacity( + // opacity: + // sentReactions.contains( + // emoji, + // ) // ? 0.33 // : 1, // child: Text( // emoji, - // style: - // const TextStyle( + // style: const TextStyle( // fontSize: // 20, // ), @@ -918,19 +911,20 @@ class Message extends StatelessWidget { // ), // onPressed: // sentReactions - // .contains( - // emoji, - // ) - // ? null - // : () { - // onSelect( - // event, - // ); - // event.room.sendReaction( - // event.eventId, - // emoji, - // ); - // }, + // .contains( + // emoji, + // ) + // ? null + // : () { + // onSelect( + // event, + // ); + // event.room.sendReaction( + // event + // .eventId, + // emoji, + // ); + // }, // ), // ), // IconButton( @@ -941,70 +935,55 @@ class Message extends StatelessWidget { // tooltip: L10n.of( // context, // ).customReaction, - // onPressed: - // () async { - // final emoji = - // await showAdaptiveBottomSheet< - // String>( + // onPressed: () async { + // final emoji = await showAdaptiveBottomSheet( // context: // context, - // builder: - // (context) => - // Scaffold( - // appBar: - // AppBar( - // title: - // Text( - // L10n.of(context) - // .customReaction, - // ), - // leading: - // CloseButton( - // onPressed: - // () => - // Navigator.of( + // builder: (context) => Scaffold( + // appBar: AppBar( + // title: Text( + // L10n.of( // context, - // ).pop( - // null, - // ), + // ).customReaction, + // ), + // leading: CloseButton( + // onPressed: () => Navigator.of( + // context, + // ).pop(null), // ), // ), - // body: - // SizedBox( + // body: SizedBox( // height: double // .infinity, - // child: - // EmojiPicker( - // onEmojiSelected: ( - // _, - // emoji, - // ) => - // Navigator.of( - // context, - // ).pop( - // emoji - // .emoji, - // ), - // config: - // Config( - // emojiViewConfig: - // const EmojiViewConfig( + // child: EmojiPicker( + // onEmojiSelected: + // ( + // _, + // emoji, + // ) => + // Navigator.of( + // context, + // ).pop( + // emoji.emoji, + // ), + // config: Config( + // locale: Localizations.localeOf( + // context, + // ), + // emojiViewConfig: const EmojiViewConfig( // backgroundColor: // Colors.transparent, // ), - // bottomActionBarConfig: - // const BottomActionBarConfig( + // bottomActionBarConfig: const BottomActionBarConfig( // enabled: // false, // ), - // categoryViewConfig: - // CategoryViewConfig( + // categoryViewConfig: CategoryViewConfig( // initCategory: // Category.SMILEYS, // backspaceColor: // theme.colorScheme.primary, - // iconColor: - // theme.colorScheme.primary.withAlpha( + // iconColor: theme.colorScheme.primary.withAlpha( // 128, // ), // iconColorSelected: @@ -1014,10 +993,8 @@ class Message extends StatelessWidget { // backgroundColor: // theme.colorScheme.surface, // ), - // skinToneConfig: - // SkinToneConfig( - // dialogBackgroundColor: - // Color.lerp( + // skinToneConfig: SkinToneConfig( + // dialogBackgroundColor: Color.lerp( // theme.colorScheme.surface, // theme.colorScheme.primaryContainer, // 0.75, @@ -1036,17 +1013,18 @@ class Message extends StatelessWidget { // } // if (sentReactions // .contains( - // emoji, - // )) { + // emoji, + // )) { // return; // } // onSelect(event); // await event.room // .sendReaction( - // event.eventId, - // emoji, - // ); + // event + // .eventId, + // emoji, + // ); // }, // ), // ], @@ -1073,7 +1051,6 @@ class Message extends StatelessWidget { // duration: FluffyThemes.animationDuration, // curve: FluffyThemes.animationCurve, // alignment: Alignment.bottomCenter, - // clipBehavior: Clip.none, // child: !showReceiptsRow // ? const SizedBox.shrink() // : Padding( @@ -1085,6 +1062,46 @@ class Message extends StatelessWidget { // child: MessageReactions(event, timeline), // ), // ), + // Pangea# + if (enterThread != null) + AnimatedSize( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + alignment: Alignment.bottomCenter, + child: threadChildren.isEmpty + ? const SizedBox.shrink() + : Padding( + padding: const EdgeInsets.only( + top: 2.0, + bottom: 8.0, + left: Avatar.defaultSize + 8, + ), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 1.5, + ), + child: TextButton.icon( + style: TextButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + foregroundColor: + theme.colorScheme.onSecondaryContainer, + backgroundColor: + theme.colorScheme.secondaryContainer, + ), + onPressed: () => enterThread(event.eventId), + icon: const Icon(Icons.message), + label: Text( + '${L10n.of(context).countReplies(threadChildren.length)} | ${threadChildren.first.calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)), withSenderNamePrefix: true)}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ), + ), + // #Pangea !showReceiptsRow ? const SizedBox.shrink() : Padding( @@ -1103,8 +1120,8 @@ class Message extends StatelessWidget { ) .key, ), - // Pangea# ), + // Pangea# if (displayReadMarker) Row( children: [ @@ -1123,14 +1140,15 @@ class Message extends StatelessWidget { vertical: 2, ), decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius / 3), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 3, + ), color: theme.colorScheme.surface.withAlpha(128), ), child: Text( L10n.of(context).readUpToHere, style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, + fontSize: 12 * AppSettings.fontSizeFactor.value, ), ), ), @@ -1195,8 +1213,10 @@ class BubblePainter extends CustomPainter { final scrollableRect = Offset.zero & scrollableBox.size; final bubbleBox = context.findRenderObject() as RenderBox; - final origin = - bubbleBox.localToGlobal(Offset.zero, ancestor: scrollableBox); + final origin = bubbleBox.localToGlobal( + Offset.zero, + ancestor: scrollableBox, + ); final paint = Paint() ..shader = ui.Gradient.linear( scrollableRect.topCenter, diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index bcd3debff..4e8f75a04 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; +import 'package:fluffychat/pages/chat/events/poll.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; @@ -71,18 +73,14 @@ class MessageContent extends StatelessWidget { // if (event.content['can_request_session'] != true) { // ScaffoldMessenger.of(context).showSnackBar( // SnackBar( - // content: Text( - // event.calcLocalizedBodyFallback(MatrixLocals(l10n)), - // ), + // content: Text(event.calcLocalizedBodyFallback(MatrixLocals(l10n))), // ), // ); // return; // } // final client = Matrix.of(context).client; // if (client.isUnknownSession && client.encryption!.crossSigning.enabled) { - // final success = await BootstrapDialog( - // client: Matrix.of(context).client, - // ).show(context); + // final success = await context.push('/backup'); // if (success != true) return; // } // event.requestKey(); @@ -114,20 +112,16 @@ class MessageContent extends StatelessWidget { // trailing: const Icon(Icons.lock_outlined), // ), // const Divider(), - // Text( - // event.calcLocalizedBodyFallback( - // MatrixLocals(l10n), - // ), - // ), + // Text(event.calcLocalizedBodyFallback(MatrixLocals(l10n))), // ], // ), // ), // ), // ); // } - void onClick(PangeaToken token) { - token = pangeaMessageEvent?.messageDisplayRepresentation + token = + pangeaMessageEvent?.messageDisplayRepresentation ?.getClosestNonPunctToken(token) ?? token; @@ -146,7 +140,8 @@ class MessageContent extends StatelessWidget { @override Widget build(BuildContext context) { - final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; + final fontSize = + AppConfig.messageFontSize * AppSettings.fontSizeFactor.value; final buttonTextColor = textColor; switch (event.type) { case EventTypes.Message: @@ -156,7 +151,9 @@ class MessageContent extends StatelessWidget { case MessageTypes.Image: case MessageTypes.Sticker: if (event.redacted) continue textmessage; - const maxSize = 256.0; + final maxSize = event.messageType == MessageTypes.Sticker + ? 128.0 + : 256.0; final w = event.content .tryGetMap('info') ?.tryGet('w'); @@ -191,12 +188,12 @@ class MessageContent extends StatelessWidget { return CuteContent(event); case MessageTypes.Audio: if (PlatformInfos.isMobile || - PlatformInfos.isMacOS || - PlatformInfos.isWeb - // Disabled until https://github.com/bleonard252/just_audio_mpv/issues/3 - // is fixed - // || PlatformInfos.isLinux - ) { + PlatformInfos.isMacOS || + PlatformInfos.isWeb + // Disabled until https://github.com/bleonard252/just_audio_mpv/issues/3 + // is fixed + // || PlatformInfos.isLinux + ) { return AudioPlayerWidget( event, color: textColor, @@ -242,8 +239,9 @@ class MessageContent extends StatelessWidget { // ); // Pangea# case MessageTypes.Location: - final geoUri = - Uri.tryParse(event.content.tryGet('geo_uri')!); + final geoUri = Uri.tryParse( + event.content.tryGet('geo_uri')!, + ); if (geoUri != null && geoUri.scheme == 'geo') { final latlong = geoUri.path .split(';') @@ -255,7 +253,7 @@ class MessageContent extends StatelessWidget { latlong.first != null && latlong.last != null) { return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ MapBubble( latitude: latlong.first!, @@ -264,8 +262,10 @@ class MessageContent extends StatelessWidget { const SizedBox(height: 6), OutlinedButton.icon( icon: Icon(Icons.location_on_outlined, color: textColor), - onPressed: - UrlLauncher(context, geoUri.toString()).launchUrl, + onPressed: UrlLauncher( + context, + geoUri.toString(), + ).launchUrl, label: Text( L10n.of(context).openInMaps, style: TextStyle(color: textColor), @@ -283,56 +283,40 @@ class MessageContent extends StatelessWidget { textmessage: default: if (event.redacted) { - return FutureBuilder( - future: event.redactedBecause?.fetchSenderUser(), - builder: (context, snapshot) { - final reason = - event.redactedBecause?.content.tryGet('reason'); - final redactedBy = snapshot.data?.calcDisplayname() ?? - event.redactedBecause?.senderId.localpart ?? - L10n.of(context).user; - return _ButtonContent( - label: reason == null - ? L10n.of(context).redactedBy(redactedBy) - : L10n.of(context).redactedByBecause( - redactedBy, - reason, - ), - icon: '🗑️', - textColor: buttonTextColor.withAlpha(128), - onPressed: () => onInfoTab!(event), - fontSize: fontSize, - ); - }, + return RedactionWidget( + event: event, + buttonTextColor: buttonTextColor, + onInfoTab: onInfoTab, + fontSize: fontSize, ); } - var html = AppConfig.renderHtml && event.isRichMessage + var html = AppSettings.renderHtml.value && event.isRichMessage ? event.formattedText - : event.body; + : event.body.replaceAll('<', '<').replaceAll('>', '>'); if (event.messageType == MessageTypes.Emote) { html = '* $html'; } - final bigEmotes = event.onlyEmotes && + final bigEmotes = + event.onlyEmotes && event.numberEmotes > 0 && event.numberEmotes <= 3; return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: HtmlMessage( html: html, textColor: textColor, room: event.room, - fontSize: AppConfig.fontSizeFactor * + fontSize: + AppSettings.fontSizeFactor.value * AppConfig.messageFontSize * (bigEmotes ? 5 : 1), limitHeight: !selected, linkStyle: TextStyle( color: linkColor, fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), @@ -349,7 +333,8 @@ class MessageContent extends StatelessWidget { pangeaMessageEvent: pangeaMessageEvent, nextEvent: nextEvent, prevEvent: prevEvent, - onClick: event.isActivityMessage || + onClick: + event.isActivityMessage || readingAssistanceMode == ReadingAssistanceMode.practiceMode ? null @@ -360,6 +345,21 @@ class MessageContent extends StatelessWidget { ), ); } + case PollEventContent.startType: + if (event.redacted) { + return RedactionWidget( + event: event, + buttonTextColor: buttonTextColor, + onInfoTab: onInfoTab, + fontSize: fontSize, + ); + } + return PollWidget( + event: event, + timeline: timeline, + textColor: textColor, + linkColor: linkColor, + ); case EventTypes.CallInvite: return FutureBuilder( future: event.fetchSenderUser(), @@ -397,6 +397,44 @@ class MessageContent extends StatelessWidget { } } +class RedactionWidget extends StatelessWidget { + const RedactionWidget({ + super.key, + required this.event, + required this.buttonTextColor, + required this.onInfoTab, + required this.fontSize, + }); + + final Event event; + final Color buttonTextColor; + final void Function(Event p1)? onInfoTab; + final double fontSize; + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: event.redactedBecause?.fetchSenderUser(), + builder: (context, snapshot) { + final reason = event.redactedBecause?.content.tryGet('reason'); + final redactedBy = + snapshot.data?.calcDisplayname() ?? + event.redactedBecause?.senderId.localpart ?? + L10n.of(context).user; + return _ButtonContent( + label: reason == null + ? L10n.of(context).redactedBy(redactedBy) + : L10n.of(context).redactedByBecause(redactedBy, reason), + icon: '🗑️', + textColor: buttonTextColor.withAlpha(128), + onPressed: () => onInfoTab!(event), + fontSize: fontSize, + ); + }, + ); + } +} + class _ButtonContent extends StatelessWidget { final void Function() onPressed; final String label; @@ -415,18 +453,12 @@ class _ButtonContent extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: InkWell( onTap: onPressed, child: Text( '$icon $label', - style: TextStyle( - color: textColor, - fontSize: fontSize, - ), + style: TextStyle(color: textColor, fontSize: fontSize), ), ), ); diff --git a/lib/pages/chat/events/message_download_content.dart b/lib/pages/chat/events/message_download_content.dart index 12216cdd3..46d5167ca 100644 --- a/lib/pages/chat/events/message_download_content.dart +++ b/lib/pages/chat/events/message_download_content.dart @@ -4,6 +4,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/url_launcher.dart'; @@ -26,15 +27,15 @@ class MessageDownloadContent extends StatelessWidget { final filetype = (filename.contains('.') ? filename.split('.').last.toUpperCase() : event.content - .tryGetMap('info') - ?.tryGet('mimetype') - ?.toUpperCase() ?? - 'UNKNOWN'); + .tryGetMap('info') + ?.tryGet('mimetype') + ?.toUpperCase() ?? + 'UNKNOWN'); final sizeString = event.sizeString ?? '?MB'; final fileDescription = event.fileDescription; return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: .min, + crossAxisAlignment: .start, spacing: 8, children: [ Material( @@ -46,20 +47,17 @@ class MessageDownloadContent extends StatelessWidget { width: 400, padding: const EdgeInsets.all(16.0), child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, spacing: 16, children: [ CircleAvatar( backgroundColor: textColor.withAlpha(32), child: Icon(Icons.file_download_outlined, color: textColor), ), - // #Pangea Flexible( - child: - // Pangea# - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + child: Column( + crossAxisAlignment: .start, + mainAxisSize: .min, children: [ Text( filename, @@ -95,12 +93,16 @@ class MessageDownloadContent extends StatelessWidget { textScaleFactor: MediaQuery.textScalerOf(context).scale(1), style: TextStyle( color: textColor, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: linkColor, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), diff --git a/lib/pages/chat/events/message_reactions.dart b/lib/pages/chat/events/message_reactions.dart index 7681c90ae..5f5b4aa2b 100644 --- a/lib/pages/chat/events/message_reactions.dart +++ b/lib/pages/chat/events/message_reactions.dart @@ -4,8 +4,6 @@ import 'package:collection/collection.dart' show IterableExtension; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -19,8 +17,10 @@ class MessageReactions extends StatelessWidget { @override Widget build(BuildContext context) { - final allReactionEvents = - event.aggregatedEvents(timeline, RelationshipTypes.reaction); + final allReactionEvents = event.aggregatedEvents( + timeline, + RelationshipTypes.reaction, + ); final reactionMap = {}; final client = Matrix.of(context).client; @@ -111,15 +111,11 @@ class _Reaction extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final textColor = - theme.brightness == Brightness.dark ? Colors.white : Colors.black; - final color = reacted == true - ? theme.bubbleColor - : theme.colorScheme.surfaceContainerHigh; + Widget content; if (reactionKey.startsWith('mxc://')) { content = Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ MxcImage( uri: Uri.parse(reactionKey), @@ -132,8 +128,9 @@ class _Reaction extends StatelessWidget { const SizedBox(width: 4), Text( count.toString(), + textAlign: TextAlign.center, style: TextStyle( - color: textColor, + color: theme.colorScheme.onSurface, fontSize: DefaultTextStyle.of(context).style.fontSize, ), ), @@ -148,7 +145,7 @@ class _Reaction extends StatelessWidget { content = Text( renderKey.toString() + (count > 1 ? ' $count' : ''), style: TextStyle( - color: reacted == true ? theme.onBubbleColor : textColor, + color: theme.colorScheme.onSurface, fontSize: DefaultTextStyle.of(context).style.fontSize, ), ); @@ -159,17 +156,18 @@ class _Reaction extends StatelessWidget { borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), child: Container( decoration: BoxDecoration( - color: color, + color: reacted == true + ? theme.colorScheme.primaryContainer + : theme.colorScheme.surfaceContainerHigh, + border: Border.all( + color: reacted == true + ? theme.colorScheme.primary + : theme.colorScheme.surfaceContainerHigh, + width: 1, + ), borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), ), - // #Pangea - // issue: https://github.com/pangeachat/client/issues/3100 - // fix: https://github.com/flutter/flutter/issues/119623#issuecomment-2476719745 - padding: PlatformInfos.isIOS - ? const EdgeInsets.fromLTRB(5.5, 1, 3, 2.5) - : const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - // padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - // Pangea# + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), child: content, ), ); @@ -194,17 +192,14 @@ class _AdaptableReactorsDialog extends StatelessWidget { final Client? client; final _ReactionEntry? reactionEntry; - const _AdaptableReactorsDialog({ - this.client, - this.reactionEntry, - }); + const _AdaptableReactorsDialog({this.client, this.reactionEntry}); Future show(BuildContext context) => showAdaptiveDialog( - context: context, - builder: (context) => this, - barrierDismissible: true, - useRootNavigator: false, - ); + context: context, + builder: (context) => this, + barrierDismissible: true, + useRootNavigator: false, + ); @override Widget build(BuildContext context) { @@ -230,9 +225,6 @@ class _AdaptableReactorsDialog extends StatelessWidget { final title = Center(child: Text(reactionEntry!.key)); - return AlertDialog.adaptive( - title: title, - content: body, - ); + return AlertDialog.adaptive(title: title, content: body); } } diff --git a/lib/pages/chat/events/pangea_message_reactions.dart b/lib/pages/chat/events/pangea_message_reactions.dart index 0150b63d4..cda342d23 100644 --- a/lib/pages/chat/events/pangea_message_reactions.dart +++ b/lib/pages/chat/events/pangea_message_reactions.dart @@ -7,7 +7,6 @@ import 'package:collection/collection.dart' show IterableExtension; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/emoji_burst.dart'; import 'package:fluffychat/pages/chat/events/reaction_listener.dart'; @@ -75,8 +74,10 @@ class _PangeaMessageReactionsState extends State { } void _updateReactionMap() { - final allReactionEvents = widget.event - .aggregatedEvents(widget.timeline, RelationshipTypes.reaction); + final allReactionEvents = widget.event.aggregatedEvents( + widget.timeline, + RelationshipTypes.reaction, + ); final newReactionMap = {}; for (final e in allReactionEvents) { @@ -221,14 +222,8 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { duration: const Duration(milliseconds: 400), vsync: this, ); - _bounceOutAnimation = Tween( - begin: 1.0, - end: 0, - ).animate( - CurvedAnimation( - parent: _bounceOutController, - curve: Curves.easeInBack, - ), + _bounceOutAnimation = Tween(begin: 1.0, end: 0).animate( + CurvedAnimation(parent: _bounceOutController, curve: Curves.easeInBack), ); _burstController = AnimationController( @@ -238,12 +233,7 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { _burstAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _burstController, - curve: Curves.easeOut, - ), - ); + ).animate(CurvedAnimation(parent: _burstController, curve: Curves.easeOut)); _growController = AnimationController( duration: const Duration(milliseconds: 500), @@ -251,25 +241,33 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { ); _growScale = TweenSequence([ TweenSequenceItem( - tween: Tween(begin: 0.6, end: 1.18) - .chain(CurveTween(curve: Curves.easeOutBack)), + tween: Tween( + begin: 0.6, + end: 1.18, + ).chain(CurveTween(curve: Curves.easeOutBack)), weight: 60, ), TweenSequenceItem( - tween: Tween(begin: 1.18, end: 1.0) - .chain(CurveTween(curve: Curves.easeIn)), + tween: Tween( + begin: 1.18, + end: 1.0, + ).chain(CurveTween(curve: Curves.easeIn)), weight: 40, ), ]).animate(_growController); _growOffset = TweenSequence([ TweenSequenceItem( - tween: Tween(begin: 0.0, end: -10.0) - .chain(CurveTween(curve: Curves.easeOut)), + tween: Tween( + begin: 0.0, + end: -10.0, + ).chain(CurveTween(curve: Curves.easeOut)), weight: 60, ), TweenSequenceItem( - tween: Tween(begin: -10.0, end: 0.0) - .chain(CurveTween(curve: Curves.easeIn)), + tween: Tween( + begin: -10.0, + end: 0.0, + ).chain(CurveTween(curve: Curves.easeIn)), weight: 40, ), ]).animate(_growController); @@ -293,7 +291,7 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { _growController.reset(); } - _animateAndReact() async { + Future _animateAndReact() async { final bool? wasReacted = widget.reacted; final bool wasSingle = (widget.count == 1); @@ -345,11 +343,6 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final textColor = - theme.brightness == Brightness.dark ? Colors.white : Colors.black; - final color = widget.reacted == true - ? theme.bubbleColor - : theme.colorScheme.surfaceContainerHigh; Widget content; if (widget.reactionKey.startsWith('mxc://')) { @@ -367,8 +360,9 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { const SizedBox(width: 4), Text( widget.count.toString(), + textAlign: TextAlign.center, style: TextStyle( - color: textColor, + color: theme.colorScheme.onSurface, fontSize: DefaultTextStyle.of(context).style.fontSize, ), ), @@ -383,7 +377,7 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { content = Text( renderKey.toString() + (widget.count > 1 ? ' ${widget.count}' : ''), style: TextStyle( - color: widget.reacted == true ? theme.onBubbleColor : textColor, + color: theme.colorScheme.onSurface, fontSize: DefaultTextStyle.of(context).style.fontSize, ), ); @@ -396,11 +390,13 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { AnimatedBuilder( animation: Listenable.merge([_bounceOutAnimation, _growController]), builder: (context, child) { - final isGrowing = _growController.isAnimating || + final isGrowing = + _growController.isAnimating || (_growController.value > 0 && _growController.value < 1.0); final isBouncing = _bounceOutController.isAnimating; - final scale = - isGrowing ? _growScale.value : _bounceOutAnimation.value; + final scale = isGrowing + ? _growScale.value + : _bounceOutAnimation.value; final offsetY = isGrowing ? _growOffset.value : 0.0; return AnimatedSize( @@ -438,7 +434,17 @@ class _ReactionState extends State<_Reaction> with TickerProviderStateMixin { ), child: Container( decoration: BoxDecoration( - color: color, + color: widget.reacted == true + ? theme.colorScheme.primaryContainer + : theme.colorScheme.surfaceContainerHigh, + border: Border.all( + color: widget.reacted == true + ? theme.colorScheme.primary + : theme + .colorScheme + .surfaceContainerHigh, + width: 1, + ), borderRadius: BorderRadius.circular( AppConfig.borderRadius / 2, ), @@ -499,17 +505,14 @@ class _AdaptableReactorsDialog extends StatelessWidget { final Client? client; final _ReactionEntry? reactionEntry; - const _AdaptableReactorsDialog({ - this.client, - this.reactionEntry, - }); + const _AdaptableReactorsDialog({this.client, this.reactionEntry}); Future show(BuildContext context) => showAdaptiveDialog( - context: context, - builder: (context) => this, - barrierDismissible: true, - useRootNavigator: true, - ); + context: context, + builder: (context) => this, + barrierDismissible: true, + useRootNavigator: true, + ); @override Widget build(BuildContext context) { @@ -538,9 +541,6 @@ class _AdaptableReactorsDialog extends StatelessWidget { final title = Center(child: Text(reactionEntry!.key)); - return AlertDialog.adaptive( - title: title, - content: body, - ); + return AlertDialog.adaptive(title: title, content: body); } } diff --git a/lib/pages/chat/events/poll.dart b/lib/pages/chat/events/poll.dart new file mode 100644 index 000000000..e6baeb7ce --- /dev/null +++ b/lib/pages/chat/events/poll.dart @@ -0,0 +1,234 @@ +import 'package:flutter/material.dart'; + +import 'package:async/async.dart'; +import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:matrix/matrix.dart' hide Result; + +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/utils/url_launcher.dart'; +import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/widgets/future_loading_dialog.dart'; + +class PollWidget extends StatelessWidget { + final Event event; + final Timeline timeline; + final Color textColor; + final Color linkColor; + const PollWidget({ + required this.event, + required this.timeline, + required this.textColor, + required this.linkColor, + super.key, + }); + + void _endPoll(BuildContext context) => + showFutureLoadingDialog(context: context, future: () => event.endPoll()); + + void _toggleVote( + BuildContext context, + String answerId, + int maxSelection, + ) async { + final userId = event.room.client.userID!; + final answerIds = event.getPollResponses(timeline)[userId] ?? {}; + if (!answerIds.remove(answerId)) { + answerIds.add(answerId); + if (answerIds.length > maxSelection) { + answerIds.clear(); + answerIds.add(answerId); + } + } + + showFutureLoadingDialog( + context: context, + future: () => event.answerPoll(answerIds.toList()), + ); + } + + @override + Widget build(BuildContext context) { + final eventContentResult = Result(() => event.parsedPollEventContent); + final eventContent = eventContentResult.asValue?.value; + if (eventContent == null) { + Logs().w('Invalid poll event', eventContentResult.error); + return const Text('Unable to parse poll event...'); + } + final responses = event.getPollResponses(timeline); + final pollHasBeenEnded = event.getPollHasBeenEnded(timeline); + final canVote = + event.room.canSendEvent(PollEventContent.responseType) && + !pollHasBeenEnded; + final maxPolls = responses.length; + final answersVisible = + eventContent.pollStartContent.kind == PollKind.disclosed || + pollHasBeenEnded; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + mainAxisSize: .min, + crossAxisAlignment: .start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Linkify( + text: eventContent.pollStartContent.question.mText, + textScaleFactor: MediaQuery.textScalerOf(context).scale(1), + style: TextStyle( + color: textColor, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, + ), + options: const LinkifyOptions(humanize: false), + linkStyle: TextStyle( + color: linkColor, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, + decoration: TextDecoration.underline, + decorationColor: linkColor, + ), + onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), + ), + ), + Divider(color: linkColor.withAlpha(64)), + ...eventContent.pollStartContent.answers.map((answer) { + final votedUserIds = responses.entries + .where((entry) => entry.value.contains(answer.id)) + .map((entry) => entry.key) + .toSet(); + return Material( + color: Colors.transparent, + clipBehavior: Clip.hardEdge, + child: CheckboxListTile.adaptive( + value: + responses[event.room.client.userID!]?.contains(answer.id) ?? + false, + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + checkboxScaleFactor: 1.5, + checkboxShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(32), + ), + onChanged: !canVote + ? null + : (_) => _toggleVote( + context, + answer.id, + eventContent.pollStartContent.maxSelections, + ), + title: Text( + answer.mText, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: textColor, + fontSize: + AppConfig.messageFontSize * + AppSettings.fontSizeFactor.value, + ), + ), + subtitle: answersVisible + ? Column( + crossAxisAlignment: .start, + mainAxisSize: .min, + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + Text( + L10n.of( + context, + ).countVotes(votedUserIds.length), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: linkColor, + fontSize: + 12 * AppSettings.fontSizeFactor.value, + ), + ), + const SizedBox(width: 2), + ...votedUserIds.map((userId) { + final user = event.room + .getState(EventTypes.RoomMember, userId) + ?.asUser(event.room); + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 2.0, + ), + child: Avatar( + mxContent: user?.avatarUrl, + name: + user?.calcDisplayname() ?? + userId.localpart, + size: + 12 * AppSettings.fontSizeFactor.value, + ), + ); + }), + const SizedBox(width: 2), + ], + ), + ), + LinearProgressIndicator( + color: linkColor, + backgroundColor: linkColor.withAlpha(128), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + value: maxPolls == 0 + ? 0 + : votedUserIds.length / maxPolls, + ), + ], + ) + : null, + ), + ); + }), + if (!pollHasBeenEnded && event.senderId == event.room.client.userID) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: OutlinedButton( + onPressed: () => _endPoll(context), + style: OutlinedButton.styleFrom( + foregroundColor: linkColor, + side: BorderSide(color: linkColor.withAlpha(64)), + ), + child: Text(L10n.of(context).endPoll), + ), + ) + else if (!answersVisible) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + L10n.of(context).answersWillBeVisibleWhenPollHasEnded, + style: TextStyle( + color: linkColor, + fontSize: 12 * AppSettings.fontSizeFactor.value, + fontStyle: FontStyle.italic, + ), + ), + ) + else if (pollHasBeenEnded) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + L10n.of(context).pollHasBeenEnded, + style: TextStyle( + color: linkColor, + fontSize: 12 * AppSettings.fontSizeFactor.value, + fontStyle: FontStyle.italic, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/chat/events/reaction_listener.dart b/lib/pages/chat/events/reaction_listener.dart index f0f51c0ee..ef260c9fb 100644 --- a/lib/pages/chat/events/reaction_listener.dart +++ b/lib/pages/chat/events/reaction_listener.dart @@ -9,22 +9,22 @@ class ReactionListener { StreamSubscription? _reactionSub; ReactionListener({required this.event, required this.onUpdate}) { - _reactionSub = event.room.client.onSync.stream.where( - (update) { - final room = event.room; - final timelineEvents = update.rooms?.join?[room.id]?.timeline?.events; - if (timelineEvents == null) return false; + _reactionSub = event.room.client.onSync.stream + .where((update) { + final room = event.room; + final timelineEvents = update.rooms?.join?[room.id]?.timeline?.events; + if (timelineEvents == null) return false; - final eventID = event.eventId; - return timelineEvents.any( - (e) => - e.type == EventTypes.Redaction || - (e.type == EventTypes.Reaction && - Event.fromMatrixEvent(e, room).relationshipEventId == - eventID), - ); - }, - ).listen(onUpdate); + final eventID = event.eventId; + return timelineEvents.any( + (e) => + e.type == EventTypes.Redaction || + (e.type == EventTypes.Reaction && + Event.fromMatrixEvent(e, room).relationshipEventId == + eventID), + ); + }) + .listen(onUpdate); } void dispose() { diff --git a/lib/pages/chat/events/reply_content.dart b/lib/pages/chat/events/reply_content.dart index 859d59243..e60a095b3 100644 --- a/lib/pages/chat/events/reply_content.dart +++ b/lib/pages/chat/events/reply_content.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import '../../../config/app_config.dart'; @@ -28,26 +29,28 @@ class ReplyContent extends StatelessWidget { final theme = Theme.of(context); final timeline = this.timeline; - final displayEvent = - timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent; - final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; + final displayEvent = timeline != null + ? replyEvent.getDisplayEvent(timeline) + : replyEvent; + final fontSize = + AppConfig.messageFontSize * AppSettings.fontSizeFactor.value; final color = theme.brightness == Brightness.dark - // Pangea# + // #Pangea ? ownMessage - ? theme.colorScheme.tertiaryContainer - : theme.colorScheme.onTertiaryContainer + ? theme.colorScheme.tertiaryContainer + : theme.colorScheme.onTertiaryContainer : theme.colorScheme.tertiary; // ? theme.colorScheme.onTertiaryContainer // : ownMessage - // ? theme.colorScheme.tertiaryContainer - // : theme.colorScheme.tertiary; + // ? theme.colorScheme.tertiaryContainer + // : theme.colorScheme.tertiary; // Pangea# return Material( color: Colors.transparent, borderRadius: borderRadius, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ Container( width: 5, @@ -60,8 +63,8 @@ class ReplyContent extends StatelessWidget { const SizedBox(width: 6), Flexible( child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: .start, + mainAxisAlignment: .center, children: [ FutureBuilder( initialData: displayEvent.senderFromMemoryOrFallback, @@ -95,6 +98,7 @@ class ReplyContent extends StatelessWidget { MatrixLocals(L10n.of(context)), withSenderNamePrefix: false, hideReply: true, + plaintextBody: true, ), overflow: TextOverflow.ellipsis, maxLines: 1, @@ -103,8 +107,8 @@ class ReplyContent extends StatelessWidget { // color: theme.brightness == Brightness.dark // ? theme.colorScheme.onSurface // : ownMessage - // ? theme.colorScheme.onTertiary - // : theme.colorScheme.onSurface, + // ? theme.colorScheme.onTertiary + // : theme.colorScheme.onSurface, color: ownMessage ? ThemeData.dark().colorScheme.onPrimary : theme.colorScheme.onSurface, diff --git a/lib/pages/chat/events/state_message.dart b/lib/pages/chat/events/state_message.dart index 2975c8a06..500767e63 100644 --- a/lib/pages/chat/events/state_message.dart +++ b/lib/pages/chat/events/state_message.dart @@ -1,56 +1,95 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import '../../../config/app_config.dart'; class StateMessage extends StatelessWidget { final Event event; - const StateMessage(this.event, {super.key}); + final void Function()? onExpand; + final bool isCollapsed; + const StateMessage( + this.event, { + this.onExpand, + this.isCollapsed = false, + super.key, + }); @override Widget build(BuildContext context) { final theme = Theme.of(context); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Center( - child: Padding( - padding: const EdgeInsets.all(4), - child: Material( - color: theme.colorScheme.surface.withAlpha(128), - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 3), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: Text( - // #Pangea - // event.calcLocalizedBodyFallback( - // MatrixLocals(L10n.of(context)), - // ), - (event.type == EventTypes.RoomMember) && - (event.roomMemberChangeType == - RoomMemberChangeType.leave) && - (event.stateKey == event.room.client.userID) - ? L10n.of(context).youLeftTheChat - : event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)), + return AnimatedSize( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: isCollapsed + ? const SizedBox.shrink() + : Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Center( + child: Padding( + padding: const EdgeInsets.all(4), + child: Material( + color: theme.colorScheme.surface.withAlpha(128), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 3, + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, ), - // Pangea# - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, - decoration: - event.redacted ? TextDecoration.lineThrough : null, + child: Text.rich( + TextSpan( + children: [ + TextSpan( + // #Pangea + // text: event.calcLocalizedBodyFallback( + // MatrixLocals(L10n.of(context)), + // ), + text: + (event.type == EventTypes.RoomMember) && + (event.roomMemberChangeType == + RoomMemberChangeType.leave) && + (event.stateKey == + event.room.client.userID) + ? L10n.of(context).youLeftTheChat + : event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)), + ), + // Pangea# + ), + if (onExpand != null) ...[ + const TextSpan(text: '\n'), + TextSpan( + style: TextStyle( + color: theme.colorScheme.primary, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = onExpand, + text: L10n.of(context).moreEvents, + ), + ], + ], + ), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 11 * AppSettings.fontSizeFactor.value, + decoration: event.redacted + ? TextDecoration.lineThrough + : null, + ), + ), + ), + ), ), ), ), - ), - ), - ), ); } } diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index 1940abc33..682886032 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -6,6 +6,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -46,8 +47,10 @@ class EventVideoPlayer extends StatelessWidget { PlatformInfos.supportsVideoPlayer && _supportedFormat; // Pangea# - final blurHash = (event.infoMap as Map) - .tryGet('xyz.amorgan.blurhash') ?? + final blurHash = + (event.infoMap as Map).tryGet( + 'xyz.amorgan.blurhash', + ) ?? fallbackBlurHash; final fileDescription = event.fileDescription; const maxDimension = 300.0; @@ -60,11 +63,12 @@ class EventVideoPlayer extends StatelessWidget { final height = videoHeight / modifier; final durationInt = infoMap?.tryGet('duration'); - final duration = - durationInt == null ? null : Duration(milliseconds: durationInt); + final duration = durationInt == null + ? null + : Duration(milliseconds: durationInt); return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, spacing: 8, children: [ Material( @@ -139,23 +143,22 @@ class EventVideoPlayer extends StatelessWidget { SizedBox( width: width, child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Linkify( text: fileDescription, textScaleFactor: MediaQuery.textScalerOf(context).scale(1), style: TextStyle( color: textColor, fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: linkColor, fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 4aec9b5ca..1294fbc96 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:emojis/emoji.dart'; +import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:matrix/matrix.dart'; import 'package:slugify/slugify.dart'; @@ -43,6 +43,7 @@ class InputBar extends StatelessWidget { final ValueChanged? onChanged; final bool? autofocus; final bool readOnly; + final List suggestionEmojis; const InputBar({ required this.room, @@ -58,6 +59,7 @@ class InputBar extends StatelessWidget { this.autofocus, this.textInputAction, this.readOnly = false, + required this.suggestionEmojis, // #Pangea required this.choreographer, required this.showNextMatch, @@ -81,18 +83,17 @@ class InputBar extends StatelessWidget { // final commandSearch = commandMatch[1]!.toLowerCase(); // for (final command in room.client.commands.keys) { // if (command.contains(commandSearch)) { - // ret.add({ - // 'type': 'command', - // 'name': command, - // }); + // ret.add({'type': 'command', 'name': command}); // } // if (ret.length > maxResults) return ret; // } // } // Pangea# - final emojiMatch = - RegExp(r'(?:\s|^):(?:([-\w]+)~)?([-\w]+)$').firstMatch(searchText); + final emojiMatch = RegExp( + r'(?:\s|^):(?:([\p{L}\p{N}_-]+)~)?([\p{L}\p{N}_-]+)$', + unicode: true, + ).firstMatch(searchText); if (emojiMatch != null) { final packSearch = emojiMatch[1]; final emoteSearch = emojiMatch[2]!.toLowerCase(); @@ -125,8 +126,8 @@ class InputBar extends StatelessWidget { 'type': 'emote', 'name': emote.key, 'pack': packSearch, - 'pack_avatar_url': - emotePacks[packSearch]!.pack.avatarUrl?.toString(), + 'pack_avatar_url': emotePacks[packSearch]!.pack.avatarUrl + ?.toString(), 'pack_display_name': emotePacks[packSearch]!.pack.displayName ?? packSearch, 'mxc': emote.value.url.toString(), @@ -137,13 +138,12 @@ class InputBar extends StatelessWidget { } } } + // aside of emote packs, also propose normal (tm) unicode emojis - final matchingUnicodeEmojis = Emoji.all() - .where( - (element) => [element.name, ...element.keywords] - .any((element) => element.toLowerCase().contains(emoteSearch)), - ) + final matchingUnicodeEmojis = suggestionEmojis + .where((emoji) => emoji.name.toLowerCase().contains(emoteSearch)) .toList(); + // sort by the index of the search term in the name in order to have // best matches first // (thanks for the hint by github.com/nextcloud/circles devs) @@ -163,9 +163,8 @@ class InputBar extends StatelessWidget { for (final emoji in matchingUnicodeEmojis) { ret.add({ 'type': 'emoji', - 'emoji': emoji.char, - // don't include sub-group names, splitting at `:` hence - 'label': '${emoji.char} - ${emoji.name.split(':').first}', + 'emoji': emoji.emoji, + 'label': emoji.name, 'current_word': ':$emoteSearch', }); if (ret.length > maxResults) { @@ -179,8 +178,9 @@ class InputBar extends StatelessWidget { for (final user in room.getParticipants()) { if ((user.displayName != null && (user.displayName!.toLowerCase().contains(userSearch) || - slugify(user.displayName!.toLowerCase()) - .contains(userSearch))) || + slugify( + user.displayName!.toLowerCase(), + ).contains(userSearch))) || user.id.split(':')[0].toLowerCase().contains(userSearch)) { ret.add({ 'type': 'user', @@ -271,7 +271,14 @@ class InputBar extends StatelessWidget { waitDuration: const Duration(days: 1), // don't show on hover child: ListTile( onTap: () => onSelected(suggestion), - title: Text(label, style: const TextStyle(fontFamily: 'RobotoMono')), + leading: SizedBox.square( + dimension: size, + child: Text( + suggestion['emoji']!, + style: const TextStyle(fontSize: 16), + ), + ), + title: Text(label, maxLines: 1, overflow: TextOverflow.ellipsis), ), ); } @@ -289,7 +296,7 @@ class InputBar extends StatelessWidget { isThumbnail: false, ), title: Row( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: .center, children: [ Text(suggestion['name']!), Expanded( @@ -320,7 +327,8 @@ class InputBar extends StatelessWidget { onTap: () => onSelected(suggestion), leading: Avatar( mxContent: url, - name: suggestion.tryGet('displayname') ?? + name: + suggestion.tryGet('displayname') ?? suggestion.tryGet('mxid'), size: size, client: client, @@ -332,8 +340,10 @@ class InputBar extends StatelessWidget { } String insertSuggestion(Map suggestion) { - final replaceText = - controller!.text.substring(0, controller!.selection.baseOffset); + final replaceText = controller!.text.substring( + 0, + controller!.selection.baseOffset, + ); var startText = ''; final afterText = replaceText == controller!.text ? '' @@ -471,15 +481,19 @@ class InputBar extends StatelessWidget { focusNode: focusNode, textEditingController: controller, optionsBuilder: getSuggestions, - fieldViewBuilder: (context, __, focusNode, _) => ListenableBuilder( + // #Pangea + // fieldViewBuilder: (context, controller, focusNode, _) => TextField( + fieldViewBuilder: (context, _, focusNode, _) => ListenableBuilder( listenable: choreographer, builder: (context, _) { return TextField( + // Pangea# controller: controller, focusNode: focusNode, // #Pangea // readOnly: readOnly, - // contextMenuBuilder: (c, e) => markdownContextBuilder(c, e, controller), + // contextMenuBuilder: (c, e) => + // markdownContextBuilder(c, e, controller), contextMenuBuilder: (c, e) => markdownContextBuilder(c, e, controller!), onTap: () => _onInputTap(context), @@ -497,10 +511,7 @@ class InputBar extends StatelessWidget { bytes: data, name: content.uri.split('/').last, ); - room.sendFileEvent( - file, - shrinkImageMaxDimension: 1600, - ); + room.sendFileEvent(file, shrinkImageMaxDimension: 1600); }, ), minLines: minLines, @@ -523,29 +534,29 @@ class InputBar extends StatelessWidget { }, // #Pangea // maxLength: AppSettings.textMessageMaxLength.value, - // decoration: decoration!, - // Pangea# + // decoration: decoration, decoration: decoration.copyWith( - // #Pangea - // hint: ShrinkableText( hint: StreamBuilder( stream: MatrixState - .pangeaController.userController.languageStream.stream, + .pangeaController + .userController + .languageStream + .stream, builder: (context, _) => SizedBox( height: 24, child: ShrinkableText( - // Pangea# text: choreographer.itController.open.value ? L10n.of(context).buildTranslation : _defaultHintText(context), maxWidth: double.infinity, style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Theme.of(context).disabledColor, - ), + color: Theme.of(context).disabledColor, + ), ), ), ), ), + // Pangea# onChanged: (text) { // fix for the library for now // it sets the types for the callback incorrectly diff --git a/lib/pages/chat/pinned_events.dart b/lib/pages/chat/pinned_events.dart index 46c17d815..f66ef4700 100644 --- a/lib/pages/chat/pinned_events.dart +++ b/lib/pages/chat/pinned_events.dart @@ -39,7 +39,8 @@ class PinnedEvents extends StatelessWidget { (event) => AdaptiveModalAction( value: event?.eventId ?? '', icon: const Icon(Icons.push_pin_outlined), - label: event?.calcLocalizedBodyFallback( + label: + event?.calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)), withSenderNamePrefix: true, hideReply: true, @@ -59,7 +60,7 @@ class PinnedEvents extends StatelessWidget { final pinnedEventIds = controller.room.pinnedEventIds; - if (pinnedEventIds.isEmpty) { + if (pinnedEventIds.isEmpty || controller.activeThreadId != null) { return const SizedBox.shrink(); } @@ -68,7 +69,8 @@ class PinnedEvents extends StatelessWidget { builder: (context, snapshot) { final event = snapshot.data; return ChatAppBarListTile( - title: event?.calcLocalizedBodyFallback( + title: + event?.calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)), withSenderNamePrefix: true, hideReply: true, @@ -86,12 +88,12 @@ class PinnedEvents extends StatelessWidget { // : null, tooltip: controller.room.canChangeStateEvent(EventTypes.RoomPinnedEvents) - ? L10n.of(context).unpin - : null, + ? L10n.of(context).unpin + : null, onPressed: controller.room.canChangeStateEvent(EventTypes.RoomPinnedEvents) - ? () => controller.unpinEvent(event!.eventId) - : null, + ? () => controller.unpinEvent(event!.eventId) + : null, // Pangea# ), onTap: () => _displayPinnedEventsDialog(context), diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart deleted file mode 100644 index 501e20663..000000000 --- a/lib/pages/chat/recording_dialog.dart +++ /dev/null @@ -1,318 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:path/path.dart' as path_lib; -import 'package:path_provider/path_provider.dart'; -import 'package:record/record.dart'; -import 'package:wakelock_plus/wakelock_plus.dart'; - -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/setting_keys.dart'; -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/utils/localized_exception_extension.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import 'events/audio_player.dart'; - -class PermissionException implements Exception {} - -class EmptyAudioException implements Exception {} - -class RecordingDialog extends StatefulWidget { - const RecordingDialog({ - super.key, - }); - - @override - RecordingDialogState createState() => RecordingDialogState(); -} - -class RecordingDialogState extends State { - Timer? _recorderSubscription; - Duration _duration = Duration.zero; - - // #Pangea - // bool error = false; - Object? error; - bool _loading = true; - // Pangea# - - final _audioRecorder = AudioRecorder(); - final List amplitudeTimeline = []; - - String? fileName; - - Future startRecording() async { - final store = Matrix.of(context).store; - try { - // #Pangea - // final codec = kIsWeb - // // Web seems to create webm instead of ogg when using opus encoder - // // which does not play on iOS right now. So we use wav for now: - // ? AudioEncoder.wav - // // Everywhere else we use opus if supported by the platform: - // : await _audioRecorder.isEncoderSupported(AudioEncoder.opus) - // ? AudioEncoder.opus - // : AudioEncoder.aacLc; - const codec = AudioEncoder.wav; - // Pangea# - fileName = - 'recording${DateTime.now().microsecondsSinceEpoch}.${codec.fileExtension}'; - String? path; - if (!kIsWeb) { - final tempDir = await getTemporaryDirectory(); - path = path_lib.join(tempDir.path, fileName); - } - - final result = await _audioRecorder.hasPermission(); - if (result != true) { - // #Pangea - throw PermissionException(); - // setState(() => error = true); - // return; - // Pangea# - } - await WakelockPlus.enable(); - - await _audioRecorder.start( - RecordConfig( - bitRate: AppSettings.audioRecordingBitRate.getItem(store), - sampleRate: AppSettings.audioRecordingSamplingRate.getItem(store), - numChannels: AppSettings.audioRecordingNumChannels.getItem(store), - autoGain: AppSettings.audioRecordingAutoGain.getItem(store), - echoCancel: AppSettings.audioRecordingEchoCancel.getItem(store), - noiseSuppress: AppSettings.audioRecordingNoiseSuppress.getItem(store), - encoder: codec, - ), - path: path ?? '', - ); - - // #Pangea - // setState(() => _duration = Duration.zero); - setState(() { - _duration = Duration.zero; - _loading = false; - }); - // Pangea# - _recorderSubscription?.cancel(); - _recorderSubscription = - Timer.periodic(const Duration(milliseconds: 100), (_) async { - final amplitude = await _audioRecorder.getAmplitude(); - var value = 100 + amplitude.current * 2; - value = value < 1 ? 1 : value; - amplitudeTimeline.add(value); - setState(() { - _duration += const Duration(milliseconds: 100); - }); - }); - // #Pangea - // } catch (_) { - // setState(() => error = true); - } catch (e) { - setState(() => error = e); - // Pangea# - rethrow; - } - } - - @override - void initState() { - super.initState(); - startRecording(); - } - - @override - void dispose() { - WakelockPlus.disable(); - _recorderSubscription?.cancel(); - _audioRecorder.stop(); - super.dispose(); - } - - void _stopAndSend() async { - _recorderSubscription?.cancel(); - final path = await _audioRecorder.stop(); - - if (path == null) throw ('Recording failed!'); - const waveCount = AudioPlayerWidget.wavesCount; - final step = amplitudeTimeline.length < waveCount - ? 1 - : (amplitudeTimeline.length / waveCount).round(); - final waveform = []; - for (var i = 0; i < amplitudeTimeline.length; i += step) { - waveform.add((amplitudeTimeline[i] / 100 * 1024).round()); - } - - // #Pangea - if (amplitudeTimeline.isEmpty || amplitudeTimeline.every((e) => e <= 1)) { - if (mounted) { - setState(() => error = EmptyAudioException()); - } - return; - } - // Pangea# - - Navigator.of(context, rootNavigator: false).pop( - RecordingResult( - path: path, - duration: _duration.inMilliseconds, - waveform: waveform, - fileName: fileName, - ), - ); - } - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - const maxDecibalWidth = 64.0; - final time = - '${_duration.inMinutes.toString().padLeft(2, '0')}:${(_duration.inSeconds % 60).toString().padLeft(2, '0')}'; - // #Pangea - // final content = error - // ? Text(L10n.of(context).oopsSomethingWentWrong) - final content = error != null - ? ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 250.0), - child: error is PermissionException - ? Text(L10n.of(context).recordingPermissionDenied) - : kIsWeb && error is! EmptyAudioException - ? Text(L10n.of(context).genericWebRecordingError) - : Text(error!.toLocalizedString(context)), - ) - // Pangea# - : Row( - children: [ - Container( - width: 16, - height: 16, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(32), - color: Colors.red, - ), - ), - Expanded( - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - children: amplitudeTimeline.reversed - .take(26) - .toList() - .reversed - .map( - (amplitude) => Container( - margin: const EdgeInsets.only(left: 2), - width: 4, - decoration: BoxDecoration( - color: theme.colorScheme.primary, - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - ), - height: maxDecibalWidth * (amplitude / 100), - ), - ) - .toList(), - ), - ), - const SizedBox(width: 8), - SizedBox( - width: 48, - // #Pangea - // child: Text(time), - child: _loading - ? const SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator.adaptive(), - ) - : Text(time), - // Pangea# - ), - ], - ); - if (PlatformInfos.isCupertinoStyle) { - return CupertinoAlertDialog( - content: content, - actions: [ - CupertinoDialogAction( - onPressed: () => Navigator.of(context, rootNavigator: false).pop(), - child: Text( - L10n.of(context).cancel, - style: TextStyle( - color: theme.textTheme.bodyMedium?.color?.withAlpha(150), - ), - ), - ), - // #Pangea - // if (error != true) - if (error == null) - // Pangea# - CupertinoDialogAction( - onPressed: _stopAndSend, - child: Text(L10n.of(context).send), - ), - ], - ); - } - return AlertDialog( - content: content, - actions: [ - TextButton( - onPressed: () => Navigator.of(context, rootNavigator: false).pop(), - child: Text( - L10n.of(context).cancel, - style: TextStyle( - color: theme.colorScheme.error, - ), - ), - ), - // #Pangea - // if (error != true) - if (error == null) - // Pangea# - TextButton( - onPressed: _stopAndSend, - child: Text(L10n.of(context).send), - ), - ], - ); - } -} - -class RecordingResult { - final String path; - final int duration; - final List waveform; - final String? fileName; - - const RecordingResult({ - required this.path, - required this.duration, - required this.waveform, - required this.fileName, - }); -} - -extension on AudioEncoder { - String get fileExtension { - switch (this) { - case AudioEncoder.aacLc: - case AudioEncoder.aacEld: - case AudioEncoder.aacHe: - return 'm4a'; - case AudioEncoder.opus: - return 'ogg'; - case AudioEncoder.wav: - return 'wav'; - case AudioEncoder.amrNb: - case AudioEncoder.amrWb: - case AudioEncoder.flac: - case AudioEncoder.pcm16bits: - throw UnsupportedError('Not yet used'); - } - } -} diff --git a/lib/pages/chat/recording_input_row.dart b/lib/pages/chat/recording_input_row.dart new file mode 100644 index 000000000..2a88a7845 --- /dev/null +++ b/lib/pages/chat/recording_input_row.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pages/chat/recording_view_model.dart'; + +class RecordingInputRow extends StatelessWidget { + final RecordingViewModelState state; + final Future Function(String, int, List, String?) onSend; + const RecordingInputRow({ + required this.state, + required this.onSend, + super.key, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + const maxDecibalWidth = 36.0; + final time = + '${state.duration.inMinutes.toString().padLeft(2, '0')}:${(state.duration.inSeconds % 60).toString().padLeft(2, '0')}'; + return Row( + children: [ + IconButton( + tooltip: L10n.of(context).cancel, + icon: const Icon(Icons.delete_outlined), + color: theme.colorScheme.error, + onPressed: state.cancel, + ), + if (state.isPaused) + IconButton( + tooltip: L10n.of(context).resume, + icon: const Icon(Icons.play_circle_outline_outlined), + onPressed: state.resume, + ) + else + IconButton( + tooltip: L10n.of(context).pause, + icon: const Icon(Icons.pause_circle_outline_outlined), + onPressed: state.pause, + ), + Text(time), + const SizedBox(width: 8), + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + const width = 4; + return Row( + mainAxisSize: .min, + mainAxisAlignment: .end, + children: state.amplitudeTimeline.reversed + .take((constraints.maxWidth / (width + 2)).floor()) + .toList() + .reversed + .map( + (amplitude) => Container( + margin: const EdgeInsets.only(left: 2), + width: width.toDouble(), + decoration: BoxDecoration( + color: theme.colorScheme.primary, + borderRadius: BorderRadius.circular(2), + ), + height: maxDecibalWidth * (amplitude / 100), + ), + ) + .toList(), + ); + }, + ), + ), + IconButton( + style: IconButton.styleFrom( + disabledBackgroundColor: theme.bubbleColor.withAlpha(128), + backgroundColor: theme.bubbleColor, + foregroundColor: theme.onBubbleColor, + ), + tooltip: L10n.of(context).sendAudio, + icon: state.isSending + ? const SizedBox.square( + dimension: 24, + child: CircularProgressIndicator.adaptive(), + ) + : const Icon(Icons.send_outlined), + onPressed: state.isSending ? null : () => state.stopAndSend(onSend), + ), + ], + ); + } +} diff --git a/lib/pages/chat/recording_view_model.dart b/lib/pages/chat/recording_view_model.dart new file mode 100644 index 000000000..f46274281 --- /dev/null +++ b/lib/pages/chat/recording_view_model.dart @@ -0,0 +1,256 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:matrix/matrix.dart'; +import 'package:path/path.dart' as path_lib; +import 'package:path_provider/path_provider.dart'; +import 'package:record/record.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + +import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; +import 'events/audio_player.dart'; + +// #Pangea +class PermissionException implements Exception {} + +class EmptyAudioException implements Exception {} +// Pangea# + +class RecordingViewModel extends StatefulWidget { + final Widget Function(BuildContext, RecordingViewModelState) builder; + + const RecordingViewModel({required this.builder, super.key}); + + @override + RecordingViewModelState createState() => RecordingViewModelState(); +} + +class RecordingViewModelState extends State { + Timer? _recorderSubscription; + Duration duration = Duration.zero; + + bool isSending = false; + + bool get isRecording => _audioRecorder != null; + + AudioRecorder? _audioRecorder; + final List amplitudeTimeline = []; + + String? fileName; + + bool isPaused = false; + + Future startRecording(Room room) async { + room.client.getConfig(); // Preload server file configuration. + if (PlatformInfos.isAndroid) { + final info = await DeviceInfoPlugin().androidInfo; + if (info.version.sdkInt < 19) { + showOkAlertDialog( + context: context, + title: L10n.of(context).unsupportedAndroidVersion, + message: L10n.of(context).unsupportedAndroidVersionLong, + okLabel: L10n.of(context).close, + ); + return; + } + } + if (await AudioRecorder().hasPermission() == false) return; + + final audioRecorder = _audioRecorder ??= AudioRecorder(); + setState(() {}); + + try { + // #Pangea + // final codec = kIsWeb + // // Web seems to create webm instead of ogg when using opus encoder + // // which does not play on iOS right now. So we use wav for now: + // ? AudioEncoder.wav + // // Everywhere else we use opus if supported by the platform: + // : !PlatformInfos + // .isIOS && // Blocked by https://github.com/llfbandit/record/issues/560 + // await audioRecorder.isEncoderSupported(AudioEncoder.opus) + // ? AudioEncoder.opus + // : AudioEncoder.aacLc; + const codec = AudioEncoder.wav; + // Pangea# + fileName = + 'recording${DateTime.now().microsecondsSinceEpoch}.${codec.fileExtension}'; + String? path; + if (!kIsWeb) { + final tempDir = await getTemporaryDirectory(); + path = path_lib.join(tempDir.path, fileName); + } + + final result = await audioRecorder.hasPermission(); + if (result != true) { + showOkAlertDialog( + context: context, + title: L10n.of(context).oopsSomethingWentWrong, + message: L10n.of(context).noPermission, + ); + return; + } + await WakelockPlus.enable(); + + await audioRecorder.start( + RecordConfig( + bitRate: AppSettings.audioRecordingBitRate.value, + sampleRate: AppSettings.audioRecordingSamplingRate.value, + numChannels: AppSettings.audioRecordingNumChannels.value, + autoGain: AppSettings.audioRecordingAutoGain.value, + echoCancel: AppSettings.audioRecordingEchoCancel.value, + noiseSuppress: AppSettings.audioRecordingNoiseSuppress.value, + encoder: codec, + ), + path: path ?? '', + ); + setState(() => duration = Duration.zero); + _subscribe(); + } catch (e, s) { + Logs().w('Unable to start voice message recording', e, s); + showOkAlertDialog( + context: context, + title: L10n.of(context).oopsSomethingWentWrong, + message: e.toString(), + ); + setState(_reset); + } + } + + @override + void dispose() { + _reset(); + super.dispose(); + } + + void _subscribe() { + _recorderSubscription?.cancel(); + _recorderSubscription = Timer.periodic(const Duration(milliseconds: 100), ( + _, + ) async { + final amplitude = await _audioRecorder!.getAmplitude(); + var value = 100 + amplitude.current * 2; + value = value < 1 ? 1 : value; + amplitudeTimeline.add(value); + setState(() { + duration += const Duration(milliseconds: 100); + }); + }); + } + + void _reset() { + WakelockPlus.disable(); + _recorderSubscription?.cancel(); + _audioRecorder?.stop(); + _audioRecorder = null; + isSending = false; + fileName = null; + duration = Duration.zero; + amplitudeTimeline.clear(); + isPaused = false; + } + + void cancel() { + setState(() { + _reset(); + }); + } + + void pause() { + _audioRecorder?.pause(); + _recorderSubscription?.cancel(); + setState(() { + isPaused = true; + }); + } + + void resume() { + _audioRecorder?.resume(); + _subscribe(); + setState(() { + isPaused = false; + }); + } + + void stopAndSend( + Future Function( + String path, + int duration, + List waveform, + String? fileName, + ) + onSend, + ) async { + _recorderSubscription?.cancel(); + final path = await _audioRecorder?.stop(); + + if (path == null) throw ('Recording failed!'); + const waveCount = AudioPlayerWidget.wavesCount; + final step = amplitudeTimeline.length < waveCount + ? 1 + : (amplitudeTimeline.length / waveCount).round(); + final waveform = []; + for (var i = 0; i < amplitudeTimeline.length; i += step) { + waveform.add((amplitudeTimeline[i] / 100 * 1024).round()); + } + + // #Pangea + if (amplitudeTimeline.isEmpty || amplitudeTimeline.every((e) => e <= 1)) { + if (mounted) { + showOkAlertDialog( + context: context, + title: L10n.of(context).oopsSomethingWentWrong, + message: EmptyAudioException().toLocalizedString(context), + ); + } + return; + } + // Pangea# + + setState(() { + isSending = true; + }); + try { + await onSend(path, duration.inMilliseconds, waveform, fileName); + } catch (e, s) { + Logs().e('Unable to send voice message', e, s); + setState(() { + isSending = false; + }); + return; + } + + cancel(); + } + + @override + Widget build(BuildContext context) => widget.builder(context, this); +} + +extension on AudioEncoder { + String get fileExtension { + switch (this) { + case AudioEncoder.aacLc: + case AudioEncoder.aacEld: + case AudioEncoder.aacHe: + return 'm4a'; + case AudioEncoder.opus: + return 'ogg'; + case AudioEncoder.wav: + return 'wav'; + case AudioEncoder.amrNb: + case AudioEncoder.amrWb: + case AudioEncoder.flac: + case AudioEncoder.pcm16bits: + throw UnsupportedError('Not yet used'); + } + } +} diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index 8d2df4b2d..57ab64ba8 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -18,9 +18,11 @@ class ReplyDisplay extends StatelessWidget { // #Pangea return ListenableBuilder( - listenable: - Listenable.merge([controller.replyEvent, controller.editEvent]), - builder: (context, __) { + listenable: Listenable.merge([ + controller.replyEvent, + controller.editEvent, + ]), + builder: (context, _) { final editEvent = controller.editEvent.value; final replyEvent = controller.replyEvent.value; // Pangea# @@ -34,9 +36,7 @@ class ReplyDisplay extends StatelessWidget { ? 56 : 0, clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: theme.colorScheme.onInverseSurface, - ), + decoration: BoxDecoration(color: theme.colorScheme.onInverseSurface), child: Row( children: [ IconButton( @@ -85,10 +85,7 @@ class _EditContent extends StatelessWidget { } return Row( children: [ - Icon( - Icons.edit, - color: theme.colorScheme.primary, - ), + Icon(Icons.edit, color: theme.colorScheme.primary), Container(width: 15.0), // #Pangea // Text( @@ -99,9 +96,7 @@ class _EditContent extends StatelessWidget { // ), // overflow: TextOverflow.ellipsis, // maxLines: 1, - // style: TextStyle( - // color: theme.textTheme.bodyMedium!.color, - // ), + // style: TextStyle(color: theme.textTheme.bodyMedium!.color), // ), Flexible( child: Text( @@ -113,9 +108,7 @@ class _EditContent extends StatelessWidget { ), overflow: TextOverflow.ellipsis, maxLines: 1, - style: TextStyle( - color: theme.textTheme.bodyMedium!.color, - ), + style: TextStyle(color: theme.textTheme.bodyMedium!.color), ), ), // Pangea# diff --git a/lib/pages/chat/seen_by_row.dart b/lib/pages/chat/seen_by_row.dart index 52d9f0eb4..3b830b1d6 100644 --- a/lib/pages/chat/seen_by_row.dart +++ b/lib/pages/chat/seen_by_row.dart @@ -20,14 +20,16 @@ class SeenByRow extends StatelessWidget { width: double.infinity, alignment: Alignment.center, child: AnimatedContainer( - constraints: - const BoxConstraints(maxWidth: FluffyThemes.maxTimelineWidth), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.maxTimelineWidth, + ), height: seenByUsers.isEmpty ? 0 : 24, duration: seenByUsers.isEmpty ? Duration.zero : FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, - alignment: controller.timeline!.events.isNotEmpty && + alignment: + controller.timeline!.events.isNotEmpty && controller.timeline!.events.first.senderId == Matrix.of(context).client.userID ? Alignment.topRight @@ -40,15 +42,15 @@ class SeenByRow extends StatelessWidget { ? seenByUsers.sublist(0, maxAvatars) : seenByUsers) .map( - (user) => Avatar( - mxContent: user.avatarUrl, - name: user.calcDisplayname(), - // #Pangea - userId: user.id, - // Pangea# - size: 16, - ), - ), + (user) => Avatar( + mxContent: user.avatarUrl, + name: user.calcDisplayname(), + size: 16, + // #Pangea + userId: user.id, + // Pangea# + ), + ), if (seenByUsers.length > maxAvatars) SizedBox( width: 16, diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index 7b215496e..57f124b2c 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -20,11 +20,14 @@ class SendFileDialog extends StatefulWidget { final Room room; final List files; final BuildContext outerContext; + final String? threadLastEventId, threadRootEventId; const SendFileDialog({ required this.room, required this.files, required this.outerContext, + required this.threadLastEventId, + required this.threadRootEventId, super.key, }); @@ -98,8 +101,6 @@ class SendFileDialogState extends State { widget.files.length, ), ); - } else { - scaffoldMessenger.clearSnackBars(); } final label = _labelTextController.text.trim(); @@ -110,14 +111,17 @@ class SendFileDialogState extends State { thumbnail: thumbnail, shrinkImageMaxDimension: compress ? 1600 : null, extraContent: label.isEmpty ? null : {'body': label}, + threadRootEventId: widget.threadRootEventId, + threadLastEventId: widget.threadLastEventId, ); } on MatrixException catch (e) { final retryAfterMs = e.retryAfterMs; if (e.error != MatrixError.M_LIMIT_EXCEEDED || retryAfterMs == null) { rethrow; } - final retryAfterDuration = - Duration(milliseconds: retryAfterMs + 1000); + final retryAfterDuration = Duration( + milliseconds: retryAfterMs + 1000, + ); scaffoldMessenger.showSnackBar( SnackBar( @@ -161,8 +165,9 @@ class SendFileDialogState extends State { } Future _calcCombinedFileSize() async { - final lengths = - await Future.wait(widget.files.map((file) => file.length())); + final lengths = await Future.wait( + widget.files.map((file) => file.length()), + ); return lengths.fold(0, (p, length) => p + length).sizeString; } @@ -213,7 +218,7 @@ class SendFileDialogState extends State { width: 256, child: SingleChildScrollView( child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const SizedBox(height: 12), if (uniqueFileType == 'image') @@ -240,8 +245,8 @@ class SendFileDialogState extends State { final bytes = snapshot.data; if (bytes == null) { return const Center( - child: CircularProgressIndicator - .adaptive(), + child: + CircularProgressIndicator.adaptive(), ); } if (snapshot.error != null) { @@ -269,8 +274,11 @@ class SendFileDialogState extends State { : null, fit: BoxFit.contain, errorBuilder: (context, e, s) { - Logs() - .w('Unable to preview image', e, s); + Logs().w( + 'Unable to preview image', + e, + s, + ); return const Center( child: SizedBox( width: 256, @@ -300,17 +308,17 @@ class SendFileDialogState extends State { uniqueFileType == null ? Icons.description_outlined : uniqueFileType == 'video' - ? Icons.video_file_outlined - : uniqueFileType == 'audio' - ? Icons.audio_file_outlined - : Icons.description_outlined, + ? Icons.video_file_outlined + : uniqueFileType == 'audio' + ? Icons.audio_file_outlined + : Icons.description_outlined, size: 32, ), const SizedBox(width: 8), Expanded( child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: .min, + crossAxisAlignment: .start, children: [ Text( fileName, @@ -344,10 +352,12 @@ class SendFileDialogState extends State { // Workaround for SwitchListTile.adaptive crashes in CupertinoDialog if ({'image', 'video'}.contains(uniqueFileType)) Row( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: .center, children: [ - if ({TargetPlatform.iOS, TargetPlatform.macOS} - .contains(theme.platform)) + if ({ + TargetPlatform.iOS, + TargetPlatform.macOS, + }.contains(theme.platform)) CupertinoSwitch( value: compressionSupported && compress, onChanged: compressionSupported @@ -364,11 +374,11 @@ class SendFileDialogState extends State { const SizedBox(width: 16), Expanded( child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: .min, + crossAxisAlignment: .start, children: [ Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ Text( L10n.of(context).compress, @@ -427,9 +437,7 @@ extension on ScaffoldMessengerState { const SizedBox( width: 16, height: 16, - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), + child: CircularProgressIndicator.adaptive(strokeWidth: 2), ), const SizedBox(width: 16), Text(title), diff --git a/lib/pages/chat/send_location_dialog.dart b/lib/pages/chat/send_location_dialog.dart index 865b909e6..8efc2cb60 100644 --- a/lib/pages/chat/send_location_dialog.dart +++ b/lib/pages/chat/send_location_dialog.dart @@ -14,10 +14,7 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; class SendLocationDialog extends StatefulWidget { final Room room; - const SendLocationDialog({ - required this.room, - super.key, - }); + const SendLocationDialog({required this.room, super.key}); @override SendLocationDialogState createState() => SendLocationDialogState(); @@ -102,12 +99,13 @@ class SendLocationDialogState extends State { } else if (denied) { contentWidget = Text(L10n.of(context).locationPermissionDeniedNotice); } else if (error != null) { - contentWidget = - Text(L10n.of(context).errorObtainingLocation(error.toString())); + contentWidget = Text( + L10n.of(context).errorObtainingLocation(error.toString()), + ); } else { contentWidget = Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: .min, + mainAxisAlignment: .center, children: [ const CupertinoActivityIndicator(), const SizedBox(width: 12), diff --git a/lib/pages/chat/start_poll_bottom_sheet.dart b/lib/pages/chat/start_poll_bottom_sheet.dart new file mode 100644 index 000000000..b90dc473b --- /dev/null +++ b/lib/pages/chat/start_poll_bottom_sheet.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; + +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; + +class StartPollBottomSheet extends StatefulWidget { + final Room room; + const StartPollBottomSheet({required this.room, super.key}); + + @override + State createState() => _StartPollBottomSheetState(); +} + +class _StartPollBottomSheetState extends State { + final TextEditingController _bodyController = TextEditingController(); + bool _allowMultipleAnswers = false; + final List _answers = [ + TextEditingController(), + TextEditingController(), + ]; + PollKind _pollKind = PollKind.disclosed; + + bool _canCreate = false; + + bool isLoading = false; + + String? _txid; + + void _createPoll() async { + try { + var id = 0; + _txid ??= widget.room.client.generateUniqueTransactionId(); + await widget.room.startPoll( + question: _bodyController.text.trim(), + answers: _answers + .map( + (answerController) => PollAnswer( + id: (++id).toString(), + mText: answerController.text.trim(), + ), + ) + .toList(), + kind: _pollKind, + maxSelections: _allowMultipleAnswers ? _answers.length : 1, + txid: _txid, + ); + Navigator.of(context).pop(); + } catch (e, s) { + Logs().w('Unable to create poll', e, s); + if (!mounted) return; + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); + } + } + + void _updateCanCreate([dynamic _]) { + final newCanCreate = + _bodyController.text.trim().isNotEmpty && + !_answers.any((controller) => controller.text.trim().isEmpty); + if (_canCreate != newCanCreate) { + setState(() { + _canCreate = newCanCreate; + }); + } + } + + @override + Widget build(BuildContext context) { + const maxAnswers = 10; + return Scaffold( + appBar: AppBar( + leading: CloseButton(onPressed: Navigator.of(context).pop), + title: Text(L10n.of(context).startPoll), + ), + body: ListView( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + children: [ + TextField( + controller: _bodyController, + minLines: 2, + maxLines: 4, + maxLength: 1024, + onChanged: _updateCanCreate, + decoration: InputDecoration( + hintText: L10n.of(context).pollQuestion, + counter: const SizedBox.shrink(), + ), + ), + const Divider(height: 32), + ..._answers.map( + (answerController) => Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: TextField( + controller: answerController, + onChanged: _updateCanCreate, + maxLength: 64, + decoration: InputDecoration( + counter: const SizedBox.shrink(), + hintText: L10n.of(context).answerOption, + suffixIcon: _answers.length == 2 + ? null + : IconButton( + icon: const Icon(Icons.cancel_outlined), + onPressed: () => setState(() { + _answers.remove(answerController..dispose()); + }), + ), + ), + ), + ), + ), + Align( + alignment: Alignment.centerLeft, + child: TextButton.icon( + icon: const Icon(Icons.add_outlined), + onPressed: _answers.length < maxAnswers + ? () => setState(() { + _answers.add(TextEditingController()); + }) + : null, + label: Text(L10n.of(context).addAnswerOption), + ), + ), + const Divider(height: 32), + ListTile( + contentPadding: EdgeInsets.zero, + leading: Switch.adaptive( + value: _pollKind == PollKind.disclosed, + onChanged: (allow) => setState(() { + _pollKind = allow ? PollKind.disclosed : PollKind.undisclosed; + }), + ), + title: Text(L10n.of(context).answersVisible), + ), + ListTile( + contentPadding: EdgeInsets.zero, + leading: Switch.adaptive( + value: _allowMultipleAnswers, + onChanged: (allow) => setState(() { + _allowMultipleAnswers = allow; + }), + ), + title: Text(L10n.of(context).allowMultipleAnswers), + ), + ElevatedButton( + onPressed: !isLoading && _canCreate ? _createPoll : null, + child: isLoading + ? const LinearProgressIndicator() + : Text(L10n.of(context).startPoll), + ), + ], + ), + ); + } +} diff --git a/lib/pages/chat/sticker_picker_dialog.dart b/lib/pages/chat/sticker_picker_dialog.dart index e8e135732..1e287651f 100644 --- a/lib/pages/chat/sticker_picker_dialog.dart +++ b/lib/pages/chat/sticker_picker_dialog.dart @@ -37,15 +37,17 @@ class StickerPickerDialogState extends State { final filteredImagePackImageEntried = pack.images.entries.toList(); if (searchFilter?.isNotEmpty ?? false) { filteredImagePackImageEntried.removeWhere( - (e) => !(e.key.toLowerCase().contains(searchFilter!.toLowerCase()) || - (e.value.body - ?.toLowerCase() - .contains(searchFilter!.toLowerCase()) ?? - false)), + (e) => + !(e.key.toLowerCase().contains(searchFilter!.toLowerCase()) || + (e.value.body?.toLowerCase().contains( + searchFilter!.toLowerCase(), + ) ?? + false)), ); } - final imageKeys = - filteredImagePackImageEntried.map((e) => e.key).toList(); + final imageKeys = filteredImagePackImageEntried + .map((e) => e.key) + .toList(); if (imageKeys.isEmpty) { return const SizedBox.shrink(); } @@ -66,32 +68,38 @@ class StickerPickerDialogState extends State { GridView.builder( itemCount: imageKeys.length, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 128, + maxCrossAxisExtent: 84, + mainAxisSpacing: 8.0, + crossAxisSpacing: 8.0, ), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (BuildContext context, int imageIndex) { final image = pack.images[imageKeys[imageIndex]]!; - return InkWell( - radius: AppConfig.borderRadius, - key: ValueKey(image.url.toString()), - onTap: () { - // copy the image - final imageCopy = - ImagePackImageContent.fromJson(image.toJson().copy()); - // set the body, if it doesn't exist, to the key - imageCopy.body ??= imageKeys[imageIndex]; - widget.onSelected(imageCopy); - }, - child: AbsorbPointer( - absorbing: true, - child: MxcImage( - uri: image.url, - fit: BoxFit.contain, - width: 128, - height: 128, - animated: true, - isThumbnail: false, + return Tooltip( + message: image.body ?? imageKeys[imageIndex], + child: InkWell( + radius: AppConfig.borderRadius, + key: ValueKey(image.url.toString()), + onTap: () { + // copy the image + final imageCopy = ImagePackImageContent.fromJson( + image.toJson().copy(), + ); + // set the body, if it doesn't exist, to the key + imageCopy.body ??= imageKeys[imageIndex]; + widget.onSelected(imageCopy); + }, + child: AbsorbPointer( + absorbing: true, + child: MxcImage( + uri: image.url, + fit: BoxFit.contain, + width: 128, + height: 128, + animated: true, + isThumbnail: false, + ), ), ), ); @@ -110,6 +118,7 @@ class StickerPickerDialogState extends State { SliverAppBar( floating: true, pinned: true, + scrolledUnderElevation: 0, automaticallyImplyLeading: false, backgroundColor: Colors.transparent, title: SizedBox( @@ -117,6 +126,7 @@ class StickerPickerDialogState extends State { child: TextField( autofocus: false, decoration: InputDecoration( + filled: true, hintText: L10n.of(context).search, prefixIcon: const Icon(Icons.search_outlined), contentPadding: EdgeInsets.zero, @@ -129,7 +139,7 @@ class StickerPickerDialogState extends State { SliverFillRemaining( child: Center( child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ Text(L10n.of(context).noEmotesFound), // #Pangea @@ -137,7 +147,7 @@ class StickerPickerDialogState extends State { // OutlinedButton.icon( // onPressed: () => UrlLauncher( // context, - // 'https://matrix.to/#/#fluffychat-stickers:janian.de', + // AppConfig.howDoIGetStickersTutorial, // ).launchUrl(), // icon: const Icon(Icons.explore_outlined), // label: Text(L10n.of(context).discover), diff --git a/lib/pages/chat/typing_indicators.dart b/lib/pages/chat/typing_indicators.dart index 640734084..efcdba3b6 100644 --- a/lib/pages/chat/typing_indicators.dart +++ b/lib/pages/chat/typing_indicators.dart @@ -21,8 +21,9 @@ class TypingIndicators extends StatelessWidget { return StreamBuilder( stream: controller.room.client.onSync.stream.where( (syncUpdate) => - syncUpdate.rooms?.join?[controller.room.id]?.ephemeral - ?.any((ephemeral) => ephemeral.type == 'm.typing') ?? + syncUpdate.rooms?.join?[controller.room.id]?.ephemeral?.any( + (ephemeral) => ephemeral.type == 'm.typing', + ) ?? false, ), builder: (context, _) { @@ -33,22 +34,21 @@ class TypingIndicators extends StatelessWidget { width: double.infinity, alignment: Alignment.center, child: AnimatedContainer( - constraints: - const BoxConstraints(maxWidth: FluffyThemes.maxTimelineWidth), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.maxTimelineWidth, + ), height: typingUsers.isEmpty ? 0 : avatarSize + 8, duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, - alignment: controller.timeline!.events.isNotEmpty && + alignment: + controller.timeline!.events.isNotEmpty && controller.timeline!.events.first.senderId == Matrix.of(context).client.userID ? Alignment.topRight : Alignment.topLeft, clipBehavior: Clip.hardEdge, decoration: const BoxDecoration(), - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), child: Row( children: [ Container( @@ -123,17 +123,14 @@ class __TypingDotsState extends State<_TypingDots> { @override void initState() { - _timer = Timer.periodic( - animationDuration, - (_) { - if (!mounted) { - return; - } - setState(() { - _tick = (_tick + 1) % 4; - }); - }, - ); + _timer = Timer.periodic(animationDuration, (_) { + if (!mounted) { + return; + } + setState(() { + _tick = (_tick + 1) % 4; + }); + }); super.initState(); } @@ -149,7 +146,7 @@ class __TypingDotsState extends State<_TypingDots> { const size = 8.0; return Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ for (var i = 1; i <= 3; i++) AnimatedContainer( diff --git a/lib/pages/chat_access_settings/chat_access_settings_controller.dart b/lib/pages/chat_access_settings/chat_access_settings_controller.dart index 9c3a48b86..8b84b8397 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_controller.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_controller.dart @@ -27,6 +27,16 @@ class ChatAccessSettingsController extends State { bool historyVisibilityLoading = false; bool guestAccessLoading = false; Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!; + Set get knownSpaceParents => { + ...room.client.rooms.where( + (space) => + space.isSpace && + space.spaceChildren.any((child) => child.roomId == room.id), + ), + ...room.spaceParents + .map((parent) => room.client.getRoomById(parent.roomId ?? '')) + .whereType(), + }; String get roomVersion => room @@ -47,9 +57,20 @@ class ChatAccessSettingsController extends State { joinRules.remove(JoinRules.knock); } - // Not yet supported in FluffyChat: - joinRules.remove(JoinRules.restricted); - joinRules.remove(JoinRules.knockRestricted); + // Restricted is only supported for rooms up from version 8: + if (roomVersionInt != null && roomVersionInt <= 7) { + joinRules.remove(JoinRules.restricted); + } + + // Knock-Restricted is only supported for rooms up from version 10: + if (roomVersionInt != null && roomVersionInt <= 9) { + joinRules.remove(JoinRules.knockRestricted); + } + + if (knownSpaceParents.isEmpty) { + joinRules.remove(JoinRules.restricted); + joinRules.remove(JoinRules.knockRestricted); + } // If an unsupported join rule is the current join rule, display it: final currentJoinRule = room.joinRules; @@ -69,7 +90,16 @@ class ChatAccessSettingsController extends State { try { // #Pangea - // await room.setJoinRules(newJoinRules); + // await room.setJoinRules( + // newJoinRules, + // allowConditionRoomIds: + // { + // JoinRules.restricted, + // JoinRules.knockRestricted, + // }.contains(newJoinRules) + // ? knownSpaceParents.map((parent) => parent.id).toList() + // : null, + // ); await room.pangeaSetJoinRules( newJoinRules.toString().replaceAll('JoinRules.', ''), ); @@ -77,13 +107,9 @@ class ChatAccessSettingsController extends State { } catch (e, s) { Logs().w('Unable to change join rules', e, s); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - e.toLocalizedString(context), - ), - ), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } } finally { if (mounted) { @@ -105,13 +131,9 @@ class ChatAccessSettingsController extends State { } catch (e, s) { Logs().w('Unable to change history visibility', e, s); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - e.toLocalizedString(context), - ), - ), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } } finally { if (mounted) { @@ -133,13 +155,9 @@ class ChatAccessSettingsController extends State { } catch (e, s) { Logs().w('Unable to change guest access', e, s); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - e.toLocalizedString(context), - ), - ), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } } finally { if (mounted) { @@ -179,7 +197,6 @@ class ChatAccessSettingsController extends State { if (newVersion == null || OkCancelResult.cancel == await showOkCancelAlertDialog( - useRootNavigator: false, context: context, okLabel: L10n.of(context).yes, cancelLabel: L10n.of(context).cancel, @@ -191,7 +208,43 @@ class ChatAccessSettingsController extends State { } final result = await showFutureLoadingDialog( context: context, - future: () => room.client.upgradeRoom(room.id, newVersion), + futureWithProgress: (onProgress) async { + final newRoomId = await room.client.upgradeRoom(room.id, newVersion); + var newRoom = room.client.getRoomById(newRoomId); + while (newRoom == null) { + await room.client.onSync.stream.first; + newRoom = room.client.getRoomById(newRoomId); + } + + if ({ + JoinRules.invite, + JoinRules.knock, + JoinRules.knockRestricted, + }.contains(room.joinRules)) { + final users = await room.requestParticipants([ + Membership.join, + Membership.invite, + ]); + users.removeWhere((user) => user.id == room.client.userID); + for (final (i, user) in users.indexed) { + try { + Logs().v('Inviting...', user.id); + await newRoom.invite(user.id); + onProgress(i / users.length); + } on MatrixException catch (e) { + final retryAfterMs = e.retryAfterMs; + if (e.error != MatrixError.M_LIMIT_EXCEEDED || + retryAfterMs == null) { + rethrow; + } + Logs().d('Limit exceeded. Retry after $retryAfterMs'); + await Future.delayed(Duration(milliseconds: retryAfterMs)); + await newRoom.invite(user.id); + onProgress(i / users.length); + } + } + } + }, ); if (result.error != null) return; if (!mounted) return; @@ -232,7 +285,8 @@ class ChatAccessSettingsController extends State { cancelLabel: L10n.of(context).no, ); - final altAliases = room + final altAliases = + room .getState(EventTypes.RoomCanonicalAlias) ?.content .tryGetList('alt_aliases') @@ -248,17 +302,13 @@ class ChatAccessSettingsController extends State { await showFutureLoadingDialog( context: context, - future: () => room.client.setRoomStateWithKey( - room.id, - EventTypes.RoomCanonicalAlias, - '', - { - 'alias': canonicalAliasConsent == OkCancelResult.ok - ? alias - : room.canonicalAlias, - if (altAliases.isNotEmpty) 'alt_aliases': altAliases.toList(), - }, - ), + future: () => room.client + .setRoomStateWithKey(room.id, EventTypes.RoomCanonicalAlias, '', { + 'alias': canonicalAliasConsent == OkCancelResult.ok + ? alias + : room.canonicalAlias, + if (altAliases.isNotEmpty) 'alt_aliases': altAliases.toList(), + }), ); } @@ -285,13 +335,9 @@ class ChatAccessSettingsController extends State { } catch (e, s) { Logs().w('Unable to change visibility', e, s); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - e.toLocalizedString(context), - ), - ), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } } finally { if (mounted) { diff --git a/lib/pages/chat_access_settings/chat_access_settings_page.dart b/lib/pages/chat_access_settings/chat_access_settings_page.dart index 5f679cffb..a2f040799 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_page.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_page.dart @@ -24,17 +24,19 @@ class ChatAccessSettingsPageView extends StatelessWidget { ), body: MaxWidthBody( child: StreamBuilder( - stream: room.client.onRoomState.stream - .where((update) => update.roomId == controller.room.id), + stream: room.client.onRoomState.stream.where( + (update) => update.roomId == controller.room.id, + ), builder: (context, snapshot) { final canonicalAlias = room.canonicalAlias; - final altAliases = room + final altAliases = + room .getState(EventTypes.RoomCanonicalAlias) ?.content .tryGetList('alt_aliases') ?? []; return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ ListTile( title: Text( @@ -47,12 +49,13 @@ class ChatAccessSettingsPageView extends StatelessWidget { ), RadioGroup( groupValue: room.historyVisibility, - onChanged: controller.historyVisibilityLoading || + onChanged: + controller.historyVisibilityLoading || !room.canChangeHistoryVisibility ? (_) {} : controller.setHistoryVisibility, child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ for (final historyVisibility in HistoryVisibility.values) RadioListTile.adaptive( @@ -80,15 +83,19 @@ class ChatAccessSettingsPageView extends StatelessWidget { groupValue: room.joinRules, onChanged: controller.setJoinRule, child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ for (final joinRule in controller.availableJoinRules) if (joinRule != JoinRules.private) RadioListTile.adaptive( - enabled: !controller.joinRulesLoading && + enabled: + !controller.joinRulesLoading && room.canChangeJoinRules, title: Text( - joinRule.localizedString(L10n.of(context)), + joinRule.localizedString( + L10n.of(context), + controller.knownSpaceParents, + ), ), value: joinRule, ), @@ -96,8 +103,10 @@ class ChatAccessSettingsPageView extends StatelessWidget { ), ), Divider(color: theme.dividerColor), - if ({JoinRules.public, JoinRules.knock} - .contains(room.joinRules)) ...[ + if ({ + JoinRules.public, + JoinRules.knock, + }.contains(room.joinRules)) ...[ ListTile( title: Text( L10n.of(context).areGuestsAllowedToJoin, @@ -111,11 +120,12 @@ class ChatAccessSettingsPageView extends StatelessWidget { groupValue: room.guestAccess, onChanged: controller.setGuestAccess, child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ for (final guestAccess in GuestAccess.values) RadioListTile.adaptive( - enabled: !controller.guestAccessLoading && + enabled: + !controller.guestAccessLoading && room.canChangeGuestAccess, title: Text( guestAccess.getLocalizedString( @@ -145,9 +155,10 @@ class ChatAccessSettingsPageView extends StatelessWidget { if (canonicalAlias.isNotEmpty) _AliasListTile( alias: canonicalAlias, - onDelete: room.canChangeStateEvent( - EventTypes.RoomCanonicalAlias, - ) + onDelete: + room.canChangeStateEvent( + EventTypes.RoomCanonicalAlias, + ) ? () => controller.deleteAlias(canonicalAlias) : null, isCanonicalAlias: true, @@ -155,9 +166,10 @@ class ChatAccessSettingsPageView extends StatelessWidget { for (final alias in altAliases) _AliasListTile( alias: alias, - onDelete: room.canChangeStateEvent( - EventTypes.RoomCanonicalAlias, - ) + onDelete: + room.canChangeStateEvent( + EventTypes.RoomCanonicalAlias, + ) ? () => controller.deleteAlias(alias) : null, ), @@ -169,10 +181,11 @@ class ChatAccessSettingsPageView extends StatelessWidget { return const SizedBox.shrink(); } localAddresses.remove(room.canonicalAlias); - localAddresses - .removeWhere((alias) => altAliases.contains(alias)); + localAddresses.removeWhere( + (alias) => altAliases.contains(alias), + ); return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: localAddresses .map( (alias) => _AliasListTile( @@ -254,10 +267,7 @@ class _AliasListTile extends StatelessWidget { ? const Icon(Icons.star) : const Icon(Icons.link_outlined), title: InkWell( - onTap: () => FluffyShare.share( - 'https://matrix.to/#/$alias', - context, - ), + onTap: () => FluffyShare.share('https://matrix.to/#/$alias', context), child: SelectableText( alias, style: TextStyle( @@ -280,7 +290,7 @@ class _AliasListTile extends StatelessWidget { } extension JoinRulesDisplayString on JoinRules { - String localizedString(L10n l10n) { + String localizedString(L10n l10n, Set spaceParents) { switch (this) { case JoinRules.public: return l10n.anyoneCanJoin; @@ -291,9 +301,17 @@ extension JoinRulesDisplayString on JoinRules { case JoinRules.private: return l10n.noOneCanJoin; case JoinRules.restricted: - return l10n.restricted; + return l10n.spaceMemberOf( + spaceParents + .map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n))) + .join(', '), + ); case JoinRules.knockRestricted: - return l10n.knockRestricted; + return l10n.spaceMemberOfCanKnock( + spaceParents + .map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n))) + .join(', '), + ); } } } diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index b1cac71cf..46f59266b 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; -import 'package:go_router/go_router.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; @@ -67,12 +67,15 @@ class ChatDetailsController extends State _loadSummaries(); _languageSubscription = MatrixState - .pangeaController.userController.languageStream.stream + .pangeaController + .userController + .languageStream + .stream .listen((update) { - if (update.prevBaseLang != update.baseLang) { - _loadCourseInfo(); - } - }); + if (update.prevBaseLang != update.baseLang) { + _loadCourseInfo(); + } + }); } @override @@ -116,11 +119,7 @@ class ChatDetailsController extends State // Pangea# okLabel: L10n.of(context).ok, cancelLabel: L10n.of(context).cancel, - initialText: room.getLocalizedDisplayname( - MatrixLocals( - L10n.of(context), - ), - ), + initialText: room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))), ); if (input == null) return; final success = await showFutureLoadingDialog( @@ -154,32 +153,16 @@ class ChatDetailsController extends State ); // final success = await showFutureLoadingDialog( // context: context, - // future: () => room.setDescription(input.single), + // future: () => room.setDescription(input), // ); // if (success.error == null) { // ScaffoldMessenger.of(context).showSnackBar( - // SnackBar( - // content: Text(L10n.of(context).chatDescriptionHasBeenChanged), - // ), + // SnackBar(content: Text(L10n.of(context).chatDescriptionHasBeenChanged)), // ); // } // Pangea# } - void goToEmoteSettings() async { - final room = Matrix.of(context).client.getRoomById(roomId!)!; - // okay, we need to test if there are any emote state events other than the default one - // if so, we need to be directed to a selection screen for which pack we want to look at - // otherwise, we just open the normal one. - if ((room.states['im.ponies.room_emotes'] ?? {}) - .keys - .any((String s) => s.isNotEmpty)) { - context.push('/rooms/${room.id}/details/multiple_emotes'); - } else { - context.push('/rooms/${room.id}/details/emotes'); - } - } - void setAvatarAction() async { final room = Matrix.of(context).client.getRoomById(roomId!); final actions = [ @@ -228,15 +211,12 @@ class ChatDetailsController extends State imageQuality: 50, ); if (result == null) return; - file = MatrixFile( - bytes: await result.readAsBytes(), - name: result.path, - ); + file = MatrixFile(bytes: await result.readAsBytes(), name: result.path); } else { final picked = await selectFiles( context, allowMultiple: false, - type: FileSelectorType.images, + type: FileType.image, ); final pickedFile = picked.firstOrNull; if (pickedFile == null) return; @@ -273,8 +253,9 @@ class ChatDetailsController extends State return L10n.of(context).enterNumber; } if (int.parse(value) < (room.summary.mJoinedMemberCount ?? 1)) { - return L10n.of(context) - .chatCapacitySetTooLow(room.summary.mJoinedMemberCount ?? 1); + return L10n.of( + context, + ).chatCapacitySetTooLow(room.summary.mJoinedMemberCount ?? 1); } return null; }, @@ -290,9 +271,7 @@ class ChatDetailsController extends State ); if (success.error == null) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(L10n.of(context).chatCapacityHasBeenChanged), - ), + SnackBar(content: Text(L10n.of(context).chatCapacityHasBeenChanged)), ); setState(() {}); } @@ -324,26 +303,21 @@ class ChatDetailsController extends State context: context, future: () async { final newRoomId = await Matrix.of(context).client.createGroupChat( - visibility: sdk.Visibility.private, - groupName: names, - initialState: [ - RoomDefaults.defaultPowerLevels( - Matrix.of(context).client.userID!, - ), - await Matrix.of(context).client.pangeaJoinRules( - 'knock_restricted', - allow: roomId != null - ? [ - { - "type": "m.room_membership", - "room_id": roomId, - } - ] - : null, - ), - ], - enableEncryption: false, - ); + visibility: sdk.Visibility.private, + groupName: names, + initialState: [ + RoomDefaults.defaultPowerLevels(Matrix.of(context).client.userID!), + await Matrix.of(context).client.pangeaJoinRules( + 'knock_restricted', + allow: roomId != null + ? [ + {"type": "m.room_membership", "room_id": roomId}, + ] + : null, + ), + ], + enableEncryption: false, + ); final client = Matrix.of(context).client; Room? room = client.getRoomById(newRoomId); if (room == null) { diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 4f15d78e2..ec9bbc210 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -29,9 +29,7 @@ class ChatDetailsView extends StatelessWidget { final room = Matrix.of(context).client.getRoomById(controller.roomId!); if (room == null) { return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).oopsSomethingWentWrong), - ), + appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)), body: Center( child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), ), @@ -42,13 +40,15 @@ class ChatDetailsView extends StatelessWidget { final roomAvatar = room.avatar; return StreamBuilder( - stream: room.client.onRoomState.stream - .where((update) => update.roomId == room.id), + stream: room.client.onRoomState.stream.where( + (update) => update.roomId == room.id, + ), builder: (context, snapshot) { var members = room.getParticipants().toList() ..sort((b, a) => a.powerLevel.compareTo(b.powerLevel)); members = members.take(10).toList(); - final actualMembersCount = (room.summary.mInvitedMemberCount ?? 0) + + final actualMembersCount = + (room.summary.mInvitedMemberCount ?? 0) + (room.summary.mJoinedMemberCount ?? 0); final canRequestMoreMembers = members.length < actualMembersCount; final iconColor = theme.textTheme.bodyLarge!.color; @@ -57,7 +57,8 @@ class ChatDetailsView extends StatelessWidget { ); return Scaffold( appBar: AppBar( - leading: controller.widget.embeddedCloseButton ?? + leading: + controller.widget.embeddedCloseButton ?? const Center(child: BackButton()), elevation: theme.appBarTheme.elevation, actions: [ @@ -65,19 +66,15 @@ class ChatDetailsView extends StatelessWidget { IconButton( tooltip: L10n.of(context).share, icon: const Icon(Icons.qr_code_rounded), - onPressed: () => showQrCodeViewer( - context, - room.canonicalAlias, - ), + onPressed: () => + showQrCodeViewer(context, room.canonicalAlias), ) else if (directChatMatrixID != null) IconButton( tooltip: L10n.of(context).share, icon: const Icon(Icons.qr_code_rounded), - onPressed: () => showQrCodeViewer( - context, - directChatMatrixID, - ), + onPressed: () => + showQrCodeViewer(context, directChatMatrixID), ), if (controller.widget.embeddedCloseButton == null) ChatSettingsPopupMenu(room, false), @@ -92,7 +89,7 @@ class ChatDetailsView extends StatelessWidget { itemCount: members.length + 1 + (canRequestMoreMembers ? 1 : 0), itemBuilder: (BuildContext context, int i) => i == 0 ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: .stretch, children: [ Row( children: [ @@ -103,9 +100,9 @@ class ChatDetailsView extends StatelessWidget { Hero( tag: controller.widget.embeddedCloseButton != - null - ? 'embedded_content_banner' - : 'content_banner', + null + ? 'embedded_content_banner' + : 'content_banner', child: Avatar( mxContent: room.avatar, name: displayname, @@ -115,10 +112,10 @@ class ChatDetailsView extends StatelessWidget { size: Avatar.defaultSize * 2.5, onTap: roomAvatar != null ? () => showDialog( - context: context, - builder: (_) => - MxcImageViewer(roomAvatar), - ) + context: context, + builder: (_) => + MxcImageViewer(roomAvatar), + ) : null, ), ), @@ -142,8 +139,8 @@ class ChatDetailsView extends StatelessWidget { ), Expanded( child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: .center, + crossAxisAlignment: .start, children: [ TextButton.icon( onPressed: () => room.isDirectChat @@ -151,20 +148,20 @@ class ChatDetailsView extends StatelessWidget { : room.canChangeStateEvent( EventTypes.RoomName, ) - ? controller.setDisplaynameAction() - : FluffyShare.share( - displayname, - context, - copyOnly: true, - ), + ? controller.setDisplaynameAction() + : FluffyShare.share( + displayname, + context, + copyOnly: true, + ), icon: Icon( room.isDirectChat ? Icons.chat_bubble_outline : room.canChangeStateEvent( EventTypes.RoomName, ) - ? Icons.edit_outlined - : Icons.copy_outlined, + ? Icons.edit_outlined + : Icons.copy_outlined, size: 16, ), style: TextButton.styleFrom( @@ -197,9 +194,9 @@ class ChatDetailsView extends StatelessWidget { iconColor: theme.colorScheme.secondary, ), label: Text( - L10n.of(context).countParticipants( - actualMembersCount, - ), + L10n.of( + context, + ).countParticipants(actualMembersCount), maxLines: 1, overflow: TextOverflow.ellipsis, // style: const TextStyle(fontSize: 12), @@ -210,8 +207,9 @@ class ChatDetailsView extends StatelessWidget { ), ], ), - Divider(color: theme.dividerColor), - if (!room.canChangeStateEvent(EventTypes.RoomTopic)) + if (room.canChangeStateEvent(EventTypes.RoomTopic) || + room.topic.isNotEmpty) ...[ + Divider(color: theme.dividerColor), ListTile( title: Text( L10n.of(context).chatDescription, @@ -220,107 +218,91 @@ class ChatDetailsView extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - ) - else + trailing: + room.canChangeStateEvent(EventTypes.RoomTopic) + ? IconButton( + onPressed: controller.setTopicAction, + tooltip: L10n.of( + context, + ).setChatDescription, + icon: const Icon(Icons.edit_outlined), + ) + : null, + ), Padding( - padding: const EdgeInsets.all(16.0), - child: TextButton.icon( - onPressed: controller.setTopicAction, - label: Text(L10n.of(context).setChatDescription), - icon: const Icon(Icons.edit_outlined), - style: TextButton.styleFrom( - iconColor: - theme.colorScheme.onSecondaryContainer, - backgroundColor: - theme.colorScheme.secondaryContainer, - foregroundColor: - theme.colorScheme.onSecondaryContainer, + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: SelectableLinkify( + text: room.topic.isEmpty + ? L10n.of(context).noChatDescriptionYet + : room.topic, + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), + options: const LinkifyOptions(humanize: false), + linkStyle: const TextStyle( + color: Colors.blueAccent, + decorationColor: Colors.blueAccent, ), + style: TextStyle( + fontSize: 14, + fontStyle: room.topic.isEmpty + ? FontStyle.italic + : FontStyle.normal, + color: theme.textTheme.bodyMedium!.color, + decorationColor: + theme.textTheme.bodyMedium!.color, + ), + onOpen: (url) => + UrlLauncher(context, url.url).launchUrl(), ), ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: SelectableLinkify( - text: room.topic.isEmpty - ? L10n.of(context).noChatDescriptionYet - : room.topic, - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), - options: const LinkifyOptions(humanize: false), - linkStyle: const TextStyle( - color: Colors.blueAccent, - decorationColor: Colors.blueAccent, - ), - style: TextStyle( - fontSize: 14, - fontStyle: room.topic.isEmpty - ? FontStyle.italic - : FontStyle.normal, - color: theme.textTheme.bodyMedium!.color, - decorationColor: - theme.textTheme.bodyMedium!.color, - ), - onOpen: (url) => - UrlLauncher(context, url.url).launchUrl(), - ), - ), - const SizedBox(height: 16), - Divider(color: theme.dividerColor), - ListTile( - leading: CircleAvatar( - backgroundColor: theme.scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.insert_emoticon_outlined, - ), - ), - title: Text(L10n.of(context).customEmojisAndStickers), - subtitle: Text(L10n.of(context).setCustomEmotes), - onTap: controller.goToEmoteSettings, - trailing: const Icon(Icons.chevron_right_outlined), - ), - if (!room.isDirectChat) + const SizedBox(height: 16), + ], + if (!room.isDirectChat) ...[ + Divider(color: theme.dividerColor), ListTile( leading: CircleAvatar( - backgroundColor: theme.scaffoldBackgroundColor, + backgroundColor: + theme.colorScheme.surfaceContainer, foregroundColor: iconColor, - child: const Icon(Icons.shield_outlined), - ), - title: Text( - L10n.of(context).accessAndVisibility, + child: const Icon( + Icons.admin_panel_settings_outlined, + ), ), + title: Text(L10n.of(context).accessAndVisibility), subtitle: Text( L10n.of(context).accessAndVisibilityDescription, ), - onTap: () => context - .push('/rooms/${room.id}/details/access'), + onTap: () => context.push( + '/rooms/${room.id}/details/access', + ), trailing: const Icon(Icons.chevron_right_outlined), ), - if (!room.isDirectChat) ListTile( title: Text(L10n.of(context).chatPermissions), subtitle: Text( L10n.of(context).whoCanPerformWhichAction, ), leading: CircleAvatar( - backgroundColor: theme.scaffoldBackgroundColor, + backgroundColor: + theme.colorScheme.surfaceContainer, foregroundColor: iconColor, - child: const Icon( - Icons.edit_attributes_outlined, - ), + child: const Icon(Icons.tune_outlined), ), trailing: const Icon(Icons.chevron_right_outlined), - onTap: () => context - .push('/rooms/${room.id}/details/permissions'), + onTap: () => context.push( + '/rooms/${room.id}/details/permissions', + ), ), + ], Divider(color: theme.dividerColor), ListTile( title: Text( - L10n.of(context).countParticipants( - actualMembersCount, - ), + L10n.of( + context, + ).countParticipants(actualMembersCount), style: TextStyle( color: theme.colorScheme.secondary, fontWeight: FontWeight.bold, @@ -344,25 +326,25 @@ class ChatDetailsView extends StatelessWidget { ], ) : i < members.length + 1 - ? ParticipantListItem(members[i - 1]) - : ListTile( - title: Text( - L10n.of(context).loadCountMoreParticipants( - (actualMembersCount - members.length), - ), - ), - leading: CircleAvatar( - backgroundColor: theme.scaffoldBackgroundColor, - child: const Icon( - Icons.group_outlined, - color: Colors.grey, - ), - ), - onTap: () => context.push( - '/rooms/${controller.roomId!}/details/members', - ), - trailing: const Icon(Icons.chevron_right_outlined), + ? ParticipantListItem(members[i - 1]) + : ListTile( + title: Text( + L10n.of(context).loadCountMoreParticipants( + (actualMembersCount - members.length), ), + ), + leading: CircleAvatar( + backgroundColor: theme.scaffoldBackgroundColor, + child: const Icon( + Icons.group_outlined, + color: Colors.grey, + ), + ), + onTap: () => context.push( + '/rooms/${controller.roomId!}/details/members', + ), + trailing: const Icon(Icons.chevron_right_outlined), + ), ), ), ); diff --git a/lib/pages/chat_details/chat_download_provider.dart b/lib/pages/chat_details/chat_download_provider.dart index ce1d9be0a..bda0d1647 100644 --- a/lib/pages/chat_details/chat_download_provider.dart +++ b/lib/pages/chat_details/chat_download_provider.dart @@ -37,11 +37,7 @@ mixin ChatDownloadProvider { await room.download(type, context); } on EmptyChatException { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - L10n.of(context).emptyChatDownloadWarning, - ), - ), + SnackBar(content: Text(L10n.of(context).emptyChatDownloadWarning)), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/pages/chat_details/participant_list_item.dart b/lib/pages/chat_details/participant_list_item.dart index bfc5f1914..71100f202 100644 --- a/lib/pages/chat_details/participant_list_item.dart +++ b/lib/pages/chat_details/participant_list_item.dart @@ -28,8 +28,8 @@ class ParticipantListItem extends StatelessWidget { final permissionBatch = user.powerLevel >= 100 ? L10n.of(context).admin : user.powerLevel >= 50 - ? L10n.of(context).moderator - : ''; + ? L10n.of(context).moderator + : ''; return ListTile( onTap: () => showMemberActionsPopupMenu(context: context, user: user), @@ -43,17 +43,12 @@ class ParticipantListItem extends StatelessWidget { ), if (permissionBatch.isNotEmpty) Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: user.powerLevel >= 100 ? theme.colorScheme.tertiary : theme.colorScheme.tertiaryContainer, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), child: Text( permissionBatch, @@ -67,8 +62,10 @@ class ParticipantListItem extends StatelessWidget { membershipBatch == null ? const SizedBox.shrink() : Container( - padding: - const EdgeInsets.symmetric(vertical: 4, horizontal: 8), + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 8, + ), margin: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( color: theme.colorScheme.secondaryContainer, @@ -87,10 +84,7 @@ class ParticipantListItem extends StatelessWidget { ), // #Pangea subtitle: LevelDisplayName(userId: user.id), - // subtitle: Text( - // user.id, - // maxLines: 1, - // overflow: TextOverflow.ellipsis, + // subtitle: Text(user.id, maxLines: 1, overflow: TextOverflow.ellipsis), // Pangea# leading: Opacity( opacity: user.membership == Membership.join ? 1 : 0.5, diff --git a/lib/pages/chat_encryption_settings/chat_encryption_settings.dart b/lib/pages/chat_encryption_settings/chat_encryption_settings.dart index 818b6ff87..49b1ad560 100644 --- a/lib/pages/chat_encryption_settings/chat_encryption_settings.dart +++ b/lib/pages/chat_encryption_settings/chat_encryption_settings.dart @@ -36,7 +36,7 @@ class ChatEncryptionSettingsController extends State { } } - void enableEncryption(_) async { + void enableEncryption(dynamic _) async { if (room.encrypted) { showOkAlertDialog( context: context, diff --git a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart index f6adffd58..d185852be 100644 --- a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart +++ b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart'; import 'package:fluffychat/utils/beautify_string_extension.dart'; +import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; class ChatEncryptionSettingsView extends StatelessWidget { @@ -41,7 +42,7 @@ class ChatEncryptionSettingsView extends StatelessWidget { ), body: MaxWidthBody( child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ SwitchListTile( secondary: CircleAvatar( @@ -58,7 +59,6 @@ class ChatEncryptionSettingsView extends StatelessWidget { size: 128, color: theme.colorScheme.onInverseSurface, ), - const Divider(), if (room.isDirectChat) Padding( padding: const EdgeInsets.all(16.0), @@ -76,16 +76,14 @@ class ChatEncryptionSettingsView extends StatelessWidget { ListTile( title: Text( L10n.of(context).deviceKeys, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontWeight: FontWeight.bold), ), ), StreamBuilder( - stream: room.client.onRoomState.stream - .where((update) => update.roomId == controller.room.id), - builder: (context, snapshot) => - FutureBuilder>( + stream: room.client.onRoomState.stream.where( + (update) => update.roomId == controller.room.id, + ), + builder: (context, snapshot) => FutureBuilder>( future: room.getUserDeviceKeys(), builder: (BuildContext context, snapshot) { if (snapshot.hasError) { @@ -107,72 +105,74 @@ class ChatEncryptionSettingsView extends StatelessWidget { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: deviceKeys.length, - itemBuilder: (BuildContext context, int i) => - SwitchListTile( - value: !deviceKeys[i].blocked, - activeThumbColor: deviceKeys[i].verified - ? Colors.green - : Colors.orange, - onChanged: (_) => - controller.toggleDeviceKey(deviceKeys[i]), - title: Row( - children: [ - Icon( - deviceKeys[i].verified - ? Icons.verified_outlined - : deviceKeys[i].blocked - ? Icons.block_outlined - : Icons.info_outlined, - color: deviceKeys[i].verified - ? Colors.green - : deviceKeys[i].blocked - ? Colors.red - : Colors.orange, - size: 20, - ), - const SizedBox(width: 4), - Text( - deviceKeys[i].deviceId ?? - L10n.of(context).unknownDevice, - ), - const SizedBox(width: 4), - Flexible( - fit: FlexFit.loose, - child: Material( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - side: BorderSide( - color: theme.colorScheme.primary, - ), - ), - color: theme.colorScheme.primaryContainer, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: Text( - deviceKeys[i].userId, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: theme.colorScheme.primary, - fontSize: 12, - fontStyle: FontStyle.italic, - ), - ), - ), + itemBuilder: (BuildContext context, int i) => Column( + mainAxisSize: .min, + children: [ + if (i == 0 || + deviceKeys[i].userId != + deviceKeys[i - 1].userId) ...[ + const Divider(), + FutureBuilder( + future: room.client.getUserProfile( + deviceKeys[i].userId, ), + builder: (context, snapshot) { + final displayname = + snapshot.data?.displayname ?? + deviceKeys[i].userId.localpart ?? + deviceKeys[i].userId; + return ListTile( + leading: Avatar( + name: displayname, + mxContent: snapshot.data?.avatarUrl, + ), + title: Text(displayname), + subtitle: Text(deviceKeys[i].userId), + ); + }, ), ], - ), - subtitle: Text( - deviceKeys[i].ed25519Key?.beautified ?? - L10n.of(context).unknownEncryptionAlgorithm, - style: TextStyle( - fontFamily: 'RobotoMono', - color: theme.colorScheme.secondary, + SwitchListTile( + value: !deviceKeys[i].blocked, + activeThumbColor: deviceKeys[i].verified + ? Colors.green + : Colors.orange, + onChanged: (_) => + controller.toggleDeviceKey(deviceKeys[i]), + title: Row( + children: [ + Text( + deviceKeys[i].verified + ? L10n.of(context).verified + : deviceKeys[i].blocked + ? L10n.of(context).blocked + : L10n.of(context).unverified, + style: TextStyle( + color: deviceKeys[i].verified + ? Colors.green + : deviceKeys[i].blocked + ? Colors.red + : Colors.orange, + ), + ), + const Text(' | ID: '), + Text( + deviceKeys[i].deviceId ?? + L10n.of(context).unknownDevice, + ), + ], + ), + subtitle: Text( + deviceKeys[i].ed25519Key?.beautified ?? + L10n.of(context).unknownEncryptionAlgorithm, + style: TextStyle( + fontFamily: 'RobotoMono', + color: theme.colorScheme.secondary, + fontSize: 11, + ), + ), ), - ), + ], ), ); }, @@ -184,9 +184,7 @@ class ChatEncryptionSettingsView extends StatelessWidget { child: Center( child: Text( L10n.of(context).encryptionNotEnabled, - style: const TextStyle( - fontStyle: FontStyle.italic, - ), + style: const TextStyle(fontStyle: FontStyle.italic), ), ), ), diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index bd2d0c0a2..7daeefea8 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -11,7 +10,6 @@ import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_list/chat_list_view.dart'; import 'package:fluffychat/pangea/chat_list/utils/app_version_util.dart'; @@ -40,9 +38,6 @@ import '../../config/setting_keys.dart'; import '../../utils/url_launcher.dart'; import '../../widgets/matrix.dart'; -import 'package:fluffychat/utils/tor_stub.dart' - if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; - enum PopupMenuAction { settings, invite, @@ -52,13 +47,7 @@ enum PopupMenuAction { archive, } -enum ActiveFilter { - allChats, - messages, - groups, - unread, - spaces, -} +enum ActiveFilter { allChats, messages, groups, unread, spaces } extension LocalizedActiveFilter on ActiveFilter { String toLocalizedString(BuildContext context) { @@ -83,17 +72,13 @@ extension LocalizedActiveFilter on ActiveFilter { class ChatList extends StatefulWidget { static BuildContext? contextForVoip; final String? activeChat; - // #Pangea - final String? activeSpaceId; - // Pangea# + final String? activeSpace; final bool displayNavigationRail; const ChatList({ super.key, required this.activeChat, - // #Pangea - this.activeSpaceId, - // Pangea# + this.activeSpace, this.displayNavigationRail = false, }); @@ -111,15 +96,10 @@ class ChatListController extends State // StreamSubscription? _intentUriStreamSubscription; // Pangea# - // #Pangea - // ActiveFilter activeFilter = AppConfig.separateChatTypes - // ? ActiveFilter.messages - // : ActiveFilter.allChats; - ActiveFilter activeFilter = ActiveFilter.allChats; - // Pangea# + late ActiveFilter activeFilter; // #Pangea - String? get activeSpaceId => widget.activeSpaceId; + String? get activeSpaceId => widget.activeSpace; // String? _activeSpaceId; // String? get activeSpaceId => _activeSpaceId; @@ -132,8 +112,8 @@ class ChatListController extends State // } // void clearActiveSpace() => setState(() { - // _activeSpaceId = null; - // }); + // _activeSpaceId = null; + // }); void clearActiveSpace() => context.go("/rooms"); void setActiveSpace(String spaceId) => context.go("/rooms/spaces/$spaceId/details"); @@ -141,6 +121,7 @@ class ChatListController extends State void onChatTap(Room room) async { if (room.membership == Membership.invite) { + // #Pangea final theme = Theme.of(context); final inviteEvent = room.getState( EventTypes.RoomMember, @@ -148,9 +129,7 @@ class ChatListController extends State ); final matrixLocals = MatrixLocals(L10n.of(context)); final action = await showAdaptiveDialog( - // #Pangea barrierDismissible: true, - // Pangea# context: context, builder: (context) => AlertDialog.adaptive( title: ConstrainedBox( @@ -168,13 +147,13 @@ class ChatListController extends State inviteEvent == null ? L10n.of(context).inviteForMe : inviteEvent.content.tryGet('reason') ?? - L10n.of(context).youInvitedBy( - room - .unsafeGetUserFromMemoryOrFallback( - inviteEvent.senderId, - ) - .calcDisplayname(i18n: matrixLocals), - ), + L10n.of(context).youInvitedBy( + room + .unsafeGetUserFromMemoryOrFallback( + inviteEvent.senderId, + ) + .calcDisplayname(i18n: matrixLocals), + ), textAlign: TextAlign.center, ), ), @@ -220,6 +199,7 @@ class ChatListController extends State return; } if (!mounted) return; + // Pangea# final joinResult = await showFutureLoadingDialog( context: context, future: () async { @@ -237,9 +217,7 @@ class ChatListController extends State if (room.membership == Membership.ban) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(L10n.of(context).youHaveBeenBannedFromThisChat), - ), + SnackBar(content: Text(L10n.of(context).youHaveBeenBannedFromThisChat)), ); return; } @@ -289,11 +267,9 @@ class ChatListController extends State } } - List get filteredRooms => Matrix.of(context) - .client - .rooms - .where(getRoomFilterByActiveFilter(activeFilter)) - .toList(); + List get filteredRooms => Matrix.of( + context, + ).client.rooms.where(getRoomFilterByActiveFilter(activeFilter)).toList(); bool isSearchMode = false; Future? publicRoomsResponse; @@ -352,14 +328,15 @@ class ChatListController extends State if (searchQuery.isValidMatrixId && searchQuery.sigil == '#' && - roomSearchResult.chunk - .any((room) => room.canonicalAlias == searchQuery) == + roomSearchResult.chunk.any( + (room) => room.canonicalAlias == searchQuery, + ) == false) { final response = await client.getRoomIdByAlias(searchQuery); final roomId = response.roomId; if (roomId != null) { roomSearchResult.chunk.add( - PublicRoomsChunk( + PublishedRoomsChunk( name: searchQuery, guestCanJoin: false, numJoinedMembers: 0, @@ -376,13 +353,9 @@ class ChatListController extends State ); } catch (e, s) { Logs().w('Searching has crashed', e, s); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - e.toLocalizedString(context), - ), - ), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } if (!isSearchMode) return; setState(() { @@ -426,8 +399,6 @@ class ChatListController extends State if (unfocus) searchFocusNode.unfocus(); } - bool isTorBrowser = false; - BoxConstraints? snappingSheetContainerSize; final ScrollController scrollController = ScrollController(); @@ -468,22 +439,17 @@ class ChatListController extends State showScaffoldDialog( context: context, builder: (context) => ShareScaffoldDialog( - items: files.map( - (file) { - if ({ - SharedMediaType.text, - SharedMediaType.url, - }.contains(file.type)) { - return TextShareItem(file.path); - } - return FileShareItem( - XFile( - file.path.replaceFirst('file://', ''), - mimeType: file.mimeType, - ), - ); - }, - ).toList(), + items: files.map((file) { + if ({SharedMediaType.text, SharedMediaType.url}.contains(file.type)) { + return TextShareItem(file.path); + } + return FileShareItem( + XFile( + file.path.replaceFirst('file://', ''), + mimeType: file.mimeType, + ), + ); + }).toList(), ), ); } @@ -507,24 +473,25 @@ class ChatListController extends State .listen(_processIncomingSharedMedia, onError: print); // For sharing images coming from outside the app while the app is closed - ReceiveSharingIntent.instance - .getInitialMedia() - .then(_processIncomingSharedMedia); + ReceiveSharingIntent.instance.getInitialMedia().then( + _processIncomingSharedMedia, + ); // #Pangea // // For receiving shared Uris - // _intentUriStreamSubscription = - // AppLinks().uriLinkStream.listen(_processIncomingUris); + // _intentUriStreamSubscription = AppLinks().uriLinkStream.listen( + // _processIncomingUris, + // ); // Pangea# if (PlatformInfos.isAndroid) { final shortcuts = FlutterShortcuts(); shortcuts.initialize().then( - (_) => shortcuts.listenAction((action) { - if (!mounted) return; - UrlLauncher(context, action).launchUrl(); - }), - ); + (_) => shortcuts.listenAction((action) { + if (!mounted) return; + UrlLauncher(context, action).launchUrl(); + }), + ); } } @@ -535,15 +502,25 @@ class ChatListController extends State @override void initState() { + // #Pangea + // activeFilter = AppSettings.separateChatTypes.value + // ? ActiveFilter.messages + // : ActiveFilter.allChats; + activeFilter = ActiveFilter.allChats; + // Pangea# _initReceiveSharingIntent(); + // #Pangea + // _activeSpaceId = widget.activeSpace; + // Pangea# scrollController.addListener(_onScroll); _waitForFirstSync(); _hackyWebRTCFixForWeb(); WidgetsBinding.instance.addPostFrameCallback((_) async { if (mounted) { - searchServer = - Matrix.of(context).store.getString(_serverStoreNamespace); + searchServer = Matrix.of( + context, + ).store.getString(_serverStoreNamespace); Matrix.of(context).backgroundPush?.setupPush(); UpdateNotifier.showUpdateSnackBar(context); // #Pangea @@ -557,64 +534,55 @@ class ChatListController extends State ); }); - _checkTorBrowser(); - //#Pangea - _invitedSpaceSubscription = Matrix.of(context) - .client - .onSync - .stream + _invitedSpaceSubscription = Matrix.of(context).client.onSync.stream .where((event) => event.rooms?.invite != null) .listen((event) async { - for (final inviteEntry in event.rooms!.invite!.entries) { - if (inviteEntry.value.inviteState == null) continue; - final isSpace = inviteEntry.value.inviteState!.any( - (event) => - event.type == EventTypes.RoomCreate && - event.content['type'] == 'm.space', - ); - final isAnalytics = inviteEntry.value.inviteState!.any( - (event) => - event.type == EventTypes.RoomCreate && - event.content['type'] == PangeaRoomTypes.analytics, - ); - - if (isSpace) { - final spaceId = inviteEntry.key; - final space = Matrix.of(context).client.getRoomById( - spaceId, - ); - - if (space?.classCode?.toLowerCase() == - SpaceCodeRepo.recentCode?.toLowerCase()) { - return; - } - - if (space != null) { - chatListHandleSpaceTap( - context, - space, + for (final inviteEntry in event.rooms!.invite!.entries) { + if (inviteEntry.value.inviteState == null) continue; + final isSpace = inviteEntry.value.inviteState!.any( + (event) => + event.type == EventTypes.RoomCreate && + event.content['type'] == 'm.space', ); - } - } - - if (isAnalytics) { - final analyticsRoom = - Matrix.of(context).client.getRoomById(inviteEntry.key); - try { - await analyticsRoom?.join(); - } catch (err, s) { - ErrorHandler.logError( - m: "Failed to join analytics room", - e: err, - s: s, - data: {"analyticsRoom": analyticsRoom?.id}, + final isAnalytics = inviteEntry.value.inviteState!.any( + (event) => + event.type == EventTypes.RoomCreate && + event.content['type'] == PangeaRoomTypes.analytics, ); + + if (isSpace) { + final spaceId = inviteEntry.key; + final space = Matrix.of(context).client.getRoomById(spaceId); + + if (space?.classCode?.toLowerCase() == + SpaceCodeRepo.recentCode?.toLowerCase()) { + return; + } + + if (space != null) { + chatListHandleSpaceTap(context, space); + } + } + + if (isAnalytics) { + final analyticsRoom = Matrix.of( + context, + ).client.getRoomById(inviteEntry.key); + try { + await analyticsRoom?.join(); + } catch (err, s) { + ErrorHandler.logError( + m: "Failed to join analytics room", + e: err, + s: s, + data: {"analyticsRoom": analyticsRoom?.id}, + ); + } + return; + } } - return; - } - } - }); + }); MatrixState.pangeaController.subscriptionController.subscriptionNotifier .addListener(_onSubscribe); @@ -628,39 +596,39 @@ class ChatListController extends State _roomCapacitySubscription ??= client.onSync.stream .where((u) => u.rooms?.join != null) .listen((update) async { - final roomUpdates = update.rooms!.join!.entries; - for (final entry in roomUpdates) { - final roomID = entry.key; - final roomUpdate = entry.value; - if (roomUpdate.timeline?.events == null) continue; - final events = roomUpdate.timeline!.events; - final memberEvents = events!.where( - (event) => - event.type == EventTypes.RoomMember && - event.senderId == client.userID, - ); - if (memberEvents.isEmpty) continue; - final room = client.getRoomById(roomID); - if (room == null || - room.isSpace || - room.isHiddenRoom || - room.capacity == null || - (room.summary.mJoinedMemberCount ?? 1) <= room.capacity!) { - continue; - } - - await showFutureLoadingDialog( - context: context, - future: () async { - await room.leave(); - if (GoRouterState.of(context).uri.toString().contains(roomID)) { - NavigationUtil.goToSpaceRoute(null, [], context); + final roomUpdates = update.rooms!.join!.entries; + for (final entry in roomUpdates) { + final roomID = entry.key; + final roomUpdate = entry.value; + if (roomUpdate.timeline?.events == null) continue; + final events = roomUpdate.timeline!.events; + final memberEvents = events!.where( + (event) => + event.type == EventTypes.RoomMember && + event.senderId == client.userID, + ); + if (memberEvents.isEmpty) continue; + final room = client.getRoomById(roomID); + if (room == null || + room.isSpace || + room.isHiddenRoom || + room.capacity == null || + (room.summary.mJoinedMemberCount ?? 1) <= room.capacity!) { + continue; } - throw L10n.of(context).roomFull; - }, - ); - } - }); + + await showFutureLoadingDialog( + context: context, + future: () async { + await room.leave(); + if (GoRouterState.of(context).uri.toString().contains(roomID)) { + NavigationUtil.goToSpaceRoute(null, [], context); + } + throw L10n.of(context).roomFull; + }, + ); + } + }); WidgetsBinding.instance.addPostFrameCallback((_) { _joinInvitedSpaces(); @@ -676,9 +644,9 @@ class ChatListController extends State } Future _joinInvitedSpaces() async { - final invitedSpaces = Matrix.of(context).client.rooms.where( - (r) => r.isSpace && r.membership == Membership.invite, - ); + final invitedSpaces = Matrix.of( + context, + ).client.rooms.where((r) => r.isSpace && r.membership == Membership.invite); for (final space in invitedSpaces) { await showInviteDialog(space, context); @@ -702,11 +670,7 @@ class ChatListController extends State } // #Pangea - void chatContextAction( - Room room, - BuildContext posContext, [ - Room? space, - ]) => + void chatContextAction(Room room, BuildContext posContext, [Room? space]) => chatContextMenuAction( room, posContext, @@ -735,8 +699,9 @@ class ChatListController extends State // Offset.zero & overlay.size, // ); - // final displayname = - // room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))); + // final displayname = room.getLocalizedDisplayname( + // MatrixLocals(L10n.of(context)), + // ); // final spacesWithPowerLevels = room.client.rooms // .where( @@ -754,19 +719,17 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.open, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // spacing: 12.0, // children: [ - // Avatar( - // mxContent: room.avatar, - // name: displayname, - // ), + // Avatar(mxContent: room.avatar, name: displayname), // ConstrainedBox( // constraints: const BoxConstraints(maxWidth: 128), // child: Text( // displayname, - // style: - // TextStyle(color: Theme.of(context).colorScheme.onSurface), + // style: TextStyle( + // color: Theme.of(context).colorScheme.onSurface, + // ), // maxLines: 2, // overflow: TextOverflow.ellipsis, // ), @@ -779,7 +742,7 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.goToSpace, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // Avatar( // mxContent: space.avatar, @@ -799,7 +762,7 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.mute, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // Icon( // room.pushRuleState == PushRuleState.notify @@ -818,7 +781,7 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.markUnread, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // Icon( // room.markedUnread @@ -837,7 +800,7 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.favorite, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // Icon( // room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined, @@ -855,7 +818,7 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.addToSpace, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // const Icon(Icons.group_work_outlined), // const SizedBox(width: 12), @@ -867,7 +830,7 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.leave, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // Icon( // Icons.delete_outlined, @@ -889,7 +852,7 @@ class ChatListController extends State // PopupMenuItem( // value: ChatContextAction.block, // child: Row( - // mainAxisSize: MainAxisSize.min, + // mainAxisSize: .min, // children: [ // Icon( // Icons.block_outlined, @@ -972,8 +935,9 @@ class ChatListController extends State // .map( // (space) => AdaptiveModalAction( // value: space, - // label: space - // .getLocalizedDisplayname(MatrixLocals(L10n.of(context))), + // label: space.getLocalizedDisplayname( + // MatrixLocals(L10n.of(context)), + // ), // ), // ) // .toList(), @@ -993,8 +957,7 @@ class ChatListController extends State context: context, ); if (result == OkCancelResult.ok) { - await Matrix.of(context).store.setBool(SettingKeys.showPresences, false); - AppConfig.showPresences = false; + AppSettings.showPresences.setItem(false); setState(() {}); } } @@ -1042,24 +1005,14 @@ class ChatListController extends State // if (client.prevBatch == null) { if (client.onSync.value?.nextBatch == null) { // Pangea# - await client.onSyncStatus.stream - .firstWhere((status) => status.status == SyncStatus.finished); + await client.onSyncStatus.stream.firstWhere( + (status) => status.status == SyncStatus.finished, + ); if (!mounted) return; setState(() { waitForFirstSync = true; }); - - // Display first login bootstrap if enabled - // #Pangea - // if (client.encryption?.keyManager.enabled == true) { - // if (await client.encryption?.keyManager.isCached() == false || - // await client.encryption?.crossSigning.isCached() == false || - // client.isUnknownSession && !mounted) { - // await BootstrapDialog(client: client).show(context); - // } - // } - // Pangea# } // #Pangea @@ -1071,34 +1024,33 @@ class ChatListController extends State }); // #Pangea - // if (client.userDeviceKeys[client.userID!]?.deviceKeys.values - // .any((device) => !device.verified && !device.blocked) ?? - // false) { - // late final ScaffoldFeatureController controller; - // final theme = Theme.of(context); - // controller = ScaffoldMessenger.of(context).showSnackBar( - // SnackBar( - // duration: const Duration(seconds: 15), - // showCloseIcon: true, - // backgroundColor: theme.colorScheme.errorContainer, - // closeIconColor: theme.colorScheme.onErrorContainer, - // content: Text( - // L10n.of(context).oneOfYourDevicesIsNotVerified, - // style: TextStyle( - // color: theme.colorScheme.onErrorContainer, - // ), - // ), - // action: SnackBarAction( - // onPressed: () { - // controller.close(); - // router.go('/rooms/settings/devices'); - // }, - // textColor: theme.colorScheme.onErrorContainer, - // label: L10n.of(context).settings, - // ), + // if (client.userDeviceKeys[client.userID!]?.deviceKeys.values.any( + // (device) => !device.verified && !device.blocked, + // ) ?? + // false) { + // late final ScaffoldFeatureController controller; + // final theme = Theme.of(context); + // controller = ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // duration: const Duration(seconds: 15), + // showCloseIcon: true, + // backgroundColor: theme.colorScheme.errorContainer, + // closeIconColor: theme.colorScheme.onErrorContainer, + // content: Text( + // L10n.of(context).oneOfYourDevicesIsNotVerified, + // style: TextStyle(color: theme.colorScheme.onErrorContainer), // ), - // ); - // } + // action: SnackBarAction( + // onPressed: () { + // controller.close(); + // router.go('/rooms/settings/devices'); + // }, + // textColor: theme.colorScheme.onErrorContainer, + // label: L10n.of(context).settings, + // ), + // ), + // ); + // } // Pangea# } @@ -1133,11 +1085,12 @@ class ChatListController extends State // setState(() { // _activeSpaceId = null; // Matrix.of(context).activeBundle = bundle; - // if (!Matrix.of(context) - // .currentBundle! - // .any((client) => client == Matrix.of(context).client)) { - // Matrix.of(context) - // .setActiveClient(Matrix.of(context).currentBundle!.first); + // if (!Matrix.of( + // context, + // ).currentBundle!.any((client) => client == Matrix.of(context).client)) { + // Matrix.of( + // context, + // ).setActiveClient(Matrix.of(context).currentBundle!.first); // } // }); // } @@ -1145,9 +1098,9 @@ class ChatListController extends State void editBundlesForAccount(String? userId, String? activeBundle) async { final l10n = L10n.of(context); - final client = Matrix.of(context) - .widget - .clients[Matrix.of(context).getClientIndexByMatrixId(userId!)]; + final client = Matrix.of( + context, + ).widget.clients[Matrix.of(context).getClientIndexByMatrixId(userId!)]; final action = await showModalActionPopup( context: context, title: L10n.of(context).editBundlesForAccount, @@ -1192,10 +1145,9 @@ class ChatListController extends State String? get secureActiveBundle { if (Matrix.of(context).activeBundle == null || - !Matrix.of(context) - .accountBundles - .keys - .contains(Matrix.of(context).activeBundle)) { + !Matrix.of( + context, + ).accountBundles.keys.contains(Matrix.of(context).activeBundle)) { return Matrix.of(context).accountBundles.keys.first; } return Matrix.of(context).activeBundle; @@ -1216,22 +1168,12 @@ class ChatListController extends State ChatList.contextForVoip = context; } - Future _checkTorBrowser() async { - if (!kIsWeb) return; - final isTor = await TorBrowserDetector.isTorBrowser; - isTorBrowser = isTor; - } - Future dehydrate() => Matrix.of(context).dehydrateAction(context); } enum EditBundleAction { addToBundle, removeFromBundle } -enum InviteActions { - accept, - decline, - block, -} +enum InviteActions { accept, decline, block } enum ChatContextAction { open, @@ -1245,6 +1187,10 @@ enum ChatContextAction { delete, endActivity, // Pangea# + block, } +// #Pangea enum InviteAction { accept, decline, block } + +// Pangea# diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 7e998777c..f4095bbd7 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -31,7 +31,9 @@ class ChatListViewBody extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = Theme.of(context); + // #Pangea + // final theme = Theme.of(context); + // Pangea# final client = Matrix.of(context).client; final activeSpace = controller.activeSpaceId; @@ -42,10 +44,7 @@ class ChatListViewBody extends StatelessWidget { // spaceId: activeSpace, // onBack: controller.clearActiveSpace, // onChatTab: (room) => controller.onChatTap(room), - // onChatContext: (room, context) => - // controller.chatContextAction(room, context), // activeChat: controller.activeChat, - // toParentSpace: controller.setActiveSpace, // ); return CourseChats( activeSpace, @@ -77,9 +76,7 @@ class ChatListViewBody extends StatelessWidget { const dummyChatCount = 4; final filter = controller.searchController.text.toLowerCase(); return StreamBuilder( - key: ValueKey( - client.userID.toString(), - ), + key: ValueKey(client.userID.toString()), stream: client.onSync.stream .where((s) => s.hasRoomUpdate) .rateLimit(const Duration(seconds: 1)), @@ -98,191 +95,173 @@ class ChatListViewBody extends StatelessWidget { ), // Pangea# SliverList( - delegate: SliverChildListDelegate( - [ - if (controller.isSearchMode) ...[ - // #Pangea - // SearchTitle( - // title: L10n.of(context).publicRooms, - // icon: const Icon(Icons.explore_outlined), - // ), - // PublicRoomsHorizontalList(publicRooms: publicRooms), - // SearchTitle( - // title: L10n.of(context).publicSpaces, - // icon: const Icon(Icons.workspaces_outlined), - // ), - // PublicRoomsHorizontalList(publicRooms: publicSpaces), - // SearchTitle( - // title: L10n.of(context).users, - // icon: const Icon(Icons.group_outlined), - // ), - // AnimatedContainer( - // clipBehavior: Clip.hardEdge, - // decoration: const BoxDecoration(), - // height: userSearchResult == null || - // userSearchResult.results.isEmpty - // ? 0 - // : 106, - // duration: FluffyThemes.animationDuration, - // curve: FluffyThemes.animationCurve, - // child: userSearchResult == null - // ? null - // : ListView.builder( - // scrollDirection: Axis.horizontal, - // itemCount: userSearchResult.results.length, - // itemBuilder: (context, i) => _SearchItem( - // title: - // userSearchResult.results[i].displayName ?? - // userSearchResult - // .results[i].userId.localpart ?? - // L10n.of(context).unknownDevice, - // avatar: userSearchResult.results[i].avatarUrl, - // onPressed: () => UserDialog.show( - // context: context, - // profile: userSearchResult.results[i], - // ), - // ), - // ), - // ), - // Pangea# - ], + delegate: SliverChildListDelegate([ + if (controller.isSearchMode) ...[ // #Pangea - // if (!controller.isSearchMode && AppConfig.showPresences) - // GestureDetector( - // onLongPress: () => controller.dismissStatusList(), - // child: StatusMessageList( - // onStatusEdit: controller.setStatus, - // ), - // ), - // Pangea# - AnimatedContainer( - height: controller.isTorBrowser ? 64 : 0, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), - child: Material( - color: theme.colorScheme.surface, - child: ListTile( - leading: const Icon(Icons.vpn_key), - title: Text(L10n.of(context).dehydrateTor), - subtitle: Text(L10n.of(context).dehydrateTorLong), - trailing: const Icon(Icons.chevron_right_outlined), - onTap: controller.dehydrate, - ), - ), - ), - // #Pangea - // if (client.rooms.isNotEmpty && !controller.isSearchMode) - // SizedBox( - // height: 64, - // child: ListView( - // padding: const EdgeInsets.symmetric( - // horizontal: 12.0, - // vertical: 12.0, - // ), - // shrinkWrap: true, - // scrollDirection: Axis.horizontal, - // children: [ - // if (AppConfig.separateChatTypes) - // ActiveFilter.messages - // else - // ActiveFilter.allChats, - // ActiveFilter.groups, - // ActiveFilter.unread, - // if (spaceDelegateCandidates.isNotEmpty && - // !AppConfig.displayNavigationRail && - // !FluffyThemes.isColumnMode(context)) - // ActiveFilter.spaces, - // ] - // .map( - // (filter) => Padding( - // padding: const EdgeInsets.symmetric( - // horizontal: 4.0, - // ), - // child: FilterChip( - // selected: filter == controller.activeFilter, - // onSelected: (_) => - // controller.setActiveFilter(filter), - // label: - // Text(filter.toLocalizedString(context)), - // ), + // SearchTitle( + // title: L10n.of(context).publicRooms, + // icon: const Icon(Icons.explore_outlined), + // ), + // PublicRoomsHorizontalList(publicRooms: publicRooms), + // SearchTitle( + // title: L10n.of(context).publicSpaces, + // icon: const Icon(Icons.workspaces_outlined), + // ), + // PublicRoomsHorizontalList(publicRooms: publicSpaces), + // SearchTitle( + // title: L10n.of(context).users, + // icon: const Icon(Icons.group_outlined), + // ), + // AnimatedContainer( + // clipBehavior: Clip.hardEdge, + // decoration: const BoxDecoration(), + // height: + // userSearchResult == null || + // userSearchResult.results.isEmpty + // ? 0 + // : 106, + // duration: FluffyThemes.animationDuration, + // curve: FluffyThemes.animationCurve, + // child: userSearchResult == null + // ? null + // : ListView.builder( + // scrollDirection: Axis.horizontal, + // itemCount: userSearchResult.results.length, + // itemBuilder: (context, i) => _SearchItem( + // title: + // userSearchResult.results[i].displayName ?? + // userSearchResult + // .results[i] + // .userId + // .localpart ?? + // L10n.of(context).unknownDevice, + // avatar: userSearchResult.results[i].avatarUrl, + // onPressed: () => UserDialog.show( + // context: context, + // profile: userSearchResult.results[i], // ), - // ) - // .toList(), - // ), - // ), - // if (controller.isSearchMode) - // SearchTitle( - // title: L10n.of(context).chats, - // icon: const Icon(Icons.forum_outlined), - // ), - if (controller.isSearchMode && - rooms - .where( - (room) => room - .getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), - ) - .toLowerCase() - .contains(filter), - ) - .isEmpty) - Padding( - padding: const EdgeInsetsGeometry.all(16.0), - child: Text( - L10n.of(context).emptyChatSearch, - textAlign: TextAlign.center, - ), - ), - // if (client.prevBatch != null && - // rooms.isEmpty && - // !controller.isSearchMode) ...[ - // Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Stack( - // alignment: Alignment.center, - // children: [ - // const Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // DummyChatListItem( - // opacity: 0.5, - // animate: false, - // ), - // DummyChatListItem( - // opacity: 0.3, - // animate: false, - // ), - // ], - // ), - // Icon( - // CupertinoIcons.chat_bubble_text_fill, - // size: 128, - // color: theme.colorScheme.secondary, - // ), - // ], - // ), - // Padding( - // padding: const EdgeInsets.all(16.0), - // child: Text( - // client.rooms.isEmpty - // ? L10n.of(context).noChatsFoundHereYet - // : L10n.of(context).noMoreChatsFound, - // textAlign: TextAlign.center, - // style: TextStyle( - // fontSize: 18, - // color: theme.colorScheme.secondary, // ), // ), - // ), - // ], - // ), - // ], + // ), // Pangea# ], - ), + // #Pangea + // if (!controller.isSearchMode && + // AppSettings.showPresences.value) + // GestureDetector( + // onLongPress: () => controller.dismissStatusList(), + // child: StatusMessageList( + // onStatusEdit: controller.setStatus, + // ), + // ), + // if (client.rooms.isNotEmpty && !controller.isSearchMode) + // SizedBox( + // height: 64, + // child: ListView( + // padding: const EdgeInsets.symmetric( + // horizontal: 12.0, + // vertical: 12.0, + // ), + // shrinkWrap: true, + // scrollDirection: Axis.horizontal, + // children: + // [ + // if (AppSettings.separateChatTypes.value) + // ActiveFilter.messages + // else + // ActiveFilter.allChats, + // ActiveFilter.groups, + // ActiveFilter.unread, + // if (spaceDelegateCandidates.isNotEmpty && + // !AppSettings + // .displayNavigationRail + // .value && + // !FluffyThemes.isColumnMode(context)) + // ActiveFilter.spaces, + // ] + // .map( + // (filter) => Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 4.0, + // ), + // child: FilterChip( + // selected: + // filter == controller.activeFilter, + // onSelected: (_) => + // controller.setActiveFilter(filter), + // label: Text( + // filter.toLocalizedString(context), + // ), + // ), + // ), + // ) + // .toList(), + // ), + // ), + // if (controller.isSearchMode) + // SearchTitle( + // title: L10n.of(context).chats, + // icon: const Icon(Icons.forum_outlined), + // ), + // if (client.prevBatch != null && + // rooms.isEmpty && + // !controller.isSearchMode) ...[ + // Column( + // mainAxisAlignment: .center, + // children: [ + // Stack( + // alignment: Alignment.center, + // children: [ + // const Column( + // mainAxisSize: .min, + // children: [ + // DummyChatListItem(opacity: 0.5, animate: false), + // DummyChatListItem(opacity: 0.3, animate: false), + // ], + // ), + // Icon( + // CupertinoIcons.chat_bubble_text_fill, + // size: 128, + // color: theme.colorScheme.secondary, + // ), + // ], + // ), + // Padding( + // padding: const EdgeInsets.all(16.0), + // child: Text( + // client.rooms.isEmpty + // ? L10n.of(context).noChatsFoundHere + // : L10n.of(context).noMoreChatsFound, + // textAlign: TextAlign.center, + // style: TextStyle( + // fontSize: 18, + // color: theme.colorScheme.secondary, + // ), + // ), + // ), + // ], + // ), + // ], + if (controller.isSearchMode && + rooms + .where( + (room) => room + .getLocalizedDisplayname( + MatrixLocals(L10n.of(context)), + ) + .toLowerCase() + .contains(filter), + ) + .isEmpty) + Padding( + padding: const EdgeInsetsGeometry.all(16.0), + child: Text( + L10n.of(context).emptyChatSearch, + textAlign: TextAlign.center, + ), + ), + // Pangea# + ]), ), if (client.prevBatch == null) SliverList( @@ -321,17 +300,16 @@ class ChatListViewBody extends StatelessWidget { vertical: 1, ), child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), clipBehavior: Clip.hardEdge, child: ListTile( leading: const BotFace( expression: BotExpression.idle, width: Avatar.defaultSize, ), - trailing: const Icon( - Icons.chat_bubble_outline, - ), + trailing: const Icon(Icons.chat_bubble_outline), title: Text(L10n.of(context).directMessageBotTitle), subtitle: Text(L10n.of(context).directMessageBotDesc), onTap: () async { @@ -357,8 +335,9 @@ class ChatListViewBody extends StatelessWidget { vertical: 1, ), child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), clipBehavior: Clip.hardEdge, child: ListTile( contentPadding: const EdgeInsets.only( @@ -385,8 +364,7 @@ class ChatListViewBody extends StatelessWidget { await showFutureLoadingDialog( context: context, future: () async { - final roomId = await Matrix.of(context) - .client + final roomId = await Matrix.of(context).client .startDirectChat( Environment.supportUserId, enableEncryption: false, @@ -410,12 +388,9 @@ class ChatListViewBody extends StatelessWidget { } class PublicRoomsHorizontalList extends StatelessWidget { - const PublicRoomsHorizontalList({ - super.key, - required this.publicRooms, - }); + const PublicRoomsHorizontalList({super.key, required this.publicRooms}); - final List? publicRooms; + final List? publicRooms; @override Widget build(BuildContext context) { @@ -432,7 +407,8 @@ class PublicRoomsHorizontalList extends StatelessWidget { scrollDirection: Axis.horizontal, itemCount: publicRooms.length, itemBuilder: (context, i) => _SearchItem( - title: publicRooms[i].name ?? + title: + publicRooms[i].name ?? publicRooms[i].canonicalAlias?.localpart ?? L10n.of(context).group, avatar: publicRooms[i].avatarUrl, @@ -463,31 +439,26 @@ class _SearchItem extends StatelessWidget { @override Widget build(BuildContext context) => InkWell( - onTap: onPressed, - child: SizedBox( - width: 84, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 8), - Avatar( - mxContent: avatar, - name: title, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - title, - maxLines: 2, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 12, - ), - ), - ), - ], + onTap: onPressed, + child: SizedBox( + width: 84, + child: Column( + mainAxisSize: .min, + children: [ + const SizedBox(height: 8), + Avatar(mxContent: avatar, name: title), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + title, + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12), + ), ), - ), - ); + ], + ), + ), + ); } diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index d994059d1..7945aa58a 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -1,4 +1,3 @@ -// #Pangea // import 'package:flutter/material.dart'; // import 'package:matrix/matrix.dart'; @@ -35,19 +34,19 @@ // title: StreamBuilder( // stream: client.onSyncStatus.stream, // builder: (context, snapshot) { -// final status = client.onSyncStatus.value ?? +// final status = +// client.onSyncStatus.value ?? // const SyncStatusUpdate(SyncStatus.waitingForResponse); -// final hide = client.onSync.value != null && +// final hide = +// client.onSync.value != null && // status.status != SyncStatus.error && // client.prevBatch != null; // return TextField( // controller: controller.searchController, // focusNode: controller.searchFocusNode, // textInputAction: TextInputAction.search, -// onChanged: (text) => controller.onSearchEnter( -// text, -// globalSearch: globalSearch, -// ), +// onChanged: (text) => +// controller.onSearchEnter(text, globalSearch: globalSearch), // decoration: InputDecoration( // filled: true, // fillColor: theme.colorScheme.secondaryContainer, @@ -67,19 +66,19 @@ // ), // prefixIcon: hide // ? controller.isSearchMode -// ? IconButton( -// tooltip: L10n.of(context).cancel, -// icon: const Icon(Icons.close_outlined), -// onPressed: controller.cancelSearch, -// color: theme.colorScheme.onPrimaryContainer, -// ) -// : IconButton( -// onPressed: controller.startSearch, -// icon: Icon( -// Icons.search_outlined, +// ? IconButton( +// tooltip: L10n.of(context).cancel, +// icon: const Icon(Icons.close_outlined), +// onPressed: controller.cancelSearch, // color: theme.colorScheme.onPrimaryContainer, -// ), -// ) +// ) +// : IconButton( +// onPressed: controller.startSearch, +// icon: Icon( +// Icons.search_outlined, +// color: theme.colorScheme.onPrimaryContainer, +// ), +// ) // : Container( // margin: const EdgeInsets.all(12), // width: 8, @@ -98,37 +97,34 @@ // ), // suffixIcon: controller.isSearchMode && globalSearch // ? controller.isSearching -// ? const Padding( -// padding: EdgeInsets.symmetric( -// vertical: 10.0, -// horizontal: 12, -// ), -// child: SizedBox.square( -// dimension: 24, -// child: CircularProgressIndicator.adaptive( -// strokeWidth: 2, +// ? const Padding( +// padding: EdgeInsets.symmetric( +// vertical: 10.0, +// horizontal: 12, // ), -// ), -// ) -// : TextButton.icon( -// onPressed: controller.setServer, -// style: TextButton.styleFrom( -// shape: RoundedRectangleBorder( -// borderRadius: BorderRadius.circular(99), +// child: SizedBox.square( +// dimension: 24, +// child: CircularProgressIndicator.adaptive( +// strokeWidth: 2, +// ), // ), -// textStyle: const TextStyle(fontSize: 12), -// ), -// icon: const Icon(Icons.edit_outlined, size: 16), -// label: Text( -// controller.searchServer ?? -// Matrix.of(context).client.homeserver!.host, -// maxLines: 2, -// ), -// ) -// : SizedBox( -// width: 0, -// child: ClientChooserButton(controller), -// ), +// ) +// : TextButton.icon( +// onPressed: controller.setServer, +// style: TextButton.styleFrom( +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(99), +// ), +// textStyle: const TextStyle(fontSize: 12), +// ), +// icon: const Icon(Icons.edit_outlined, size: 16), +// label: Text( +// controller.searchServer ?? +// Matrix.of(context).client.homeserver!.host, +// maxLines: 2, +// ), +// ) +// : SizedBox(width: 0, child: ClientChooserButton(controller)), // ), // ); // }, @@ -139,4 +135,3 @@ // @override // Size get preferredSize => const Size.fromHeight(56); // } -// Pangea# diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index bbcaa7e54..5a6ba101b 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -4,6 +4,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pages/chat_list/unread_bubble.dart'; import 'package:fluffychat/pangea/chat_list/utils/get_chat_list_item_subtitle.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -43,40 +44,6 @@ class ChatListItem extends StatelessWidget { // Pangea# }); - Future archiveAction(BuildContext context) async { - { - if ([Membership.leave, Membership.ban].contains(room.membership)) { - final forgetResult = await showFutureLoadingDialog( - context: context, - future: () => room.forget(), - ); - return forgetResult.isValue; - } - final confirmed = await showOkCancelAlertDialog( - context: context, - title: L10n.of(context).areYouSure, - okLabel: L10n.of(context).leave, - cancelLabel: L10n.of(context).cancel, - // #Pangea - // message: L10n.of(context).archiveRoomDescription, - message: room.isSpace - ? L10n.of(context).leaveSpaceDescription - : L10n.of(context).leaveRoomDescription, - // Pangea# - isDestructive: true, - ); - if (confirmed != OkCancelResult.ok) return false; - final leaveResult = await showFutureLoadingDialog( - context: context, - // #Pangea - // future: () => room.leave(), - future: () => room.isSpace ? room.leaveSpace() : room.leave(), - // Pangea# - ); - return leaveResult.isValue; - } - } - @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -85,17 +52,13 @@ class ChatListItem extends StatelessWidget { final typingText = room.getLocalizedTypingText(context); final lastEvent = room.lastEvent; final ownMessage = lastEvent?.senderId == room.client.userID; - final unread = room.isUnread || room.membership == Membership.invite; + final unread = room.isUnread; final directChatMatrixId = room.directChatMatrixID; final isDirectChat = directChatMatrixId != null; - final unreadBubbleSize = unread || room.hasNewMessages - ? room.notificationCount > 0 - ? 20.0 - : 14.0 - : 0.0; final hasNotifications = room.notificationCount > 0; - final backgroundColor = - activeChat ? theme.colorScheme.secondaryContainer : null; + final backgroundColor = activeChat + ? theme.colorScheme.secondaryContainer + : null; final displayname = room.getLocalizedDisplayname( MatrixLocals(L10n.of(context)), ); @@ -116,17 +79,14 @@ class ChatListItem extends StatelessWidget { final space = this.space; return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), child: Material( borderRadius: BorderRadius.circular(AppConfig.borderRadius), clipBehavior: Clip.hardEdge, color: backgroundColor, child: FutureBuilder( - future: room.loadHeroUsers(), - builder: (context, snapshot) => HoverBuilder( + future: room.name.isEmpty ? room.loadHeroUsers() : null, + builder: (context, _) => HoverBuilder( builder: (context, listTileHovered) => ListTile( visualDensity: const VisualDensity(vertical: -0.5), contentPadding: const EdgeInsets.symmetric(horizontal: 8), @@ -148,7 +108,8 @@ class ChatListItem extends StatelessWidget { child: Avatar( border: BorderSide( width: 2, - color: backgroundColor ?? + color: + backgroundColor ?? theme.colorScheme.surface, ), borderRadius: BorderRadius.circular( @@ -170,14 +131,15 @@ class ChatListItem extends StatelessWidget { child: Avatar( border: space == null ? room.isSpace - ? BorderSide( - width: 1, - color: theme.dividerColor, - ) - : null + ? BorderSide( + width: 1, + color: theme.dividerColor, + ) + : null : BorderSide( width: 2, - color: backgroundColor ?? + color: + backgroundColor ?? theme.colorScheme.surface, ), // #Pangea @@ -186,7 +148,8 @@ class ChatListItem extends StatelessWidget { // AppConfig.borderRadius / 4, // ) // : null, - borderRadius: borderRadius ?? + borderRadius: + borderRadius ?? (room.isSpace ? BorderRadius.circular( AppConfig.borderRadius / 4, @@ -246,10 +209,7 @@ class ChatListItem extends StatelessWidget { if (isMuted) const Padding( padding: EdgeInsets.only(left: 4.0), - child: Icon( - Icons.notifications_off_outlined, - size: 16, - ), + child: Icon(Icons.notifications_off_outlined, size: 16), ), if (room.isFavourite) Padding( @@ -262,13 +222,13 @@ class ChatListItem extends StatelessWidget { color: theme.colorScheme.primary, ), ), - if (!room.isSpace && - lastEvent != null && - room.membership != Membership.invite) + if (!room.isSpace && room.membership != Membership.invite) Padding( padding: const EdgeInsets.only(left: 4.0), child: Text( - lastEvent.originServerTs.localizedTimeShort(context), + room.latestEventReceivedTime.localizedTimeShort( + context, + ), style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, @@ -278,12 +238,12 @@ class ChatListItem extends StatelessWidget { ], ), subtitle: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: .start, + mainAxisAlignment: .center, children: [ if (typingText.isEmpty && ownMessage && - room.lastEvent!.status.isSending) ...[ + room.lastEvent?.status.isSending == true) ...[ const SizedBox( width: 16, height: 16, @@ -291,159 +251,172 @@ class ChatListItem extends StatelessWidget { ), const SizedBox(width: 4), ], - AnimatedContainer( - width: typingText.isEmpty ? 0 : 18, + AnimatedSize( clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, - padding: const EdgeInsets.only(right: 4), - child: Icon( - Icons.edit_outlined, - color: theme.colorScheme.secondary, - size: 14, - ), + child: typingText.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 4.0), + child: Icon( + Icons.edit_outlined, + color: theme.colorScheme.secondary, + size: 16, + ), + ) + : room.lastEvent?.relationshipType == + RelationshipTypes.thread + ? Container( + decoration: BoxDecoration( + border: Border.all( + color: theme.colorScheme.outline, + ), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + ), + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), + margin: const EdgeInsets.only(right: 4.0), + child: Row( + mainAxisSize: .min, + children: [ + Icon( + Icons.message_outlined, + size: 12, + color: theme.colorScheme.outline, + ), + const SizedBox(width: 4), + Text( + L10n.of(context).thread, + style: TextStyle( + fontSize: 12, + color: theme.colorScheme.outline, + ), + ), + ], + ), + ) + : const SizedBox.shrink(), ), Expanded( child: room.isSpace && room.membership == Membership.join ? Text( - L10n.of(context).countChatsAndCountParticipants( - // #Pangea - // room.spaceChildren.length, - room.spaceChildCount, - // Pangea# - (room.summary.mJoinedMemberCount ?? 1), - ), + // #Pangea + // L10n.of( + // context, + // ).countChats(room.spaceChildren.length), + L10n.of(context).countChats(room.spaceChildCount), + // Pangea# style: TextStyle(color: theme.colorScheme.outline), ) : typingText.isNotEmpty - ? Text( - typingText, - style: TextStyle( - color: theme.colorScheme.primary, - ), - maxLines: 1, - softWrap: false, - ) - // #Pangea - : room.lastEvent != null - ? ChatListItemSubtitle( - room: room, - style: TextStyle( - fontWeight: unread || room.hasNewMessages - ? FontWeight.bold - : null, - color: theme.colorScheme.onSurfaceVariant, - ), + ? Text( + typingText, + style: TextStyle(color: theme.colorScheme.primary), + maxLines: 1, + softWrap: false, + ) + // #Pangea + : room.lastEvent != null + ? ChatListItemSubtitle( + room: room, + style: TextStyle( + fontWeight: unread || room.hasNewMessages + ? FontWeight.bold + : null, + color: theme.colorScheme.onSurfaceVariant, + ), + ) + // Pangea# + : FutureBuilder( + key: ValueKey( + '${lastEvent?.eventId}_${lastEvent?.type}_${lastEvent?.redacted}', + ), + future: needLastEventSender + ? lastEvent.calcLocalizedBody( + MatrixLocals(L10n.of(context)), + hideReply: true, + hideEdit: true, + plaintextBody: true, + removeMarkdown: true, + withSenderNamePrefix: + (!isDirectChat || + directChatMatrixId != + room.lastEvent?.senderId), ) - // Pangea# - : FutureBuilder( - key: ValueKey( - '${lastEvent?.eventId}_${lastEvent?.type}_${lastEvent?.redacted}', - ), - future: needLastEventSender - ? lastEvent.calcLocalizedBody( - MatrixLocals(L10n.of(context)), - hideReply: true, - hideEdit: true, - plaintextBody: true, - removeMarkdown: true, - withSenderNamePrefix: - (!isDirectChat || - directChatMatrixId != - room.lastEvent - ?.senderId), - ) - : null, - initialData: - lastEvent?.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)), - hideReply: true, - hideEdit: true, - plaintextBody: true, - removeMarkdown: true, - withSenderNamePrefix: (!isDirectChat || - directChatMatrixId != - room.lastEvent?.senderId), - ), - builder: (context, snapshot) => Text( - room.membership == Membership.invite - ? room - .getState( - EventTypes.RoomMember, - room.client.userID!, - ) - ?.content - .tryGet('reason') ?? - (isDirectChat - ? L10n.of(context) - .newChatRequest - // #Pangea - // : L10n.of(context) - // .inviteGroupChat) - : L10n.of(context).inviteChat) - // Pangea# - : snapshot.data ?? - L10n.of(context).emptyChat, - softWrap: false, - maxLines: - room.notificationCount >= 1 ? 2 : 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: unread || room.hasNewMessages - ? theme.colorScheme.onSurface - : theme.colorScheme.outline, - decoration: - room.lastEvent?.redacted == true - ? TextDecoration.lineThrough - : null, - ), - ), - ), + : null, + initialData: lastEvent?.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)), + hideReply: true, + hideEdit: true, + plaintextBody: true, + removeMarkdown: true, + withSenderNamePrefix: + (!isDirectChat || + directChatMatrixId != + room.lastEvent?.senderId), + ), + builder: (context, snapshot) => Text( + room.membership == Membership.invite + ? room + .getState( + EventTypes.RoomMember, + room.client.userID!, + ) + ?.content + .tryGet('reason') ?? + (isDirectChat + ? L10n.of(context).newChatRequest + // #Pangea + // : L10n.of(context).inviteGroupChat) + : L10n.of(context).inviteChat) + // Pangea# + : snapshot.data ?? + L10n.of(context).noMessagesYet, + softWrap: false, + maxLines: room.notificationCount >= 1 ? 2 : 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: unread || room.hasNewMessages + ? theme.colorScheme.onSurface + : theme.colorScheme.outline, + decoration: room.lastEvent?.redacted == true + ? TextDecoration.lineThrough + : null, + ), + ), + ), ), const SizedBox(width: 8), - AnimatedContainer( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 7), - height: unreadBubbleSize, - width: !hasNotifications && !unread && !room.hasNewMessages - ? 0 - : (unreadBubbleSize - 9) * - room.notificationCount.toString().length + - 9, - decoration: BoxDecoration( - color: room.highlightCount > 0 || - room.membership == Membership.invite - ? theme.colorScheme.error - : hasNotifications || room.markedUnread - ? theme.colorScheme.primary - : theme.colorScheme.primaryContainer, - borderRadius: BorderRadius.circular(7), - ), - child: hasNotifications - ? Text( - room.notificationCount.toString(), - style: TextStyle( - color: room.highlightCount > 0 || - room.membership == Membership.invite - ? theme.colorScheme.onError - : hasNotifications - ? theme.colorScheme.onPrimary - : theme.colorScheme.onPrimaryContainer, - fontSize: 13, - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.center, - ) - : const SizedBox.shrink(), - ), + UnreadBubble(room: room), ], ), onTap: onTap, trailing: onForget == null - ? null + ? room.membership == Membership.invite + ? IconButton( + tooltip: L10n.of(context).declineInvitation, + icon: const Icon(Icons.delete_forever_outlined), + color: theme.colorScheme.error, + onPressed: () async { + final consent = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).declineInvitation, + message: L10n.of(context).areYouSure, + okLabel: L10n.of(context).yes, + isDestructive: true, + ); + if (consent != OkCancelResult.ok) return; + if (!context.mounted) return; + await showFutureLoadingDialog( + context: context, + future: room.leave, + ); + }, + ) + : null : IconButton( icon: const Icon(Icons.delete_outlined), onPressed: onForget, diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index 6bf986215..9011648d5 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -30,16 +30,13 @@ class ChatListView extends StatelessWidget { children: [ // #Pangea // if (FluffyThemes.isColumnMode(context) || - // AppConfig.displayNavigationRail) ...[ + // AppSettings.displayNavigationRail.value) ...[ // SpacesNavigationRail( // activeSpaceId: controller.activeSpaceId, // onGoToChats: controller.clearActiveSpace, // onGoToSpaceId: controller.setActiveSpace, // ), - // Container( - // color: Theme.of(context).dividerColor, - // width: 1, - // ), + // Container(color: Theme.of(context).dividerColor, width: 1), // ], // Pangea# Expanded( @@ -52,8 +49,8 @@ class ChatListView extends StatelessWidget { // body: ChatListViewBody(controller), body: ChatListViewBodyWrapper(controller: controller), // Pangea# - floatingActionButton: !controller.isSearchMode && - controller.activeSpaceId == null + floatingActionButton: + !controller.isSearchMode && controller.activeSpaceId == null ? FloatingActionButton.extended( onPressed: () => context.go('/rooms/newprivatechat'), // #Pangea diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 643a6055c..87a7314c1 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -1,9 +1,10 @@ -// #Pangea // import 'package:flutter/material.dart'; // import 'package:go_router/go_router.dart'; // import 'package:matrix/matrix.dart'; +// import 'package:url_launcher/url_launcher_string.dart'; +// import 'package:fluffychat/config/app_config.dart'; // import 'package:fluffychat/config/themes.dart'; // import 'package:fluffychat/l10n/l10n.dart'; // import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -24,8 +25,8 @@ // (a, b) => a!.isValidMatrixId == b!.isValidMatrixId // ? 0 // : a.isValidMatrixId && !b.isValidMatrixId -// ? -1 -// : 1, +// ? -1 +// : 1, // ); // return >[ // PopupMenuItem( @@ -68,6 +69,17 @@ // ], // ), // ), +// if (Matrix.of(context).backgroundPush?.firebaseEnabled != true) +// PopupMenuItem( +// value: SettingsAction.support, +// child: Row( +// children: [ +// const Icon(Icons.favorite, color: Colors.red), +// const SizedBox(width: 18), +// Text(L10n.of(context).donate), +// ], +// ), +// ), // PopupMenuItem( // value: SettingsAction.settings, // child: Row( @@ -85,8 +97,8 @@ // PopupMenuItem( // value: null, // child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// mainAxisSize: MainAxisSize.min, +// crossAxisAlignment: .start, +// mainAxisSize: .min, // children: [ // Text( // bundle!, @@ -111,7 +123,8 @@ // children: [ // Avatar( // mxContent: snapshot.data?.avatarUrl, -// name: snapshot.data?.displayName ?? +// name: +// snapshot.data?.displayName ?? // client.userID!.localpart, // size: 32, // ), @@ -181,10 +194,7 @@ // ); // } -// void _clientSelected( -// Object object, -// BuildContext context, -// ) async { +// void _clientSelected(Object object, BuildContext context) async { // if (object is Client) { // controller.setActiveClient(object); // } else if (object is String) { @@ -208,6 +218,9 @@ // case SettingsAction.invite: // FluffyShare.shareInviteLink(context); // break; +// case SettingsAction.support: +// launchUrlString(AppConfig.donationUrl); +// break; // case SettingsAction.settings: // context.go('/rooms/settings'); // break; @@ -227,7 +240,7 @@ // newGroup, // setStatus, // invite, +// support, // settings, // archive, // } -// Pangea# diff --git a/lib/pages/chat_list/navi_rail_item.dart b/lib/pages/chat_list/navi_rail_item.dart index 19eb929e8..fc51891f6 100644 --- a/lib/pages/chat_list/navi_rail_item.dart +++ b/lib/pages/chat_list/navi_rail_item.dart @@ -59,13 +59,13 @@ class NaviRailItem extends StatelessWidget { // #Pangea // return SizedBox( // height: 72, + // width: FluffyThemes.navRailWidth, return Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox( height: width - (isColumnMode ? 16.0 : 12.0), width: width, - // width: FluffyThemes.navRailWidth, // Pangea# child: Stack( children: [ @@ -76,8 +76,8 @@ class NaviRailItem extends StatelessWidget { child: AnimatedContainer( width: isSelected ? FluffyThemes.isColumnMode(context) - ? 8 - : 4 + ? 8 + : 4 : 0, duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, @@ -97,10 +97,10 @@ class NaviRailItem extends StatelessWidget { curve: FluffyThemes.animationCurve, // #Pangea // child: Material( - // borderRadius: borderRadius, - // color: isSelected - // ? theme.colorScheme.primaryContainer - // : theme.colorScheme.surfaceContainerHigh, + // borderRadius: borderRadius, + // color: isSelected + // ? theme.colorScheme.primaryContainer + // : theme.colorScheme.surfaceContainerHigh, child: UnreadRoomsBadge( filter: unreadBadgeFilter ?? (_) => false, badgePosition: BadgePosition.topEnd( @@ -110,7 +110,8 @@ class NaviRailItem extends StatelessWidget { child: Container( alignment: Alignment.center, decoration: BoxDecoration( - color: backgroundColor ?? + color: + backgroundColor ?? (isSelected ? theme.colorScheme.primaryContainer : theme.colorScheme.surfaceContainerHigh), @@ -151,6 +152,7 @@ class NaviRailItem extends StatelessWidget { ], ), ), + // #Pangea if (expanded) Flexible( child: Container( @@ -174,6 +176,7 @@ class NaviRailItem extends StatelessWidget { ), ), ), + // Pangea# ], ); }, diff --git a/lib/pages/chat_list/search_title.dart b/lib/pages/chat_list/search_title.dart index 496a5feec..95bb5364d 100644 --- a/lib/pages/chat_list/search_title.dart +++ b/lib/pages/chat_list/search_title.dart @@ -22,14 +22,8 @@ class SearchTitle extends StatelessWidget { return Material( shape: Border( - top: BorderSide( - color: theme.dividerColor, - width: 1, - ), - bottom: BorderSide( - color: theme.dividerColor, - width: 1, - ), + top: BorderSide(color: theme.dividerColor, width: 1), + bottom: BorderSide(color: theme.dividerColor, width: 1), ), color: color ?? theme.colorScheme.surface, child: InkWell( @@ -38,10 +32,7 @@ class SearchTitle extends StatelessWidget { child: Align( alignment: Alignment.centerLeft, child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: IconTheme( data: theme.iconTheme.copyWith(size: 16), child: Row( diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 294bad50e..16058eb74 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; @@ -8,26 +10,30 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; -import 'package:fluffychat/pages/chat_list/search_title.dart'; +import 'package:fluffychat/pages/chat_list/unread_bubble.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/stream_extension.dart'; +import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/public_room_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; +import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/matrix.dart'; enum AddRoomType { chat, subspace } +enum SpaceChildAction { edit, moveToSpace, removeFromSpace } + +enum SpaceActions { settings, invite, members, leave } + class SpaceView extends StatefulWidget { final String spaceId; final void Function() onBack; - final void Function(String spaceId) toParentSpace; final void Function(Room room) onChatTab; - final void Function(Room room, BuildContext context) onChatContext; final String? activeChat; const SpaceView({ @@ -35,8 +41,6 @@ class SpaceView extends StatefulWidget { required this.onBack, required this.onChatTab, required this.activeChat, - required this.toParentSpace, - required this.onChatContext, super.key, }); @@ -45,7 +49,7 @@ class SpaceView extends StatefulWidget { } class _SpaceViewState extends State { - final List _discoveredChildren = []; + final List _discoveredChildren = []; final TextEditingController _filterController = TextEditingController(); String? _nextBatch; bool _noMoreRooms = false; @@ -58,9 +62,28 @@ class _SpaceViewState extends State { } void _loadHierarchy() async { - final room = Matrix.of(context).client.getRoomById(widget.spaceId); + final matrix = Matrix.of(context); + final room = matrix.client.getRoomById(widget.spaceId); if (room == null) return; + final cacheKey = 'spaces_history_cache${room.id}'; + if (_discoveredChildren.isEmpty) { + final cachedChildren = matrix.store.getStringList(cacheKey); + if (cachedChildren != null) { + try { + _discoveredChildren.addAll( + cachedChildren.map( + (jsonString) => + SpaceRoomsChunk$2.fromJson(jsonDecode(jsonString)), + ), + ); + } catch (e, s) { + Logs().e('Unable to json decode spaces hierarchy cache!', e, s); + matrix.store.remove(cacheKey); + } + } + } + setState(() { _isLoading = true; }); @@ -74,28 +97,38 @@ class _SpaceViewState extends State { ); if (!mounted) return; setState(() { + if (_nextBatch == null) _discoveredChildren.clear(); _nextBatch = hierarchy.nextBatch; if (hierarchy.nextBatch == null) { _noMoreRooms = true; } _discoveredChildren.addAll( - hierarchy.rooms - .where((c) => room.client.getRoomById(c.roomId) == null), + hierarchy.rooms.where((room) => room.roomId != widget.spaceId), ); _isLoading = false; }); + + if (_nextBatch == null) { + matrix.store.setStringList( + cacheKey, + _discoveredChildren + .map((child) => jsonEncode(child.toJson())) + .toList(), + ); + } } catch (e, s) { Logs().w('Unable to load hierarchy', e, s); if (!mounted) return; - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); setState(() { _isLoading = false; }); } } - void _joinChildRoom(SpaceRoomsChunk item) async { + void _joinChildRoom(SpaceRoomsChunk$2 item) async { final client = Matrix.of(context).client; final space = client.getRoomById(widget.spaceId); @@ -104,16 +137,12 @@ class _SpaceViewState extends State { builder: (_) => PublicRoomDialog( chunk: item, via: space?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == item.roomId, - ) + .firstWhereOrNull((child) => child.roomId == item.roomId) ?.via, ), ); if (mounted && joined == true) { - setState(() { - _discoveredChildren.remove(item); - }); + setState(() {}); } } @@ -129,6 +158,10 @@ class _SpaceViewState extends State { await space?.postLoad(); context.push('/rooms/${widget.spaceId}/invite'); break; + case SpaceActions.members: + await space?.postLoad(); + context.push('/rooms/${widget.spaceId}/details/members'); + break; case SpaceActions.leave: final confirmed = await showOkCancelAlertDialog( context: context, @@ -151,27 +184,11 @@ class _SpaceViewState extends State { } } - void _addChatOrSubspace() async { - final roomType = await showModalActionPopup( - context: context, - title: L10n.of(context).addChatOrSubSpace, - actions: [ - AdaptiveModalAction( - value: AddRoomType.subspace, - label: L10n.of(context).createNewSpace, - ), - AdaptiveModalAction( - value: AddRoomType.chat, - label: L10n.of(context).createGroup, - ), - ], - ); - if (roomType == null) return; - + void _addChatOrSubspace(AddRoomType roomType) async { final names = await showTextInputDialog( context: context, title: roomType == AddRoomType.subspace - ? L10n.of(context).createNewSpace + ? L10n.of(context).newSubSpace : L10n.of(context).createGroup, hintText: roomType == AddRoomType.subspace ? L10n.of(context).spaceName @@ -196,29 +213,172 @@ class _SpaceViewState extends State { late final String roomId; final activeSpace = client.getRoomById(widget.spaceId)!; await activeSpace.postLoad(); + final isPublicSpace = activeSpace.joinRules == JoinRules.public; if (roomType == AddRoomType.subspace) { roomId = await client.createSpace( name: names, - visibility: activeSpace.joinRules == JoinRules.public + visibility: isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private, ); } else { roomId = await client.createGroupChat( + enableEncryption: !isPublicSpace, groupName: names, - preset: activeSpace.joinRules == JoinRules.public + preset: isPublicSpace ? CreateRoomPreset.publicChat : CreateRoomPreset.privateChat, - visibility: activeSpace.joinRules == JoinRules.public + visibility: isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private, + initialState: isPublicSpace + ? null + : [ + StateEvent( + content: { + 'join_rule': 'restricted', + 'allow': [ + { + 'room_id': widget.spaceId, + 'type': 'm.room_membership', + }, + ], + }, + type: EventTypes.RoomJoinRules, + ), + ], ); } await activeSpace.setSpaceChild(roomId); }, ); if (result.error != null) return; + setState(() { + _nextBatch = null; + _discoveredChildren.clear(); + }); + _loadHierarchy(); + } + + void _showSpaceChildEditMenu(BuildContext posContext, String roomId) async { + final overlay = + Overlay.of(posContext).context.findRenderObject() as RenderBox; + + final button = posContext.findRenderObject() as RenderBox; + + final position = RelativeRect.fromRect( + Rect.fromPoints( + button.localToGlobal(const Offset(0, -65), ancestor: overlay), + button.localToGlobal( + button.size.bottomRight(Offset.zero) + const Offset(-50, 0), + ancestor: overlay, + ), + ), + Offset.zero & overlay.size, + ); + + final action = await showMenu( + context: posContext, + position: position, + items: [ + PopupMenuItem( + value: SpaceChildAction.moveToSpace, + child: Row( + mainAxisSize: .min, + children: [ + const Icon(Icons.move_down_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).moveToDifferentSpace), + ], + ), + ), + PopupMenuItem( + value: SpaceChildAction.edit, + child: Row( + mainAxisSize: .min, + children: [ + const Icon(Icons.edit_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).edit), + ], + ), + ), + PopupMenuItem( + value: SpaceChildAction.removeFromSpace, + child: Row( + mainAxisSize: .min, + children: [ + const Icon(Icons.group_remove_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).removeFromSpace), + ], + ), + ), + ], + ); + if (action == null) return; + if (!mounted) return; + final space = Matrix.of(context).client.getRoomById(widget.spaceId); + if (space == null) return; + switch (action) { + case SpaceChildAction.edit: + context.push('/rooms/${widget.spaceId}/details'); + case SpaceChildAction.moveToSpace: + final spacesWithPowerLevels = space.client.rooms + .where( + (room) => + room.isSpace && + room.canChangeStateEvent(EventTypes.SpaceChild) && + room.id != widget.spaceId, + ) + .toList(); + final newSpace = await showModalActionPopup( + context: context, + title: L10n.of(context).space, + actions: spacesWithPowerLevels + .map( + (space) => AdaptiveModalAction( + value: space, + label: space.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)), + ), + ), + ) + .toList(), + ); + if (newSpace == null) return; + final result = await showFutureLoadingDialog( + context: context, + future: () async { + await newSpace.setSpaceChild(newSpace.id); + await space.removeSpaceChild(roomId); + }, + ); + if (result.isError) return; + if (!mounted) return; + _nextBatch = null; + _loadHierarchy(); + return; + + case SpaceChildAction.removeFromSpace: + final consent = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).removeFromSpace, + message: L10n.of(context).removeFromSpaceDescription, + ); + if (consent != OkCancelResult.ok) return; + if (!mounted) return; + final result = await showFutureLoadingDialog( + context: context, + future: () => space.removeSpaceChild(roomId), + ); + if (result.isError) return; + if (!mounted) return; + _nextBatch = null; + _loadHierarchy(); + return; + } } @override @@ -228,20 +388,19 @@ class _SpaceViewState extends State { final room = Matrix.of(context).client.getRoomById(widget.spaceId); final displayname = room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound; + const avatarSize = Avatar.defaultSize / 1.5; + final isAdmin = room?.canChangeStateEvent(EventTypes.SpaceChild) == true; return Scaffold( appBar: AppBar( leading: FluffyThemes.isColumnMode(context) ? null - : Center( - child: CloseButton( - onPressed: widget.onBack, - ), - ), + : Center(child: CloseButton(onPressed: widget.onBack)), automaticallyImplyLeading: false, titleSpacing: FluffyThemes.isColumnMode(context) ? null : 0, title: ListTile( contentPadding: EdgeInsets.zero, leading: Avatar( + size: avatarSize, mxContent: room?.avatar, name: displayname, border: BorderSide(width: 1, color: theme.dividerColor), @@ -252,18 +411,38 @@ class _SpaceViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - subtitle: room == null - ? null - : Text( - L10n.of(context).countChatsAndCountParticipants( - room.spaceChildren.length, - room.summary.mJoinedMemberCount ?? 1, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), ), actions: [ + if (isAdmin) + PopupMenuButton( + icon: const Icon(Icons.add_outlined), + onSelected: _addChatOrSubspace, + tooltip: L10n.of(context).addChatOrSubSpace, + itemBuilder: (context) => [ + PopupMenuItem( + value: AddRoomType.chat, + child: Row( + mainAxisSize: .min, + children: [ + const Icon(Icons.group_add_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).newGroup), + ], + ), + ), + PopupMenuItem( + value: AddRoomType.subspace, + child: Row( + mainAxisSize: .min, + children: [ + const Icon(Icons.workspaces_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).newSubSpace), + ], + ), + ), + ], + ), PopupMenuButton( useRootNavigator: true, onSelected: _onSpaceAction, @@ -271,7 +450,7 @@ class _SpaceViewState extends State { PopupMenuItem( value: SpaceActions.settings, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.settings_outlined), const SizedBox(width: 12), @@ -282,7 +461,7 @@ class _SpaceViewState extends State { PopupMenuItem( value: SpaceActions.invite, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.person_add_outlined), const SizedBox(width: 12), @@ -290,10 +469,25 @@ class _SpaceViewState extends State { ], ), ), + PopupMenuItem( + value: SpaceActions.members, + child: Row( + mainAxisSize: .min, + children: [ + const Icon(Icons.group_outlined), + const SizedBox(width: 12), + Text( + L10n.of(context).countParticipants( + room?.summary.mJoinedMemberCount ?? 1, + ), + ), + ], + ), + ), PopupMenuItem( value: SpaceActions.leave, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.delete_outlined), const SizedBox(width: 12), @@ -305,51 +499,18 @@ class _SpaceViewState extends State { ), ], ), - floatingActionButton: room?.canChangeStateEvent( - EventTypes.SpaceChild, - ) == - true - ? FloatingActionButton.extended( - onPressed: _addChatOrSubspace, - label: Text(L10n.of(context).group), - icon: const Icon(Icons.group_add_outlined), - ) - : null, body: room == null - ? const Center( - child: Icon( - Icons.search_outlined, - size: 80, - ), - ) + ? const Center(child: Icon(Icons.search_outlined, size: 80)) : StreamBuilder( stream: room.client.onSync.stream .where((s) => s.hasRoomUpdate) .rateLimit(const Duration(seconds: 1)), builder: (context, snapshot) { - final childrenIds = room.spaceChildren - .map((c) => c.roomId) - .whereType() - .toSet(); - - final joinedRooms = room.client.rooms - .where((room) => childrenIds.remove(room.id)) - .toList(); - - final joinedParents = room.spaceParents - .map((parent) { - final roomId = parent.roomId; - if (roomId == null) return null; - return room.client.getRoomById(roomId); - }) - .whereType() - .toList(); final filter = _filterController.text.trim().toLowerCase(); return CustomScrollView( slivers: [ SliverAppBar( floating: true, - toolbarHeight: 72, scrolledUnderElevation: 0, backgroundColor: Colors.transparent, automaticallyImplyLeading: false, @@ -359,11 +520,6 @@ class _SpaceViewState extends State { textInputAction: TextInputAction.search, decoration: InputDecoration( filled: true, - fillColor: theme.colorScheme.secondaryContainer, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(99), - ), contentPadding: EdgeInsets.zero, hintText: L10n.of(context).search, hintStyle: TextStyle( @@ -382,83 +538,11 @@ class _SpaceViewState extends State { ), ), SliverList.builder( - itemCount: joinedParents.length, + itemCount: _discoveredChildren.length + 1, itemBuilder: (context, i) { - final displayname = - joinedParents[i].getLocalizedDisplayname(); - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), - child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - clipBehavior: Clip.hardEdge, - child: ListTile( - minVerticalPadding: 0, - leading: Icon( - Icons.adaptive.arrow_back_outlined, - size: 16, - ), - title: Row( - children: [ - Avatar( - mxContent: joinedParents[i].avatar, - name: displayname, - size: Avatar.defaultSize / 2, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 4, - ), - ), - const SizedBox(width: 8), - Expanded(child: Text(displayname)), - ], - ), - onTap: () => - widget.toParentSpace(joinedParents[i].id), - ), - ), - ); - }, - ), - SliverList.builder( - itemCount: joinedRooms.length, - itemBuilder: (context, i) { - final joinedRoom = joinedRooms[i]; - return ChatListItem( - joinedRoom, - filter: filter, - onTap: () => widget.onChatTab(joinedRoom), - onLongPress: (context) => widget.onChatContext( - joinedRoom, - context, - ), - activeChat: widget.activeChat == joinedRoom.id, - ); - }, - ), - SliverList.builder( - itemCount: _discoveredChildren.length + 2, - itemBuilder: (context, i) { - if (i == 0) { - return SearchTitle( - title: L10n.of(context).discover, - icon: const Icon(Icons.explore_outlined), - ); - } - i--; if (i == _discoveredChildren.length) { if (_noMoreRooms) { - return Padding( - padding: const EdgeInsets.all(12.0), - child: Center( - child: Text( - L10n.of(context).noMoreChatsFound, - style: const TextStyle(fontSize: 13), - ), - ), - ); + return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric( @@ -468,76 +552,117 @@ class _SpaceViewState extends State { child: TextButton( onPressed: _isLoading ? null : _loadHierarchy, child: _isLoading - ? LinearProgressIndicator( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ) + ? const CircularProgressIndicator.adaptive() : Text(L10n.of(context).loadMore), ), ); } final item = _discoveredChildren[i]; - final displayname = item.name ?? + final displayname = + item.name ?? item.canonicalAlias ?? L10n.of(context).emptyChat; if (!displayname.toLowerCase().contains(filter)) { return const SizedBox.shrink(); } + var joinedRoom = room.client.getRoomById(item.roomId); + if (joinedRoom?.membership == Membership.leave) { + joinedRoom = null; + } return Padding( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 1, ), child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), clipBehavior: Clip.hardEdge, - child: ListTile( - visualDensity: - const VisualDensity(vertical: -0.5), - contentPadding: - const EdgeInsets.symmetric(horizontal: 8), - onTap: () => _joinChildRoom(item), - leading: Avatar( - mxContent: item.avatarUrl, - name: displayname, - borderRadius: item.roomType == 'm.space' - ? BorderRadius.circular( - AppConfig.borderRadius / 2, + color: + joinedRoom != null && + widget.activeChat == joinedRoom.id + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + child: HoverBuilder( + builder: (context, hovered) => ListTile( + visualDensity: const VisualDensity( + vertical: -0.5, + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 8, + ), + onTap: joinedRoom != null + ? () => widget.onChatTab(joinedRoom!) + : () => _joinChildRoom(item), + onLongPress: isAdmin + ? () => _showSpaceChildEditMenu( + context, + item.roomId, ) : null, - ), - title: Row( - children: [ - Expanded( - child: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, + leading: hovered && isAdmin + ? SizedBox.square( + dimension: avatarSize, + child: IconButton( + splashRadius: avatarSize, + iconSize: 14, + style: IconButton.styleFrom( + foregroundColor: theme + .colorScheme + .onTertiaryContainer, + backgroundColor: theme + .colorScheme + .tertiaryContainer, + ), + onPressed: () => + _showSpaceChildEditMenu( + context, + item.roomId, + ), + icon: const Icon(Icons.edit_outlined), + ), + ) + : Avatar( + size: avatarSize, + mxContent: item.avatarUrl, + name: '#', + backgroundColor: + theme.colorScheme.surfaceContainer, + textColor: + item.name?.darkColor ?? + theme.colorScheme.onSurface, + border: item.roomType == 'm.space' + ? BorderSide( + color: theme + .colorScheme + .surfaceContainerHighest, + ) + : null, + borderRadius: item.roomType == 'm.space' + ? BorderRadius.circular( + AppConfig.borderRadius / 4, + ) + : null, + ), + title: Row( + children: [ + Expanded( + child: Opacity( + opacity: joinedRoom == null ? 0.5 : 1, + child: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), ), - ), - Text( - item.numJoinedMembers.toString(), - style: TextStyle( - fontSize: 13, - color: theme.textTheme.bodyMedium!.color, - ), - ), - const SizedBox(width: 4), - const Icon( - Icons.people_outlined, - size: 14, - ), - ], - ), - subtitle: Text( - item.topic ?? - L10n.of(context).countParticipants( - item.numJoinedMembers, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, + if (joinedRoom != null) + UnreadBubble(room: joinedRoom) + else + const Icon(Icons.chevron_right_outlined), + ], + ), ), ), ), @@ -552,9 +677,3 @@ class _SpaceViewState extends State { ); } } - -enum SpaceActions { - settings, - invite, - leave, -} diff --git a/lib/pages/chat_list/status_msg_list.dart b/lib/pages/chat_list/status_msg_list.dart index 15402c99a..aa604748a 100644 --- a/lib/pages/chat_list/status_msg_list.dart +++ b/lib/pages/chat_list/status_msg_list.dart @@ -13,10 +13,7 @@ import '../../widgets/adaptive_dialogs/user_dialog.dart'; class StatusMessageList extends StatelessWidget { final void Function() onStatusEdit; - const StatusMessageList({ - required this.onStatusEdit, - super.key, - }); + const StatusMessageList({required this.onStatusEdit, super.key}); static const double height = 116; @@ -24,10 +21,7 @@ class StatusMessageList extends StatelessWidget { final client = Matrix.of(context).client; if (profile.userId == client.userID) return onStatusEdit(); - UserDialog.show( - context: context, - profile: profile, - ); + UserDialog.show(context: context, profile: profile); return; } @@ -56,8 +50,9 @@ class StatusMessageList extends StatelessWidget { ), ), builder: (context, snapshot) { - final presences = - snapshot.data?.where(isInterestingPresence).toList(); + final presences = snapshot.data + ?.where(isInterestingPresence) + .toList(); // If no other presences than the own entry is interesting, we // hide the presence header. @@ -76,7 +71,12 @@ class StatusMessageList extends StatelessWidget { return SizedBox( height: StatusMessageList.height, child: ListView.builder( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.only( + left: 8.0, + right: 8.0, + top: 8.0, + bottom: 6.0, + ), scrollDirection: Axis.horizontal, itemCount: presences.length, itemBuilder: (context, i) => PresenceAvatar( @@ -118,7 +118,7 @@ class PresenceAvatar extends StatelessWidget { @override Widget build(BuildContext context) { - final avatarSize = height - 16 - 16 - 8; + final avatarSize = height - 16 - 16 - 6; final client = Matrix.of(context).client; return FutureBuilder( future: client.getProfileFromUserId(presence.userid), @@ -126,7 +126,8 @@ class PresenceAvatar extends StatelessWidget { final theme = Theme.of(context); final profile = snapshot.data; - final displayName = profile?.displayName ?? + final displayName = + profile?.displayName ?? presence.userid.localpart ?? presence.userid; final statusMsg = presence.statusMsg; @@ -158,19 +159,33 @@ class PresenceAvatar extends StatelessWidget { decoration: BoxDecoration( // #Pangea // gradient: presence.gradient, - gradient: gradient ?? + gradient: + gradient ?? (showPresence ? presence.gradient : null), // Pangea# - borderRadius: - BorderRadius.circular(avatarSize), + borderRadius: BorderRadius.circular( + avatarSize, + ), ), - child: Avatar( - name: displayName, - // #Pangea - userId: profile?.userId, - // Pangea# - mxContent: profile?.avatarUrl, - size: avatarSize - 6, + alignment: Alignment.center, + child: Container( + height: avatarSize - 6, + alignment: Alignment.center, + decoration: BoxDecoration( + color: theme.colorScheme.surface, + borderRadius: BorderRadius.circular( + avatarSize, + ), + ), + padding: const EdgeInsets.all(3.0), + child: Avatar( + name: displayName, + mxContent: profile?.avatarUrl, + size: avatarSize - 12, + // #Pangea + userId: profile?.userId, + // Pangea# + ), ), ), // #Pangea @@ -201,64 +216,76 @@ class PresenceAvatar extends StatelessWidget { ), ), // #Pangea - if (floatingIndicator != null) floatingIndicator!, + ?floatingIndicator, // Pangea# if (statusMsg != null) ...[ Positioned( left: 0, top: 0, right: 8, - child: Material( - elevation: statusMsgBubbleElevation, - shadowColor: statusMsgBubbleShadowColor, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 2, - ), - color: statusMsgBubbleColor, - child: Padding( - padding: const EdgeInsets.all(2.0), - child: Text( - statusMsg, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - color: Colors.black, - fontSize: 10.5, + child: Column( + spacing: 2, + crossAxisAlignment: .start, + mainAxisSize: .min, + children: [ + Material( + elevation: statusMsgBubbleElevation, + shadowColor: statusMsgBubbleShadowColor, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 2, + ), + color: statusMsgBubbleColor, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 2.0, + horizontal: 4.0, + ), + child: Text( + statusMsg, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Colors.black, + fontSize: 9, + ), + ), ), ), - ), - ), - ), - Positioned( - left: 8, - top: 32, - child: Material( - color: statusMsgBubbleColor, - elevation: statusMsgBubbleElevation, - shadowColor: statusMsgBubbleShadowColor, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 2, - ), - child: const SizedBox( - width: 8, - height: 8, - ), - ), - ), - Positioned( - left: 14, - top: 40, - child: Material( - color: statusMsgBubbleColor, - elevation: statusMsgBubbleElevation, - shadowColor: statusMsgBubbleShadowColor, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 2, - ), - child: const SizedBox( - width: 4, - height: 4, - ), + Padding( + padding: const EdgeInsets.only( + left: 8.0, + ), + child: Material( + color: statusMsgBubbleColor, + elevation: statusMsgBubbleElevation, + shadowColor: + statusMsgBubbleShadowColor, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + child: const SizedBox.square( + dimension: 8, + ), + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 13.0, + ), + child: Material( + color: statusMsgBubbleColor, + elevation: statusMsgBubbleElevation, + shadowColor: + statusMsgBubbleShadowColor, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + child: const SizedBox.square( + dimension: 5, + ), + ), + ), + ], ), ), ], @@ -277,9 +304,7 @@ class PresenceAvatar extends StatelessWidget { textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 13, - ), + style: const TextStyle(fontSize: 11), ), ), ], @@ -293,10 +318,12 @@ class PresenceAvatar extends StatelessWidget { extension on Client { Set get interestingPresences { - final allHeroes = rooms.map((room) => room.summary.mHeroes).fold( - {}, - (previousValue, element) => previousValue..addAll(element ?? {}), - ); + final allHeroes = rooms + .map((room) => room.summary.mHeroes) + .fold( + {}, + (previousValue, element) => previousValue..addAll(element ?? {}), + ); allHeroes.add(userID!); return allHeroes; } @@ -314,31 +341,23 @@ extension on CachedPresence { LinearGradient get gradient => presence.isOnline == true ? LinearGradient( - colors: [ - Colors.green, - Colors.green.shade200, - Colors.green.shade900, - ], + colors: [Colors.green, Colors.green.shade200, Colors.green.shade900], begin: Alignment.topLeft, end: Alignment.bottomRight, ) : presence.isUnavailable - ? LinearGradient( - colors: [ - Colors.yellow, - Colors.yellow.shade200, - Colors.yellow.shade900, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ) - : LinearGradient( - colors: [ - Colors.grey, - Colors.grey.shade200, - Colors.grey.shade900, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ); + ? LinearGradient( + colors: [ + Colors.yellow, + Colors.yellow.shade200, + Colors.yellow.shade900, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) + : LinearGradient( + colors: [Colors.grey, Colors.grey.shade200, Colors.grey.shade900], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ); } diff --git a/lib/pages/chat_list/unread_bubble.dart b/lib/pages/chat_list/unread_bubble.dart new file mode 100644 index 000000000..29266ff0f --- /dev/null +++ b/lib/pages/chat_list/unread_bubble.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/config/themes.dart'; + +class UnreadBubble extends StatelessWidget { + final Room room; + const UnreadBubble({required this.room, super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final unread = room.isUnread; + final hasNotifications = room.notificationCount > 0; + final unreadBubbleSize = unread || room.hasNewMessages + ? room.notificationCount > 0 + ? 20.0 + : 14.0 + : 0.0; + return AnimatedContainer( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(horizontal: 7), + height: unreadBubbleSize, + width: !hasNotifications && !unread && !room.hasNewMessages + ? 0 + : (unreadBubbleSize - 9) * room.notificationCount.toString().length + + 9, + decoration: BoxDecoration( + color: room.highlightCount > 0 + ? theme.colorScheme.error + : hasNotifications || room.markedUnread + ? theme.colorScheme.primary + : theme.colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(7), + ), + child: hasNotifications + ? Text( + room.notificationCount.toString(), + style: TextStyle( + color: room.highlightCount > 0 + ? theme.colorScheme.onError + : hasNotifications + ? theme.colorScheme.onPrimary + : theme.colorScheme.onPrimaryContainer, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ) + : const SizedBox.shrink(), + ); + } +} diff --git a/lib/pages/chat_members/chat_members.dart b/lib/pages/chat_members/chat_members.dart index 971b538f4..7c785e84c 100644 --- a/lib/pages/chat_members/chat_members.dart +++ b/lib/pages/chat_members/chat_members.dart @@ -15,11 +15,7 @@ class ChatMembersPage extends StatefulWidget { // #Pangea // const ChatMembersPage({required this.roomId, super.key}); - const ChatMembersPage({ - required this.roomId, - this.filter, - super.key, - }); + const ChatMembersPage({required this.roomId, this.filter, super.key}); // Pangea# @override @@ -55,11 +51,10 @@ class ChatMembersController extends State { setFilter(); } - void setFilter([_]) async { + void setFilter([dynamic _]) async { final filter = filterController.text.toLowerCase().trim(); - final members = this - .members + final members = this.members ?.where((member) => member.membership == membershipFilter) .toList(); @@ -71,25 +66,25 @@ class ChatMembersController extends State { return; } setState(() { - filteredMembers = members - ?.where( - (user) => - user.displayName?.toLowerCase().contains(filter) ?? - user.id.toLowerCase().contains(filter), - ) - .toList() - ?..sort((b, a) => a.powerLevel.compareTo(b.powerLevel)); + filteredMembers = + members + ?.where( + (user) => + user.displayName?.toLowerCase().contains(filter) ?? + user.id.toLowerCase().contains(filter), + ) + .toList() + ?..sort((b, a) => a.powerLevel.compareTo(b.powerLevel)); }); } - void refreshMembers([_]) async { + void refreshMembers([dynamic _]) async { Logs().d('Load room members from', widget.roomId); try { setState(() { error = null; }); - final participants = await Matrix.of(context) - .client + final participants = await Matrix.of(context).client .getRoomById(widget.roomId) ?.requestParticipants( // #Pangea @@ -107,9 +102,7 @@ class ChatMembersController extends State { // #Pangea final availableFilters = (participants ?? []) - .map( - (p) => p.membership, - ) + .map((p) => p.membership) .toSet(); if (availableFilters.length == 1 && @@ -123,8 +116,11 @@ class ChatMembersController extends State { }); setFilter(); } catch (e, s) { - Logs() - .d('Unable to request participants. Try again in 3 seconds...', e, s); + Logs().d( + 'Unable to request participants. Try again in 3 seconds...', + e, + s, + ); setState(() { error = e; }); @@ -138,14 +134,12 @@ class ChatMembersController extends State { super.initState(); refreshMembers(); - _updateSub = Matrix.of(context) - .client - .onSync - .stream + _updateSub = Matrix.of(context).client.onSync.stream .where( (syncUpdate) => - syncUpdate.rooms?.join?[widget.roomId]?.timeline?.events - ?.any((state) => state.type == EventTypes.RoomMember) ?? + syncUpdate.rooms?.join?[widget.roomId]?.timeline?.events?.any( + (state) => state.type == EventTypes.RoomMember, + ) ?? false, ) .listen(refreshMembers); diff --git a/lib/pages/chat_members/chat_members_view.dart b/lib/pages/chat_members/chat_members_view.dart index e4c812e8a..89707a100 100644 --- a/lib/pages/chat_members/chat_members_view.dart +++ b/lib/pages/chat_members/chat_members_view.dart @@ -17,13 +17,12 @@ class ChatMembersView extends StatelessWidget { @override Widget build(BuildContext context) { - final room = - Matrix.of(context).client.getRoomById(controller.widget.roomId); + final room = Matrix.of( + context, + ).client.getRoomById(controller.widget.roomId); if (room == null) { return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).oopsSomethingWentWrong), - ), + appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)), body: Center( child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), ), @@ -32,7 +31,8 @@ class ChatMembersView extends StatelessWidget { final members = controller.filteredMembers; - final roomCount = (room.summary.mJoinedMemberCount ?? 0) + + final roomCount = + (room.summary.mJoinedMemberCount ?? 0) + (room.summary.mInvitedMemberCount ?? 0); final error = controller.error; @@ -41,16 +41,12 @@ class ChatMembersView extends StatelessWidget { return Scaffold( appBar: AppBar( leading: const Center(child: BackButton()), - title: Text( - L10n.of(context).countParticipants(roomCount), - ), + title: Text(L10n.of(context).countParticipants(roomCount)), actions: [ if (room.canInvite) IconButton( onPressed: () => context.go('/rooms/${room.id}/invite'), - icon: const Icon( - Icons.person_add_outlined, - ), + icon: const Icon(Icons.person_add_outlined), ), ], ), @@ -62,7 +58,7 @@ class ChatMembersView extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(16.0), child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.error_outline), Text(error.toLocalizedString(context)), @@ -77,120 +73,117 @@ class ChatMembersView extends StatelessWidget { ), ) : members == null - ? const Center( - child: Padding( - padding: EdgeInsets.all(16.0), - child: CircularProgressIndicator.adaptive(), - ), - ) - : ListView.builder( - shrinkWrap: true, - itemCount: members.length + 1, - itemBuilder: (context, i) { - if (i == 0) { - final availableFilters = Membership.values - .where( - (membership) => - controller.members?.any( - (member) => member.membership == membership, - ) ?? - false, - ) - .toList(); - availableFilters - .sort((a, b) => a == Membership.join ? -1 : 1); - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: TextField( - controller: controller.filterController, - onChanged: controller.setFilter, - decoration: InputDecoration( - filled: true, - fillColor: - theme.colorScheme.secondaryContainer, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(99), - ), - hintStyle: TextStyle( - color: theme.colorScheme.onPrimaryContainer, - fontWeight: FontWeight.normal, - ), - prefixIcon: const Icon(Icons.search_outlined), - hintText: L10n.of(context).search, + ? const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: CircularProgressIndicator.adaptive(), + ), + ) + : ListView.builder( + shrinkWrap: true, + itemCount: members.length + 1, + itemBuilder: (context, i) { + if (i == 0) { + final availableFilters = Membership.values + .where( + (membership) => + controller.members?.any( + (member) => member.membership == membership, + ) ?? + false, + ) + .toList(); + availableFilters.sort( + (a, b) => a == Membership.join ? -1 : 1, + ); + return Column( + mainAxisSize: .min, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + controller: controller.filterController, + onChanged: controller.setFilter, + decoration: InputDecoration( + filled: true, + fillColor: theme.colorScheme.secondaryContainer, + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(99), + ), + hintStyle: TextStyle( + color: theme.colorScheme.onPrimaryContainer, + fontWeight: FontWeight.normal, + ), + prefixIcon: const Icon(Icons.search_outlined), + hintText: L10n.of(context).search, + ), + ), + ), + if (availableFilters.length > 1) + SizedBox( + height: 64, + child: ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 12.0, + ), + scrollDirection: Axis.horizontal, + itemCount: availableFilters.length, + itemBuilder: (context, i) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + ), + child: FilterChip( + label: Text(switch (availableFilters[i]) { + Membership.ban => L10n.of(context).banned, + Membership.invite => + L10n.of(context).countInvited( + room.summary.mInvitedMemberCount ?? + controller.members + ?.where( + (member) => + member.membership == + Membership.invite, + ) + .length ?? + 0, + ), + Membership.join => + L10n.of(context).countParticipants( + room.summary.mJoinedMemberCount ?? + controller.members + ?.where( + (member) => + member.membership == + Membership.join, + ) + .length ?? + 0, + ), + Membership.knock => L10n.of( + context, + ).knocking, + Membership.leave => L10n.of( + context, + ).leftTheChat, + }), + selected: + controller.membershipFilter == + availableFilters[i], + onSelected: (_) => controller + .setMembershipFilter(availableFilters[i]), ), ), ), - if (availableFilters.length > 1) - SizedBox( - height: 64, - child: ListView.builder( - padding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 12.0, - ), - scrollDirection: Axis.horizontal, - itemCount: availableFilters.length, - itemBuilder: (context, i) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - ), - child: FilterChip( - label: Text( - switch (availableFilters[i]) { - Membership.ban => - L10n.of(context).banned, - Membership.invite => - L10n.of(context).countInvited( - room.summary - .mInvitedMemberCount ?? - controller.members - ?.where( - (member) => - member.membership == - Membership.invite, - ) - .length ?? - 0, - ), - Membership.join => - L10n.of(context).countParticipants( - room.summary.mJoinedMemberCount ?? - controller.members - ?.where( - (member) => - member.membership == - Membership.join, - ) - .length ?? - 0, - ), - Membership.knock => - L10n.of(context).knocking, - Membership.leave => - L10n.of(context).leftTheChat, - }, - ), - selected: controller.membershipFilter == - availableFilters[i], - onSelected: (_) => - controller.setMembershipFilter( - availableFilters[i], - ), - ), - ), - ), - ), - ], - ); - } - i--; - return ParticipantListItem(members[i]); - }, - ), + ), + ], + ); + } + i--; + return ParticipantListItem(members[i]); + }, + ), ), ); } diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart index cc24f2e1e..10aa9cfb5 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart @@ -38,9 +38,9 @@ class ChatPermissionsSettingsController extends State { }) async { final room = Matrix.of(context).client.getRoomById(roomId!)!; if (!room.canSendEvent(EventTypes.RoomPowerLevels)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context).noPermission)), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(L10n.of(context).noPermission))); return; } newLevel ??= await showPermissionChooser( @@ -72,12 +72,13 @@ class ChatPermissionsSettingsController extends State { } Stream get onChanged => Matrix.of(context).client.onSync.stream.where( - (e) => - (e.rooms?.join?.containsKey(roomId) ?? false) && - (e.rooms!.join![roomId!]?.timeline?.events - ?.any((s) => s.type == EventTypes.RoomPowerLevels) ?? - false), - ); + (e) => + (e.rooms?.join?.containsKey(roomId) ?? false) && + (e.rooms!.join![roomId!]?.timeline?.events?.any( + (s) => s.type == EventTypes.RoomPowerLevels, + ) ?? + false), + ); // #Pangea Map get defaultPowerLevels { @@ -96,10 +97,7 @@ class ChatPermissionsSettingsController extends State { return room.isSpace ? spacePowerLevels : chatPowerLevels; } - int getDefaultValue( - String permissionKey, { - String? category, - }) { + int getDefaultValue(String permissionKey, {String? category}) { final room = Matrix.of(context).client.getRoomById(roomId!); if (room == null) return 0; final powerLevelsContent = Map.from( diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart index 64d2000bc..21c0e370c 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart @@ -65,11 +65,12 @@ class ChatPermissionsSettingsView extends StatelessWidget { (key, value) => MapEntry(key, controller.getDefaultValue(key)), ); - Map missingEventsPowerLevels = Map.from( - defaults.tryGetMap('events') ?? {}, - )..removeWhere( - (k, v) => v is! int || eventsPowerLevels.containsKey(k), - ); + Map missingEventsPowerLevels = + Map.from( + defaults.tryGetMap('events') ?? {}, + )..removeWhere( + (k, v) => v is! int || eventsPowerLevels.containsKey(k), + ); missingEventsPowerLevels = missingEventsPowerLevels.map( (key, value) => MapEntry( @@ -95,9 +96,7 @@ class ChatPermissionsSettingsView extends StatelessWidget { children: [ ListTile( leading: const Icon(Icons.info_outlined), - subtitle: Text( - L10n.of(context).chatPermissionsDescription, - ), + subtitle: Text(L10n.of(context).chatPermissionsDescription), ), Divider(color: theme.dividerColor), ListTile( @@ -113,7 +112,7 @@ class ChatPermissionsSettingsView extends StatelessWidget { ), ), Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ for (final entry in powerLevels.entries) PermissionsListTile( @@ -148,13 +147,13 @@ class ChatPermissionsSettingsView extends StatelessWidget { const key = 'rooms'; final value = powerLevelsContent.containsKey('notifications') - ? powerLevelsContent - .tryGetMap( - 'notifications', - ) - ?.tryGet('rooms') ?? - 0 - : 0; + ? powerLevelsContent + .tryGetMap( + 'notifications', + ) + ?.tryGet('rooms') ?? + 0 + : 0; return PermissionsListTile( permissionKey: key, permission: value, diff --git a/lib/pages/chat_permissions_settings/permission_list_tile.dart b/lib/pages/chat_permissions_settings/permission_list_tile.dart index d05f5de5a..bb61c5bb8 100644 --- a/lib/pages/chat_permissions_settings/permission_list_tile.dart +++ b/lib/pages/chat_permissions_settings/permission_list_tile.dart @@ -116,8 +116,8 @@ class PermissionsListTile extends StatelessWidget { final color = permission >= 100 ? Colors.orangeAccent : permission >= 50 - ? Colors.blueAccent - : Colors.greenAccent; + ? Colors.blueAccent + : Colors.greenAccent; return ListTile( title: Text( getLocalizedPowerLevelString(context), @@ -150,14 +150,12 @@ class PermissionsListTile extends StatelessWidget { DropdownMenuItem( value: permission >= 100 ? permission : 100, child: Text( - L10n.of(context) - .adminLevel(permission >= 100 ? permission : 100), + L10n.of( + context, + ).adminLevel(permission >= 100 ? permission : 100), ), ), - DropdownMenuItem( - value: null, - child: Text(L10n.of(context).custom), - ), + DropdownMenuItem(value: null, child: Text(L10n.of(context).custom)), ], ), ), diff --git a/lib/pages/chat_search/chat_search_files_tab.dart b/lib/pages/chat_search/chat_search_files_tab.dart index 81fdf5196..24d27cece 100644 --- a/lib/pages/chat_search/chat_search_files_tab.dart +++ b/lib/pages/chat_search/chat_search_files_tab.dart @@ -4,172 +4,106 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pages/chat_search/search_footer.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; class ChatSearchFilesTab extends StatelessWidget { final Room room; - final Stream<(List, String?)>? searchStream; - final void Function({ - String? prevBatch, - List? previousSearchResult, - }) startSearch; + final List events; + final void Function() onStartSearch; + final bool endReached, isLoading; + final DateTime? searchedUntil; const ChatSearchFilesTab({ required this.room, - required this.startSearch, - required this.searchStream, + required this.events, + required this.onStartSearch, + required this.endReached, + required this.isLoading, super.key, + required this.searchedUntil, }); @override Widget build(BuildContext context) { - return StreamBuilder( - stream: searchStream, - builder: (context, snapshot) { - final theme = Theme.of(context); - final events = snapshot.data?.$1; - if (searchStream == null || events == null) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CircularProgressIndicator.adaptive(strokeWidth: 2), - const SizedBox(height: 8), - Text( - L10n.of(context).searchIn( - room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), - ), - ), - ), - ], - ); - } - - if (events.isEmpty) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.file_present_outlined, size: 64), - const SizedBox(height: 8), - Text(L10n.of(context).nothingFound), - ], - ); - } - - return SelectionArea( - child: ListView.builder( + final theme = Theme.of(context); + return SelectionArea( + child: ListView.builder( + padding: const EdgeInsets.all(8.0), + itemCount: events.length + 1, + itemBuilder: (context, i) { + if (i == events.length) { + return SearchFooter( + searchedUntil: searchedUntil, + endReached: endReached, + isLoading: isLoading, + onStartSearch: onStartSearch, + ); + } + final event = events[i]; + final filename = + event.content.tryGet('filename') ?? + event.content.tryGet('body') ?? + L10n.of(context).unknownEvent('File'); + final filetype = (filename.contains('.') + ? filename.split('.').last.toUpperCase() + : event.content + .tryGetMap('info') + ?.tryGet('mimetype') + ?.toUpperCase() ?? + 'UNKNOWN'); + final sizeString = event.sizeString; + final prevEvent = i > 0 ? events[i - 1] : null; + final sameEnvironment = prevEvent == null + ? false + : prevEvent.originServerTs.sameEnvironment(event.originServerTs); + return Padding( padding: const EdgeInsets.all(8.0), - itemCount: events.length + 1, - itemBuilder: (context, i) { - if (i == events.length) { - if (snapshot.connectionState != ConnectionState.done) { - return const Padding( - padding: EdgeInsets.all(16.0), - child: Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (!sameEnvironment) ...[ + Row( + children: [ + Expanded( + child: Container(height: 1, color: theme.dividerColor), ), - ), - ); - } - final nextBatch = snapshot.data?.$2; - if (nextBatch == null) { - return const SizedBox.shrink(); - } - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: TextButton.icon( - style: TextButton.styleFrom( - backgroundColor: theme.colorScheme.secondaryContainer, - foregroundColor: theme.colorScheme.onSecondaryContainer, - ), - onPressed: () => startSearch( - prevBatch: nextBatch, - previousSearchResult: events, - ), - icon: const Icon( - Icons.arrow_downward_outlined, - ), - label: Text(L10n.of(context).searchMore), - ), - ), - ); - } - final event = events[i]; - final filename = event.content.tryGet('filename') ?? - event.content.tryGet('body') ?? - L10n.of(context).unknownEvent('File'); - final filetype = (filename.contains('.') - ? filename.split('.').last.toUpperCase() - : event.content - .tryGetMap('info') - ?.tryGet('mimetype') - ?.toUpperCase() ?? - 'UNKNOWN'); - final sizeString = event.sizeString; - final prevEvent = i > 0 ? events[i - 1] : null; - final sameEnvironment = prevEvent == null - ? false - : prevEvent.originServerTs - .sameEnvironment(event.originServerTs); - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (!sameEnvironment) ...[ - Row( - children: [ - Expanded( - child: Container( - height: 1, - color: theme.dividerColor, - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - event.originServerTs.localizedTime(context), - style: theme.textTheme.labelSmall, - textAlign: TextAlign.center, - ), - ), - Expanded( - child: Container( - height: 1, - color: theme.dividerColor, - ), - ), - ], - ), - const SizedBox(height: 4), - ], - Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - color: theme.colorScheme.onInverseSurface, - clipBehavior: Clip.hardEdge, - child: ListTile( - leading: const Icon(Icons.file_present_outlined), - title: Text( - filename, - maxLines: 1, - overflow: TextOverflow.ellipsis, + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + event.originServerTs.localizedTime(context), + style: theme.textTheme.labelSmall, + textAlign: TextAlign.center, ), - subtitle: Text('$sizeString | $filetype'), - onTap: () => event.saveFile(context), ), + Expanded( + child: Container(height: 1, color: theme.dividerColor), + ), + ], + ), + const SizedBox(height: 4), + ], + Material( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + color: theme.colorScheme.onInverseSurface, + clipBehavior: Clip.hardEdge, + child: ListTile( + leading: const Icon(Icons.file_present_outlined), + title: Text( + filename, + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - ], + subtitle: Text('$sizeString | $filetype'), + onTap: () => event.saveFile(context), + ), ), - ); - }, - ), - ); - }, + ], + ), + ); + }, + ), ); } } diff --git a/lib/pages/chat_search/chat_search_images_tab.dart b/lib/pages/chat_search/chat_search_images_tab.dart index 566e44cc1..5b018ccc4 100644 --- a/lib/pages/chat_search/chat_search_images_tab.dart +++ b/lib/pages/chat_search/chat_search_images_tab.dart @@ -4,189 +4,121 @@ import 'package:intl/intl.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; +import 'package:fluffychat/pages/chat_search/search_footer.dart'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; class ChatSearchImagesTab extends StatelessWidget { final Room room; - final Stream<(List, String?)>? searchStream; - final void Function({ - String? prevBatch, - List? previousSearchResult, - }) startSearch; + final List events; + final void Function() onStartSearch; + final bool endReached, isLoading; + final DateTime? searchedUntil; const ChatSearchImagesTab({ required this.room, - required this.startSearch, - required this.searchStream, + required this.events, + required this.onStartSearch, + required this.endReached, + required this.isLoading, super.key, + required this.searchedUntil, }); @override Widget build(BuildContext context) { final borderRadius = BorderRadius.circular(AppConfig.borderRadius / 2); - return StreamBuilder( - stream: searchStream, - builder: (context, snapshot) { - final theme = Theme.of(context); - final events = snapshot.data?.$1; - if (searchStream == null || events == null) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CircularProgressIndicator.adaptive(strokeWidth: 2), - const SizedBox(height: 8), - Text( - L10n.of(context).searchIn( - room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), + final theme = Theme.of(context); + + final eventsByMonth = >{}; + for (final event in events) { + final month = DateTime( + event.originServerTs.year, + event.originServerTs.month, + ); + eventsByMonth[month] ??= []; + eventsByMonth[month]!.add(event); + } + final eventsByMonthList = eventsByMonth.entries.toList(); + + const padding = 8.0; + + return ListView.builder( + itemCount: eventsByMonth.length + 1, + itemBuilder: (context, i) { + if (i == eventsByMonth.length) { + return SearchFooter( + searchedUntil: searchedUntil, + endReached: endReached, + isLoading: isLoading, + onStartSearch: onStartSearch, + ); + } + + final monthEvents = eventsByMonthList[i].value; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 4), + Row( + children: [ + Expanded( + child: Container(height: 1, color: theme.dividerColor), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + DateFormat.yMMMM( + Localizations.localeOf(context).languageCode, + ).format(eventsByMonthList[i].key), + style: theme.textTheme.labelSmall, + textAlign: TextAlign.center, ), ), - ), - ], - ); - } - if (events.isEmpty) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.photo_outlined, size: 64), - const SizedBox(height: 8), - Text(L10n.of(context).nothingFound), - ], - ); - } - final eventsByMonth = >{}; - for (final event in events) { - final month = DateTime( - event.originServerTs.year, - event.originServerTs.month, - ); - eventsByMonth[month] ??= []; - eventsByMonth[month]!.add(event); - } - final eventsByMonthList = eventsByMonth.entries.toList(); - - const padding = 8.0; - - return ListView.builder( - itemCount: eventsByMonth.length + 1, - itemBuilder: (context, i) { - if (i == eventsByMonth.length) { - if (snapshot.connectionState != ConnectionState.done) { - return const Padding( - padding: EdgeInsets.all(16.0), - child: Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, + Expanded( + child: Container(height: 1, color: theme.dividerColor), + ), + ], + ), + GridView.count( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + mainAxisSpacing: padding, + crossAxisSpacing: padding, + clipBehavior: Clip.hardEdge, + padding: const EdgeInsets.all(padding), + crossAxisCount: 3, + children: monthEvents.map((event) { + if (event.messageType == MessageTypes.Video) { + return Material( + clipBehavior: Clip.hardEdge, + borderRadius: borderRadius, + child: EventVideoPlayer(event), + ); + } + return InkWell( + onTap: () => showDialog( + context: context, + builder: (_) => ImageViewer(event, outerContext: context), + ), + borderRadius: borderRadius, + child: Material( + clipBehavior: Clip.hardEdge, + borderRadius: borderRadius, + child: MxcImage( + event: event, + width: 128, + height: 128, + fit: BoxFit.cover, + animated: true, + isThumbnail: true, ), ), ); - } - final nextBatch = snapshot.data?.$2; - if (nextBatch == null) { - return const SizedBox.shrink(); - } - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: TextButton.icon( - style: TextButton.styleFrom( - backgroundColor: theme.colorScheme.secondaryContainer, - foregroundColor: theme.colorScheme.onSecondaryContainer, - ), - onPressed: () => startSearch( - prevBatch: nextBatch, - previousSearchResult: events, - ), - icon: const Icon( - Icons.arrow_downward_outlined, - ), - label: Text(L10n.of(context).searchMore), - ), - ), - ); - } - - final monthEvents = eventsByMonthList[i].value; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 4), - Row( - children: [ - Expanded( - child: Container( - height: 1, - color: theme.dividerColor, - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - DateFormat.yMMMM( - Localizations.localeOf(context).languageCode, - ).format(eventsByMonthList[i].key), - style: theme.textTheme.labelSmall, - textAlign: TextAlign.center, - ), - ), - Expanded( - child: Container( - height: 1, - color: theme.dividerColor, - ), - ), - ], - ), - GridView.count( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - mainAxisSpacing: padding, - crossAxisSpacing: padding, - clipBehavior: Clip.hardEdge, - padding: const EdgeInsets.all(padding), - crossAxisCount: 3, - children: monthEvents.map( - (event) { - if (event.messageType == MessageTypes.Video) { - return Material( - clipBehavior: Clip.hardEdge, - borderRadius: borderRadius, - child: EventVideoPlayer(event), - ); - } - return InkWell( - onTap: () => showDialog( - context: context, - builder: (_) => ImageViewer( - event, - outerContext: context, - ), - ), - borderRadius: borderRadius, - child: Material( - clipBehavior: Clip.hardEdge, - borderRadius: borderRadius, - child: MxcImage( - event: event, - width: 128, - height: 128, - fit: BoxFit.cover, - animated: true, - isThumbnail: true, - ), - ), - ); - }, - ).toList(), - ), - ], - ); - }, + }).toList(), + ), + ], ); }, ); diff --git a/lib/pages/chat_search/chat_search_message_tab.dart b/lib/pages/chat_search/chat_search_message_tab.dart index 5896b5943..eff074bdb 100644 --- a/lib/pages/chat_search/chat_search_message_tab.dart +++ b/lib/pages/chat_search/chat_search_message_tab.dart @@ -4,6 +4,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pages/chat_search/search_footer.dart'; import 'package:fluffychat/pangea/navigation/navigation_util.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -13,111 +14,71 @@ import 'package:fluffychat/widgets/avatar.dart'; class ChatSearchMessageTab extends StatelessWidget { final String searchQuery; final Room room; - final Stream<(List, String?)>? searchStream; - final void Function({ - String? prevBatch, - List? previousSearchResult, - }) startSearch; + final List events; + final void Function() onStartSearch; + final bool endReached, isLoading; + final DateTime? searchedUntil; const ChatSearchMessageTab({ required this.searchQuery, required this.room, - required this.searchStream, - required this.startSearch, + required this.onStartSearch, + required this.events, + required this.searchedUntil, + required this.endReached, + required this.isLoading, super.key, }); @override Widget build(BuildContext context) { - return StreamBuilder( - key: ValueKey(searchQuery), - stream: searchStream, - builder: (context, snapshot) { - final theme = Theme.of(context); - if (searchStream == null) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.search_outlined, size: 64), - const SizedBox(height: 8), - Text( - L10n.of(context).searchIn( - room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), - ), - ), + final theme = Theme.of(context); + if (events.isEmpty && searchQuery.isEmpty) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.search_outlined, size: 64), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: Text( + L10n.of(context).searchIn( + room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))), ), - ], - ); - } - final events = snapshot.data?.$1 ?? []; - // #Pangea - events.removeWhere( - (event) => - event.type != EventTypes.Message || - event.messageType != MessageTypes.Text || - event.redacted, - ); - // Pangea# - - return SelectionArea( - child: ListView.separated( - itemCount: events.length + 1, - separatorBuilder: (context, _) => Divider( - color: theme.dividerColor, - height: 1, + textAlign: TextAlign.center, ), - itemBuilder: (context, i) { - if (i == events.length) { - if (snapshot.connectionState != ConnectionState.done) { - return const Padding( - padding: EdgeInsets.all(16.0), - child: Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ), - ); - } - final nextBatch = snapshot.data?.$2; - if (nextBatch == null) { - return const SizedBox.shrink(); - } - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: TextButton.icon( - style: TextButton.styleFrom( - backgroundColor: theme.colorScheme.secondaryContainer, - foregroundColor: theme.colorScheme.onSecondaryContainer, - ), - onPressed: () => startSearch( - prevBatch: nextBatch, - previousSearchResult: events, - ), - icon: const Icon( - Icons.arrow_downward_outlined, - ), - label: Text(L10n.of(context).searchMore), - ), - ), - ); - } - final event = events[i]; - final sender = event.senderFromMemoryOrFallback; - final displayname = sender.calcDisplayname( - i18n: MatrixLocals(L10n.of(context)), - ); - return _MessageSearchResultListTile( - sender: sender, - displayname: displayname, - event: event, - room: room, - ); - }, ), - ); - }, + ], + ); + } + + return SelectionArea( + child: ListView.separated( + itemCount: events.length + 1, + separatorBuilder: (context, _) => + Divider(color: theme.dividerColor, height: 1), + itemBuilder: (context, i) { + if (i == events.length) { + return SearchFooter( + searchedUntil: searchedUntil, + endReached: endReached, + isLoading: isLoading, + onStartSearch: onStartSearch, + ); + } + final event = events[i]; + final sender = event.senderFromMemoryOrFallback; + final displayname = sender.calcDisplayname( + i18n: MatrixLocals(L10n.of(context)), + ); + return _MessageSearchResultListTile( + sender: sender, + displayname: displayname, + event: event, + room: room, + ); + }, + ), ); } } @@ -142,19 +103,18 @@ class _MessageSearchResultListTile extends StatelessWidget { return ListTile( title: Row( children: [ + // #Pangea + // Avatar(mxContent: sender.avatarUrl, name: displayname, size: 16), Avatar( mxContent: sender.avatarUrl, name: displayname, - // #Pangea userId: sender.id, - // Pangea# size: 16, ), + // Pangea# const SizedBox(width: 8), // #Pangea - // Text( - // displayname, - // ), + // Text(displayname), // Expanded( // child: Text( // ' | ${event.originServerTs.localizedTimeShort(context)}', @@ -188,24 +148,17 @@ class _MessageSearchResultListTile extends StatelessWidget { .calcLocalizedBodyFallback( plaintextBody: true, removeMarkdown: true, - MatrixLocals( - L10n.of(context), - ), + MatrixLocals(L10n.of(context)), ) .trim(), maxLines: 7, overflow: TextOverflow.ellipsis, ), trailing: IconButton( - icon: const Icon( - Icons.chevron_right_outlined, - ), + icon: const Icon(Icons.chevron_right_outlined), // #Pangea // onPressed: () => context.go( - // '/${Uri( - // pathSegments: ['rooms', room.id], - // queryParameters: {'event': event.eventId}, - // )}', + // '/${Uri(pathSegments: ['rooms', room.id], queryParameters: {'event': event.eventId})}', // ), onPressed: () => NavigationUtil.goToSpaceRoute( room.id, diff --git a/lib/pages/chat_search/chat_search_page.dart b/lib/pages/chat_search/chat_search_page.dart index f4a25e35d..98a31a8eb 100644 --- a/lib/pages/chat_search/chat_search_page.dart +++ b/lib/pages/chat_search/chat_search_page.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; @@ -22,171 +20,112 @@ class ChatSearchController extends State final TextEditingController searchController = TextEditingController(); late final TabController tabController; - Timeline? timeline; - - Stream<(List, String?)>? searchStream; - Stream<(List, String?)>? galleryStream; - Stream<(List, String?)>? fileStream; + final List messages = []; + final List images = []; + final List files = []; + String? messagesNextBatch, imagesNextBatch, filesNextBatch; + bool messagesEndReached = false; + bool imagesEndReached = false; + bool filesEndReached = false; + bool isLoading = false; + DateTime? searchedUntil; void restartSearch() { - if (searchController.text.isEmpty) { - setState(() { - searchStream = null; - }); - return; - } setState(() { - searchStream = const Stream.empty(); + messages.clear(); + images.clear(); + files.clear(); + messagesNextBatch = imagesNextBatch = filesNextBatch = searchedUntil = + null; + messagesEndReached = imagesEndReached = filesEndReached = false; }); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - startMessageSearch(); + startSearch(); }); } - void startMessageSearch({ - String? prevBatch, - List? previousSearchResult, - }) async { - final timeline = this.timeline ??= await room!.getTimeline(); - - if (tabController.index == 0 && searchController.text.isEmpty) { - return; - } - - setState(() { - searchStream = timeline - .startSearch( - searchTerm: searchController.text, - prevBatch: prevBatch, - requestHistoryCount: 1000, - limit: 32, - ) - .map( - (result) => ( - [ - if (previousSearchResult != null) ...previousSearchResult, - ...result.$1, - ], - result.$2, - ), - ) - // Deduplication workaround for - // https://github.com/famedly/matrix-dart-sdk/issues/1831 - .map( - (result) => ( - { - for (final event in result.$1) event.eventId: event, - // #Pangea - // }.values.toList(), - } - .values - .toList() - .where( - (e) => !e.hasAggregatedEvents( - timeline, - RelationshipTypes.edit, - ), - ) - .toList(), - // Pangea# - result.$2, - ), - ) + void startSearch() async { + switch (tabController.index) { + case 0: + final searchQuery = searchController.text.trim(); + if (searchQuery.isEmpty) return; + setState(() { + isLoading = true; + }); + final result = await room!.searchEvents( + searchTerm: searchController.text.trim(), + nextBatch: messagesNextBatch, + ); + setState(() { + isLoading = false; // #Pangea - .where((result) => result.$1.isNotEmpty) + // messages.addAll(result.events); + messages.addAll( + result.events.where( + (e) => + room?.timeline == null || + !e.hasAggregatedEvents( + room!.timeline!, + RelationshipTypes.edit, + ), + ), + ); // Pangea# - .asBroadcastStream(); - }); - } - - void startGallerySearch({ - String? prevBatch, - List? previousSearchResult, - }) async { - final timeline = this.timeline ??= await room!.getTimeline(); - - setState(() { - galleryStream = timeline - .startSearch( - searchFunc: (event) => { - MessageTypes.Image, - MessageTypes.Video, - }.contains(event.messageType), - prevBatch: prevBatch, - requestHistoryCount: 1000, - limit: 32, - ) - .map( - (result) => ( - [ - if (previousSearchResult != null) ...previousSearchResult, - ...result.$1, - ], - result.$2, - ), - ) - // Deduplication workaround for - // https://github.com/famedly/matrix-dart-sdk/issues/1831 - .map( - (result) => ( - { - for (final event in result.$1) event.eventId: event, - }.values.toList(), - result.$2, - ), - ) - .asBroadcastStream(); - }); - } - - void startFileSearch({ - String? prevBatch, - List? previousSearchResult, - }) async { - final timeline = this.timeline ??= await room!.getTimeline(); - - setState(() { - fileStream = timeline - .startSearch( - searchFunc: (event) => - event.messageType == MessageTypes.File || - (event.messageType == MessageTypes.Audio && - !event.content.containsKey('org.matrix.msc3245.voice')), - prevBatch: prevBatch, - requestHistoryCount: 1000, - limit: 32, - ) - .map( - (result) => ( - [ - if (previousSearchResult != null) ...previousSearchResult, - ...result.$1, - ], - result.$2, - ), - ) - // Deduplication workaround for - // https://github.com/famedly/matrix-dart-sdk/issues/1831 - .map( - (result) => ( - { - for (final event in result.$1) event.eventId: event, - }.values.toList(), - result.$2, - ), - ) - .asBroadcastStream(); - }); + messagesNextBatch = result.nextBatch; + messagesEndReached = result.nextBatch == null; + searchedUntil = result.searchedUntil; + }); + return; + case 1: + setState(() { + isLoading = true; + }); + final result = await room!.searchEvents( + searchFunc: (event) => { + MessageTypes.Image, + MessageTypes.Video, + }.contains(event.messageType), + nextBatch: imagesNextBatch, + ); + setState(() { + isLoading = false; + images.addAll(result.events); + imagesNextBatch = result.nextBatch; + imagesEndReached = result.nextBatch == null; + searchedUntil = result.searchedUntil; + }); + return; + case 2: + setState(() { + isLoading = true; + }); + final result = await room!.searchEvents( + searchFunc: (event) => + event.messageType == MessageTypes.File || + (event.messageType == MessageTypes.Audio && + !event.content.containsKey('org.matrix.msc3245.voice')), + nextBatch: filesNextBatch, + ); + setState(() { + isLoading = false; + files.addAll(result.events); + filesNextBatch = result.nextBatch; + filesEndReached = result.nextBatch == null; + searchedUntil = result.searchedUntil; + }); + return; + default: + return; + } } void _onTabChanged() { switch (tabController.index) { case 1: - startGallerySearch(); - break; case 2: - startFileSearch(); + startSearch(); break; + case 0: default: restartSearch(); break; diff --git a/lib/pages/chat_search/chat_search_view.dart b/lib/pages/chat_search/chat_search_view.dart index 8776582b4..278478fa3 100644 --- a/lib/pages/chat_search/chat_search_view.dart +++ b/lib/pages/chat_search/chat_search_view.dart @@ -48,9 +48,7 @@ class ChatSearchView extends StatelessWidget { if (FluffyThemes.isThreeColumnMode(context)) const SizedBox(height: 16), Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 16), child: TextField( controller: controller.searchController, onSubmitted: (_) => controller.restartSearch(), @@ -87,18 +85,27 @@ class ChatSearchView extends StatelessWidget { ChatSearchMessageTab( searchQuery: controller.searchController.text, room: room, - startSearch: controller.startMessageSearch, - searchStream: controller.searchStream, + onStartSearch: controller.startSearch, + events: controller.messages, + endReached: controller.messagesEndReached, + isLoading: controller.isLoading, + searchedUntil: controller.searchedUntil, ), ChatSearchImagesTab( room: room, - startSearch: controller.startGallerySearch, - searchStream: controller.galleryStream, + onStartSearch: controller.startSearch, + events: controller.images, + endReached: controller.imagesEndReached, + isLoading: controller.isLoading, + searchedUntil: controller.searchedUntil, ), ChatSearchFilesTab( room: room, - startSearch: controller.startFileSearch, - searchStream: controller.fileStream, + onStartSearch: controller.startSearch, + events: controller.files, + endReached: controller.filesEndReached, + isLoading: controller.isLoading, + searchedUntil: controller.searchedUntil, ), ], ), diff --git a/lib/pages/chat_search/search_footer.dart b/lib/pages/chat_search/search_footer.dart new file mode 100644 index 000000000..cac19c4ea --- /dev/null +++ b/lib/pages/chat_search/search_footer.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/utils/date_time_extension.dart'; + +class SearchFooter extends StatelessWidget { + final DateTime? searchedUntil; + final bool endReached, isLoading; + final void Function() onStartSearch; + + const SearchFooter({ + super.key, + required this.searchedUntil, + required this.endReached, + required this.isLoading, + required this.onStartSearch, + }); + + @override + Widget build(BuildContext context) { + if (endReached) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text(L10n.of(context).noMoreResultsFound), + ), + ); + } + final theme = Theme.of(context); + final searchedUntil = this.searchedUntil; + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: .min, + children: [ + if (searchedUntil != null) + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + L10n.of( + context, + ).chatSearchedUntil(searchedUntil.localizedTime(context)), + style: TextStyle(fontSize: 10.5), + ), + ), + TextButton.icon( + style: TextButton.styleFrom( + backgroundColor: theme.colorScheme.secondaryContainer, + foregroundColor: theme.colorScheme.onSecondaryContainer, + ), + onPressed: isLoading ? null : onStartSearch, + icon: isLoading + ? SizedBox.square( + dimension: 18, + child: const CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ) + : const Icon(Icons.arrow_downward_outlined), + label: Text(L10n.of(context).searchMore), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/device_settings/device_settings.dart b/lib/pages/device_settings/device_settings.dart index a23545f0b..12c1d1fce 100644 --- a/lib/pages/device_settings/device_settings.dart +++ b/lib/pages/device_settings/device_settings.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart' show IterableExtension; import 'package:matrix/encryption/utils/key_verification.dart'; import 'package:matrix/matrix.dart'; +import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/device_settings/device_settings_view.dart'; @@ -52,6 +53,15 @@ class DevicesSettingsController extends State { } void removeDevicesAction(List devices) async { + final client = Matrix.of(context).client; + + final accountManageUrl = client.wellKnown?.additionalProperties + .tryGetMap('org.matrix.msc2965.authentication') + ?.tryGet('account'); + if (accountManageUrl != null) { + launchUrlString(accountManageUrl, mode: LaunchMode.inAppBrowserView); + return; + } if (await showOkCancelAlertDialog( context: context, title: L10n.of(context).areYouSure, @@ -73,10 +83,7 @@ class DevicesSettingsController extends State { context: context, delay: false, future: () => matrix.client.uiaRequestBackground( - (auth) => matrix.client.deleteDevices( - deviceIds, - auth: auth, - ), + (auth) => matrix.client.deleteDevices(deviceIds, auth: auth), ), ); reload(); @@ -93,9 +100,9 @@ class DevicesSettingsController extends State { if (displayName == null) return; final success = await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context) - .client - .updateDevice(device.deviceId, displayName: displayName), + future: () => Matrix.of( + context, + ).client.updateDevice(device.deviceId, displayName: displayName), ); if (success.error == null) { reload(); @@ -117,8 +124,10 @@ class DevicesSettingsController extends State { .deviceKeys[device.deviceId]! .startVerification(); req.onUpdate = () { - if ({KeyVerificationState.error, KeyVerificationState.done} - .contains(req.state)) { + if ({ + KeyVerificationState.error, + KeyVerificationState.done, + }.contains(req.state)) { setState(() {}); } }; @@ -149,9 +158,7 @@ class DevicesSettingsController extends State { bool _isOwnDevice(Device userDevice) => userDevice.deviceId == Matrix.of(context).client.deviceID; - Device? get thisDevice => devices!.firstWhereOrNull( - _isOwnDevice, - ); + Device? get thisDevice => devices!.firstWhereOrNull(_isOwnDevice); List get notThisDevice => List.from(devices!) ..removeWhere(_isOwnDevice) diff --git a/lib/pages/device_settings/device_settings_view.dart b/lib/pages/device_settings/device_settings_view.dart index 74e190397..232ae06c9 100644 --- a/lib/pages/device_settings/device_settings_view.dart +++ b/lib/pages/device_settings/device_settings_view.dart @@ -27,7 +27,7 @@ class DevicesSettingsView extends StatelessWidget { if (snapshot.hasError) { return Center( child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.error_outlined), Text(snapshot.error.toString()), @@ -47,7 +47,7 @@ class DevicesSettingsView extends StatelessWidget { itemBuilder: (BuildContext context, int i) { if (i == 0) { return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ if (controller.chatBackupEnabled == false) Padding( @@ -57,8 +57,9 @@ class DevicesSettingsView extends StatelessWidget { child: Icon(Icons.info_outlined), ), subtitle: Text( - L10n.of(context) - .noticeChatBackupDeviceVerification, + L10n.of( + context, + ).noticeChatBackupDeviceVerification, ), ), ), diff --git a/lib/pages/device_settings/user_device_list_item.dart b/lib/pages/device_settings/user_device_list_item.dart index 02c915916..095a4335c 100644 --- a/lib/pages/device_settings/user_device_list_item.dart +++ b/lib/pages/device_settings/user_device_list_item.dart @@ -9,13 +9,7 @@ import '../../utils/date_time_extension.dart'; import '../../utils/matrix_sdk_extensions/device_extension.dart'; import '../../widgets/matrix.dart'; -enum UserDeviceListItemAction { - rename, - remove, - verify, - block, - unblock, -} +enum UserDeviceListItemAction { rename, remove, verify, block, unblock } class UserDeviceListItem extends StatelessWidget { final Device userDevice; @@ -38,7 +32,8 @@ class UserDeviceListItem extends StatelessWidget { @override Widget build(BuildContext context) { final client = Matrix.of(context).client; - final keys = client.userDeviceKeys[Matrix.of(context).client.userID] + final keys = client + .userDeviceKeys[Matrix.of(context).client.userID] ?.deviceKeys[userDevice.deviceId]; final isOwnDevice = userDevice.deviceId == client.deviceID; @@ -113,10 +108,10 @@ class UserDeviceListItem extends StatelessWidget { backgroundColor: keys == null ? Colors.grey[700] : keys.blocked - ? Colors.red - : keys.verified - ? Colors.green - : Colors.orange, + ? Colors.red + : keys.verified + ? Colors.green + : Colors.orange, child: Icon(userDevice.icon), ), title: Text( @@ -126,8 +121,9 @@ class UserDeviceListItem extends StatelessWidget { ), subtitle: Text( L10n.of(context).lastActiveAgo( - DateTime.fromMillisecondsSinceEpoch(userDevice.lastSeenTs ?? 0) - .localizedTimeShort(context), + DateTime.fromMillisecondsSinceEpoch( + userDevice.lastSeenTs ?? 0, + ).localizedTimeShort(context), ), style: const TextStyle(fontWeight: FontWeight.w300), ), @@ -137,14 +133,14 @@ class UserDeviceListItem extends StatelessWidget { keys.blocked ? L10n.of(context).blocked : keys.verified - ? L10n.of(context).verified - : L10n.of(context).unverified, + ? L10n.of(context).verified + : L10n.of(context).unverified, style: TextStyle( color: keys.blocked ? Colors.red : keys.verified - ? Colors.green - : Colors.orange, + ? Colors.green + : Colors.orange, ), ), ), diff --git a/lib/pages/dialer/dialer.dart b/lib/pages/dialer/dialer.dart index 9ff54030e..d3cca97c1 100644 --- a/lib/pages/dialer/dialer.dart +++ b/lib/pages/dialer/dialer.dart @@ -70,9 +70,7 @@ class _StreamView extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - decoration: const BoxDecoration( - color: Colors.black54, - ), + decoration: const BoxDecoration(color: Colors.black54), child: Stack( alignment: Alignment.center, children: [ @@ -133,9 +131,8 @@ class Calling extends StatefulWidget { class MyCallingPage extends State { Room? get room => call.room; - String get displayName => call.room.getLocalizedDisplayname( - MatrixLocals(L10n.of(widget.context)), - ); + String get displayName => + call.room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context))); String get callId => widget.callId; @@ -219,10 +216,7 @@ class MyCallingPage extends State { } void cleanUp() { - Timer( - const Duration(seconds: 2), - () => widget.onClear?.call(), - ); + Timer(const Duration(seconds: 2), () => widget.onClear?.call()); if (call.type == CallType.kVideo) { try { unawaited(WakelockPlus.disable()); @@ -238,18 +232,18 @@ class MyCallingPage extends State { void _resizeLocalVideo(Orientation orientation) { final shortSide = min( - MediaQuery.of(widget.context).size.width, - MediaQuery.of(widget.context).size.height, + MediaQuery.sizeOf(widget.context).width, + MediaQuery.sizeOf(widget.context).height, ); _localVideoMargin = remoteStream != null ? const EdgeInsets.only(top: 20.0, right: 20.0) : EdgeInsets.zero; _localVideoWidth = remoteStream != null ? shortSide / 3 - : MediaQuery.of(widget.context).size.width; + : MediaQuery.sizeOf(widget.context).width; _localVideoHeight = remoteStream != null ? shortSide / 4 - : MediaQuery.of(widget.context).size.height; + : MediaQuery.sizeOf(widget.context).height; } void _handleCallState(CallState state) { @@ -295,11 +289,14 @@ class MyCallingPage extends State { androidNotificationOptions: AndroidNotificationOptions( channelId: 'notification_channel_id', channelName: 'Foreground Notification', - channelDescription: - L10n.of(widget.context).foregroundServiceRunning, + channelDescription: L10n.of( + widget.context, + ).foregroundServiceRunning, ), iosNotificationOptions: const IOSNotificationOptions(), - foregroundTaskOptions: const ForegroundTaskOptions(), + foregroundTaskOptions: ForegroundTaskOptions( + eventAction: ForegroundTaskEventAction.nothing(), + ), ); FlutterForegroundTask.startService( notificationTitle: L10n.of(widget.context).screenSharingTitle, @@ -432,9 +429,7 @@ class MyCallingPage extends State { hangupButton, ]; case CallState.kEnded: - return [ - hangupButton, - ]; + return [hangupButton]; case CallState.kFledgling: case CallState.kWaitLocalMedia: case CallState.kCreateOffer: @@ -456,28 +451,20 @@ class MyCallingPage extends State { if (call.localHold || call.remoteOnHold) { var title = ''; if (call.localHold) { - title = '${call.room.getLocalizedDisplayname( - MatrixLocals(L10n.of(widget.context)), - )} held the call.'; + title = + '${call.room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)))} held the call.'; } else if (call.remoteOnHold) { title = 'You held the call.'; } stackWidgets.add( Center( child: Column( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: .center, children: [ - const Icon( - Icons.pause, - size: 48.0, - color: Colors.white, - ), + const Icon(Icons.pause, size: 48.0, color: Colors.white), Text( title, - style: const TextStyle( - color: Colors.white, - fontSize: 24.0, - ), + style: const TextStyle(color: Colors.white, fontSize: 24.0), ), ], ), @@ -486,7 +473,8 @@ class MyCallingPage extends State { return stackWidgets; } - var primaryStream = call.remoteScreenSharingStream ?? + var primaryStream = + call.remoteScreenSharingStream ?? call.localScreenSharingStream ?? call.remoteUserMediaStream ?? call.localUserMediaStream; @@ -525,8 +513,10 @@ class MyCallingPage extends State { SizedBox( width: _localVideoWidth, height: _localVideoHeight, - child: - _StreamView(remoteUserMediaStream!, matrixClient: widget.client), + child: _StreamView( + remoteUserMediaStream!, + matrixClient: widget.client, + ), ), ); secondaryStreamViews.add(const SizedBox(height: 10)); @@ -567,9 +557,7 @@ class MyCallingPage extends State { child: Container( width: _localVideoWidth, margin: _localVideoMargin, - child: Column( - children: secondaryStreamViews, - ), + child: Column(children: secondaryStreamViews), ), ), ); @@ -590,16 +578,14 @@ class MyCallingPage extends State { width: 320.0, height: 150.0, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisAlignment: .spaceAround, children: _buildActionButtons(isFloating), ), ), body: OrientationBuilder( builder: (BuildContext context, Orientation orientation) { return Container( - decoration: const BoxDecoration( - color: Colors.black87, - ), + decoration: const BoxDecoration(color: Colors.black87), child: Stack( children: [ ..._buildContent(orientation, isFloating), diff --git a/lib/pages/dialer/pip/pip_view.dart b/lib/pages/dialer/pip/pip_view.dart index d9aa3d90f..1cf1e5ecc 100644 --- a/lib/pages/dialer/pip/pip_view.dart +++ b/lib/pages/dialer/pip/pip_view.dart @@ -9,10 +9,7 @@ class PIPView extends StatefulWidget { final double? floatingHeight; final bool avoidKeyboard; - final Widget Function( - BuildContext context, - bool isFloating, - ) builder; + final Widget Function(BuildContext context, bool isFloating) builder; const PIPView({ super.key, @@ -95,10 +92,7 @@ class PIPViewState extends State with TickerProviderStateMixin { void _onPanUpdate(DragUpdateDetails details) { if (!_isDragging) return; setState(() { - _dragOffset = _dragOffset.translate( - details.delta.dx, - details.delta.dy, - ); + _dragOffset = _dragOffset.translate(details.delta.dx, details.delta.dy); }); } @@ -111,7 +105,7 @@ class PIPViewState extends State with TickerProviderStateMixin { }); } - void _onPanEnd(_) { + void _onPanEnd(dynamic _) { if (!_isDragging) return; final nearestCorner = _calculateNearestCorner( @@ -128,7 +122,7 @@ class PIPViewState extends State with TickerProviderStateMixin { }); } - void _onPanStart(_) { + void _onPanStart(dynamic _) { if (_isAnimating()) return; setState(() { _dragOffset = _offsets[_corner]!; @@ -138,10 +132,9 @@ class PIPViewState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { - final mediaQuery = MediaQuery.of(context); - var windowPadding = mediaQuery.padding; + var windowPadding = MediaQuery.paddingOf(context); if (widget.avoidKeyboard) { - windowPadding += mediaQuery.viewInsets; + windowPadding += MediaQuery.viewInsetsOf(context); } final isFloating = _floating; @@ -183,9 +176,7 @@ class PIPViewState extends State with TickerProviderStateMixin { _dragAnimationController, ]), builder: (context, child) { - final animationCurve = CurveTween( - curve: Curves.easeInOutQuad, - ); + final animationCurve = CurveTween(curve: Curves.easeInOutQuad); final dragAnimationValue = animationCurve.transform( _dragAnimationController.value, ); @@ -266,21 +257,13 @@ class PIPViewState extends State with TickerProviderStateMixin { } } -enum PIPViewCorner { - topLeft, - topRight, - bottomLeft, - bottomRight, -} +enum PIPViewCorner { topLeft, topRight, bottomLeft, bottomRight } class _CornerDistance { final PIPViewCorner corner; final double distance; - _CornerDistance({ - required this.corner, - required this.distance, - }); + _CornerDistance({required this.corner, required this.distance}); } PIPViewCorner _calculateNearestCorner({ @@ -289,15 +272,9 @@ PIPViewCorner _calculateNearestCorner({ }) { _CornerDistance calculateDistance(PIPViewCorner corner) { final distance = offsets[corner]! - .translate( - -offset.dx, - -offset.dy, - ) + .translate(-offset.dx, -offset.dy) .distanceSquared; - return _CornerDistance( - corner: corner, - distance: distance, - ); + return _CornerDistance(corner: corner, distance: distance); } final distances = PIPViewCorner.values.map(calculateDistance).toList(); diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index 01d3c2184..9a85951ce 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -6,10 +6,9 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:flutter_web_auth_2/flutter_web_auth_2.dart'; import 'package:go_router/go_router.dart'; -import 'package:hive_flutter/hive_flutter.dart'; import 'package:matrix/matrix.dart'; import 'package:universal_html/html.dart' as html; -import 'package:url_launcher/url_launcher_string.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; @@ -20,9 +19,6 @@ import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog. import 'package:fluffychat/widgets/matrix.dart'; import '../../utils/localized_exception_extension.dart'; -import 'package:fluffychat/utils/tor_stub.dart' - if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; - class HomeserverPicker extends StatefulWidget { final bool addMultiAccount; const HomeserverPicker({required this.addMultiAccount, super.key}); @@ -35,38 +31,23 @@ class HomeserverPickerController extends State { bool isLoading = false; final TextEditingController homeserverController = TextEditingController( + // #Pangea + // text: AppSettings.defaultHomeserver.value, text: AppConfig.defaultHomeserver, + // Pangea# ); String? error; - bool isTorBrowser = false; - - Future _checkTorBrowser() async { - if (!kIsWeb) return; - - Hive.openBox('test').then((value) => null).catchError( - (e, s) async { - await showOkAlertDialog( - context: context, - title: L10n.of(context).indexedDbErrorTitle, - message: L10n.of(context).indexedDbErrorLong, - ); - _checkTorBrowser(); - }, - ); - - final isTor = await TorBrowserDetector.isTorBrowser; - isTorBrowser = isTor; - } - /// Starts an analysis of the given homeserver. It uses the current domain and /// makes sure that it is prefixed with https. Then it searches for the /// well-known information and forwards to the login page depending on the /// login type. Future checkHomeserverAction({bool legacyPasswordLogin = false}) async { - final homeserverInput = - homeserverController.text.trim().toLowerCase().replaceAll(' ', '-'); + final homeserverInput = homeserverController.text + .trim() + .toLowerCase() + .replaceAll(' ', '-'); if (homeserverInput.isEmpty) { final client = await Matrix.of(context).getLoginClient(); @@ -90,7 +71,7 @@ class HomeserverPickerController extends State { homeserver = Uri.https(homeserverInput, ''); } final client = await Matrix.of(context).getLoginClient(); - final (_, _, loginFlows) = await client.checkHomeserver(homeserver); + final (_, _, loginFlows, _) = await client.checkHomeserver(homeserver); this.loginFlows = loginFlows; if (supportsSso && !legacyPasswordLogin) { if (!PlatformInfos.isMobile) { @@ -136,14 +117,12 @@ class HomeserverPickerController extends State { void ssoLoginAction() async { final redirectUrl = kIsWeb - ? Uri.parse(html.window.location.href) - .resolveUri( - Uri(pathSegments: ['auth.html']), - ) - .toString() + ? Uri.parse( + html.window.location.href, + ).resolveUri(Uri(pathSegments: ['auth.html'])).toString() : isDefaultPlatform - ? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login' - : 'http://localhost:3001//login'; + ? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login' + : 'http://localhost:3001//login'; final client = await Matrix.of(context).getLoginClient(); final url = client.homeserver!.replace( path: '/_matrix/client/v3/login/sso/redirect', @@ -156,7 +135,7 @@ class HomeserverPickerController extends State { final result = await FlutterWebAuth2.authenticate( url: url.toString(), callbackUrlScheme: urlScheme, - options: const FlutterWebAuth2Options(), + options: FlutterWebAuth2Options(useWebview: PlatformInfos.isMobile), ); final token = Uri.parse(result).queryParameters['loginToken']; if (token?.isEmpty ?? false) return; @@ -184,12 +163,6 @@ class HomeserverPickerController extends State { } } - @override - void initState() { - _checkTorBrowser(); - super.initState(); - } - @override Widget build(BuildContext context) => HomeserverPickerView(this); @@ -223,7 +196,7 @@ class HomeserverPickerController extends State { case MoreLoginActions.importBackup: restoreBackup(); case MoreLoginActions.privacy: - launchUrlString(AppConfig.privacyUrl); + launchUrl(AppConfig.privacyUrl); case MoreLoginActions.about: PlatformInfos.showDialog(context); } diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index 4161f1f9a..653095c48 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -9,24 +9,21 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../../config/themes.dart'; import 'homeserver_picker.dart'; class HomeserverPickerView extends StatelessWidget { final HomeserverPickerController controller; - const HomeserverPickerView( - this.controller, { - super.key, - }); + const HomeserverPickerView(this.controller, {super.key}); @override Widget build(BuildContext context) { final theme = Theme.of(context); return LoginScaffold( - enforceMobileMode: - Matrix.of(context).widget.clients.any((client) => client.isLogged()), + enforceMobileMode: Matrix.of( + context, + ).widget.clients.any((client) => client.isLogged()), appBar: AppBar( centerTitle: true, title: Text( @@ -42,7 +39,7 @@ class HomeserverPickerView extends StatelessWidget { PopupMenuItem( value: MoreLoginActions.importBackup, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.import_export_outlined), const SizedBox(width: 12), @@ -53,7 +50,7 @@ class HomeserverPickerView extends StatelessWidget { PopupMenuItem( value: MoreLoginActions.privacy, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.privacy_tip_outlined), const SizedBox(width: 12), @@ -64,7 +61,7 @@ class HomeserverPickerView extends StatelessWidget { PopupMenuItem( value: MoreLoginActions.about, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const Icon(Icons.info_outlined), const SizedBox(width: 12), @@ -84,30 +81,6 @@ class HomeserverPickerView extends StatelessWidget { child: IntrinsicHeight( child: Column( children: [ - // display a prominent banner to import session for TOR browser - // users. This feature is just some UX sugar as TOR users are - // usually forced to logout as TOR browser is non-persistent - AnimatedContainer( - height: controller.isTorBrowser ? 64 : 0, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), - child: Material( - clipBehavior: Clip.hardEdge, - borderRadius: const BorderRadius.vertical( - bottom: Radius.circular(8), - ), - color: theme.colorScheme.surface, - child: ListTile( - leading: const Icon(Icons.vpn_key), - title: Text(L10n.of(context).hydrateTor), - subtitle: Text(L10n.of(context).hydrateTorLong), - trailing: const Icon(Icons.chevron_right_outlined), - onTap: controller.restoreBackup, - ), - ), - ), Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 8.0), @@ -124,8 +97,9 @@ class HomeserverPickerView extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 32.0), child: SelectableLinkify( text: L10n.of(context).appIntroduction, - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), textAlign: TextAlign.center, linkStyle: TextStyle( color: theme.colorScheme.secondary, @@ -138,8 +112,8 @@ class HomeserverPickerView extends StatelessWidget { Padding( padding: const EdgeInsets.all(32.0), child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: .min, + crossAxisAlignment: .stretch, children: [ TextField( onSubmitted: (_) => @@ -159,7 +133,7 @@ class HomeserverPickerView extends StatelessWidget { hintStyle: TextStyle( color: theme.colorScheme.surfaceTint, ), - labelText: 'Sign in with:', + labelText: L10n.of(context).signInWithLabel, errorText: controller.error, errorMaxLines: 4, suffixIcon: IconButton( @@ -171,11 +145,13 @@ class HomeserverPickerView extends StatelessWidget { L10n.of(context).whatIsAHomeserver, ), content: Linkify( - text: L10n.of(context) - .homeserverDescription, + text: L10n.of( + context, + ).homeserverDescription, textScaleFactor: - MediaQuery.textScalerOf(context) - .scale(1), + MediaQuery.textScalerOf( + context, + ).scale(1), options: const LinkifyOptions( humanize: false, ), @@ -193,8 +169,9 @@ class HomeserverPickerView extends StatelessWidget { Uri.https('servers.joinmatrix.org'), ), child: Text( - L10n.of(context) - .discoverHomeservers, + L10n.of( + context, + ).discoverHomeservers, ), ), AdaptiveDialogAction( @@ -230,8 +207,8 @@ class HomeserverPickerView extends StatelessWidget { onPressed: controller.isLoading ? null : () => controller.checkHomeserverAction( - legacyPasswordLogin: true, - ), + legacyPasswordLogin: true, + ), child: Text(L10n.of(context).loginWithMatrixId), ), ], diff --git a/lib/pages/image_viewer/image_viewer.dart b/lib/pages/image_viewer/image_viewer.dart index 3527ea68a..1dba46b28 100644 --- a/lib/pages/image_viewer/image_viewer.dart +++ b/lib/pages/image_viewer/image_viewer.dart @@ -32,7 +32,8 @@ class ImageViewerController extends State { @override void initState() { super.initState(); - allEvents = widget.timeline?.events + allEvents = + widget.timeline?.events .where( (event) => { MessageTypes.Image, @@ -44,8 +45,9 @@ class ImageViewerController extends State { .reversed .toList() ?? [widget.event]; - var index = - allEvents.indexWhere((event) => event.eventId == widget.event.eventId); + var index = allEvents.indexWhere( + (event) => event.eventId == widget.event.eventId, + ); if (index < 0) index = 0; pageController = PageController(initialPage: index); } @@ -93,11 +95,10 @@ class ImageViewerController extends State { /// Forward this image to another room. void forwardAction() => showScaffoldDialog( - context: context, - builder: (context) => ShareScaffoldDialog( - items: [ContentShareItem(currentEvent.content)], - ), - ); + context: context, + builder: (context) => + ShareScaffoldDialog(items: [ContentShareItem(currentEvent.content)]), + ); /// Save this file with a system call. void saveFileAction(BuildContext context) => currentEvent.saveFile(context); @@ -111,7 +112,7 @@ class ImageViewerController extends State { void onInteractionEnds(ScaleEndDetails endDetails) { if (PlatformInfos.usesTouchscreen == false) { if (endDetails.velocity.pixelsPerSecond.dy > - MediaQuery.of(context).size.height * maxScaleFactor) { + MediaQuery.sizeOf(context).height * maxScaleFactor) { Navigator.of(context, rootNavigator: false).pop(); } } diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index f6cfa96f2..7eb529494 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -124,7 +124,7 @@ class ImageViewerView extends StatelessWidget { Align( alignment: Alignment.centerRight, child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ if (controller.canGoBack) Padding( diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart index b90fcd1f8..4140f41d0 100644 --- a/lib/pages/image_viewer/video_player.dart +++ b/lib/pages/image_viewer/video_player.dart @@ -20,10 +20,7 @@ import '../../widgets/mxc_image.dart'; class EventVideoPlayer extends StatefulWidget { final Event event; - const EventVideoPlayer( - this.event, { - super.key, - }); + const EventVideoPlayer(this.event, {super.key}); @override EventVideoPlayerState createState() => EventVideoPlayerState(); @@ -41,7 +38,9 @@ class EventVideoPlayerState extends State { final mimetype = infoMap?.tryGet('mimetype'); return PlatformInfos.isAndroid ? mimetype != "video/quicktime" : true; } + // Pangea# + double? _downloadProgress; // The video_player package only doesn't support Windows and Linux. // #Pangea @@ -60,7 +59,21 @@ class EventVideoPlayerState extends State { // #Pangea setState(() => _error = null); // Pangea# - final videoFile = await widget.event.downloadAndDecryptAttachment(); + final fileSize = widget.event.content + .tryGetMap('info') + ?.tryGet('size'); + final videoFile = await widget.event.downloadAndDecryptAttachment( + onDownloadProgress: fileSize == null + ? null + : (progress) { + final progressPercentage = progress / fileSize; + setState(() { + _downloadProgress = progressPercentage < 1 + ? progressPercentage + : null; + }); + }, + ); // Dispose the controllers if we already have them. _disposeControllers(); @@ -102,11 +115,9 @@ class EventVideoPlayerState extends State { ); }); } on IOException catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(e.toLocalizedString(context)), - ), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); // #Pangea setState(() => _error = e); // Pangea# @@ -144,13 +155,15 @@ class EventVideoPlayerState extends State { @override Widget build(BuildContext context) { final hasThumbnail = widget.event.hasThumbnail; - final blurHash = (widget.event.infoMap as Map) - .tryGet('xyz.amorgan.blurhash') ?? + final blurHash = + (widget.event.infoMap as Map).tryGet( + 'xyz.amorgan.blurhash', + ) ?? fallbackBlurHash; final infoMap = widget.event.content.tryGetMap('info'); final videoWidth = infoMap?.tryGet('w') ?? 400; final videoHeight = infoMap?.tryGet('h') ?? 300; - final height = MediaQuery.of(context).size.height - 52; + final height = MediaQuery.sizeOf(context).height - 52; final width = videoWidth * (height / videoHeight); final chewieController = _chewieController; @@ -189,7 +202,11 @@ class EventVideoPlayerState extends State { ), ), // #Pangea - // const Center(child: CircularProgressIndicator.adaptive()), + // Center( + // child: CircularProgressIndicator.adaptive( + // value: _downloadProgress, + // ), + // ), _error != null ? Center( child: Column( @@ -220,7 +237,11 @@ class EventVideoPlayerState extends State { ], ), ) - : const Center(child: CircularProgressIndicator.adaptive()), + : Center( + child: CircularProgressIndicator.adaptive( + value: _downloadProgress, + ), + ), // Pangea# ], ); diff --git a/lib/pages/invitation_selection/invitation_selection.dart b/lib/pages/invitation_selection/invitation_selection.dart index 0c0d27fe5..1701a1a0f 100644 --- a/lib/pages/invitation_selection/invitation_selection.dart +++ b/lib/pages/invitation_selection/invitation_selection.dart @@ -12,10 +12,7 @@ import '../../utils/localized_exception_extension.dart'; class InvitationSelection extends StatefulWidget { final String roomId; - const InvitationSelection({ - super.key, - required this.roomId, - }); + const InvitationSelection({super.key, required this.roomId}); @override InvitationSelectionController createState() => @@ -47,8 +44,8 @@ class InvitationSelectionController extends State { .toList(); contacts.sort( (a, b) => a.calcDisplayname().toLowerCase().compareTo( - b.calcDisplayname().toLowerCase(), - ), + b.calcDisplayname().toLowerCase(), + ), ); return contacts; } @@ -91,9 +88,9 @@ class InvitationSelectionController extends State { try { response = await matrix.client.searchUserDirectory(text, limit: 10); } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text((e).toLocalizedString(context))), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text((e).toLocalizedString(context)))); return; } finally { setState(() => loading = false); diff --git a/lib/pages/invitation_selection/invitation_selection_view.dart b/lib/pages/invitation_selection/invitation_selection_view.dart index f4a84d5c8..4db33841f 100644 --- a/lib/pages/invitation_selection/invitation_selection_view.dart +++ b/lib/pages/invitation_selection/invitation_selection_view.dart @@ -16,13 +16,12 @@ class InvitationSelectionView extends StatelessWidget { @override Widget build(BuildContext context) { - final room = - Matrix.of(context).client.getRoomById(controller.widget.roomId); + final room = Matrix.of( + context, + ).client.getRoomById(controller.widget.roomId); if (room == null) { return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).oopsSomethingWentWrong), - ), + appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)), body: Center( child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), ), @@ -76,11 +75,14 @@ class InvitationSelectionView extends StatelessWidget { ), ), StreamBuilder( - stream: room.client.onRoomState.stream - .where((update) => update.roomId == room.id), + stream: room.client.onRoomState.stream.where( + (update) => update.roomId == room.id, + ), builder: (context, snapshot) { - final participants = - room.getParticipants().map((user) => user.id).toSet(); + final participants = room + .getParticipants() + .map((user) => user.id) + .toSet(); return controller.foundProfiles.isNotEmpty ? ListView.builder( physics: const NeverScrollableScrollPhysics(), @@ -88,17 +90,21 @@ class InvitationSelectionView extends StatelessWidget { itemCount: controller.foundProfiles.length, itemBuilder: (BuildContext context, int i) => _InviteContactListTile( - profile: controller.foundProfiles[i], - isMember: participants - .contains(controller.foundProfiles[i].userId), - onTap: () => controller.inviteAction( - context, - controller.foundProfiles[i].userId, - controller.foundProfiles[i].displayName ?? - controller.foundProfiles[i].userId.localpart ?? - L10n.of(context).user, - ), - ), + profile: controller.foundProfiles[i], + isMember: participants.contains( + controller.foundProfiles[i].userId, + ), + onTap: () => controller.inviteAction( + context, + controller.foundProfiles[i].userId, + controller.foundProfiles[i].displayName ?? + controller + .foundProfiles[i] + .userId + .localpart ?? + L10n.of(context).user, + ), + ), ) : FutureBuilder>( future: controller.getContacts(context), @@ -117,23 +123,26 @@ class InvitationSelectionView extends StatelessWidget { itemCount: contacts.length, itemBuilder: (BuildContext context, int i) => _InviteContactListTile( - user: contacts[i], - profile: Profile( - avatarUrl: contacts[i].avatarUrl, - displayName: contacts[i].displayName ?? - contacts[i].id.localpart ?? - L10n.of(context).user, - userId: contacts[i].id, - ), - isMember: participants.contains(contacts[i].id), - onTap: () => controller.inviteAction( - context, - contacts[i].id, - contacts[i].displayName ?? - contacts[i].id.localpart ?? - L10n.of(context).user, - ), - ), + user: contacts[i], + profile: Profile( + avatarUrl: contacts[i].avatarUrl, + displayName: + contacts[i].displayName ?? + contacts[i].id.localpart ?? + L10n.of(context).user, + userId: contacts[i].id, + ), + isMember: participants.contains( + contacts[i].id, + ), + onTap: () => controller.inviteAction( + context, + contacts[i].id, + contacts[i].displayName ?? + contacts[i].id.localpart ?? + L10n.of(context).user, + ), + ), ); }, ); @@ -169,10 +178,7 @@ class _InviteContactListTile extends StatelessWidget { mxContent: profile.avatarUrl, name: profile.displayName, presenceUserId: profile.userId, - onTap: () => UserDialog.show( - context: context, - profile: profile, - ), + onTap: () => UserDialog.show(context: context, profile: profile), ), title: Text( profile.displayName ?? profile.userId.localpart ?? l10n.user, @@ -183,9 +189,7 @@ class _InviteContactListTile extends StatelessWidget { profile.userId, maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( - color: theme.colorScheme.secondary, - ), + style: TextStyle(color: theme.colorScheme.secondary), ), trailing: TextButton.icon( onPressed: isMember ? null : onTap, diff --git a/lib/pages/key_verification/key_verification_dialog.dart b/lib/pages/key_verification/key_verification_dialog.dart index cea371355..10121f9b9 100644 --- a/lib/pages/key_verification/key_verification_dialog.dart +++ b/lib/pages/key_verification/key_verification_dialog.dart @@ -14,18 +14,15 @@ import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; class KeyVerificationDialog extends StatefulWidget { - Future show(BuildContext context) => showAdaptiveDialog( - context: context, - builder: (context) => this, - barrierDismissible: false, - ); + Future show(BuildContext context) => showAdaptiveDialog( + context: context, + builder: (context) => this, + barrierDismissible: false, + ); final KeyVerification request; - const KeyVerificationDialog({ - super.key, - required this.request, - }); + const KeyVerificationDialog({super.key, required this.request}); @override KeyVerificationPageState createState() => KeyVerificationPageState(); @@ -57,8 +54,10 @@ class KeyVerificationPageState extends State { void dispose() { widget.request.onUpdate = originalOnUpdate; // don't want to get updates anymore - if (![KeyVerificationState.error, KeyVerificationState.done] - .contains(widget.request.state)) { + if (![ + KeyVerificationState.error, + KeyVerificationState.done, + ].contains(widget.request.state)) { widget.request.cancel('m.user'); } super.dispose(); @@ -98,8 +97,9 @@ class KeyVerificationPageState extends State { final theme = Theme.of(context); User? user; - final directChatId = - widget.request.client.getDirectChatFromUserId(widget.request.userId); + final directChatId = widget.request.client.getDirectChatFromUserId( + widget.request.userId, + ); if (directChatId != null) { user = widget.request.client .getRoomById(directChatId)! @@ -122,7 +122,7 @@ class KeyVerificationPageState extends State { body = Container( margin: const EdgeInsets.only(left: 8.0, right: 8.0), child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ Text( L10n.of(context).askSSSSSign, @@ -152,17 +152,13 @@ class KeyVerificationPageState extends State { ); buttons.add( AdaptiveDialogAction( - child: Text( - L10n.of(context).submit, - ), + child: Text(L10n.of(context).submit), onPressed: () => checkInput(textEditingController.text), ), ); buttons.add( AdaptiveDialogAction( - child: Text( - L10n.of(context).skip, - ), + child: Text(L10n.of(context).skip), onPressed: () => widget.request.openSSSS(skip: true), ), ); @@ -170,7 +166,7 @@ class KeyVerificationPageState extends State { case KeyVerificationState.askAccept: title = Text(L10n.of(context).newVerificationRequest); body = Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const SizedBox(height: 16), Avatar( @@ -179,16 +175,14 @@ class KeyVerificationPageState extends State { size: Avatar.defaultSize * 2, ), const SizedBox(height: 16), - Text( - L10n.of(context).askVerificationRequest(displayName), - ), + Text(L10n.of(context).askVerificationRequest(displayName)), ], ); buttons.add( AdaptiveDialogAction( - onPressed: () => widget.request - .rejectVerification() - .then((_) => Navigator.of(context, rootNavigator: false).pop()), + onPressed: () => widget.request.rejectVerification().then( + (_) => Navigator.of(context, rootNavigator: false).pop(false), + ), child: Text( L10n.of(context).reject, style: TextStyle(color: theme.colorScheme.error), @@ -211,10 +205,7 @@ class KeyVerificationPageState extends State { Stack( alignment: Alignment.center, children: [ - Avatar( - mxContent: user?.avatarUrl, - name: displayName, - ), + Avatar(mxContent: user?.avatarUrl, name: displayName), const SizedBox( width: Avatar.defaultSize + 2, height: Avatar.defaultSize + 2, @@ -258,16 +249,15 @@ class KeyVerificationPageState extends State { title = Text(L10n.of(context).compareNumbersMatch); final numbers = widget.request.sasNumbers; final numbstr = '${numbers[0]}-${numbers[1]}-${numbers[2]}'; - compareWidget = - TextSpan(text: numbstr, style: const TextStyle(fontSize: 40)); + compareWidget = TextSpan( + text: numbstr, + style: const TextStyle(fontSize: 40), + ); } body = Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ - Text.rich( - compareWidget, - textAlign: TextAlign.center, - ), + Text.rich(compareWidget, textAlign: TextAlign.center), ], ); buttons.add( @@ -291,15 +281,12 @@ class KeyVerificationPageState extends State { ? L10n.of(context).waitingPartnerEmoji : L10n.of(context).waitingPartnerNumbers; body = Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const SizedBox(height: 16), const CircularProgressIndicator.adaptive(strokeWidth: 2), const SizedBox(height: 16), - Text( - acceptText, - textAlign: TextAlign.center, - ), + Text(acceptText, textAlign: TextAlign.center), ], ); break; @@ -315,17 +302,16 @@ class KeyVerificationPageState extends State { ); buttons.add( AdaptiveDialogAction( - child: Text( - L10n.of(context).close, - ), - onPressed: () => Navigator.of(context, rootNavigator: false).pop(), + child: Text(L10n.of(context).close), + onPressed: () => + Navigator.of(context, rootNavigator: false).pop(true), ), ); break; case KeyVerificationState.error: title = const Text(''); body = Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const SizedBox(height: 16), Icon(Icons.cancel, color: theme.colorScheme.error, size: 64.0), @@ -339,10 +325,9 @@ class KeyVerificationPageState extends State { ); buttons.add( AdaptiveDialogAction( - child: Text( - L10n.of(context).close, - ), - onPressed: () => Navigator.of(context, rootNavigator: false).pop(), + child: Text(L10n.of(context).close), + onPressed: () => + Navigator.of(context, rootNavigator: false).pop(false), ), ); break; @@ -353,9 +338,7 @@ class KeyVerificationPageState extends State { content: SizedBox( height: 256, width: 256, - child: ListView( - children: [body], - ), + child: ListView(children: [body]), ), actions: buttons, ); @@ -397,7 +380,7 @@ class _Emoji extends StatelessWidget { @override Widget build(BuildContext context) { return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ Text(emoji.emoji, style: const TextStyle(fontSize: 50)), Padding( diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index 1354566fc..c80612e77 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -20,10 +20,7 @@ class Login extends StatefulWidget { // const Login({required this.client, super.key}); final bool withEmail; - const Login({ - super.key, - this.withEmail = false, - }); + const Login({super.key, this.withEmail = false}); // Pangea# @override @@ -58,22 +55,24 @@ class LoginController extends State { // TODO: implement initState super.initState(); loadingSignIn = true; - checkHomeServerAction().then((client) { - if (mounted) { - setState(() { - loadingSignIn = false; - this.client = client; + checkHomeServerAction() + .then((client) { + if (mounted) { + setState(() { + loadingSignIn = false; + this.client = client; + }); + } + }) + .catchError((e) { + final String err = e.toString(); + if (mounted) { + setState(() { + loadingSignIn = false; + passwordError = err.toLocalizedString(context); + }); + } }); - } - }).catchError((e) { - final String err = e.toString(); - if (mounted) { - setState(() { - loadingSignIn = false; - passwordError = err.toLocalizedString(context); - }); - } - }); usernameController.addListener(() => setState(() {})); passwordController.addListener(() => setState(() {})); @@ -194,8 +193,9 @@ class LoginController extends State { // final dialogResult = await showOkCancelAlertDialog( // context: context, // useRootNavigator: false, - // title: L10n.of(context) - // .noMatrixServer(newDomain.toString(), oldHomeserver.toString()), + // title: L10n.of( + // context, + // ).noMatrixServer(newDomain.toString(), oldHomeserver.toString()), // okLabel: L10n.of(context).ok, // cancelLabel: L10n.of(context).cancel, // ); @@ -228,8 +228,10 @@ class LoginController extends State { return client; } - final String homeServer = - AppConfig.defaultHomeserver.trim().toLowerCase().replaceAll(' ', '-'); + final String homeServer = AppConfig.defaultHomeserver + .trim() + .toLowerCase() + .replaceAll(' ', '-'); var homeserver = Uri.parse(homeServer); if (homeserver.scheme.isEmpty) { homeserver = Uri.https(homeServer, ''); @@ -257,8 +259,9 @@ class LoginController extends State { message: L10n.of(context).enterAnEmailAddress, okLabel: L10n.of(context).ok, cancelLabel: L10n.of(context).cancel, - initialText: - usernameController.text.isEmail ? usernameController.text : '', + initialText: usernameController.text.isEmail + ? usernameController.text + : '', hintText: L10n.of(context).enterAnEmailAddress, keyboardType: TextInputType.emailAddress, ); @@ -366,9 +369,10 @@ class LoginController extends State { // #Pangea // extension on String { extension LoginExtension on String { -// Pangea# - static final RegExp _phoneRegex = - RegExp(r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$'); + // Pangea# + static final RegExp _phoneRegex = RegExp( + r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$', + ); static final RegExp _emailRegex = RegExp(r'(.+)@(.+)\.(.+)'); bool get isEmail => _emailRegex.hasMatch(this); diff --git a/lib/pages/login/login_view.dart b/lib/pages/login/login_view.dart index 7904e545f..1c2bc5a43 100644 --- a/lib/pages/login/login_view.dart +++ b/lib/pages/login/login_view.dart @@ -21,13 +21,13 @@ // final titleParts = title.split(homeserver); // return LoginScaffold( -// enforceMobileMode: -// Matrix.of(context).widget.clients.any((client) => client.isLogged()), +// enforceMobileMode: Matrix.of( +// context, +// ).widget.clients.any((client) => client.isLogged()), // appBar: AppBar( -// leading: -// controller.loadingSignIn ? null : const Center(child: BackButton()), -// automaticallyImplyLeading: !controller.loadingSignIn, -// titleSpacing: !controller.loadingSignIn ? 0 : null, +// leading: controller.loading ? null : const Center(child: BackButton()), +// automaticallyImplyLeading: !controller.loading, +// titleSpacing: !controller.loading ? 0 : null, // title: Text.rich( // TextSpan( // children: [ @@ -56,14 +56,14 @@ // Padding( // padding: const EdgeInsets.symmetric(horizontal: 24.0), // child: TextField( -// readOnly: controller.loadingSignIn, +// readOnly: controller.loading, // autocorrect: false, // autofocus: true, // onChanged: controller.checkWellKnownWithCoolDown, // controller: controller.usernameController, // textInputAction: TextInputAction.next, // keyboardType: TextInputType.emailAddress, -// autofillHints: controller.loadingSignIn +// autofillHints: controller.loading // ? null // : [AutofillHints.username], // decoration: InputDecoration( @@ -79,9 +79,9 @@ // Padding( // padding: const EdgeInsets.symmetric(horizontal: 24.0), // child: TextField( -// readOnly: controller.loadingSignIn, +// readOnly: controller.loading, // autocorrect: false, -// autofillHints: controller.loadingSignIn +// autofillHints: controller.loading // ? null // : [AutofillHints.password], // controller: controller.passwordController, @@ -114,9 +114,8 @@ // backgroundColor: theme.colorScheme.primary, // foregroundColor: theme.colorScheme.onPrimary, // ), -// onPressed: -// controller.loadingSignIn ? null : controller.login, -// child: controller.loadingSignIn +// onPressed: controller.loading ? null : controller.login, +// child: controller.loading // ? const LinearProgressIndicator() // : Text(L10n.of(context).login), // ), @@ -125,7 +124,7 @@ // Padding( // padding: const EdgeInsets.symmetric(horizontal: 24.0), // child: TextButton( -// onPressed: controller.loadingSignIn +// onPressed: controller.loading // ? () {} // : controller.passwordForgotten, // style: TextButton.styleFrom( diff --git a/lib/pages/new_group/new_group.dart b/lib/pages/new_group/new_group.dart index b30d78046..c5737a32b 100644 --- a/lib/pages/new_group/new_group.dart +++ b/lib/pages/new_group/new_group.dart @@ -2,33 +2,19 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/new_group/new_group_view.dart'; -import 'package:fluffychat/pangea/chat/constants/default_power_level.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; -import 'package:fluffychat/pangea/extensions/join_rule_extension.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/spaces/client_spaces_extension.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/widgets/matrix.dart'; class NewGroup extends StatefulWidget { - // #Pangea - final String? spaceId; - // Pangea# final CreateGroupType createGroupType; - const NewGroup({ - // #Pangea - this.spaceId, - // Pangea# - this.createGroupType = CreateGroupType.group, - super.key, - }); + const NewGroup({this.createGroupType = CreateGroupType.group, super.key}); @override NewGroupController createState() => NewGroupController(); @@ -37,14 +23,8 @@ class NewGroup extends StatefulWidget { class NewGroupController extends State { TextEditingController nameController = TextEditingController(); - // #Pangea - // bool publicGroup = false; - // bool groupCanBeFound = false; - final GlobalKey formKey = GlobalKey(); - final FocusNode focusNode = FocusNode(); - - bool get canSubmit => nameController.text.trim().isNotEmpty; - // Pangea# + bool publicGroup = false; + bool groupCanBeFound = false; Uint8List? avatar; @@ -62,32 +42,15 @@ class NewGroupController extends State { void setCreateGroupType(Set b) => setState(() => _createGroupType = b.single); - // #Pangea - // void setPublicGroup(bool b) => - // setState(() => publicGroup = groupCanBeFound = b); + void setPublicGroup(bool b) => + setState(() => publicGroup = groupCanBeFound = b); - @override - void initState() { - super.initState(); - nameController.addListener(() => setState(() {})); - } - - @override - void dispose() { - nameController.dispose(); - focusNode.dispose(); - super.dispose(); - } - // Pangea# - - // #Pangea - // void setGroupCanBeFound(bool b) => setState(() => groupCanBeFound = b); - // Pangea# + void setGroupCanBeFound(bool b) => setState(() => groupCanBeFound = b); void selectPhoto() async { final photo = await selectFiles( context, - type: FileSelectorType.images, + type: FileType.image, allowMultiple: false, ); final bytes = await photo.singleOrNull?.readAsBytes(); @@ -101,134 +64,56 @@ class NewGroupController extends State { Future _createGroup() async { if (!mounted) return; final roomId = await Matrix.of(context).client.createGroupChat( - // #Pangea - // visibility: - // groupCanBeFound ? sdk.Visibility.public : sdk.Visibility.private, - // preset: publicGroup - // ? sdk.CreateRoomPreset.publicChat - // : sdk.CreateRoomPreset.privateChat, - preset: sdk.CreateRoomPreset.publicChat, - visibility: sdk.Visibility.private, - // Pangea# - groupName: - nameController.text.isNotEmpty ? nameController.text : null, - initialState: [ - if (avatar != null) - sdk.StateEvent( - type: sdk.EventTypes.RoomAvatar, - content: {'url': avatarUrl.toString()}, - ), - // #Pangea - RoomDefaults.defaultPowerLevels( - Matrix.of(context).client.userID!, - ), - await Matrix.of(context).client.pangeaJoinRules( - widget.spaceId != null - ? 'knock_restricted' - : JoinRules.public - .toString() - .replaceAll('JoinRules.', ''), - allow: widget.spaceId != null - ? [ - { - "type": "m.room_membership", - "room_id": widget.spaceId, - } - ] - : null, - ), - // Pangea# - ], - // #Pangea - enableEncryption: false, - // Pangea# - ); + visibility: groupCanBeFound + ? sdk.Visibility.public + : sdk.Visibility.private, + preset: publicGroup + ? sdk.CreateRoomPreset.publicChat + : sdk.CreateRoomPreset.privateChat, + groupName: nameController.text.isNotEmpty ? nameController.text : null, + initialState: [ + if (avatar != null) + sdk.StateEvent( + type: sdk.EventTypes.RoomAvatar, + content: {'url': avatarUrl.toString()}, + ), + ], + ); if (!mounted) return; - // #Pangea - final client = Matrix.of(context).client; - Room? room = client.getRoomById(roomId); - if (room == null) { - await client.waitForRoomInSync(roomId); - room = client.getRoomById(roomId); - } - if (room == null) return; - - if (widget.spaceId != null) { - try { - final space = client.getRoomById(widget.spaceId!); - await space?.addToSpace(room.id); - if (room.pangeaSpaceParents.isEmpty) { - await client.waitForRoomInSync(roomId); - } - } catch (err) { - ErrorHandler.logError( - e: "Failed to add room to space", - data: {"spaceId": widget.spaceId, "error": err}, - ); - } - } context.go('/rooms/$roomId/invite'); - // Pangea# } Future _createSpace() async { if (!mounted) return; - // #Pangea - // final spaceId = await Matrix.of(context).client.createRoom( - // preset: publicGroup - // ? sdk.CreateRoomPreset.publicChat - // : sdk.CreateRoomPreset.privateChat, - // creationContent: {'type': RoomCreationTypes.mSpace}, - // visibility: publicGroup ? sdk.Visibility.public : null, - // roomAliasName: publicGroup - // ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') - // : null, - // name: nameController.text.trim(), - // powerLevelContentOverride: {'events_default': 100}, - // initialState: [ - // if (avatar != null) - // sdk.StateEvent( - // type: sdk.EventTypes.RoomAvatar, - // content: {'url': avatarUrl.toString()}, - // ), - // ], - // ); - // if (!mounted) return; - // context.pop(spaceId); - final spaceId = await Matrix.of(context).client.createPangeaSpace( - name: nameController.text, - visibility: sdk.Visibility.private, - joinRules: sdk.JoinRules.knock, - avatarUrl: avatarUrl.toString(), - ); - + final spaceId = await Matrix.of(context).client.createRoom( + preset: publicGroup + ? sdk.CreateRoomPreset.publicChat + : sdk.CreateRoomPreset.privateChat, + creationContent: {'type': RoomCreationTypes.mSpace}, + visibility: publicGroup ? sdk.Visibility.public : null, + roomAliasName: publicGroup + ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') + : null, + name: nameController.text.trim(), + powerLevelContentOverride: {'events_default': 100}, + initialState: [ + if (avatar != null) + sdk.StateEvent( + type: sdk.EventTypes.RoomAvatar, + content: {'url': avatarUrl.toString()}, + ), + ], + ); if (!mounted) return; - - final room = Matrix.of(context).client.getRoomById(spaceId); - if (room == null) return; - final spaceCode = room.classCode; - if (spaceCode != null) { - GoogleAnalytics.createClass(room.name, spaceCode); - } - - context.go("/rooms/spaces/$spaceId/details"); - // Pangea# + context.pop(spaceId); } - void submitAction([_]) async { + void submitAction([dynamic _]) async { final client = Matrix.of(context).client; try { - // #Pangea - if (!formKey.currentState!.validate()) { - focusNode.requestFocus(); - return; - } - - // if (nameController.text.trim().isEmpty && - // createGroupType == CreateGroupType.space) { - if (!canSubmit) { - // Pangea# + if (nameController.text.trim().isEmpty && + createGroupType == CreateGroupType.space) { setState(() => error = L10n.of(context).pleaseFillOut); return; } diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart index b12dff279..61f86736b 100644 --- a/lib/pages/new_group/new_group_view.dart +++ b/lib/pages/new_group/new_group_view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/new_group/new_group.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; @@ -13,14 +14,10 @@ class NewGroupView extends StatelessWidget { @override Widget build(BuildContext context) { - // #Pangea - // final theme = Theme.of(context); - // Pangea# + final theme = Theme.of(context); final avatar = controller.avatar; - // #Pangea - // final error = controller.error; - // Pangea# + final error = controller.error; return Scaffold( appBar: AppBar( leading: Center( @@ -30,47 +27,32 @@ class NewGroupView extends StatelessWidget { ), title: Text( controller.createGroupType == CreateGroupType.space - // #Pangea - // ? L10n.of(context).newSpace - ? L10n.of(context).newCourse - // : L10n.of(context).createGroup, - : L10n.of(context).newChat, - // Pangea# + ? L10n.of(context).newSpace + : L10n.of(context).createGroup, ), ), body: MaxWidthBody( - // #Pangea - showBorder: false, - padding: const EdgeInsets.only( - left: 32.0, - right: 32.0, - bottom: 32.0, - ), - // Pangea# child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ - // #Pangea - // Simplying options here - // Padding( - // padding: const EdgeInsets.all(16.0), - // child: SegmentedButton( - // selected: {controller.createGroupType}, - // onSelectionChanged: controller.setCreateGroupType, - // segments: [ - // ButtonSegment( - // value: CreateGroupType.group, - // label: Text(L10n.of(context).group), - // ), - // ButtonSegment( - // value: CreateGroupType.space, - // label: Text(L10n.of(context).space), - // ), - // ], - // ), - // ), - // const SizedBox(height: 16), - // Pangea# + Padding( + padding: const EdgeInsets.all(16.0), + child: SegmentedButton( + selected: {controller.createGroupType}, + onSelectionChanged: controller.setCreateGroupType, + segments: [ + ButtonSegment( + value: CreateGroupType.group, + label: Text(L10n.of(context).group), + ), + ButtonSegment( + value: CreateGroupType.space, + label: Text(L10n.of(context).space), + ), + ], + ), + ), + const SizedBox(height: 16), InkWell( borderRadius: BorderRadius.circular(90), onTap: controller.loading ? null : controller.selectPhoto, @@ -92,107 +74,82 @@ class NewGroupView extends StatelessWidget { const SizedBox(height: 32), Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), - // #Pangea - // child: TextField( - child: Form( - key: controller.formKey, - child: TextFormField( - // Pangea# - autofocus: true, - controller: controller.nameController, - autocorrect: false, - readOnly: controller.loading, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.people_outlined), - labelText: - controller.createGroupType == CreateGroupType.space - // #Pangea - // ? L10n.of(context).spaceName - ? L10n.of(context).courseName - // : L10n.of(context).groupName, - : L10n.of(context).chatName, - // Pangea# - ), - // #Pangea - onFieldSubmitted: (value) { - controller.loading ? null : controller.submitAction(); - }, - validator: (value) => controller.canSubmit - ? null - : L10n.of(context).pleaseFillOut, - focusNode: controller.focusNode, - // Pangea# + child: TextField( + autofocus: true, + controller: controller.nameController, + autocorrect: false, + readOnly: controller.loading, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.people_outlined), + labelText: controller.createGroupType == CreateGroupType.space + ? L10n.of(context).spaceName + : L10n.of(context).groupName, ), ), ), const SizedBox(height: 16), - // #Pangea - // SwitchListTile.adaptive( - // contentPadding: const EdgeInsets.symmetric(horizontal: 32), - // secondary: const Icon(Icons.public_outlined), - // title: Text( - // controller.createGroupType == CreateGroupType.space - // ? L10n.of(context).spaceIsPublic - // : L10n.of(context).groupIsPublic, - // ), - // value: controller.publicGroup, - // onChanged: controller.loading ? null : controller.setPublicGroup, - // ), - // AnimatedSize( - // duration: FluffyThemes.animationDuration, - // curve: FluffyThemes.animationCurve, - // child: controller.publicGroup - // ? SwitchListTile.adaptive( - // contentPadding: - // const EdgeInsets.symmetric(horizontal: 32), - // secondary: const Icon(Icons.search_outlined), - // title: Text(L10n.of(context).groupCanBeFoundViaSearch), - // value: controller.groupCanBeFound, - // onChanged: controller.loading - // ? null - // : controller.setGroupCanBeFound, - // ) - // : const SizedBox.shrink(), - // ), - // AnimatedSize( - // duration: FluffyThemes.animationDuration, - // curve: FluffyThemes.animationCurve, - // child: controller.createGroupType == CreateGroupType.space - // ? const SizedBox.shrink() - // : SwitchListTile.adaptive( - // contentPadding: - // const EdgeInsets.symmetric(horizontal: 32), - // secondary: Icon( - // Icons.lock_outlined, - // color: theme.colorScheme.onSurface, - // ), - // title: Text( - // L10n.of(context).enableEncryption, - // style: TextStyle( - // color: theme.colorScheme.onSurface, - // ), - // ), - // value: !controller.publicGroup, - // onChanged: null, - // ), - // ), - // Pangea# + SwitchListTile.adaptive( + contentPadding: const EdgeInsets.symmetric(horizontal: 32), + secondary: const Icon(Icons.public_outlined), + title: Text( + controller.createGroupType == CreateGroupType.space + ? L10n.of(context).spaceIsPublic + : L10n.of(context).groupIsPublic, + ), + value: controller.publicGroup, + onChanged: controller.loading ? null : controller.setPublicGroup, + ), + AnimatedSize( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: controller.publicGroup + ? SwitchListTile.adaptive( + contentPadding: const EdgeInsets.symmetric( + horizontal: 32, + ), + secondary: const Icon(Icons.search_outlined), + title: Text(L10n.of(context).groupCanBeFoundViaSearch), + value: controller.groupCanBeFound, + onChanged: controller.loading + ? null + : controller.setGroupCanBeFound, + ) + : const SizedBox.shrink(), + ), + AnimatedSize( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: controller.createGroupType == CreateGroupType.space + ? const SizedBox.shrink() + : SwitchListTile.adaptive( + contentPadding: const EdgeInsets.symmetric( + horizontal: 32, + ), + secondary: Icon( + Icons.lock_outlined, + color: theme.colorScheme.onSurface, + ), + title: Text( + L10n.of(context).enableEncryption, + style: TextStyle(color: theme.colorScheme.onSurface), + ), + value: !controller.publicGroup, + onChanged: null, + ), + ), AnimatedSize( duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, child: controller.createGroupType == CreateGroupType.space ? ListTile( - contentPadding: - const EdgeInsets.symmetric(horizontal: 32), + contentPadding: const EdgeInsets.symmetric( + horizontal: 32, + ), trailing: const Padding( padding: EdgeInsets.symmetric(horizontal: 16.0), child: Icon(Icons.info_outlined), ), - // #Pangea - // subtitle: Text(L10n.of(context).newSpaceDescription), - subtitle: - Text(L10n.of(context).updatedNewSpaceDescription), - // Pangea# + subtitle: Text(L10n.of(context).newSpaceDescription), ) : const SizedBox.shrink(), ), @@ -201,42 +158,35 @@ class NewGroupView extends StatelessWidget { child: SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: - controller.loading ? null : controller.submitAction, + onPressed: controller.loading + ? null + : controller.submitAction, child: controller.loading ? const LinearProgressIndicator() : Text( controller.createGroupType == CreateGroupType.space - // #Pangea - // ? L10n.of(context).createNewSpace - ? L10n.of(context).createNewCourse - // : L10n.of(context).createGroupAndInviteUsers, - : L10n.of(context).createChatAndInviteUsers, - // Pangea# + ? L10n.of(context).createNewSpace + : L10n.of(context).createGroupAndInviteUsers, ), ), ), ), - // #Pangea - // AnimatedSize( - // duration: FluffyThemes.animationDuration, - // curve: FluffyThemes.animationCurve, - // child: error == null - // ? const SizedBox.shrink() - // : ListTile( - // leading: Icon( - // Icons.warning_outlined, - // color: theme.colorScheme.error, - // ), - // title: Text( - // error.toLocalizedString(context), - // style: TextStyle( - // color: theme.colorScheme.error, - // ), - // ), - // ), - // ), - // Pangea# + AnimatedSize( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: error == null + ? const SizedBox.shrink() + : ListTile( + leading: Icon( + Icons.warning_outlined, + color: theme.colorScheme.error, + ), + title: Text( + error.toLocalizedString(context), + style: TextStyle(color: theme.colorScheme.error), + ), + ), + ), ], ), ), diff --git a/lib/pages/new_private_chat/new_private_chat.dart b/lib/pages/new_private_chat/new_private_chat.dart index 4c7984ce2..08cd4530a 100644 --- a/lib/pages/new_private_chat/new_private_chat.dart +++ b/lib/pages/new_private_chat/new_private_chat.dart @@ -54,8 +54,9 @@ class NewPrivateChatController extends State { Future> _searchUser(String searchTerm) async { // #Pangea - // final result = - // await Matrix.of(context).client.searchUserDirectory(searchTerm); + // final result = await Matrix.of( + // context, + // ).client.searchUserDirectory(searchTerm); final result = await Matrix.of(context).client.searchUser(searchTerm); // Pangea# final profiles = result.results; @@ -79,9 +80,7 @@ class NewPrivateChatController extends State { if (info.version.sdkInt < 21) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - L10n.of(context).unsupportedAndroidVersionLong, - ), + content: Text(L10n.of(context).unsupportedAndroidVersionLong), ), ); return; @@ -99,15 +98,13 @@ class NewPrivateChatController extends State { await Clipboard.setData( ClipboardData(text: Matrix.of(context).client.userID!), ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context).copiedToClipboard)), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(L10n.of(context).copiedToClipboard))); } - void openUserModal(Profile profile) => UserDialog.show( - context: context, - profile: profile, - ); + void openUserModal(Profile profile) => + UserDialog.show(context: context, profile: profile); @override Widget build(BuildContext context) => NewPrivateChatView(this); diff --git a/lib/pages/new_private_chat/new_private_chat_view.dart b/lib/pages/new_private_chat/new_private_chat_view.dart index b7aec47bb..94bda0fa5 100644 --- a/lib/pages/new_private_chat/new_private_chat_view.dart +++ b/lib/pages/new_private_chat/new_private_chat_view.dart @@ -38,8 +38,10 @@ class NewPrivateChatView extends StatelessWidget { // #Pangea // actions: [ // TextButton( - // onPressed: - // UrlLauncher(context, AppConfig.startChatTutorial).launchUrl, + // onPressed: UrlLauncher( + // context, + // AppConfig.startChatTutorial, + // ).launchUrl, // child: Text(L10n.of(context).help), // ), // ], @@ -103,186 +105,196 @@ class NewPrivateChatView extends StatelessWidget { ), ), Expanded( - child: AnimatedCrossFade( + child: AnimatedSwitcher( duration: FluffyThemes.animationDuration, - crossFadeState: searchResponse == null - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - firstChild: ListView( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18.0), - child: SelectableText.rich( - TextSpan( - children: [ - TextSpan( - text: L10n.of(context).yourGlobalUserIdIs, + child: searchResponse == null + ? ListView( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 18.0, ), - TextSpan( - text: Matrix.of(context).client.userID, - style: const TextStyle( - fontWeight: FontWeight.w600, + child: SelectableText.rich( + TextSpan( + children: [ + TextSpan( + text: L10n.of(context).yourGlobalUserIdIs, + ), + TextSpan( + text: Matrix.of(context).client.userID, + style: const TextStyle( + fontWeight: FontWeight.w600, + ), + ), + ], + ), + style: TextStyle( + color: theme.colorScheme.onSurface, + fontSize: 12, ), ), - ], - ), - style: TextStyle( - color: theme.colorScheme.onSurface, - fontSize: 12, - ), - ), - ), - const SizedBox(height: 8), - ListTile( - leading: CircleAvatar( - backgroundColor: theme.colorScheme.secondaryContainer, - foregroundColor: theme.colorScheme.onSecondaryContainer, - child: Icon(Icons.adaptive.share_outlined), - ), - title: Text(L10n.of(context).shareInviteLink), - onTap: controller.inviteAction, - ), - // #Pangea - // ListTile( - // leading: CircleAvatar( - // backgroundColor: theme.colorScheme.tertiaryContainer, - // foregroundColor: theme.colorScheme.onTertiaryContainer, - // child: const Icon(Icons.group_add_outlined), - // ), - // title: Text(L10n.of(context).createGroup), - // onTap: () => context.go('/rooms/newgroup'), - // ), - // Pangea# - if (PlatformInfos.isMobile) - ListTile( - leading: CircleAvatar( - backgroundColor: theme.colorScheme.primaryContainer, - foregroundColor: theme.colorScheme.onPrimaryContainer, - child: const Icon(Icons.qr_code_scanner_outlined), - ), - title: Text(L10n.of(context).scanQrCode), - onTap: controller.openScannerAction, - ), - Center( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 64.0, - vertical: 24.0, - ), - child: Material( - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - side: BorderSide( - width: 3, - color: theme.colorScheme.primary, - ), ), - color: Colors.transparent, - clipBehavior: Clip.hardEdge, - child: InkWell( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - onTap: () => showQrCodeViewer( - context, - userId, + const SizedBox(height: 8), + ListTile( + leading: CircleAvatar( + backgroundColor: + theme.colorScheme.secondaryContainer, + foregroundColor: + theme.colorScheme.onSecondaryContainer, + child: Icon(Icons.adaptive.share_outlined), ), + title: Text(L10n.of(context).shareInviteLink), + onTap: controller.inviteAction, + ), + // #Pangea + // ListTile( + // leading: CircleAvatar( + // backgroundColor: + // theme.colorScheme.tertiaryContainer, + // foregroundColor: + // theme.colorScheme.onTertiaryContainer, + // child: const Icon(Icons.group_add_outlined), + // ), + // title: Text(L10n.of(context).createGroup), + // onTap: () => context.go('/rooms/newgroup'), + // ), + // Pangea# + if (PlatformInfos.isMobile) + ListTile( + leading: CircleAvatar( + backgroundColor: + theme.colorScheme.primaryContainer, + foregroundColor: + theme.colorScheme.onPrimaryContainer, + child: const Icon( + Icons.qr_code_scanner_outlined, + ), + ), + title: Text(L10n.of(context).scanQrCode), + onTap: controller.openScannerAction, + ), + Center( child: Padding( - padding: const EdgeInsets.all(16.0), - child: ConstrainedBox( - constraints: - const BoxConstraints(maxWidth: 200), - child: PrettyQrView.data( - // #Pangea - // data: 'https://matrix.to/#/$userId', - data: Environment.frontendURL, - // Pangea# - decoration: PrettyQrDecoration( - shape: PrettyQrSmoothSymbol( - roundFactor: 1, - color: theme.colorScheme.primary, + padding: const EdgeInsets.symmetric( + horizontal: 64.0, + vertical: 24.0, + ), + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + side: BorderSide( + width: 3, + color: theme.colorScheme.primary, + ), + ), + color: Colors.transparent, + clipBehavior: Clip.hardEdge, + child: InkWell( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + onTap: () => + showQrCodeViewer(context, userId), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 200, + ), + child: PrettyQrView.data( + // #Pangea + // data: 'https://matrix.to/#/$userId', + data: Environment.frontendURL, + // Pangea# + decoration: PrettyQrDecoration( + shape: PrettyQrSmoothSymbol( + roundFactor: 1, + color: theme.colorScheme.primary, + ), + ), + ), ), ), ), ), ), ), - ), + ], + ) + : FutureBuilder( + future: searchResponse, + builder: (context, snapshot) { + final result = snapshot.data; + final error = snapshot.error; + if (error != null) { + return Column( + mainAxisAlignment: .center, + children: [ + Text( + error.toLocalizedString(context), + textAlign: TextAlign.center, + style: TextStyle( + color: theme.colorScheme.error, + ), + ), + const SizedBox(height: 12), + OutlinedButton.icon( + onPressed: controller.searchUsers, + icon: const Icon(Icons.refresh_outlined), + label: Text(L10n.of(context).tryAgain), + ), + ], + ); + } + if (result == null) { + return const Center( + child: CircularProgressIndicator.adaptive(), + ); + } + if (result.isEmpty) { + return Column( + mainAxisAlignment: .center, + children: [ + const Icon(Icons.search_outlined, size: 86), + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + L10n.of(context).noUsersFoundWithQuery( + controller.controller.text, + ), + style: TextStyle( + color: theme.colorScheme.primary, + ), + textAlign: TextAlign.center, + ), + ), + ], + ); + } + return ListView.builder( + itemCount: result.length, + itemBuilder: (context, i) { + final contact = result[i]; + final displayname = + contact.displayName ?? + contact.userId.localpart ?? + contact.userId; + return ListTile( + leading: Avatar( + name: displayname, + mxContent: contact.avatarUrl, + presenceUserId: contact.userId, + ), + title: Text(displayname), + subtitle: Text(contact.userId), + onTap: () => controller.openUserModal(contact), + ); + }, + ); + }, ), - ), - ], - ), - secondChild: FutureBuilder( - future: searchResponse, - builder: (context, snapshot) { - final result = snapshot.data; - final error = snapshot.error; - if (error != null) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - error.toLocalizedString(context), - textAlign: TextAlign.center, - style: TextStyle( - color: theme.colorScheme.error, - ), - ), - const SizedBox(height: 12), - OutlinedButton.icon( - onPressed: controller.searchUsers, - icon: const Icon(Icons.refresh_outlined), - label: Text(L10n.of(context).tryAgain), - ), - ], - ); - } - if (result == null) { - return const Center( - child: CircularProgressIndicator.adaptive(), - ); - } - if (result.isEmpty) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.search_outlined, size: 86), - Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - L10n.of(context).noUsersFoundWithQuery( - controller.controller.text, - ), - style: TextStyle( - color: theme.colorScheme.primary, - ), - textAlign: TextAlign.center, - ), - ), - ], - ); - } - return ListView.builder( - itemCount: result.length, - itemBuilder: (context, i) { - final contact = result[i]; - final displayname = contact.displayName ?? - contact.userId.localpart ?? - contact.userId; - return ListTile( - leading: Avatar( - name: displayname, - mxContent: contact.avatarUrl, - presenceUserId: contact.userId, - ), - title: Text(displayname), - subtitle: Text(contact.userId), - onTap: () => controller.openUserModal(contact), - ); - }, - ); - }, - ), ), ), ], diff --git a/lib/pages/onboarding/enable_notifications.dart b/lib/pages/onboarding/enable_notifications.dart index e3586033a..d8c67c1dd 100644 --- a/lib/pages/onboarding/enable_notifications.dart +++ b/lib/pages/onboarding/enable_notifications.dart @@ -30,17 +30,9 @@ class EnableNotificationsController extends State { Future _setProfile() async { final client = Matrix.of(context).client; try { - profile = await client.getProfileFromUserId( - client.userID!, - ); + profile = await client.getProfileFromUserId(client.userID!); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'userId': client.userID, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'userId': client.userID}); } finally { if (mounted) setState(() {}); } @@ -58,21 +50,14 @@ class EnableNotificationsController extends State { return PangeaLoginScaffold( customAppBar: AppBar( title: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 450, - ), + constraints: const BoxConstraints(maxWidth: 450), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ BackButton( - onPressed: () => pLogoutAction( - context, - bypassWarning: true, - ), - ), - const SizedBox( - width: 40.0, + onPressed: () => pLogoutAction(context, bypassWarning: true), ), + const SizedBox(width: 40.0), ], ), ), @@ -90,10 +75,9 @@ class EnableNotificationsController extends State { Matrix.of(context).client.userID?.localpart ?? "", ), - style: Theme.of(context) - .textTheme - .titleLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: Theme.of( + context, + ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), Text( L10n.of(context).enableNotificationsTitle, @@ -103,14 +87,13 @@ class EnableNotificationsController extends State { onPressed: _requestNotificationPermission, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primaryContainer, - foregroundColor: - Theme.of(context).colorScheme.onPrimaryContainer, + foregroundColor: Theme.of( + context, + ).colorScheme.onPrimaryContainer, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).enableNotificationsDesc), - ], + children: [Text(L10n.of(context).enableNotificationsDesc)], ), ), TextButton( diff --git a/lib/pages/onboarding/space_code_onboarding.dart b/lib/pages/onboarding/space_code_onboarding.dart index 54c50f557..5f998404e 100644 --- a/lib/pages/onboarding/space_code_onboarding.dart +++ b/lib/pages/onboarding/space_code_onboarding.dart @@ -39,17 +39,9 @@ class SpaceCodeOnboardingState extends State { Future _setProfile() async { try { - profile = await client.getProfileFromUserId( - client.userID!, - ); + profile = await client.getProfileFromUserId(client.userID!); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'userId': client.userID, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'userId': client.userID}); } finally { if (mounted) setState(() {}); } diff --git a/lib/pages/onboarding/space_code_onboarding_view.dart b/lib/pages/onboarding/space_code_onboarding_view.dart index bbd57d5eb..e4de82b76 100644 --- a/lib/pages/onboarding/space_code_onboarding_view.dart +++ b/lib/pages/onboarding/space_code_onboarding_view.dart @@ -9,28 +9,19 @@ import 'package:fluffychat/pangea/login/pages/pangea_login_scaffold.dart'; class SpaceCodeOnboardingView extends StatelessWidget { final SpaceCodeOnboardingState controller; - const SpaceCodeOnboardingView({ - super.key, - required this.controller, - }); + const SpaceCodeOnboardingView({super.key, required this.controller}); @override Widget build(BuildContext context) { return PangeaLoginScaffold( customAppBar: AppBar( title: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 450, - ), + constraints: const BoxConstraints(maxWidth: 450), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BackButton( - onPressed: Navigator.of(context).pop, - ), - const SizedBox( - width: 40.0, - ), + BackButton(onPressed: Navigator.of(context).pop), + const SizedBox(width: 40.0), ], ), ), @@ -48,10 +39,9 @@ class SpaceCodeOnboardingView extends StatelessWidget { controller.client.userID?.localpart ?? "", ), - style: Theme.of(context) - .textTheme - .titleLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: Theme.of( + context, + ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), Text( L10n.of(context).joinSpaceOnboardingDesc, @@ -70,14 +60,13 @@ class SpaceCodeOnboardingView extends StatelessWidget { : null, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primaryContainer, - foregroundColor: - Theme.of(context).colorScheme.onPrimaryContainer, + foregroundColor: Theme.of( + context, + ).colorScheme.onPrimaryContainer, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).join), - ], + children: [Text(L10n.of(context).join)], ), ), TextButton( diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index 2928126f6..291e467ec 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; import 'package:matrix/matrix.dart'; @@ -26,9 +27,9 @@ class SettingsController extends State { bool profileUpdated = false; void updateProfile() => setState(() { - profileUpdated = true; - profileFuture = null; - }); + profileUpdated = true; + profileFuture = null; + }); void setDisplaynameAction() async { final profile = await profileFuture; @@ -45,7 +46,11 @@ class SettingsController extends State { final matrix = Matrix.of(context); final success = await showFutureLoadingDialog( context: context, - future: () => matrix.client.setDisplayName(matrix.client.userID!, input), + future: () => matrix.client.setProfileField( + matrix.client.userID!, + 'displayname', + {'displayname': input}, + ), ); if (success.error == null) { updateProfile(); @@ -128,15 +133,9 @@ class SettingsController extends State { imageQuality: 50, ); if (result == null) return; - file = MatrixFile( - bytes: await result.readAsBytes(), - name: result.path, - ); + file = MatrixFile(bytes: await result.readAsBytes(), name: result.path); } else { - final result = await selectFiles( - context, - type: FileSelectorType.images, - ); + final result = await selectFiles(context, type: FileType.image); final pickedFile = result.firstOrNull; if (pickedFile == null) return; file = MatrixFile( @@ -178,8 +177,8 @@ class SettingsController extends State { await client.encryption?.crossSigning.isCached() ?? false; final needsBootstrap = await client.encryption?.keyManager.isCached() == false || - client.encryption?.crossSigning.enabled == false || - crossSigning == false; + client.encryption?.crossSigning.enabled == false || + crossSigning == false; final isUnknownSession = client.isUnknownSession; setState(() { showChatBackupBanner = needsBootstrap || isUnknownSession; @@ -189,7 +188,7 @@ class SettingsController extends State { bool? crossSigningCached; bool? showChatBackupBanner; - void firstRunBootstrapAction([_]) async { + void firstRunBootstrapAction([dynamic _]) async { // #Pangea // if (showChatBackupBanner != true) { // showOkAlertDialog( @@ -200,9 +199,7 @@ class SettingsController extends State { // ); // return; // } - // await BootstrapDialog( - // client: Matrix.of(context).client, - // ).show(context); + // await context.push('/backup'); // checkBootstrap(); // Pangea# } @@ -240,9 +237,7 @@ class SettingsController extends State { @override Widget build(BuildContext context) { final client = Matrix.of(context).client; - profileFuture ??= client.getProfileFromUserId( - client.userID!, - ); + profileFuture ??= client.getProfileFromUserId(client.userID!); return SettingsView(this); } } diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 874e7ebe3..5f87b994f 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -8,14 +8,14 @@ import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pages/settings/settings.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/local_notifications_extension.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../../widgets/mxc_image_viewer.dart'; -import 'settings.dart'; +import 'package:fluffychat/widgets/mxc_image_viewer.dart'; class SettingsView extends StatelessWidget { final SettingsController controller; @@ -28,8 +28,9 @@ class SettingsView extends StatelessWidget { // #Pangea // final showChatBackupBanner = controller.showChatBackupBanner; // Pangea# - final activeRoute = - GoRouter.of(context).routeInformationProvider.value.uri.path; + final activeRoute = GoRouter.of( + context, + ).routeInformationProvider.value.uri.path; // #Pangea // final accountManageUrl = Matrix.of(context) // .client @@ -47,10 +48,7 @@ class SettingsView extends StatelessWidget { // onGoToChats: () => context.go('/rooms'), // onGoToSpaceId: (spaceId) => context.go('/rooms?spaceId=$spaceId'), // ), - // Container( - // color: Theme.of(context).dividerColor, - // width: 1, - // ), + // Container(color: Theme.of(context).dividerColor, width: 1), // ], // Pangea# Expanded( @@ -61,9 +59,7 @@ class SettingsView extends StatelessWidget { // : AppBar( // title: Text(L10n.of(context).settings), // leading: Center( - // child: BackButton( - // onPressed: () => context.go('/rooms'), - // ), + // child: BackButton(onPressed: () => context.go('/rooms')), // ), // ), // Pangea# @@ -80,7 +76,8 @@ class SettingsView extends StatelessWidget { builder: (context, snapshot) { final profile = snapshot.data; final avatar = profile?.avatarUrl; - final mxid = Matrix.of(context).client.userID ?? + final mxid = + Matrix.of(context).client.userID ?? L10n.of(context).user; final displayname = profile?.displayName ?? mxid.localpart ?? mxid; @@ -106,10 +103,10 @@ class SettingsView extends StatelessWidget { size: Avatar.defaultSize * 2.5, onTap: avatar != null ? () => showDialog( - context: context, - builder: (_) => - MxcImageViewer(avatar), - ) + context: context, + builder: (_) => + MxcImageViewer(avatar), + ) : null, ), if (profile != null) @@ -130,8 +127,8 @@ class SettingsView extends StatelessWidget { ), Expanded( child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: .center, + crossAxisAlignment: .start, children: [ TextButton.icon( onPressed: controller.setDisplaynameAction, @@ -203,16 +200,14 @@ class SettingsView extends StatelessWidget { // title: Text(L10n.of(context).chatBackup), // onChanged: controller.firstRunBootstrapAction, // ), - // Divider( - // color: theme.dividerColor, - // ), + // Divider(color: theme.dividerColor), ListTile( leading: const Icon(Icons.language_outlined), title: Text(L10n.of(context).learningSettings), tileColor: activeRoute.startsWith('/rooms/settings/learning') - ? theme.colorScheme.surfaceContainerHigh - : null, + ? theme.colorScheme.surfaceContainerHigh + : null, onTap: () => context.go('/rooms/settings/learning'), ), // Pangea# @@ -227,16 +222,19 @@ class SettingsView extends StatelessWidget { ListTile( leading: const Icon(Icons.notifications_outlined), title: Text(L10n.of(context).notifications), - tileColor: activeRoute - .startsWith('/rooms/settings/notifications') + tileColor: + activeRoute.startsWith( + '/rooms/settings/notifications', + ) ? theme.colorScheme.surfaceContainerHigh : null, onTap: () => context.go('/rooms/settings/notifications'), // #Pangea trailing: ValueListenableBuilder( - valueListenable: - Matrix.of(context).notifPermissionNotifier, - builder: (context, _, __) => FutureBuilder( + valueListenable: Matrix.of( + context, + ).notifPermissionNotifier, + builder: (context, _, _) => FutureBuilder( future: Matrix.of(context).notificationsEnabled, builder: (context, snapshot) { return snapshot.data != false @@ -256,8 +254,8 @@ class SettingsView extends StatelessWidget { onTap: () => context.go('/rooms/settings/devices'), tileColor: activeRoute.startsWith('/rooms/settings/devices') - ? theme.colorScheme.surfaceContainerHigh - : null, + ? theme.colorScheme.surfaceContainerHigh + : null, ), ListTile( leading: const Icon(Icons.forum_outlined), @@ -274,8 +272,8 @@ class SettingsView extends StatelessWidget { onTap: () => context.go('/rooms/settings/subscription'), tileColor: activeRoute.startsWith('/rooms/settings/subscription') - ? theme.colorScheme.surfaceContainerHigh - : null, + ? theme.colorScheme.surfaceContainerHigh + : null, ), // Pangea# ListTile( @@ -284,8 +282,8 @@ class SettingsView extends StatelessWidget { onTap: () => context.go('/rooms/settings/security'), tileColor: activeRoute.startsWith('/rooms/settings/security') - ? theme.colorScheme.surfaceContainerHigh - : null, + ? theme.colorScheme.surfaceContainerHigh + : null, ), Divider(color: theme.dividerColor), // #Pangea @@ -297,11 +295,11 @@ class SettingsView extends StatelessWidget { await showFutureLoadingDialog( context: context, future: () async { - final roomId = - await Matrix.of(context).client.startDirectChat( - Environment.supportUserId, - enableEncryption: false, - ); + final roomId = await Matrix.of(context).client + .startDirectChat( + Environment.supportUserId, + enableEncryption: false, + ); context.go('/rooms/$roomId'); }, ); @@ -330,8 +328,9 @@ class SettingsView extends StatelessWidget { ); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - Text(L10n.of(context).copiedToClipboard), + content: Text( + L10n.of(context).copiedToClipboard, + ), ), ); }, @@ -374,13 +373,13 @@ class SettingsView extends StatelessWidget { // onTap: () => context.go('/rooms/settings/homeserver'), // tileColor: // activeRoute.startsWith('/rooms/settings/homeserver') - // ? theme.colorScheme.surfaceContainerHigh - // : null, + // ? theme.colorScheme.surfaceContainerHigh + // : null, // ), // ListTile( // leading: const Icon(Icons.privacy_tip_outlined), // title: Text(L10n.of(context).privacy), - // onTap: () => launchUrlString(AppConfig.privacyUrl), + // onTap: () => launchUrl(AppConfig.privacyUrl), // ), // ListTile( // leading: const Icon(Icons.info_outline_rounded), diff --git a/lib/pages/settings_3pid/settings_3pid.dart b/lib/pages/settings_3pid/settings_3pid.dart index 9014c4155..616cc2eeb 100644 --- a/lib/pages/settings_3pid/settings_3pid.dart +++ b/lib/pages/settings_3pid/settings_3pid.dart @@ -34,10 +34,10 @@ class Settings3PidController extends State { final response = await showFutureLoadingDialog( context: context, future: () => Matrix.of(context).client.requestTokenToRegisterEmail( - clientSecret, - input, - Settings3Pid.sendAttempt++, - ), + clientSecret, + input, + Settings3Pid.sendAttempt++, + ), ); if (response.error != null) return; final ok = await showOkAlertDialog( @@ -55,12 +55,10 @@ class Settings3PidController extends State { context: context, delay: false, future: () => Matrix.of(context).client.uiaRequestBackground( - (auth) => Matrix.of(context).client.add3PID( - clientSecret, - response.result!.sid, - auth: auth, - ), - ), + (auth) => Matrix.of( + context, + ).client.add3PID(clientSecret, response.result!.sid, auth: auth), + ), // #Pangea showError: (e) => !e.toString().contains("Request has been canceled"), // Pangea# @@ -84,10 +82,9 @@ class Settings3PidController extends State { } final success = await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context).client.delete3pidFromAccount( - identifier.address, - identifier.medium, - ), + future: () => Matrix.of( + context, + ).client.delete3pidFromAccount(identifier.address, identifier.medium), ); if (success.error != null) return; setState(() => request = null); diff --git a/lib/pages/settings_3pid/settings_3pid_view.dart b/lib/pages/settings_3pid/settings_3pid_view.dart index e854ff634..716f0df1b 100644 --- a/lib/pages/settings_3pid/settings_3pid_view.dart +++ b/lib/pages/settings_3pid/settings_3pid_view.dart @@ -36,71 +36,75 @@ class Settings3PidView extends StatelessWidget { withScrolling: false, child: FutureBuilder?>( future: controller.request, - builder: ( - BuildContext context, - AsyncSnapshot?> snapshot, - ) { - if (snapshot.hasError) { - return Center( - child: Text( - snapshot.error.toString(), - textAlign: TextAlign.center, - ), - ); - } - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ); - } - final identifier = snapshot.data!; - return Column( - children: [ - ListTile( - leading: CircleAvatar( - backgroundColor: theme.scaffoldBackgroundColor, - foregroundColor: - identifier.isEmpty ? Colors.orange : Colors.grey, - child: Icon( - identifier.isEmpty - ? Icons.warning_outlined - : Icons.info_outlined, + builder: + ( + BuildContext context, + AsyncSnapshot?> snapshot, + ) { + if (snapshot.hasError) { + return Center( + child: Text( + snapshot.error.toString(), + textAlign: TextAlign.center, ), - ), - title: Text( - identifier.isEmpty - // #Pangea - // ? L10n.of(context).noPasswordRecoveryDescription - // : L10n.of(context) - // .withTheseAddressesRecoveryDescription,isEmpty - ? L10n.of(context).noAddressDescription - : L10n.of(context).withTheseAddressesDescription, - // Pangea# - ), - ), - const Divider(), - Expanded( - child: ListView.builder( - itemCount: identifier.length, - itemBuilder: (BuildContext context, int i) => ListTile( + ); + } + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ); + } + final identifier = snapshot.data!; + return Column( + children: [ + ListTile( leading: CircleAvatar( backgroundColor: theme.scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(identifier[i].iconData), + foregroundColor: identifier.isEmpty + ? Colors.orange + : Colors.grey, + child: Icon( + identifier.isEmpty + ? Icons.warning_outlined + : Icons.info_outlined, + ), ), - title: Text(identifier[i].address), - trailing: IconButton( - tooltip: L10n.of(context).delete, - icon: const Icon(Icons.delete_forever_outlined), - color: Colors.red, - onPressed: () => controller.delete3Pid(identifier[i]), + title: Text( + identifier.isEmpty + // #Pangea + // ? L10n.of(context).noPasswordRecoveryDescription + // : L10n.of( + // context, + // ).withTheseAddressesRecoveryDescription, + ? L10n.of(context).noAddressDescription + : L10n.of(context).withTheseAddressesDescription, + // Pangea# ), ), - ), - ), - ], - ); - }, + const Divider(), + Expanded( + child: ListView.builder( + itemCount: identifier.length, + itemBuilder: (BuildContext context, int i) => ListTile( + leading: CircleAvatar( + backgroundColor: theme.scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(identifier[i].iconData), + ), + title: Text(identifier[i].address), + trailing: IconButton( + tooltip: L10n.of(context).delete, + icon: const Icon(Icons.delete_forever_outlined), + color: Colors.red, + onPressed: () => + controller.delete3Pid(identifier[i]), + ), + ), + ), + ), + ], + ); + }, ), ), ); diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 16db97abc..0d635c0fd 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -4,7 +4,6 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/settings_switch_list_tile.dart'; import 'settings_chat.dart'; @@ -32,53 +31,33 @@ class SettingsChatView extends StatelessWidget { // SettingsSwitchListTile.adaptive( // title: L10n.of(context).formattedMessages, // subtitle: L10n.of(context).formattedMessagesDescription, - // onChanged: (b) => AppConfig.renderHtml = b, - // storeKey: SettingKeys.renderHtml, - // defaultValue: AppConfig.renderHtml, - // ), - // SettingsSwitchListTile.adaptive( - // title: L10n.of(context).hideMemberChangesInPublicChats, - // subtitle: L10n.of(context).hideMemberChangesInPublicChatsBody, - // onChanged: (b) => AppConfig.hideUnimportantStateEvents = b, - // storeKey: SettingKeys.hideUnimportantStateEvents, - // defaultValue: AppConfig.hideUnimportantStateEvents, + // setting: AppSettings.renderHtml, // ), // Pangea# SettingsSwitchListTile.adaptive( title: L10n.of(context).hideRedactedMessages, subtitle: L10n.of(context).hideRedactedMessagesBody, - onChanged: (b) => AppConfig.hideRedactedEvents = b, - storeKey: SettingKeys.hideRedactedEvents, - defaultValue: AppConfig.hideRedactedEvents, + setting: AppSettings.hideRedactedEvents, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).hideInvalidOrUnknownMessageFormats, - onChanged: (b) => AppConfig.hideUnknownEvents = b, - storeKey: SettingKeys.hideUnknownEvents, - defaultValue: AppConfig.hideUnknownEvents, + setting: AppSettings.hideUnknownEvents, ), // #Pangea // if (PlatformInfos.isMobile) // SettingsSwitchListTile.adaptive( // title: L10n.of(context).autoplayImages, - // onChanged: (b) => AppConfig.autoplayImages = b, - // storeKey: SettingKeys.autoplayImages, - // defaultValue: AppConfig.autoplayImages, + // setting: AppSettings.autoplayImages, // ), // Pangea# SettingsSwitchListTile.adaptive( title: L10n.of(context).sendOnEnter, - onChanged: (b) => AppConfig.sendOnEnter = b, - storeKey: SettingKeys.sendOnEnter, - defaultValue: AppConfig.sendOnEnter ?? !PlatformInfos.isMobile, + setting: AppSettings.sendOnEnter, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).swipeRightToLeftToReply, - onChanged: (b) => AppConfig.swipeRightToLeftToReply = b, - storeKey: SettingKeys.swipeRightToLeftToReply, - defaultValue: AppConfig.swipeRightToLeftToReply, + setting: AppSettings.swipeRightToLeftToReply, ), - // #Pangea SwitchListTile.adaptive( value: AppConfig.useActivityImageAsChatBackground, @@ -117,12 +96,10 @@ class SettingsChatView extends StatelessWidget { // SettingsSwitchListTile.adaptive( // title: L10n.of(context).experimentalVideoCalls, // onChanged: (b) { - // AppConfig.experimentalVoip = b; // Matrix.of(context).createVoipPlugin(); // return; // }, - // storeKey: SettingKeys.experimentalVoip, - // defaultValue: AppConfig.experimentalVoip, + // setting: AppSettings.experimentalVoip, // ), // Pangea# ], diff --git a/lib/pages/settings_emotes/import_archive_dialog.dart b/lib/pages/settings_emotes/import_archive_dialog.dart index 1e0c2e880..46bca4d3a 100644 --- a/lib/pages/settings_emotes/import_archive_dialog.dart +++ b/lib/pages/settings_emotes/import_archive_dialog.dart @@ -46,11 +46,7 @@ class _ImportEmoteArchiveDialogState extends State { return AlertDialog( title: Text(L10n.of(context).importEmojis), content: _loading - ? Center( - child: CircularProgressIndicator( - value: _progress, - ), - ) + ? Center(child: CircularProgressIndicator(value: _progress)) : SingleChildScrollView( child: Wrap( alignment: WrapAlignment.spaceEvenly, @@ -79,8 +75,8 @@ class _ImportEmoteArchiveDialogState extends State { onPressed: _loading ? null : _importMap.isNotEmpty - ? _addEmotePack - : null, + ? _addEmotePack + : null, child: Text(L10n.of(context).importNow), ), ], @@ -91,12 +87,8 @@ class _ImportEmoteArchiveDialogState extends State { _importMap = Map.fromEntries( widget.archive.files .where((e) => e.isFile) - .map( - (e) => MapEntry(e, e.name.emoteNameFromPath), - ) - .sorted( - (a, b) => a.value.compareTo(b.value), - ), + .map((e) => MapEntry(e, e.name.emoteNameFromPath)) + .sorted((a, b) => a.value.compareTo(b.value)), ); } @@ -148,10 +140,7 @@ class _ImportEmoteArchiveDialogState extends State { final imageCode = entry.value; try { - var mxcFile = MatrixImageFile( - bytes: file.content, - name: file.name, - ); + var mxcFile = MatrixImageFile(bytes: file.content, name: file.name); final thumbnail = (await mxcFile.generateThumbnail( nativeImplementations: ClientManager.nativeImplementations, @@ -162,14 +151,12 @@ class _ImportEmoteArchiveDialogState extends State { mxcFile = thumbnail; } final uri = await Matrix.of(context).client.uploadContent( - mxcFile.bytes, - filename: mxcFile.name, - contentType: mxcFile.mimeType, - ); + mxcFile.bytes, + filename: mxcFile.name, + contentType: mxcFile.mimeType, + ); - final info = { - ...mxcFile.info, - }; + final info = {...mxcFile.info}; // normalize width / height to 256, required for stickers if (info['w'] is int && info['h'] is int) { @@ -184,9 +171,9 @@ class _ImportEmoteArchiveDialogState extends State { } widget.controller.pack!.images[imageCode] = ImagePackImageContent.fromJson({ - 'url': uri.toString(), - 'info': info, - }); + 'url': uri.toString(), + 'info': info, + }); successfulUploads.add(file.name); } catch (e) { Logs().d('Could not upload emote $imageCode'); @@ -204,8 +191,9 @@ class _ImportEmoteArchiveDialogState extends State { // in case we have unhandled / duplicated emotes left, don't pop if (mounted) setState(() {}); if (_importMap.isEmpty) { - WidgetsBinding.instance - .addPostFrameCallback((_) => Navigator.of(context).pop()); + WidgetsBinding.instance.addPostFrameCallback( + (_) => Navigator.of(context).pop(), + ); } } } @@ -250,21 +238,20 @@ class _EmojiImportPreviewState extends State<_EmojiImportPreview> { if (hasError) return _ImageFileError(name: widget.entry.key.name); return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: .min, + mainAxisAlignment: .center, + crossAxisAlignment: .center, children: [ Image.memory( widget.entry.key.content, height: 64, width: 64, errorBuilder: (context, e, s) { - WidgetsBinding.instance - .addPostFrameCallback((_) => _setRenderError()); - - return _ImageFileError( - name: widget.entry.key.name, + WidgetsBinding.instance.addPostFrameCallback( + (_) => _setRenderError(), ); + + return _ImageFileError(name: widget.entry.key.name); }, ), SizedBox( @@ -303,7 +290,7 @@ class _EmojiImportPreviewState extends State<_EmojiImportPreview> { ); } - _setRenderError() { + void _setRenderError() { hasErrorNotifier.value = true; widget.onRemove.call(); } @@ -323,9 +310,9 @@ class _ImageFileError extends StatelessWidget { child: Tooltip( message: name, child: Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: .start, + mainAxisSize: .min, + crossAxisAlignment: .center, children: [ const Icon(Icons.error), Text( @@ -347,8 +334,7 @@ extension on String { /// Used to compute emote name proposal based on file name String get emoteNameFromPath { // ... removing leading path - return split(RegExp(r'[/\\]')) - .last + return split(RegExp(r'[/\\]')).last // ... removing file extension .split('.') .first diff --git a/lib/pages/settings_emotes/settings_emotes.dart b/lib/pages/settings_emotes/settings_emotes.dart index cd1db0518..5999b7168 100644 --- a/lib/pages/settings_emotes/settings_emotes.dart +++ b/lib/pages/settings_emotes/settings_emotes.dart @@ -4,8 +4,7 @@ import 'package:archive/archive.dart' import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:collection/collection.dart'; -import 'package:go_router/go_router.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:http/http.dart' hide Client; import 'package:matrix/matrix.dart'; @@ -14,46 +13,65 @@ import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../../widgets/matrix.dart'; import 'import_archive_dialog.dart'; import 'settings_emotes_view.dart'; class EmotesSettings extends StatefulWidget { - const EmotesSettings({super.key}); + final String? roomId; + const EmotesSettings({required this.roomId, super.key}); @override EmotesSettingsController createState() => EmotesSettingsController(); } class EmotesSettingsController extends State { - // #Pangea - // String? get roomId => GoRouterState.of(context).pathParameters['roomid']; - String? get roomId { - final pathParameters = GoRouterState.of(context).pathParameters; - return pathParameters['roomid'] ?? pathParameters['spaceid']; + late final Room? room; + + String? stateKey; + + List? get packKeys { + final room = this.room; + if (room == null) return null; + final keys = room.states['im.ponies.room_emotes']?.keys.toList() ?? []; + keys.sort(); + return keys; } - // Pangea# - Room? get room => - roomId != null ? Matrix.of(context).client.getRoomById(roomId!) : null; + @override + void initState() { + super.initState(); + room = widget.roomId != null + ? Matrix.of(context).client.getRoomById(widget.roomId!) + : null; + setStateKey(packKeys?.firstOrNull, reset: false); + } - String? get stateKey => GoRouterState.of(context).pathParameters['state_key']; + void setStateKey(String? key, {reset = true}) { + stateKey = key; + + final event = key == null + ? null + : room?.getState('im.ponies.room_emotes', key); + final eventPack = event?.content.tryGetMap('pack'); + packDisplayNameController.text = + eventPack?.tryGet('display_name') ?? ''; + packAttributionController.text = + eventPack?.tryGet('attribution') ?? ''; + if (reset) resetAction(); + } bool showSave = false; - TextEditingController newImageCodeController = TextEditingController(); - ValueNotifier newImageController = - ValueNotifier(null); ImagePackContent _getPack() { final client = Matrix.of(context).client; - final event = (room != null + final event = + (room != null ? room!.getState('im.ponies.room_emotes', stateKey ?? '') : client.accountData['im.ponies.user_emotes']) ?? - BasicEvent( - type: 'm.dummy', - content: {}, - ); + BasicEvent(type: 'm.dummy', content: {}); // make sure we work on a *copy* of the event return BasicEvent.fromJson(event.toJson()).parsedImagePackContent; } @@ -73,25 +91,25 @@ class EmotesSettingsController extends State { return; } final client = Matrix.of(context).client; - if (room != null) { - await showFutureLoadingDialog( - context: context, - future: () => client.setRoomStateWithKey( - room!.id, - 'im.ponies.room_emotes', - stateKey ?? '', - pack!.toJson(), - ), - ); - } else { - await showFutureLoadingDialog( - context: context, - future: () => client.setAccountData( - client.userID!, - 'im.ponies.user_emotes', - pack!.toJson(), - ), - ); + final result = await showFutureLoadingDialog( + context: context, + future: () => room != null + ? client.setRoomStateWithKey( + room!.id, + 'im.ponies.room_emotes', + stateKey ?? '', + pack!.toJson(), + ) + : client.setAccountData( + client.userID!, + 'im.ponies.user_emotes', + pack!.toJson(), + ), + ); + if (!result.isError) { + setState(() { + showSave = false; + }); } } @@ -100,7 +118,8 @@ class EmotesSettingsController extends State { return; } final client = Matrix.of(context).client; - final content = client.accountData['im.ponies.emote_rooms']?.content ?? + final content = + client.accountData['im.ponies.emote_rooms']?.content ?? {}; if (active) { if (content['rooms'] is! Map) { @@ -127,17 +146,57 @@ class EmotesSettingsController extends State { setState(() {}); } + final TextEditingController packDisplayNameController = + TextEditingController(); + + final TextEditingController packAttributionController = + TextEditingController(); + void removeImageAction(String oldImageCode) => setState(() { - pack!.images.remove(oldImageCode); - showSave = true; - }); + pack!.images.remove(oldImageCode); + showSave = true; + }); + + void toggleUsage(String imageCode, ImagePackUsage usage) { + setState(() { + final usages = pack!.images[imageCode]!.usage ??= List.from( + ImagePackUsage.values, + ); + if (!usages.remove(usage)) usages.add(usage); + showSave = true; + }); + } + + void submitDisplaynameAction() { + if (readonly) return; + packDisplayNameController.text = packDisplayNameController.text.trim(); + final input = packDisplayNameController.text; + + setState(() { + pack!.pack.displayName = input; + showSave = true; + }); + } + + void submitAttributionAction() { + if (readonly) return; + packAttributionController.text = packAttributionController.text.trim(); + final input = packAttributionController.text; + + setState(() { + pack!.pack.attribution = input; + showSave = true; + }); + } void submitImageAction( String oldImageCode, - String imageCode, ImagePackImageContent image, TextEditingController controller, ) { + controller.text = controller.text.trim().replaceAll(' ', '-'); + final imageCode = controller.text; + if (imageCode == oldImageCode) return; if (pack!.images.keys.any((k) => k == imageCode && k != oldImageCode)) { controller.text = oldImageCode; showOkAlertDialog( @@ -173,8 +232,58 @@ class EmotesSettingsController extends State { ?.tryGetMap(stateKey ?? '') != null; - bool get readonly => - room == null ? false : !(room!.canSendEvent('im.ponies.room_emotes')); + bool get readonly => room == null + ? false + : room?.canChangeStateEvent('im.ponies.room_emotes') == false; + + void resetAction() { + setState(() { + _pack = _getPack(); + showSave = false; + }); + } + + void createImagePack() async { + final room = this.room; + if (room == null) throw Exception('Cannot create image pack without room'); + + final input = await showTextInputDialog( + context: context, + title: L10n.of(context).newStickerPack, + hintText: L10n.of(context).name, + okLabel: L10n.of(context).create, + ); + final name = input?.trim(); + if (name == null || name.isEmpty) return; + if (!mounted) return; + + final keyName = name.toLowerCase().replaceAll(' ', '_'); + + if (packKeys?.contains(name) ?? false) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(L10n.of(context).stickerPackNameAlreadyExists)), + ); + return; + } + + await showFutureLoadingDialog( + context: context, + future: () => room.client.setRoomStateWithKey( + room.id, + 'im.ponies.room_emotes', + keyName, + { + 'images': {}, + 'pack': {'display_name': name}, + }, + ), + ); + if (!mounted) return; + setState(() {}); + await room.client.oneShotSync(); + if (!mounted) return; + setState(() {}); + } void saveAction() async { await save(context); @@ -183,95 +292,60 @@ class EmotesSettingsController extends State { }); } - void addImageAction() async { - if (newImageCodeController.text.isEmpty || - newImageController.value == null) { - await showOkAlertDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context).emoteWarnNeedToPick, - okLabel: L10n.of(context).ok, - ); - return; - } - final imageCode = newImageCodeController.text; - if (pack!.images.containsKey(imageCode)) { - await showOkAlertDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context).emoteExists, - okLabel: L10n.of(context).ok, - ); - return; - } - if (!RegExp(r'^[-\w]+$').hasMatch(imageCode)) { - await showOkAlertDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context).emoteInvalid, - okLabel: L10n.of(context).ok, - ); - return; - } - pack!.images[imageCode] = newImageController.value!; - await save(context); - setState(() { - newImageCodeController.text = ''; - newImageController.value = null; - showSave = false; - }); - } - - void imagePickerAction( - ValueNotifier controller, - ) async { - final result = await selectFiles( + void createStickers() async { + final pickedFiles = await selectFiles( context, - type: FileSelectorType.images, + type: FileType.image, + allowMultiple: true, ); - final pickedFile = result.firstOrNull; - if (pickedFile == null) return; - var file = MatrixImageFile( - bytes: await pickedFile.readAsBytes(), - name: pickedFile.name, - ); - try { - file = (await file.generateThumbnail( - nativeImplementations: ClientManager.nativeImplementations, - ))!; - } catch (e, s) { - Logs().w('Unable to create thumbnail', e, s); - } - final uploadResp = await showFutureLoadingDialog( + if (pickedFiles.isEmpty) return; + if (!mounted) return; + + await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context).client.uploadContent( + futureWithProgress: (setProgress) async { + for (final (i, pickedFile) in pickedFiles.indexed) { + setProgress(i / pickedFiles.length); + var file = MatrixImageFile( + bytes: await pickedFile.readAsBytes(), + name: pickedFile.name, + ); + file = + await file.generateThumbnail( + nativeImplementations: ClientManager.nativeImplementations, + ) ?? + file; + final uri = await Matrix.of(context).client.uploadContent( file.bytes, filename: file.name, contentType: file.mimeType, - ), - ); - if (uploadResp.error == null) { - setState(() { - final info = { - ...file.info, - }; - // normalize width / height to 256, required for stickers - if (info['w'] is int && info['h'] is int) { - final ratio = info['w'] / info['h']; - if (info['w'] > info['h']) { - info['w'] = 256; - info['h'] = (256.0 / ratio).round(); - } else { - info['h'] = 256; - info['w'] = (ratio * 256.0).round(); - } + ); + + setState(() { + final info = {...file.info}; + // normalize width / height to 256, required for stickers + if (info['w'] is int && info['h'] is int) { + final ratio = info['w'] / info['h']; + if (info['w'] > info['h']) { + info['w'] = 256; + info['h'] = (256.0 / ratio).round(); + } else { + info['h'] = 256; + info['w'] = (ratio * 256.0).round(); + } + } + final imageCode = pickedFile.name.split('.').first; + pack!.images[imageCode] = ImagePackImageContent.fromJson( + {'url': uri.toString(), 'info': info}, + ); + }); } - controller.value = ImagePackImageContent.fromJson({ - 'url': uploadResp.result.toString(), - 'info': info, - }); - }); - } + }, + ); + + setState(() { + showSave = true; + }); } @override @@ -280,35 +354,20 @@ class EmotesSettingsController extends State { } Future importEmojiZip() async { - final result = await showFutureLoadingDialog( - context: context, - future: () async { - final result = await selectFiles( - context, - type: FileSelectorType.zip, - ); + final result = await selectFiles(context, type: FileType.any); - if (result.isEmpty) return null; + if (result.isEmpty) return; - final buffer = InputStream(await result.first.readAsBytes()); + final buffer = InputMemoryStream(await result.single.readAsBytes()); - final archive = ZipDecoder().decodeBuffer(buffer); - - return archive; - }, - ); - - final archive = result.result; - if (archive == null) return; + final archive = ZipDecoder().decodeStream(buffer); await showDialog( context: context, // breaks [Matrix.of] calls otherwise useRootNavigator: false, - builder: (context) => ImportEmoteArchiveDialog( - controller: this, - archive: archive, - ), + builder: (context) => + ImportEmoteArchiveDialog(controller: this, archive: archive), ); setState(() {}); } @@ -331,19 +390,13 @@ class EmotesSettingsController extends State { ); archive.addFile( - ArchiveFile( - name, - response.bodyBytes.length, - response.bodyBytes, - ), + ArchiveFile(name, response.bodyBytes.length, response.bodyBytes), ); } final fileName = '${pack.pack.displayName ?? client.userID?.localpart ?? 'emotes'}.zip'; final output = ZipEncoder().encode(archive); - if (output == null) return; - MatrixFile( name: fileName, bytes: Uint8List.fromList(output), diff --git a/lib/pages/settings_emotes/settings_emotes_view.dart b/lib/pages/settings_emotes/settings_emotes_view.dart index 3134cba8d..a5baf3979 100644 --- a/lib/pages/settings_emotes/settings_emotes_view.dart +++ b/lib/pages/settings_emotes/settings_emotes_view.dart @@ -5,8 +5,10 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; +import 'package:fluffychat/widgets/mxc_image_viewer.dart'; import '../../widgets/matrix.dart'; import 'settings_emotes.dart'; @@ -19,108 +21,195 @@ class EmotesSettingsView extends StatelessWidget { @override Widget build(BuildContext context) { + if (controller.widget.roomId != null && controller.room == null) { + return Scaffold( + appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)), + body: Center( + child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), + ), + ); + } final theme = Theme.of(context); final client = Matrix.of(context).client; final imageKeys = controller.pack!.images.keys.toList(); + final packKeys = controller.packKeys; + if (packKeys != null && packKeys.isEmpty) { + packKeys.add(''); + } + final attributionUrl = Uri.tryParse( + controller.packAttributionController.text, + ); + return Scaffold( appBar: AppBar( - leading: const Center(child: BackButton()), - title: Text(L10n.of(context).customEmojisAndStickers), + automaticallyImplyLeading: !controller.showSave, + title: controller.showSave + ? TextButton( + onPressed: controller.resetAction, + child: Text(L10n.of(context).cancel), + ) + : Text(L10n.of(context).customEmojisAndStickers), actions: [ - PopupMenuButton( - useRootNavigator: true, - onSelected: (value) { - switch (value) { - case PopupMenuEmojiActions.export: - controller.exportAsZip(); - break; - case PopupMenuEmojiActions.import: - controller.importEmojiZip(); - break; - } - }, - enabled: !controller.readonly, - itemBuilder: (context) => [ - PopupMenuItem( - value: PopupMenuEmojiActions.import, - child: Text(L10n.of(context).importFromZipFile), + if (controller.showSave) + ElevatedButton( + onPressed: () => controller.save(context), + style: ElevatedButton.styleFrom( + backgroundColor: theme.colorScheme.primary, + foregroundColor: theme.colorScheme.onPrimary, ), - PopupMenuItem( - value: PopupMenuEmojiActions.export, - child: Text(L10n.of(context).exportEmotePack), - ), - ], - ), - ], - ), - floatingActionButton: controller.showSave - ? FloatingActionButton( - onPressed: controller.saveAction, - child: const Icon(Icons.save_outlined, color: Colors.white), + child: Text(L10n.of(context).saveChanges), ) - : null, + else + PopupMenuButton( + useRootNavigator: true, + onSelected: (value) { + switch (value) { + case PopupMenuEmojiActions.export: + controller.exportAsZip(); + break; + case PopupMenuEmojiActions.import: + controller.importEmojiZip(); + break; + } + }, + itemBuilder: (context) => [ + if (!controller.readonly) + PopupMenuItem( + value: PopupMenuEmojiActions.import, + child: Text(L10n.of(context).importFromZipFile), + ), + if (imageKeys.isNotEmpty) + PopupMenuItem( + value: PopupMenuEmojiActions.export, + child: Text(L10n.of(context).exportEmotePack), + ), + ], + ), + ], + bottom: packKeys == null + ? null + : PreferredSize( + preferredSize: const Size.fromHeight(48), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: SizedBox( + height: 40, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: packKeys.length + 1, + itemBuilder: (context, i) { + if (i == 0) { + if (controller.readonly) { + return const SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + ), + child: FilterChip( + label: const Icon(Icons.add_outlined, size: 20), + onSelected: controller.showSave + ? null + : (_) => controller.createImagePack(), + ), + ); + } + i--; + final key = packKeys[i]; + final event = controller.room?.getState( + 'im.ponies.room_emotes', + packKeys[i], + ); + + final eventPack = event?.content + .tryGetMap('pack'); + final packName = + eventPack?.tryGet('display_name') ?? + eventPack?.tryGet('name') ?? + (key.isNotEmpty ? key : 'Default'); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: FilterChip( + label: Text(packName), + selected: + controller.stateKey == key || + (controller.stateKey == null && key.isEmpty), + onSelected: controller.showSave + ? null + : (_) => controller.setStateKey(key), + ), + ); + }, + ), + ), + ), + ), + ), body: MaxWidthBody( child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, + crossAxisAlignment: .stretch, children: [ - if (!controller.readonly) - Container( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - ), - child: ListTile( - leading: Container( - width: 180.0, - height: 38, - padding: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(10)), - color: theme.secondaryHeaderColor, - ), - child: TextField( - controller: controller.newImageCodeController, - autocorrect: false, - minLines: 1, - maxLines: 1, - decoration: InputDecoration( - hintText: L10n.of(context).emoteShortcode, - prefixText: ': ', - suffixText: ':', - prefixStyle: TextStyle( - color: theme.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - suffixStyle: TextStyle( - color: theme.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - border: InputBorder.none, - ), - ), - ), - title: _ImagePicker( - controller: controller.newImageController, - onPressed: controller.imagePickerAction, - ), - trailing: InkWell( - onTap: controller.addImageAction, - child: const Icon( - Icons.add_outlined, - color: Colors.green, - size: 32.0, - ), + if (controller.room != null) ...[ + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: TextField( + maxLength: 256, + controller: controller.packDisplayNameController, + readOnly: controller.readonly, + onSubmitted: (_) => controller.submitDisplaynameAction(), + decoration: InputDecoration( + counter: const SizedBox.shrink(), + hintText: controller.stateKey, + labelText: L10n.of(context).stickerPackName, ), ), ), - if (controller.room != null) + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: TextField( + maxLength: 256, + controller: controller.packAttributionController, + readOnly: controller.readonly, + keyboardType: TextInputType.url, + onSubmitted: (_) => controller.submitAttributionAction(), + decoration: InputDecoration( + counter: const SizedBox.shrink(), + labelText: L10n.of(context).attribution, + suffixIcon: attributionUrl == null + ? null + : IconButton( + icon: const Icon(Icons.link_outlined), + onPressed: () => UrlLauncher( + context, + attributionUrl.toString(), + ).launchUrl(), + ), + ), + ), + ), + ], + if (!controller.readonly) ...[ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton.icon( + onPressed: controller.createStickers, + icon: const Icon(Icons.upload_outlined), + label: Text(L10n.of(context).createSticker), + ), + ), + const Divider(), + ], + if (controller.room != null && imageKeys.isNotEmpty) SwitchListTile.adaptive( title: Text(L10n.of(context).enableEmotesGlobally), value: controller.isGloballyActive(client), onChanged: controller.setIsGloballyActive, ), - if (!controller.readonly || controller.room != null) - const Divider(), imageKeys.isEmpty ? Center( child: Padding( @@ -136,11 +225,8 @@ class EmotesSettingsView extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), separatorBuilder: (BuildContext context, int i) => const SizedBox.shrink(), - itemCount: imageKeys.length + 1, + itemCount: imageKeys.length, itemBuilder: (BuildContext context, int i) { - if (i >= imageKeys.length) { - return Container(height: 70); - } final imageCode = imageKeys[i]; final image = controller.pack!.images[imageCode]!; final textEditingController = TextEditingController(); @@ -148,80 +234,108 @@ class EmotesSettingsView extends StatelessWidget { final useShortCuts = (PlatformInfos.isWeb || PlatformInfos.isDesktop); return ListTile( - leading: Container( - width: 180.0, - height: 38, - padding: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - borderRadius: - const BorderRadius.all(Radius.circular(10)), - color: theme.secondaryHeaderColor, - ), - child: Shortcuts( - shortcuts: !useShortCuts - ? {} - : { - LogicalKeySet(LogicalKeyboardKey.enter): - SubmitLineIntent(), - }, - child: Actions( - actions: !useShortCuts - ? {} - : { - SubmitLineIntent: CallbackAction( - onInvoke: (i) { - controller.submitImageAction( - imageCode, - textEditingController.text, - image, - textEditingController, - ); - return null; + title: Row( + children: [ + Expanded( + child: Shortcuts( + shortcuts: !useShortCuts + ? {} + : { + LogicalKeySet(LogicalKeyboardKey.enter): + SubmitLineIntent(), + }, + child: Actions( + actions: !useShortCuts + ? {} + : { + SubmitLineIntent: CallbackAction( + onInvoke: (i) { + controller.submitImageAction( + imageCode, + image, + textEditingController, + ); + return null; + }, + ), }, + child: TextField( + readOnly: controller.readonly, + controller: textEditingController, + autocorrect: false, + minLines: 1, + maxLines: 1, + maxLength: 128, + decoration: InputDecoration( + hintText: L10n.of(context).emoteShortcode, + prefixText: ': ', + suffixText: ':', + counter: const SizedBox.shrink(), + filled: false, + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.transparent, + ), ), - }, - child: TextField( - readOnly: controller.readonly, - controller: textEditingController, - autocorrect: false, - minLines: 1, - maxLines: 1, - decoration: InputDecoration( - hintText: L10n.of(context).emoteShortcode, - prefixText: ': ', - suffixText: ':', - prefixStyle: TextStyle( - color: theme.colorScheme.secondary, - fontWeight: FontWeight.bold, + ), + onSubmitted: (s) => + controller.submitImageAction( + imageCode, + image, + textEditingController, + ), ), - suffixStyle: TextStyle( - color: theme.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - border: InputBorder.none, - ), - onSubmitted: (s) => - controller.submitImageAction( - imageCode, - s, - image, - textEditingController, ), ), ), - ), + if (!controller.readonly) + PopupMenuButton( + onSelected: (usage) => + controller.toggleUsage(imageCode, usage), + itemBuilder: (context) => [ + PopupMenuItem( + value: ImagePackUsage.sticker, + child: Row( + mainAxisSize: .min, + children: [ + if (image.usage?.contains( + ImagePackUsage.sticker, + ) ?? + true) + const Icon(Icons.check_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).useAsSticker), + ], + ), + ), + PopupMenuItem( + value: ImagePackUsage.emoticon, + child: Row( + mainAxisSize: .min, + children: [ + if (image.usage?.contains( + ImagePackUsage.emoticon, + ) ?? + true) + const Icon(Icons.check_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).useAsEmoji), + ], + ), + ), + ], + icon: const Icon(Icons.edit_outlined), + ), + ], ), - title: _EmoteImage(image.url), + leading: _EmoteImage(image.url), trailing: controller.readonly ? null - : InkWell( - onTap: () => + : IconButton( + tooltip: L10n.of(context).delete, + onPressed: () => controller.removeImageAction(imageCode), - child: const Icon( - Icons.delete_outlined, - color: Colors.red, - size: 32.0, - ), + icon: const Icon(Icons.delete_outlined), ), ); }, @@ -240,43 +354,24 @@ class _EmoteImage extends StatelessWidget { @override Widget build(BuildContext context) { - const size = 38.0; - return SizedBox.square( - dimension: size, + const size = 44.0; + final key = 'sticker_preview_$mxc'; + return InkWell( + borderRadius: BorderRadius.circular(4), + onTap: () => + showDialog(context: context, builder: (_) => MxcImageViewer(mxc)), child: MxcImage( + key: ValueKey(key), + cacheKey: key, uri: mxc, fit: BoxFit.contain, width: size, height: size, - isThumbnail: false, + isThumbnail: true, + animated: true, ), ); } } -class _ImagePicker extends StatefulWidget { - final ValueNotifier controller; - - final void Function(ValueNotifier) onPressed; - - const _ImagePicker({required this.controller, required this.onPressed}); - - @override - _ImagePickerState createState() => _ImagePickerState(); -} - -class _ImagePickerState extends State<_ImagePicker> { - @override - Widget build(BuildContext context) { - if (widget.controller.value == null) { - return ElevatedButton( - onPressed: () => widget.onPressed(widget.controller), - child: Text(L10n.of(context).pickImage), - ); - } else { - return _EmoteImage(widget.controller.value!.url); - } - } -} - class SubmitLineIntent extends Intent {} diff --git a/lib/pages/settings_homeserver/settings_homeserver.dart b/lib/pages/settings_homeserver/settings_homeserver.dart index 76ca08232..13843336d 100644 --- a/lib/pages/settings_homeserver/settings_homeserver.dart +++ b/lib/pages/settings_homeserver/settings_homeserver.dart @@ -17,7 +17,7 @@ class SettingsHomeserver extends StatefulWidget { class SettingsHomeserverController extends State { Future<({String name, String version, Uri federationBaseUrl})> - fetchServerInfo() async { + fetchServerInfo() async { final client = Matrix.of(context).client; final domain = client.userID!.domain!; final httpClient = client.httpClient; @@ -37,15 +37,10 @@ class SettingsHomeserverController extends State { } final serverVersionResult = await http.get( - federationBaseUrl.resolveUri( - Uri(path: '/_matrix/federation/v1/version'), - ), + federationBaseUrl.resolveUri(Uri(path: '/_matrix/federation/v1/version')), ); final { - 'server': { - 'name': String name, - 'version': String version, - }, + 'server': {'name': String name, 'version': String version}, } = Map>.from( jsonDecode(serverVersionResult.body), ); diff --git a/lib/pages/settings_homeserver/settings_homeserver_view.dart b/lib/pages/settings_homeserver/settings_homeserver_view.dart index ee02337f0..678ff6c31 100644 --- a/lib/pages/settings_homeserver/settings_homeserver_view.dart +++ b/lib/pages/settings_homeserver/settings_homeserver_view.dart @@ -30,15 +30,16 @@ class SettingsHomeserverView extends StatelessWidget { automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), centerTitle: FluffyThemes.isColumnMode(context), title: Text( - L10n.of(context) - .aboutHomeserver(client.userID?.domain ?? 'Homeserver'), + L10n.of( + context, + ).aboutHomeserver(client.userID?.domain ?? 'Homeserver'), ), ), body: MaxWidthBody( withScrolling: true, child: SelectionArea( child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ ListTile( title: Text( @@ -68,9 +69,7 @@ class SettingsHomeserverView extends StatelessWidget { } if (data == null) { return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), + child: CircularProgressIndicator.adaptive(strokeWidth: 2), ); } final supportPage = data.supportPage; @@ -85,7 +84,7 @@ class SettingsHomeserverView extends StatelessWidget { ); } return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ if (supportPage != null) ListTile( @@ -93,32 +92,28 @@ class SettingsHomeserverView extends StatelessWidget { subtitle: Text(supportPage.toString()), ), if (contacts != null) - ...contacts.map( - (contact) { - return ListTile( - title: Text( - contact.role.localizedString( - L10n.of(context), - ), - ), - subtitle: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (contact.emailAddress != null) - TextButton( - onPressed: () {}, - child: Text(contact.emailAddress!), - ), - if (contact.matrixId != null) - TextButton( - onPressed: () {}, - child: Text(contact.matrixId!), - ), - ], - ), - ); - }, - ), + ...contacts.map((contact) { + return ListTile( + title: Text( + contact.role.localizedString(L10n.of(context)), + ), + subtitle: Column( + mainAxisSize: .min, + children: [ + if (contact.emailAddress != null) + TextButton( + onPressed: () {}, + child: Text(contact.emailAddress!), + ), + if (contact.matrixId != null) + TextButton( + onPressed: () {}, + child: Text(contact.matrixId!), + ), + ], + ), + ); + }), ], ); }, @@ -129,7 +124,7 @@ class SettingsHomeserverView extends StatelessWidget { final error = snapshot.error; if (error != null) { return Column( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: .center, children: [ Icon( Icons.error_outlined, @@ -139,9 +134,7 @@ class SettingsHomeserverView extends StatelessWidget { Text( error.toLocalizedString(context), textAlign: TextAlign.center, - style: TextStyle( - color: theme.colorScheme.error, - ), + style: TextStyle(color: theme.colorScheme.error), ), ], ); @@ -149,13 +142,11 @@ class SettingsHomeserverView extends StatelessWidget { final data = snapshot.data; if (data == null) { return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), + child: CircularProgressIndicator.adaptive(strokeWidth: 2), ); } return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ ListTile( title: Text(L10n.of(context).name), @@ -166,11 +157,12 @@ class SettingsHomeserverView extends StatelessWidget { subtitle: Text(data.version), ), ListTile( - title: const Text('Federation Base URL'), + title: Text(L10n.of(context).federationBaseUrl), subtitle: Linkify( text: data.federationBaseUrl.toString(), - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: theme.colorScheme.primary, @@ -191,7 +183,7 @@ class SettingsHomeserverView extends StatelessWidget { final error = snapshot.error; if (error != null) { return Column( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: .center, children: [ Icon( Icons.error_outlined, @@ -201,9 +193,7 @@ class SettingsHomeserverView extends StatelessWidget { Text( error.toLocalizedString(context), textAlign: TextAlign.center, - style: TextStyle( - color: theme.colorScheme.error, - ), + style: TextStyle(color: theme.colorScheme.error), ), ], ); @@ -211,18 +201,16 @@ class SettingsHomeserverView extends StatelessWidget { final wellKnown = snapshot.data; if (wellKnown == null) { return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), + child: CircularProgressIndicator.adaptive(strokeWidth: 2), ); } final identityServer = wellKnown.mIdentityServer; return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ ListTile( title: Text( - 'Client-Well-Known Information:', + L10n.of(context).clientWellKnownInformation, style: TextStyle( color: theme.colorScheme.secondary, fontWeight: FontWeight.bold, @@ -230,11 +218,12 @@ class SettingsHomeserverView extends StatelessWidget { ), ), ListTile( - title: const Text('Base URL'), + title: Text(L10n.of(context).baseUrl), subtitle: Linkify( text: wellKnown.mHomeserver.baseUrl.toString(), - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: theme.colorScheme.primary, @@ -245,11 +234,12 @@ class SettingsHomeserverView extends StatelessWidget { ), if (identityServer != null) ListTile( - title: const Text('Identity Server:'), + title: Text(L10n.of(context).identityServer), subtitle: Linkify( text: identityServer.baseUrl.toString(), - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: theme.colorScheme.primary, @@ -262,15 +252,17 @@ class SettingsHomeserverView extends StatelessWidget { (entry) => ListTile( title: Text(entry.key), subtitle: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), color: theme.colorScheme.surfaceContainer, child: SingleChildScrollView( padding: const EdgeInsets.all(16), scrollDirection: Axis.horizontal, child: Text( - const JsonEncoder.withIndent(' ') - .convert(entry.value), + const JsonEncoder.withIndent( + ' ', + ).convert(entry.value), style: TextStyle( color: theme.colorScheme.onSurface, ), diff --git a/lib/pages/settings_ignore_list/settings_ignore_list.dart b/lib/pages/settings_ignore_list/settings_ignore_list.dart index 525ead873..3bcea3dfe 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list.dart @@ -43,10 +43,12 @@ class SettingsIgnoreListController extends State { errorText = null; }); + final client = Matrix.of(context).client; showFutureLoadingDialog( context: context, - future: () => Matrix.of(context).client.ignoreUser(userId), + future: () => client.ignoreUser(userId), ); + setState(() {}); controller.clear(); } diff --git a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart index d967dc168..7dee64d11 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import '../../widgets/matrix.dart'; @@ -26,87 +23,72 @@ class SettingsIgnoreListView extends StatelessWidget { ), body: MaxWidthBody( withScrolling: false, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: controller.controller, - autocorrect: false, - textInputAction: TextInputAction.done, - onSubmitted: (_) => controller.ignoreUser(context), - decoration: InputDecoration( - errorText: controller.errorText, - hintText: '@bad_guy:domain.abc', - floatingLabelBehavior: FloatingLabelBehavior.always, - labelText: L10n.of(context).blockUsername, - suffixIcon: IconButton( - tooltip: L10n.of(context).block, - icon: const Icon(Icons.add), - onPressed: () => controller.ignoreUser(context), - ), - ), - ), - const SizedBox(height: 16), - Text( - L10n.of(context).blockListDescription, - style: const TextStyle(color: Colors.orange), - ), - ], - ), - ), - Divider( - color: theme.dividerColor, - ), - Expanded( - child: StreamBuilder( - stream: client.onSync.stream.where( - (syncUpdate) => - syncUpdate.accountData?.any( - (accountData) => - accountData.type == 'm.ignored_user_list', - ) ?? - false, - ), - builder: (context, snapshot) { - return ListView.builder( - itemCount: client.ignoredUsers.length, - itemBuilder: (c, i) => FutureBuilder( - future: - client.getProfileFromUserId(client.ignoredUsers[i]), - builder: (c, s) => ListTile( - leading: Avatar( - mxContent: s.data?.avatarUrl ?? Uri.parse(''), - name: s.data?.displayName ?? client.ignoredUsers[i], - // #Pangea - userId: s.data?.userId, - // Pangea# - ), - title: Text( - s.data?.displayName ?? client.ignoredUsers[i], - ), - subtitle: - Text(s.data?.userId ?? client.ignoredUsers[i]), - trailing: IconButton( - tooltip: L10n.of(context).delete, - icon: const Icon(Icons.delete_outlined), - onPressed: () => showFutureLoadingDialog( - context: context, - future: () => - client.unignoreUser(client.ignoredUsers[i]), + child: StreamBuilder( + stream: client.onSync.stream.where( + (syncUpdate) => + syncUpdate.accountData?.any( + (accountData) => accountData.type == 'm.ignored_user_list', + ) ?? + false, + ), + builder: (context, asyncSnapshot) { + if (client.prevBatch == null) { + return const Center(child: CircularProgressIndicator.adaptive()); + } + return Column( + mainAxisSize: .min, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: .min, + children: [ + TextField( + controller: controller.controller, + autocorrect: false, + textInputAction: TextInputAction.done, + onSubmitted: (_) => controller.ignoreUser(context), + decoration: InputDecoration( + errorText: controller.errorText, + hintText: '@bad_guy:domain.abc', + floatingLabelBehavior: FloatingLabelBehavior.always, + labelText: L10n.of(context).blockUsername, + suffixIcon: IconButton( + tooltip: L10n.of(context).block, + icon: const Icon(Icons.add), + onPressed: () => controller.ignoreUser(context), ), ), ), + const SizedBox(height: 16), + Text( + L10n.of(context).blockListDescription, + style: const TextStyle(color: Colors.orange), + ), + ], + ), + ), + Divider(color: theme.dividerColor), + Expanded( + child: ListView.builder( + itemCount: client.ignoredUsers.length, + itemBuilder: (c, i) => ListTile( + title: Text(client.ignoredUsers[i]), + trailing: IconButton( + tooltip: L10n.of(context).delete, + icon: const Icon(Icons.delete_outlined), + onPressed: () => showFutureLoadingDialog( + context: context, + future: () => + client.unignoreUser(client.ignoredUsers[i]), + ), + ), ), - ); - }, - ), - ), - ], + ), + ), + ], + ); + }, ), ), ); diff --git a/lib/pages/settings_multiple_emotes/settings_multiple_emotes.dart b/lib/pages/settings_multiple_emotes/settings_multiple_emotes.dart deleted file mode 100644 index 229a82104..000000000 --- a/lib/pages/settings_multiple_emotes/settings_multiple_emotes.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:go_router/go_router.dart'; - -import 'settings_multiple_emotes_view.dart'; - -class MultipleEmotesSettings extends StatefulWidget { - const MultipleEmotesSettings({super.key}); - - @override - MultipleEmotesSettingsController createState() => - MultipleEmotesSettingsController(); -} - -class MultipleEmotesSettingsController extends State { - // #Pangea - // String? get roomId => GoRouterState.of(context).pathParameters['roomid']; - String? get roomId { - final pathParameters = GoRouterState.of(context).pathParameters; - return pathParameters['roomid'] ?? pathParameters['spaceid']; - } - // Pangea# - - @override - Widget build(BuildContext context) => MultipleEmotesSettingsView(this); -} diff --git a/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart b/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart deleted file mode 100644 index 6559d0f15..000000000 --- a/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:go_router/go_router.dart'; -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pages/settings_multiple_emotes/settings_multiple_emotes.dart'; -import 'package:fluffychat/widgets/matrix.dart'; - -class MultipleEmotesSettingsView extends StatelessWidget { - final MultipleEmotesSettingsController controller; - - const MultipleEmotesSettingsView(this.controller, {super.key}); - - @override - Widget build(BuildContext context) { - final room = Matrix.of(context).client.getRoomById(controller.roomId!)!; - return Scaffold( - appBar: AppBar( - leading: const Center(child: BackButton()), - title: Text(L10n.of(context).emotePacks), - ), - body: StreamBuilder( - stream: room.client.onRoomState.stream - .where((update) => update.roomId == room.id), - builder: (context, snapshot) { - final packStateEvents = room.states['im.ponies.room_emotes']; - // we need to manually convert the map using Map.of, otherwise assigning null will throw a type error. - final packs = packStateEvents != null - ? Map.of(packStateEvents) - : {}; - if (!packs.containsKey('')) { - packs[''] = null; - } - final keys = packs.keys.toList(); - keys.sort(); - return ListView.separated( - separatorBuilder: (BuildContext context, int i) => - const SizedBox.shrink(), - itemCount: keys.length, - itemBuilder: (BuildContext context, int i) { - final event = packs[keys[i]]; - final eventPack = - event?.content.tryGetMap('pack'); - final packName = eventPack?.tryGet('displayname') ?? - eventPack?.tryGet('name') ?? - (keys[i].isNotEmpty ? keys[i] : 'Default Pack'); - - return ListTile( - title: Text(packName), - onTap: () async { - context.go( - ['', 'rooms', room.id, 'details', 'emotes', keys[i]] - .join('/'), - ); - }, - ); - }, - ); - }, - ), - ); - } -} diff --git a/lib/pages/settings_notifications/push_rule_extensions.dart b/lib/pages/settings_notifications/push_rule_extensions.dart index b531f6529..464c6a5fa 100644 --- a/lib/pages/settings_notifications/push_rule_extensions.dart +++ b/lib/pages/settings_notifications/push_rule_extensions.dart @@ -122,6 +122,7 @@ extension PushRuleExtension on PushRule { // '.im.vector.jitsi', ]; } + // Pangea# } diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart index b0974940c..dd1a67577 100644 --- a/lib/pages/settings_notifications/settings_notifications.dart +++ b/lib/pages/settings_notifications/settings_notifications.dart @@ -47,11 +47,8 @@ class SettingsNotificationsController extends State { final success = await showFutureLoadingDialog( context: context, future: () => Matrix.of(context).client.deletePusher( - PusherId( - appId: pusher.appId, - pushkey: pusher.pushkey, - ), - ), + PusherId(appId: pusher.appId, pushkey: pusher.pushkey), + ), ); if (success.error != null) return; @@ -68,10 +65,7 @@ class SettingsNotificationsController extends State { isLoading = true; }); try { - final updateFromSync = Matrix.of(context) - .client - .onSync - .stream + final updateFromSync = Matrix.of(context).client.onSync.stream .where( (syncUpdate) => syncUpdate.accountData?.any( @@ -80,17 +74,16 @@ class SettingsNotificationsController extends State { false, ) .first; - await Matrix.of(context).client.setPushRuleEnabled( - kind, - pushRule.ruleId, - !pushRule.enabled, - ); + await Matrix.of( + context, + ).client.setPushRuleEnabled(kind, pushRule.ruleId, !pushRule.enabled); await updateFromSync; } catch (e, s) { Logs().w('Unable to toggle push rule', e, s); if (!mounted) return; - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } finally { if (mounted) { setState(() { @@ -118,9 +111,7 @@ class SettingsNotificationsController extends State { scrollDirection: Axis.horizontal, child: SelectableText( prettyJson(rule.toJson()), - style: TextStyle( - color: theme.colorScheme.onSurface, - ), + style: TextStyle(color: theme.colorScheme.onSurface), ), ), ), @@ -160,10 +151,7 @@ class SettingsNotificationsController extends State { isLoading = true; }); try { - final updateFromSync = Matrix.of(context) - .client - .onSync - .stream + final updateFromSync = Matrix.of(context).client.onSync.stream .where( (syncUpdate) => syncUpdate.accountData?.any( @@ -172,17 +160,14 @@ class SettingsNotificationsController extends State { false, ) .first; - await Matrix.of(context).client.deletePushRule( - kind, - rule.ruleId, - ); + await Matrix.of(context).client.deletePushRule(kind, rule.ruleId); await updateFromSync; } catch (e, s) { Logs().w('Unable to delete push rule', e, s); if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(e.toLocalizedString(context))), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); } finally { if (mounted) { setState(() { @@ -195,16 +180,13 @@ class SettingsNotificationsController extends State { } // #Pangea - final ValueNotifier volumeNotifier = - ValueNotifier(AppConfig.volume); + final ValueNotifier volumeNotifier = ValueNotifier( + AppSettings.volume.value, + ); void updateVolume(double value) { volumeNotifier.value = value; - AppConfig.volume = value; - Matrix.of(context).store.setDouble( - SettingKeys.volume, - value, - ); + AppSettings.volume.setItem(value); } Future requestNotificationPermission() async { diff --git a/lib/pages/settings_notifications/settings_notifications_view.dart b/lib/pages/settings_notifications/settings_notifications_view.dart index e601fcb31..9f3e1780b 100644 --- a/lib/pages/settings_notifications/settings_notifications_view.dart +++ b/lib/pages/settings_notifications/settings_notifications_view.dart @@ -38,12 +38,12 @@ class SettingsNotificationsView extends StatelessWidget { body: MaxWidthBody( child: StreamBuilder( stream: Matrix.of(context).client.onSync.stream.where( - (syncUpdate) => - syncUpdate.accountData?.any( - (accountData) => accountData.type == 'm.push_rules', - ) ?? - false, - ), + (syncUpdate) => + syncUpdate.accountData?.any( + (accountData) => accountData.type == 'm.push_rules', + ) ?? + false, + ), builder: (BuildContext context, _) { final theme = Theme.of(context); return SelectionArea( @@ -114,8 +114,9 @@ class SettingsNotificationsView extends StatelessWidget { // #Pangea // for (final rule in category.rules) for (final rule in category.rules.where( - (rule) => PushRuleExtension.defaultPushRuleIds - .contains(rule.ruleId), + (rule) => PushRuleExtension.defaultPushRuleIds.contains( + rule.ruleId, + ), )) // Pangea# ListTile( @@ -154,14 +155,14 @@ class SettingsNotificationsView extends StatelessWidget { onChanged: controller.isLoading ? null : rule.ruleId != '.m.rule.master' && - Matrix.of(context) - .client - .allPushNotificationsMuted - ? null - : (_) => controller.togglePushRule( - category.kind, - rule, - ), + Matrix.of( + context, + ).client.allPushNotificationsMuted + ? null + : (_) => controller.togglePushRule( + category.kind, + rule, + ), ), ), Divider(color: theme.dividerColor), @@ -176,8 +177,9 @@ class SettingsNotificationsView extends StatelessWidget { ), ), FutureBuilder?>( - future: controller.pusherFuture ??= - Matrix.of(context).client.getPushers(), + future: controller.pusherFuture ??= Matrix.of( + context, + ).client.getPushers(), builder: (context, snapshot) { if (snapshot.hasError) { Center( diff --git a/lib/pages/settings_password/settings_password.dart b/lib/pages/settings_password/settings_password.dart index bc2400c8c..413f80259 100644 --- a/lib/pages/settings_password/settings_password.dart +++ b/lib/pages/settings_password/settings_password.dart @@ -55,13 +55,11 @@ class SettingsPasswordController extends State { try { final scaffoldMessenger = ScaffoldMessenger.of(context); await Matrix.of(context).client.changePassword( - newPassword1Controller.text, - oldPassword: oldPasswordController.text, - ); + newPassword1Controller.text, + oldPassword: oldPasswordController.text, + ); scaffoldMessenger.showSnackBar( - SnackBar( - content: Text(L10n.of(context).passwordHasBeenChanged), - ), + SnackBar(content: Text(L10n.of(context).passwordHasBeenChanged)), ); if (mounted) context.pop(); } catch (e) { diff --git a/lib/pages/settings_password/settings_password_view.dart b/lib/pages/settings_password/settings_password_view.dart index ceb9f9186..6525ecef8 100644 --- a/lib/pages/settings_password/settings_password_view.dart +++ b/lib/pages/settings_password/settings_password_view.dart @@ -13,9 +13,7 @@ class SettingsPasswordView extends StatelessWidget { final theme = Theme.of(context); return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).changePassword), - ), + appBar: AppBar(title: Text(L10n.of(context).changePassword)), body: ListTileTheme( iconColor: theme.colorScheme.onSurface, child: MaxWidthBody( @@ -67,8 +65,9 @@ class SettingsPasswordView extends StatelessWidget { SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: - controller.loading ? null : controller.changePassword, + onPressed: controller.loading + ? null + : controller.changePassword, child: controller.loading ? const LinearProgressIndicator() : Text(L10n.of(context).changePassword), diff --git a/lib/pages/settings_security/settings_security.dart b/lib/pages/settings_security/settings_security.dart index e55d4c3c2..26fed6714 100644 --- a/lib/pages/settings_security/settings_security.dart +++ b/lib/pages/settings_security/settings_security.dart @@ -10,7 +10,6 @@ import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart' import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../bootstrap/bootstrap_dialog.dart'; import 'settings_security_view.dart'; class SettingsSecurity extends StatefulWidget { @@ -102,63 +101,28 @@ class SettingsSecurityController extends State { if (mxid == null || mxid.isEmpty || mxid != supposedMxid) { return; } - // #Pangea - // final input = await showTextInputDialog( - // useRootNavigator: false, - // context: context, - // title: L10n.of(context).pleaseEnterYourPassword, - // okLabel: L10n.of(context).ok, - // cancelLabel: L10n.of(context).cancel, - // isDestructive: true, - // obscureText: true, - // hintText: '******', - // minLines: 1, - // maxLines: 1, - // ); - // if (input == null) return; - // await showFutureLoadingDialog( - // context: context, - // future: () => Matrix.of(context).client.deactivateAccount( - // auth: AuthenticationPassword( - // password: input, - // identifier: AuthenticationUserIdentifier( - // user: Matrix.of(context).client.userID!, - // ), - // ), - // ), - // ); - // Pangea# - final resp = await showFutureLoadingDialog( context: context, delay: false, future: () => Matrix.of(context).client.uiaRequestBackground( - (auth) => Matrix.of(context).client.deactivateAccount( - auth: auth, - ), - ), + (auth) => Matrix.of(context).client.deactivateAccount(auth: auth), + ), ); if (!resp.isError) { - await Matrix.of(context).client.logout(); + await showFutureLoadingDialog( + context: context, + future: () => Matrix.of(context).client.logout(), + ); } } - void showBootstrapDialog(BuildContext context) async { - await BootstrapDialog( - client: Matrix.of(context).client, - ).show(context); - } - Future dehydrateAction() => Matrix.of(context).dehydrateAction(context); void changeShareKeysWith(ShareKeysWith? shareKeysWith) async { if (shareKeysWith == null) return; - AppSettings.shareKeysWith.setItem( - Matrix.of(context).store, - shareKeysWith.name, - ); + AppSettings.shareKeysWith.setItem(shareKeysWith.name); Matrix.of(context).client.shareKeysWith = shareKeysWith; setState(() {}); } diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index d2715fa6c..2da16e534 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -33,10 +33,9 @@ class SettingsSecurityView extends StatelessWidget { iconColor: theme.colorScheme.onSurface, child: MaxWidthBody( child: FutureBuilder( - future: Matrix.of(context) - .client - .getCapabilities() - .timeout(const Duration(seconds: 10)), + future: Matrix.of( + context, + ).client.getCapabilities().timeout(const Duration(seconds: 10)), builder: (context, snapshot) { final capabilities = snapshot.data; final error = snapshot.error; @@ -61,18 +60,15 @@ class SettingsSecurityView extends StatelessWidget { ), SettingsSwitchListTile.adaptive( title: L10n.of(context).sendTypingNotifications, - subtitle: - L10n.of(context).sendTypingNotificationsDescription, - onChanged: (b) => AppConfig.sendTypingNotifications = b, - storeKey: SettingKeys.sendTypingNotifications, - defaultValue: AppConfig.sendTypingNotifications, + subtitle: L10n.of( + context, + ).sendTypingNotificationsDescription, + setting: AppSettings.sendTypingNotifications, ), SettingsSwitchListTile.adaptive( title: L10n.of(context).sendReadReceipts, subtitle: L10n.of(context).sendReadReceiptsDescription, - onChanged: (b) => AppConfig.sendPublicReadReceipts = b, - storeKey: SettingKeys.sendPublicReadReceipts, - defaultValue: AppConfig.sendPublicReadReceipts, + setting: AppSettings.sendPublicReadReceipts, ), ListTile( trailing: const Icon(Icons.chevron_right_outlined), @@ -107,14 +103,16 @@ class SettingsSecurityView extends StatelessWidget { ), ListTile( title: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius / 2), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 2, + ), color: theme.colorScheme.onInverseSurface, child: DropdownButton( isExpanded: true, padding: const EdgeInsets.symmetric(horizontal: 8.0), - borderRadius: - BorderRadius.circular(AppConfig.borderRadius / 2), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 2, + ), underline: const SizedBox.shrink(), value: Matrix.of(context).client.shareKeysWith, items: ShareKeysWith.values diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index 5a0092a9a..b22e98fbb 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:file_picker/file_picker.dart'; + import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/user/style_settings_repo.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -18,16 +20,15 @@ class SettingsStyle extends StatefulWidget { class SettingsStyleController extends State { void setChatColor(Color? color) async { - AppConfig.colorSchemeSeed = color; + AppSettings.colorSchemeSeedInt.setItem( + color?.toARGB32() ?? AppSettings.colorSchemeSeedInt.defaultValue, + ); ThemeController.of(context).setPrimaryColor(color); } void setWallpaper() async { final client = Matrix.of(context).client; - final picked = await selectFiles( - context, - type: FileSelectorType.images, - ); + final picked = await selectFiles(context, type: FileType.image); final pickedFile = picked.firstOrNull; if (pickedFile == null) return; @@ -101,14 +102,11 @@ class SettingsStyleController extends State { } void deleteChatWallpaper() => showFutureLoadingDialog( - context: context, - future: () => Matrix.of(context).client.setApplicationAccountConfig( - const ApplicationAccountConfig( - wallpaperUrl: null, - wallpaperBlur: null, - ), - ), - ); + context: context, + future: () => Matrix.of(context).client.setApplicationAccountConfig( + const ApplicationAccountConfig(wallpaperUrl: null, wallpaperBlur: null), + ), + ); ThemeMode get currentTheme => ThemeController.of(context).themeMode; Color? get currentColor => ThemeController.of(context).primaryColor; @@ -155,18 +153,9 @@ class SettingsStyleController extends State { setState(() {}); } - void changeFontSizeFactor(double d) { - setState(() => AppConfig.fontSizeFactor = d); - // #Pangea - // Matrix.of(context).store.setString( - // SettingKeys.fontSizeFactor, - // AppConfig.fontSizeFactor.toString(), - // ); - StyleSettingsRepo.setFontSizeFactor( - Matrix.of(context).client.userID!, - AppConfig.fontSizeFactor, - ); - // Pangea# + void changeFontSizeFactor(double d) async { + await AppSettings.fontSizeFactor.setItem(d); + setState(() {}); } @override diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index 6bf3b2a79..ef2f94f08 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/events/state_message.dart'; @@ -37,7 +38,7 @@ class SettingsStyleView extends StatelessWidget { backgroundColor: theme.colorScheme.surface, body: MaxWidthBody( child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: .stretch, children: [ Padding( padding: const EdgeInsets.all(12.0), @@ -64,9 +65,7 @@ class SettingsStyleView extends StatelessWidget { ], ), ), - Divider( - color: theme.dividerColor, - ), + Divider(color: theme.dividerColor), ListTile( title: Text( L10n.of(context).setColorTheme, @@ -80,10 +79,11 @@ class SettingsStyleView extends StatelessWidget { builder: (light, dark) { final systemColor = Theme.of(context).brightness == Brightness.light - ? light?.primary - : dark?.primary; - final colors = - List.from(SettingsStyleController.customColors); + ? light?.primary + : dark?.primary; + final colors = List.from( + SettingsStyleController.customColors, + ); if (systemColor == null) { colors.remove(null); } @@ -107,8 +107,9 @@ class SettingsStyleView extends StatelessWidget { child: Material( color: color ?? systemColor, elevation: 6, - borderRadius: - BorderRadius.circular(colorPickerSize), + borderRadius: BorderRadius.circular( + colorPickerSize, + ), child: SizedBox( width: colorPickerSize, height: colorPickerSize, @@ -117,9 +118,9 @@ class SettingsStyleView extends StatelessWidget { child: Icon( Icons.check, size: 16, - color: Theme.of(context) - .colorScheme - .onPrimary, + color: Theme.of( + context, + ).colorScheme.onPrimary, ), ) : null, @@ -132,9 +133,7 @@ class SettingsStyleView extends StatelessWidget { ); }, ), - Divider( - color: theme.dividerColor, - ), + Divider(color: theme.dividerColor), ListTile( title: Text( L10n.of(context).messagesStyle, @@ -158,7 +157,7 @@ class SettingsStyleView extends StatelessWidget { final accountConfig = client.applicationAccountConfig; return Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ AnimatedContainer( duration: FluffyThemes.animationDuration, @@ -187,14 +186,16 @@ class SettingsStyleView extends StatelessWidget { ), ), Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ const SizedBox(height: 16), StateMessage( Event( eventId: 'style_dummy', - room: - Room(id: '!style_dummy', client: client), + room: Room( + id: '!style_dummy', + client: client, + ), content: {'membership': 'join'}, type: EventTypes.RoomMember, senderId: client.userID!, @@ -227,8 +228,9 @@ class SettingsStyleView extends StatelessWidget { 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor', style: TextStyle( color: theme.onBubbleColor, - fontSize: AppConfig.messageFontSize * - AppConfig.fontSizeFactor, + fontSize: + AppConfig.messageFontSize * + AppSettings.fontSizeFactor.value, ), ), ), @@ -260,8 +262,9 @@ class SettingsStyleView extends StatelessWidget { 'Lorem ipsum dolor sit amet', style: TextStyle( color: theme.colorScheme.onSurface, - fontSize: AppConfig.messageFontSize * - AppConfig.fontSizeFactor, + fontSize: + AppConfig.messageFontSize * + AppSettings.fontSizeFactor.value, ), ), ), @@ -273,9 +276,7 @@ class SettingsStyleView extends StatelessWidget { ], ), ), - Divider( - color: theme.dividerColor, - ), + Divider(color: theme.dividerColor), ListTile( title: TextButton.icon( style: TextButton.styleFrom( @@ -323,20 +324,18 @@ class SettingsStyleView extends StatelessWidget { ), ListTile( title: Text(L10n.of(context).fontSize), - trailing: Text('× ${AppConfig.fontSizeFactor}'), + trailing: Text('× ${AppSettings.fontSizeFactor.value}'), ), Slider.adaptive( min: 0.5, max: 2.5, divisions: 20, - value: AppConfig.fontSizeFactor, + value: AppSettings.fontSizeFactor.value, semanticFormatterCallback: (d) => d.toString(), onChanged: controller.changeFontSizeFactor, ), // #Pangea - // Divider( - // color: theme.dividerColor, - // ), + // Divider(color: theme.dividerColor), // ListTile( // title: Text( // L10n.of(context).overview, @@ -348,21 +347,15 @@ class SettingsStyleView extends StatelessWidget { // ), // SettingsSwitchListTile.adaptive( // title: L10n.of(context).presencesToggle, - // onChanged: (b) => AppConfig.showPresences = b, - // storeKey: SettingKeys.showPresences, - // defaultValue: AppConfig.showPresences, + // setting: AppSettings.showPresences, // ), // SettingsSwitchListTile.adaptive( // title: L10n.of(context).separateChatTypes, - // onChanged: (b) => AppConfig.separateChatTypes = b, - // storeKey: SettingKeys.separateChatTypes, - // defaultValue: AppConfig.separateChatTypes, + // setting: AppSettings.separateChatTypes, // ), // SettingsSwitchListTile.adaptive( // title: L10n.of(context).displayNavigationRail, - // onChanged: (b) => AppConfig.displayNavigationRail = b, - // storeKey: SettingKeys.displayNavigationRail, - // defaultValue: AppConfig.displayNavigationRail, + // setting: AppSettings.displayNavigationRail, // ), // Pangea# ], diff --git a/lib/pangea/activity_generator/media_enum.dart b/lib/pangea/activity_generator/media_enum.dart index e6e3ed515..555bf090b 100644 --- a/lib/pangea/activity_generator/media_enum.dart +++ b/lib/pangea/activity_generator/media_enum.dart @@ -2,12 +2,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; -enum MediaEnum { - images, - videos, - voiceMessages, - nan, -} +enum MediaEnum { images, videos, voiceMessages, nan } extension MediaEnumExtension on MediaEnum { //fromString diff --git a/lib/pangea/activity_planner/activity_plan_model.dart b/lib/pangea/activity_planner/activity_plan_model.dart index 4546aaacd..8dd68655f 100644 --- a/lib/pangea/activity_planner/activity_plan_model.dart +++ b/lib/pangea/activity_planner/activity_plan_model.dart @@ -41,27 +41,29 @@ class ActivityPlanModel { this.endAt, this.duration, this.isDeprecatedModel = false, - }) : description = (description == null || description.isEmpty) - ? learningObjective - : description, - _roles = roles, - _imageURL = imageURL; + }) : description = (description == null || description.isEmpty) + ? learningObjective + : description, + _roles = roles, + _imageURL = imageURL; List get placeholderImages => [ - "${AppConfig.assetsBaseURL}/Space%20template%202.png", - "${AppConfig.assetsBaseURL}/Space%20template%203.png", - "${AppConfig.assetsBaseURL}/Space%20template%204.png", - ]; + "${AppConfig.assetsBaseURL}/Space%20template%202.png", + "${AppConfig.assetsBaseURL}/Space%20template%203.png", + "${AppConfig.assetsBaseURL}/Space%20template%204.png", + ]; - String get randomPlaceholder => placeholderImages[ - Random(title.hashCode).nextInt(placeholderImages.length)]; + String get randomPlaceholder => + placeholderImages[Random( + title.hashCode, + ).nextInt(placeholderImages.length)]; Uri? get imageURL => _imageURL != null ? Uri.tryParse("${Environment.cmsApi}$_imageURL") : Uri.tryParse(randomPlaceholder); Map get roles { - if (_roles != null) return _roles!; + if (_roles != null) return _roles; final defaultRoles = {}; for (int i = 0; i < req.numberOfParticipants; i++) { defaultRoles['role_$i'] = ActivityRole( @@ -75,18 +77,16 @@ class ActivityPlanModel { } factory ActivityPlanModel.fromJson(Map json) { - final req = - ActivityPlanRequest.fromJson(json[ModelKey.activityPlanRequest]); + final req = ActivityPlanRequest.fromJson( + json[ModelKey.activityPlanRequest], + ); Map? roles; final roleContent = json['roles']; if (roleContent is Map) { roles = Map.from( json['roles'].map( - (key, value) => MapEntry( - key, - ActivityRole.fromJson(value), - ), + (key, value) => MapEntry(key, ActivityRole.fromJson(value)), ), ); } @@ -97,7 +97,8 @@ class ActivityPlanModel { instructions: json[ModelKey.activityPlanInstructions], req: req, title: json[ModelKey.activityPlanTitle], - description: json[ModelKey.description] ?? + description: + json[ModelKey.description] ?? json[ModelKey.activityPlanLearningObjective], learningObjective: json[ModelKey.activityPlanLearningObjective], vocab: List.from( @@ -135,9 +136,7 @@ class ActivityPlanModel { 'hours': duration?.inHours.remainder(24) ?? 0, 'minutes': duration?.inMinutes.remainder(60) ?? 0, }, - 'roles': _roles?.map( - (key, value) => MapEntry(key, value.toJson()), - ), + 'roles': _roles?.map((key, value) => MapEntry(key, value.toJson())), }; } @@ -187,16 +186,10 @@ class Vocab { final String lemma; final String pos; - Vocab({ - required this.lemma, - required this.pos, - }); + Vocab({required this.lemma, required this.pos}); factory Vocab.fromJson(Map json) { - return Vocab( - lemma: json[ModelKey.lemma], - pos: json['pos'], - ); + return Vocab(lemma: json[ModelKey.lemma], pos: json['pos']); } PangeaToken asToken() { @@ -215,10 +208,7 @@ class Vocab { } Map toJson() { - return { - ModelKey.lemma: lemma, - 'pos': pos, - }; + return {ModelKey.lemma: lemma, 'pos': pos}; } @override @@ -261,11 +251,6 @@ class ActivityRole { } Map toJson() { - return { - 'id': id, - 'name': name, - 'goal': goal, - 'avatar_url': avatarUrl, - }; + return {'id': id, 'name': name, 'goal': goal, 'avatar_url': avatarUrl}; } } diff --git a/lib/pangea/activity_sessions/activity_participant_indicator.dart b/lib/pangea/activity_sessions/activity_participant_indicator.dart index c5871e820..c85f619b8 100644 --- a/lib/pangea/activity_sessions/activity_participant_indicator.dart +++ b/lib/pangea/activity_sessions/activity_participant_indicator.dart @@ -49,13 +49,14 @@ class ActivityParticipantIndicator extends StatelessWidget { return MouseRegion( cursor: SystemMouseCursors.basic, child: GestureDetector( - onTap: onTap ?? + onTap: + onTap ?? (user != null ? () => showMemberActionsPopupMenu( - context: context, - user: user!, - room: room, - ) + context: context, + user: user!, + room: room, + ) : null), child: AbsorbPointer( absorbing: !selectable, @@ -63,38 +64,38 @@ class ActivityParticipantIndicator extends StatelessWidget { builder: (context, hovered) { final avatar = userId != null ? user?.avatarUrl == null || - user!.avatarUrl!.toString().startsWith("mxc") - ? Avatar( - mxContent: - user?.avatarUrl != null ? user!.avatarUrl! : null, - name: userId!.localpart, - size: 60.0, - userId: userId, - miniIcon: - room != null && user?.id == BotName.byEnvironment - ? BotSettingsLanguageIcon(user: user!) - : null, - presenceOffset: - room != null && user?.id == BotName.byEnvironment - ? const Offset(0, 0) - : null, - ) - : ClipRRect( - borderRadius: BorderRadius.circular(30), - child: CachedNetworkImage( - imageUrl: user!.avatarUrl!.toString(), - width: 60.0, - height: 60.0, - fit: BoxFit.cover, - ), - ) + user!.avatarUrl!.toString().startsWith("mxc") + ? Avatar( + mxContent: user?.avatarUrl != null + ? user!.avatarUrl! + : null, + name: userId!.localpart, + size: 60.0, + userId: userId, + miniIcon: + room != null && + user?.id == BotName.byEnvironment + ? BotSettingsLanguageIcon(user: user!) + : null, + presenceOffset: + room != null && + user?.id == BotName.byEnvironment + ? const Offset(0, 0) + : null, + ) + : ClipRRect( + borderRadius: BorderRadius.circular(30), + child: CachedNetworkImage( + imageUrl: user!.avatarUrl!.toString(), + width: 60.0, + height: 60.0, + fit: BoxFit.cover, + ), + ) : CircleAvatar( radius: 30.0, backgroundColor: theme.colorScheme.primaryContainer, - child: const Icon( - Icons.question_mark, - size: 30.0, - ), + child: const Icon(Icons.question_mark, size: 30.0), ); return Opacity( opacity: opacity, @@ -103,7 +104,8 @@ class ActivityParticipantIndicator extends StatelessWidget { borderRadius: borderRadius, child: Container( alignment: Alignment.center, - padding: padding ?? + padding: + padding ?? const EdgeInsets.symmetric( vertical: 4.0, horizontal: 8.0, @@ -121,9 +123,7 @@ class ActivityParticipantIndicator extends StatelessWidget { avatar, Text( name, - style: const TextStyle( - fontSize: 12.0, - ), + style: const TextStyle(fontSize: 12.0), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, @@ -132,12 +132,13 @@ class ActivityParticipantIndicator extends StatelessWidget { userId?.localpart ?? L10n.of(context).openRoleLabel, style: TextStyle( fontSize: 12.0, - color: (Theme.of(context).brightness == + color: + (Theme.of(context).brightness == Brightness.light ? (userId?.localpart?.darkColor ?? - name.darkColor) + name.darkColor) : (userId?.localpart?.lightColorText ?? - name.lightColorText)), + name.lightColorText)), ), textAlign: TextAlign.center, maxLines: 1, diff --git a/lib/pangea/activity_sessions/activity_participant_list.dart b/lib/pangea/activity_sessions/activity_participant_list.dart index a29c3f23a..530d52f4f 100644 --- a/lib/pangea/activity_sessions/activity_participant_list.dart +++ b/lib/pangea/activity_sessions/activity_participant_list.dart @@ -42,8 +42,9 @@ class ActivityParticipantList extends StatelessWidget { room: room, builder: (context, participants) { final theme = Theme.of(context); - final availableRoles = - activity.roles.values.sorted((a, b) => a.id.compareTo(b.id)); + final availableRoles = activity.roles.values.sorted( + (a, b) => a.id.compareTo(b.id), + ); final remainingMembers = participants.participants.where( (p) => !assignedRoles.values.any((r) => r.userId == p.id), @@ -56,9 +57,10 @@ class ActivityParticipantList extends StatelessWidget { builder: (context, constraints) { const minItemWidth = 125.0; - final rows = (availableRoles.length / - (constraints.maxWidth / minItemWidth)) - .ceil(); + final rows = + (availableRoles.length / + (constraints.maxWidth / minItemWidth)) + .ceil(); final entriesPerRow = (availableRoles.length / rows).ceil(); @@ -78,7 +80,8 @@ class ActivityParticipantList extends StatelessWidget { ? isSelected!(availableRole.id) : false; - final assignedRole = assignedRoles[availableRole.id] ?? + final assignedRole = + assignedRoles[availableRole.id] ?? (selected ? ActivityRoleModel( id: availableRole.id, @@ -89,11 +92,11 @@ class ActivityParticipantList extends StatelessWidget { final User? user = participants.participants.firstWhereOrNull( - (u) => u.id == assignedRole?.userId, - ) ?? - course?.getParticipants().firstWhereOrNull( - (u) => u.id == assignedRole?.userId, - ); + (u) => u.id == assignedRole?.userId, + ) ?? + course?.getParticipants().firstWhereOrNull( + (u) => u.id == assignedRole?.userId, + ); final selectable = canSelect != null ? canSelect!(availableRole.id) @@ -156,9 +159,7 @@ class ActivityParticipantList extends StatelessWidget { userId: member.id, ), ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 80.0, - ), + constraints: const BoxConstraints(maxWidth: 80.0), child: Text( member.calcDisplayname(), style: TextStyle( diff --git a/lib/pangea/activity_sessions/activity_role_model.dart b/lib/pangea/activity_sessions/activity_role_model.dart index 1e862068b..96294259e 100644 --- a/lib/pangea/activity_sessions/activity_role_model.dart +++ b/lib/pangea/activity_sessions/activity_role_model.dart @@ -30,10 +30,7 @@ class ActivityRoleModel { return l10n.finishedTheActivity(displayName); } - return l10n.joinedTheActivity( - displayName, - role ?? l10n.participant, - ); + return l10n.joinedTheActivity(displayName, role ?? l10n.participant); } factory ActivityRoleModel.fromJson(Map json) { diff --git a/lib/pangea/activity_sessions/activity_roles_model.dart b/lib/pangea/activity_sessions/activity_roles_model.dart index ff8b392ae..ae26e39aa 100644 --- a/lib/pangea/activity_sessions/activity_roles_model.dart +++ b/lib/pangea/activity_sessions/activity_roles_model.dart @@ -32,17 +32,14 @@ class ActivityRolesModel { } Map toJson() { - return { - "roles": roles.map((id, role) => MapEntry(id, role.toJson())), - }; + return {"roles": roles.map((id, role) => MapEntry(id, role.toJson()))}; } static ActivityRolesModel fromJson(Map json) { - final roles = (json['roles'] as Map?) - ?.map((id, value) => MapEntry(id, ActivityRoleModel.fromJson(value))); - - return ActivityRolesModel( - roles ?? {}, + final roles = (json['roles'] as Map?)?.map( + (id, value) => MapEntry(id, ActivityRoleModel.fromJson(value)), ); + + return ActivityRolesModel(roles ?? {}); } } diff --git a/lib/pangea/activity_sessions/activity_room_extension.dart b/lib/pangea/activity_sessions/activity_room_extension.dart index e48a8cfd5..f4d9ab1e0 100644 --- a/lib/pangea/activity_sessions/activity_room_extension.dart +++ b/lib/pangea/activity_sessions/activity_room_extension.dart @@ -41,10 +41,7 @@ extension ActivityRoomExtension on Room { ErrorHandler.logError( e: e, s: s, - data: { - "roomID": id, - "stateEvent": stateEvent.content, - }, + data: {"roomID": id, "stateEvent": stateEvent.content}, ); return null; } @@ -60,10 +57,7 @@ extension ActivityRoomExtension on Room { ErrorHandler.logError( e: e, s: s, - data: { - "roomID": id, - "stateEvent": stateEvent.content, - }, + data: {"roomID": id, "stateEvent": stateEvent.content}, ); return null; } @@ -80,10 +74,7 @@ extension ActivityRoomExtension on Room { ErrorHandler.logError( e: e, s: s, - data: { - "roomID": id, - "stateEvent": content, - }, + data: {"roomID": id, "stateEvent": content}, ); } return null; @@ -187,9 +178,7 @@ extension ActivityRoomExtension on Room { ); } - Future setActivitySummary( - ActivitySummaryModel summary, - ) async { + Future setActivitySummary(ActivitySummaryModel summary) async { await client.setRoomStateWithKey( id, PangeaEventTypes.activitySummary, @@ -250,10 +239,16 @@ extension ActivityRoomExtension on Room { ) : ActivitySummaryResultsMessage( userId: event.senderId, - sent: - pangeaMessage.getSpeechToTextLocal()!.transcript.text.trim(), - written: - pangeaMessage.getSpeechToTextLocal()!.transcript.text.trim(), + sent: pangeaMessage + .getSpeechToTextLocal()! + .transcript + .text + .trim(), + written: pangeaMessage + .getSpeechToTextLocal()! + .transcript + .text + .trim(), time: event.originServerTs, tool: [], ); @@ -277,10 +272,7 @@ extension ActivityRoomExtension on Room { ); await setActivitySummary( - ActivitySummaryModel( - summary: resp, - analytics: analytics, - ), + ActivitySummaryModel(summary: resp, analytics: analytics), ); ActivitySummaryRepo.delete(id, activityPlan!); @@ -299,10 +291,7 @@ extension ActivityRoomExtension on Room { if (activitySummary?.summary == null) { await setActivitySummary( - ActivitySummaryModel( - errorAt: DateTime.now(), - analytics: analytics, - ), + ActivitySummaryModel(errorAt: DateTime.now(), analytics: analytics), ); } } @@ -381,9 +370,7 @@ extension ActivityRoomExtension on Room { // if the user is in the chat (not null && membership is join), // then the activity is not finished for them - final user = getParticipants().firstWhereOrNull( - (u) => u.id == r.userId, - ); + final user = getParticipants().firstWhereOrNull((u) => u.id == r.userId); return user == null || user.membership != Membership.join; }); } @@ -404,6 +391,6 @@ extension ActivityRoomExtension on Room { // helper functions for activity course context Room? get courseParent => pangeaSpaceParents.firstWhereOrNull( - (parent) => parent.coursePlan != null, - ); + (parent) => parent.coursePlan != null, + ); } diff --git a/lib/pangea/activity_sessions/activity_session_analytics_repo.dart b/lib/pangea/activity_sessions/activity_session_analytics_repo.dart index 9de51dbba..50155dbba 100644 --- a/lib/pangea/activity_sessions/activity_session_analytics_repo.dart +++ b/lib/pangea/activity_sessions/activity_session_analytics_repo.dart @@ -15,8 +15,9 @@ class CachedActivityAnalytics { } class ActivitySessionAnalyticsRepo { - static final GetStorage _activityAnalyticsStorage = - GetStorage('activity_analytics_storage'); + static final GetStorage _activityAnalyticsStorage = GetStorage( + 'activity_analytics_storage', + ); static Duration cacheDuration = const Duration(minutes: 30); @@ -31,8 +32,9 @@ class ActivitySessionAnalyticsRepo { return null; } - final lastUseTimestamp = - DateTime.parse(json['last_use_timestamp'] as String); + final lastUseTimestamp = DateTime.parse( + json['last_use_timestamp'] as String, + ); final analyticsJson = json['analytics'] as Map; final analytics = ActivitySummaryAnalyticsModel.fromJson(analyticsJson); return CachedActivityAnalytics(timestamp, lastUseTimestamp, analytics); diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_analytics_chip.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_analytics_chip.dart index afeb7d792..53cac76f2 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_analytics_chip.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_analytics_chip.dart @@ -3,11 +3,7 @@ import 'package:flutter/material.dart'; class ActivityAnalyticsChip extends StatelessWidget { final IconData icon; final String text; - const ActivityAnalyticsChip( - this.icon, - this.text, { - super.key, - }); + const ActivityAnalyticsChip(this.icon, this.text, {super.key}); @override Widget build(BuildContext context) { @@ -23,12 +19,7 @@ class ActivityAnalyticsChip extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 12.0), - Text( - text, - style: const TextStyle( - fontSize: 12.0, - ), - ), + Text(text, style: const TextStyle(fontSize: 12.0)), ], ), ); diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_controller.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_controller.dart index d5a38e17b..0f9b7d634 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_controller.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_controller.dart @@ -17,10 +17,7 @@ class ActivityChatController { final String userID; final Room room; - ActivityChatController({ - required this.userID, - required this.room, - }) { + ActivityChatController({required this.userID, required this.room}) { init(); } @@ -36,8 +33,13 @@ class ActivityChatController { void init() { _updateUsedVocab(); - _analyticsSubscription = MatrixState.pangeaController.matrixState - .analyticsDataService.updateDispatcher.constructUpdateStream.stream + _analyticsSubscription = MatrixState + .pangeaController + .matrixState + .analyticsDataService + .updateDispatcher + .constructUpdateStream + .stream .listen((_) => _updateUsedVocab()); } @@ -80,7 +82,8 @@ class ActivityChatController { try { final analytics = await getActivityAnalytics(); if (!_disposed) { - usedVocab.value = analytics.constructs[userID] + usedVocab.value = + analytics.constructs[userID] ?.constructsOfType(ConstructTypeEnum.vocab) .map((id) => id.lemma.toLowerCase()) .toSet() ?? diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart index 582fddb85..30ce61385 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart @@ -1,7 +1,6 @@ import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; @@ -10,7 +9,7 @@ import 'package:fluffychat/widgets/matrix.dart'; extension ActivityMenuLogic on ChatController { bool get shouldShowActivityInstructions { - if (AppConfig.showedActivityMenu || + if (InstructionsEnum.showedActivityMenu.isToggledOff || InstructionsEnum.activityStatsMenu.isToggledOff || MatrixState.pAnyState.isOverlayOpen(RegExp(r"^word-zoom-card-.*$")) || timeline == null || @@ -31,8 +30,10 @@ extension ActivityMenuLogic on ChatController { (event) => event.senderId == userID && event.type == EventTypes.Message && - {MessageTypes.Text, MessageTypes.Audio} - .contains(event.messageType), + { + MessageTypes.Text, + MessageTypes.Audio, + }.contains(event.messageType), ) .length; diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart index dbd0b0578..5a85ec999 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart @@ -15,10 +15,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class ActivityFinishedStatusMessage extends StatelessWidget { final ChatController controller; - const ActivityFinishedStatusMessage({ - super.key, - required this.controller, - }); + const ActivityFinishedStatusMessage({super.key, required this.controller}); void _onArchive(BuildContext context) { _archiveToAnalytics(); @@ -38,16 +35,13 @@ class ActivityFinishedStatusMessage extends StatelessWidget { final langModel = PLanguageStore.byLangCode(lang)!; await controller.room.archiveActivity(); await MatrixState - .pangeaController.matrixState.analyticsDataService.updateService + .pangeaController + .matrixState + .analyticsDataService + .updateService .sendActivityAnalytics(controller.room.id, langModel); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomId': controller.room.id, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'roomId': controller.room.id}); } } @@ -66,15 +60,13 @@ class ActivityFinishedStatusMessage extends StatelessWidget { final theme = Theme.of(context); final isSubscribed = MatrixState.pangeaController.subscriptionController.isSubscribed != - false; + false; return Container( padding: const EdgeInsets.all(12.0), decoration: BoxDecoration( color: theme.colorScheme.surface, - border: Border( - top: BorderSide(color: theme.dividerColor), - ), + border: Border(top: BorderSide(color: theme.dividerColor)), ), child: Center( child: ConstrainedBox( @@ -99,9 +91,7 @@ class ActivityFinishedStatusMessage extends StatelessWidget { onArchive: () => _onArchive(context), ), if (!controller.room.isActivityFinished) - _WaitSection( - onContinue: controller.room.continueActivity, - ), + _WaitSection(onContinue: controller.room.continueActivity), ], ), ), @@ -114,10 +104,7 @@ class _SummarySection extends StatelessWidget { final ChatController controller; final bool isSubscribed; - const _SummarySection({ - required this.controller, - required this.isSubscribed, - }); + const _SummarySection({required this.controller, required this.isSubscribed}); ActivitySummaryModel? get summary => controller.room.activitySummary; @@ -153,8 +140,9 @@ class _SummarySection extends StatelessWidget { return ErrorIndicator( message: L10n.of(context).subscribeToUnlockActivitySummaries, onTap: () { - MatrixState.pangeaController.subscriptionController - .showPaywall(context); + MatrixState.pangeaController.subscriptionController.showPaywall( + context, + ); }, ); } @@ -192,10 +180,7 @@ class _ArchiveSection extends StatelessWidget { final bool enabled; final VoidCallback onArchive; - const _ArchiveSection({ - required this.enabled, - required this.onArchive, - }); + const _ArchiveSection({required this.enabled, required this.onArchive}); @override Widget build(BuildContext context) { @@ -212,10 +197,7 @@ class _ArchiveSection extends StatelessWidget { ElevatedButton( onPressed: enabled ? onArchive : null, style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), foregroundColor: theme.colorScheme.onPrimaryContainer, backgroundColor: theme.colorScheme.primaryContainer, ), @@ -250,24 +232,16 @@ class _WaitSection extends StatelessWidget { children: [ Text( L10n.of(context).waitingForOthersToFinish, - style: const TextStyle( - fontSize: 12, - fontStyle: FontStyle.italic, - ), + style: const TextStyle(fontSize: 12, fontStyle: FontStyle.italic), textAlign: TextAlign.center, ), ElevatedButton( onPressed: onContinue, style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), foregroundColor: theme.colorScheme.onSurface, backgroundColor: theme.colorScheme.surface, - side: BorderSide( - color: theme.colorScheme.primaryContainer, - ), + side: BorderSide(color: theme.colorScheme.primaryContainer), ), child: Text( L10n.of(context).waitNotDone, diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_menu_button.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_menu_button.dart index ffb7cb9af..b2f72cdce 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_menu_button.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_menu_button.dart @@ -17,10 +17,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class ActivityMenuButton extends StatefulWidget { final ChatController controller; - const ActivityMenuButton({ - super.key, - required this.controller, - }); + const ActivityMenuButton({super.key, required this.controller}); @override State createState() => _ActivityMenuButtonState(); @@ -60,7 +57,7 @@ class _ActivityMenuButtonState extends State { /// Show a tutorial overlay that blocks the screen and points /// to the stats menu button with an explanation of what it does. - void _showStatsMenuDropdownInstructions(_) { + void _showStatsMenuDropdownInstructions(dynamic _) { if (!mounted) return; if (!widget.controller.shouldShowActivityInstructions) { return; diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart index a6e3f3fd9..9ab643b7f 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart @@ -4,15 +4,13 @@ import 'package:collection/collection.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart'; class ActivityRolesEvent extends StatelessWidget { final Event event; - const ActivityRolesEvent({ - super.key, - required this.event, - }); + const ActivityRolesEvent({super.key, required this.event}); @override Widget build(BuildContext context) { @@ -26,11 +24,10 @@ class ActivityRolesEvent extends StatelessWidget { .toSet(); final previousRoles = - (event.prevContent?['roles'] as Map?) - ?.values - .map((v) => ActivityRoleModel.fromJson(v)) - .toSet() ?? - {}; + (event.prevContent?['roles'] as Map?)?.values + .map((v) => ActivityRoleModel.fromJson(v)) + .toSet() ?? + {}; difference = currentRoles.difference(previousRoles); } catch (e) { @@ -44,8 +41,8 @@ class ActivityRolesEvent extends StatelessWidget { return Column( children: difference.map((role) { final user = event.room.getParticipants().firstWhereOrNull( - (u) => u.id == role.userId, - ); + (u) => u.id == role.userId, + ); final displayName = user?.calcDisplayname() ?? role.userId.localpart ?? role.userId; @@ -72,9 +69,10 @@ class ActivityRolesEvent extends StatelessWidget { maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, - decoration: - event.redacted ? TextDecoration.lineThrough : null, + fontSize: 12 * AppSettings.fontSizeFactor.value, + decoration: event.redacted + ? TextDecoration.lineThrough + : null, ), ), ), diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_session_popup_menu.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_session_popup_menu.dart index b28b6813a..8dd0c102f 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_session_popup_menu.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_session_popup_menu.dart @@ -31,11 +31,7 @@ class ActivitySessionPopupMenuState extends State widget.onLeave(); break; case ActivityPopupMenuActions.invite: - NavigationUtil.goToSpaceRoute( - widget.room.id, - ['invite'], - context, - ); + NavigationUtil.goToSpaceRoute(widget.room.id, ['invite'], context); break; case ActivityPopupMenuActions.download: downloadChatAction(widget.room.id, context); diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_stats_menu.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_stats_menu.dart index ef96f27e4..341eaf6be 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_stats_menu.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_stats_menu.dart @@ -16,10 +16,7 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; class ActivityStatsMenu extends StatelessWidget { final ChatController controller; - const ActivityStatsMenu( - this.controller, { - super.key, - }); + const ActivityStatsMenu(this.controller, {super.key}); int _getAssignedRolesCount() { final assignedRoles = controller.room.assignedRoles; @@ -125,9 +122,7 @@ class ActivityStatsMenu extends StatelessWidget { }, child: Container( width: double.infinity, - decoration: BoxDecoration( - color: theme.colorScheme.surface, - ), + decoration: BoxDecoration(color: theme.colorScheme.surface), padding: const EdgeInsets.all(12.0), child: Column( spacing: 12.0, @@ -191,9 +186,7 @@ class ActivityStatsMenu extends StatelessWidget { children: [ Text( L10n.of(context).endForAll, - style: TextStyle( - fontSize: isColumnMode ? 16.0 : 12.0, - ), + style: TextStyle(fontSize: isColumnMode ? 16.0 : 12.0), ), ], ), @@ -212,9 +205,7 @@ class ActivityStatsMenu extends StatelessWidget { children: [ Text( L10n.of(context).endActivity, - style: TextStyle( - fontSize: isColumnMode ? 16.0 : 12.0, - ), + style: TextStyle(fontSize: isColumnMode ? 16.0 : 12.0), ), ], ), diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart index 0a05e5c47..c03693cc1 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart @@ -44,7 +44,7 @@ class ActivityVocabWidget extends StatelessWidget { return ValueListenableBuilder( valueListenable: usedVocab!, - builder: (context, used, __) => _VocabChips( + builder: (context, used, _) => _VocabChips( vocab: vocab, targetId: targetId, langCode: langCode, @@ -83,10 +83,7 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin { super.dispose(); } - void _onTap( - Vocab v, - bool isNew, - ) { + void _onTap(Vocab v, bool isNew) { setState(() { _selectedVocab = v; }); @@ -139,57 +136,54 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin { spacing: 4.0, runSpacing: 4.0, children: [ - ...widget.vocab.map( - (v) { - final target = "${widget.targetId}-${v.lemma}"; - final color = widget.usedVocab.contains(v.lemma.toLowerCase()) - ? Color.alphaBlend( - Theme.of(context).colorScheme.surface.withAlpha(150), - AppConfig.gold, - ) - : Theme.of(context).colorScheme.primary.withAlpha(20); + ...widget.vocab.map((v) { + final target = "${widget.targetId}-${v.lemma}"; + final color = widget.usedVocab.contains(v.lemma.toLowerCase()) + ? Color.alphaBlend( + Theme.of(context).colorScheme.surface.withAlpha(150), + AppConfig.gold, + ) + : Theme.of(context).colorScheme.primary.withAlpha(20); - final linkAndKey = MatrixState.pAnyState.layerLinkAndKey(target); - final isNew = newTokens - .any((t) => t.content.toLowerCase() == v.lemma.toLowerCase()); + final linkAndKey = MatrixState.pAnyState.layerLinkAndKey(target); + final isNew = newTokens.any( + (t) => t.content.toLowerCase() == v.lemma.toLowerCase(), + ); - return CompositedTransformTarget( - link: linkAndKey.link, - child: InkWell( - key: linkAndKey.key, - borderRadius: BorderRadius.circular( - 24.0, - ), - onTap: () => _onTap(v, isNew), - child: HoverBuilder( - builder: (context, hovered) => Container( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, + return CompositedTransformTarget( + link: linkAndKey.link, + child: InkWell( + key: linkAndKey.key, + borderRadius: BorderRadius.circular(24.0), + onTap: () => _onTap(v, isNew), + child: HoverBuilder( + builder: (context, hovered) => Container( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(20), + ), + child: UnderlineText( + text: v.lemma, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontSize: 14.0, ), - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(20), - ), - child: UnderlineText( - text: v.lemma, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - fontSize: 14.0, - ), - underlineColor: TokenRenderingUtil.underlineColor( - Theme.of(context).colorScheme.primary.withAlpha(200), - isNew: isNew, - selected: _selectedVocab == v, - hovered: hovered, - ), + underlineColor: TokenRenderingUtil.underlineColor( + Theme.of(context).colorScheme.primary.withAlpha(200), + isNew: isNew, + selected: _selectedVocab == v, + hovered: hovered, ), ), ), ), - ); - }, - ), + ), + ); + }), ], ); } diff --git a/lib/pangea/activity_sessions/activity_session_chat/load_activity_summary_widget.dart b/lib/pangea/activity_sessions/activity_session_chat/load_activity_summary_widget.dart index 9cfef83b2..47e553fbf 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/load_activity_summary_widget.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/load_activity_summary_widget.dart @@ -40,8 +40,9 @@ class LoadActivitySummaryWidgetState extends State { // The summary state event is waiting (<= 10 seconds since request) // Wait for 10 seconds (or time remaining until not waiting). If summary still not there, run request. if (_summaryEvent!.isLoading) { - final remainingTime = - DateTime.now().difference(_summaryEvent!.requestedAt!).inSeconds; + final remainingTime = DateTime.now() + .difference(_summaryEvent!.requestedAt!) + .inSeconds; await Future.delayed( Duration(seconds: remainingTime < 10 ? 10 - remainingTime : 0), diff --git a/lib/pangea/activity_sessions/activity_session_details_row.dart b/lib/pangea/activity_sessions/activity_session_details_row.dart index 0e5d44a8b..29057b818 100644 --- a/lib/pangea/activity_sessions/activity_session_details_row.dart +++ b/lib/pangea/activity_sessions/activity_session_details_row.dart @@ -21,12 +21,8 @@ class ActivitySessionDetailsRow extends StatelessWidget { child: Row( spacing: 12.0, children: [ - if (leading != null) leading!, - if (icon != null) - Icon( - icon, - size: iconSize ?? 24.0, - ), + ?leading, + if (icon != null) Icon(icon, size: iconSize ?? 24.0), Expanded(child: child), ], ), diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart index ecfaeae14..070d8d521 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart @@ -101,21 +101,17 @@ class ActivitySessionStartController extends State } Room? get activityRoom => widget.roomId != null - ? Matrix.of(context).client.getRoomById( - widget.roomId!, - ) + ? Matrix.of(context).client.getRoomById(widget.roomId!) : null; Room? get courseParent => widget.parentId != null - ? Matrix.of(context).client.getRoomById( - widget.parentId!, - ) + ? Matrix.of(context).client.getRoomById(widget.parentId!) : null; bool get isBotRoomMember => activityRoom?.getParticipants().any( - (p) => p.id == BotName.byEnvironment, - ) ?? + (p) => p.id == BotName.byEnvironment, + ) ?? false; SessionState get state { @@ -140,8 +136,9 @@ class ActivitySessionStartController extends State String? get descriptionText { switch (state) { case SessionState.confirmedRole: - return L10n.of(context) - .waitingToFillRole(activityRoom!.numRemainingRoles); + return L10n.of( + context, + ).waitingToFillRole(activityRoom!.numRemainingRoles); case SessionState.selectedRole: return activity!.roles[_selectedRoleId!]!.goal; case SessionState.notStarted: @@ -157,10 +154,8 @@ class ActivitySessionStartController extends State } } - bool get enableButtons => [ - SessionState.notStarted, - SessionState.selectedRole, - ].contains(state); + bool get enableButtons => + [SessionState.notStarted, SessionState.selectedRole].contains(state); Map get assignedRoles { if (activityRoom != null && activityRoom!.membership == Membership.join) { @@ -176,7 +171,8 @@ class ActivitySessionStartController extends State } final availableRoles = activity!.roles; - final assignedRoles = activityRoom?.assignedRoles ?? + final assignedRoles = + activityRoom?.assignedRoles ?? roomSummaries?[widget.roomId]?.joinedUsersWithRoles ?? {}; final unassignedIds = availableRoles.keys @@ -191,7 +187,8 @@ class ActivitySessionStartController extends State } final availableRoles = activity!.roles; - final assignedRoles = activityRoom?.assignedRoles ?? + final assignedRoles = + activityRoom?.assignedRoles ?? roomSummaries?[widget.roomId]?.activityRoles?.roles ?? {}; final unassignedIds = availableRoles.keys @@ -237,7 +234,7 @@ class ActivitySessionStartController extends State } Map> - get activityStatuses => activitySessionStatuses(widget.activityId); + get activityStatuses => activitySessionStatuses(widget.activityId); void toggleInstructions() { setState(() { @@ -252,7 +249,8 @@ class ActivitySessionStartController extends State } Future neededCourseParticipants() async { - final courseParticipants = await courseParent?.requestParticipants( + final courseParticipants = + await courseParent?.requestParticipants( [Membership.join, Membership.invite, Membership.knock], false, true, @@ -352,9 +350,7 @@ class ActivitySessionStartController extends State await client.joinRoom( widget.roomId!, serverName: courseParent?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == widget.roomId, - ) + .firstWhereOrNull((child) => child.roomId == widget.roomId) ?.via, ); @@ -363,9 +359,11 @@ class ActivitySessionStartController extends State } if (activityRoom == null || activityRoom!.membership != Membership.join) { - throw Exception("Failed to join activity room. " - "Room ID: ${widget.roomId}, " - "Membership status: ${activityRoom?.membership}"); + throw Exception( + "Failed to join activity room. " + "Room ID: ${widget.roomId}, " + "Membership status: ${activityRoom?.membership}", + ); } } @@ -379,20 +377,14 @@ class ActivitySessionStartController extends State true, ); - await activityRoom!.joinActivity( - activity!.roles[_selectedRoleId!]!, - ); + await activityRoom!.joinActivity(activity!.roles[_selectedRoleId!]!); } catch (e) { if (e is! RoleException) { rethrow; } } - NavigationUtil.goToSpaceRoute( - widget.roomId, - [], - context, - ); + NavigationUtil.goToSpaceRoute(widget.roomId, [], context); } Future confirmRoleSelection() async { @@ -400,15 +392,11 @@ class ActivitySessionStartController extends State if (activityRoom?.membership == Membership.join) { await showFutureLoadingDialog( context: context, - future: () => activityRoom!.joinActivity( - activity!.roles[_selectedRoleId!]!, - ), + future: () => + activityRoom!.joinActivity(activity!.roles[_selectedRoleId!]!), ); } else if (widget.roomId != null) { - await showFutureLoadingDialog( - context: context, - future: joinActivity, - ); + await showFutureLoadingDialog(context: context, future: joinActivity); } else { final resp = await showFutureLoadingDialog( context: context, @@ -419,11 +407,7 @@ class ActivitySessionStartController extends State ); if (!resp.isError) { - NavigationUtil.goToSpaceRoute( - resp.result, - [], - context, - ); + NavigationUtil.goToSpaceRoute(resp.result, [], context); } } } @@ -440,9 +424,7 @@ class ActivitySessionStartController extends State await courseParent!.client.joinRoom( sessionId, via: courseParent?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == sessionId, - ) + .firstWhereOrNull((child) => child.roomId == sessionId) ?.via, ); joinedSessionId = sessionId; @@ -478,9 +460,7 @@ class ActivitySessionStartController extends State await courseParent!.client.joinRoom( roomId, via: courseParent?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == roomId, - ) + .firstWhereOrNull((child) => child.roomId == roomId) ?.via, ); @@ -511,27 +491,21 @@ class ActivitySessionStartController extends State if (mounted) setState(() {}); }); - await activityRoom!.courseParent!.sendEvent( - { - "body": L10n.of(context).pingParticipantsNotification( - activityRoom!.client.userID!.localpart ?? - activityRoom!.client.userID!, - activityRoom!.getLocalizedDisplayname(MatrixLocals(L10n.of(context))), - ), - "msgtype": "m.text", - "pangea.activity.session_room_id": activityRoom!.id, - "pangea.activity.id": widget.activityId, - }, - ); + await activityRoom!.courseParent!.sendEvent({ + "body": L10n.of(context).pingParticipantsNotification( + activityRoom!.client.userID!.localpart ?? activityRoom!.client.userID!, + activityRoom!.getLocalizedDisplayname(MatrixLocals(L10n.of(context))), + ), + "msgtype": "m.text", + "pangea.activity.session_room_id": activityRoom!.id, + "pangea.activity.id": widget.activityId, + }); if (mounted) { setState(() {}); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - L10n.of(context).pingSent, - textAlign: TextAlign.center, - ), + content: Text(L10n.of(context).pingSent, textAlign: TextAlign.center), duration: const Duration(milliseconds: 2000), ), ); @@ -546,10 +520,7 @@ class ActivitySessionStartController extends State ); if (resp.isError && resp.error is TimeoutException) { - showDialog( - context: context, - builder: (_) => const BotJoinErrorDialog(), - ); + showDialog(context: context, builder: (_) => const BotJoinErrorDialog()); } } @@ -572,9 +543,7 @@ class ActivitySessionStartController extends State ) .first; activityRoom!.invite(BotName.byEnvironment); - await future.timeout( - const Duration(seconds: 5), - ); + await future.timeout(const Duration(seconds: 5)); } @override diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart index eb11a47fa..a1ff1a22f 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart @@ -28,10 +28,7 @@ import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; class ActivitySessionStartView extends StatelessWidget { final ActivitySessionStartController controller; - const ActivitySessionStartView( - this.controller, { - super.key, - }); + const ActivitySessionStartView(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -40,17 +37,13 @@ class ActivitySessionStartView extends StatelessWidget { backgroundColor: theme.colorScheme.primaryContainer, foregroundColor: theme.colorScheme.onPrimaryContainer, padding: const EdgeInsets.all(8.0), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), ); return StreamBuilder( - stream: Matrix.of(context) - .client - .onRoomState - .stream - .rateLimit(const Duration(seconds: 1)), + stream: Matrix.of( + context, + ).client.onRoomState.stream.rateLimit(const Duration(seconds: 1)), builder: (context, snapshot) { return Scaffold( appBar: AppBar( @@ -64,9 +57,7 @@ class ActivitySessionStartView extends StatelessWidget { overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: !FluffyThemes.isColumnMode(context) - ? const TextStyle( - fontSize: 16, - ) + ? const TextStyle(fontSize: 16) : null, ), ), @@ -108,9 +99,13 @@ class ActivitySessionStartView extends StatelessWidget { feedbackText: feedback, userId: Matrix.of(context).client.userID!, userL1: MatrixState - .pangeaController.userController.userL1Code!, + .pangeaController + .userController + .userL1Code!, userL2: MatrixState - .pangeaController.userController.userL2Code!, + .pangeaController + .userController + .userL2Code!, ), ), ); @@ -142,189 +137,184 @@ class ActivitySessionStartView extends StatelessWidget { child: controller.loading ? const Center(child: CircularProgressIndicator.adaptive()) : controller.error != null - ? Center( - child: ErrorIndicator( - message: L10n.of(context).activityNotFound, - ), - ) - : Column( - children: [ - Expanded( - child: SingleChildScrollView( - controller: controller.scrollController, - child: Container( - constraints: const BoxConstraints( - maxWidth: 600.0, + ? Center( + child: ErrorIndicator( + message: L10n.of(context).activityNotFound, + ), + ) + : Column( + children: [ + Expanded( + child: SingleChildScrollView( + controller: controller.scrollController, + child: Container( + constraints: const BoxConstraints(maxWidth: 600.0), + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + ActivitySummary( + activity: controller.activity!, + room: controller.activityRoom, + course: controller.courseParent, + showInstructions: controller.showInstructions, + toggleInstructions: + controller.toggleInstructions, + onTapParticipant: controller.selectRole, + isParticipantSelected: + controller.isParticipantSelected, + isParticipantShimmering: + controller.isParticipantShimmering, + canSelectParticipant: + controller.canSelectParticipant, + assignedRoles: controller.assignedRoles, ), - padding: const EdgeInsets.all(12.0), - child: Column( - children: [ - ActivitySummary( - activity: controller.activity!, - room: controller.activityRoom, - course: controller.courseParent, - showInstructions: - controller.showInstructions, - toggleInstructions: - controller.toggleInstructions, - onTapParticipant: controller.selectRole, - isParticipantSelected: - controller.isParticipantSelected, - isParticipantShimmering: - controller.isParticipantShimmering, - canSelectParticipant: - controller.canSelectParticipant, - assignedRoles: controller.assignedRoles, - ), - if (controller.courseParent != null && - controller.state == - SessionState.notStarted) - _ActivityStatuses( - statuses: controller.activityStatuses, - space: controller.courseParent!, - onTap: controller.joinActivityByRoomId, - ), - ], - ), - ), + if (controller.courseParent != null && + controller.state == SessionState.notStarted) + _ActivityStatuses( + statuses: controller.activityStatuses, + space: controller.courseParent!, + onTap: controller.joinActivityByRoomId, + ), + ], ), ), - AnimatedSize( - duration: FluffyThemes.animationDuration, - child: Container( - decoration: BoxDecoration( - border: Border( - top: BorderSide(color: theme.dividerColor), - ), - color: theme.colorScheme.surface, - ), - padding: const EdgeInsets.all(24.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: FluffyThemes.maxTimelineWidth, - ), - child: Column( - spacing: 16.0, - children: [ - if (controller.descriptionText != - null) - Text( - controller.descriptionText!, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), - textAlign: TextAlign.center, - ), - if (controller.state == - SessionState.notStarted) - _ActivityStartButtons( - controller, - buttonStyle, - ) - else if (controller.state == - SessionState.confirmedRole) ...[ - if (controller.courseParent != null) - ElevatedButton( - style: buttonStyle, - onPressed: controller - .canPingParticipants - ? () { - showFutureLoadingDialog( - context: context, - future: controller - .pingCourse, - ); - } - : null, - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Text( - L10n.of(context) - .pingParticipants, - ), - ], - ), - ), - if (controller - .activityRoom!.isRoomAdmin) ...[ - if (!controller.isBotRoomMember) - ElevatedButton( - style: buttonStyle, - onPressed: - controller.playWithBot, - child: Row( - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - Text( - L10n.of(context) - .playWithBot, - ), - ], - ), - ), - ElevatedButton( - style: buttonStyle, - onPressed: () { - NavigationUtil.goToSpaceRoute( - controller.activityRoom!.id, - ['invite'], + ), + ), + AnimatedSize( + duration: FluffyThemes.animationDuration, + child: Container( + decoration: BoxDecoration( + border: Border( + top: BorderSide(color: theme.dividerColor), + ), + color: theme.colorScheme.surface, + ), + padding: const EdgeInsets.all(24.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: FluffyThemes.maxTimelineWidth, + ), + child: Column( + spacing: 16.0, + children: [ + if (controller.descriptionText != null) + Text( + controller.descriptionText!, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + textAlign: TextAlign.center, + ), + if (controller.state == + SessionState.notStarted) + _ActivityStartButtons( + controller, + buttonStyle, + ) + else if (controller.state == + SessionState.confirmedRole) ...[ + if (controller.courseParent != null) + ElevatedButton( + style: buttonStyle, + onPressed: + controller.canPingParticipants + ? () { + showFutureLoadingDialog( + context: context, + future: + controller.pingCourse, + ); + } + : null, + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text( + L10n.of( context, - ); - }, - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Text( - L10n.of(context) - .inviteFriends, - ), - ], + ).pingParticipants, ), - ), - ], - ] else + ], + ), + ), + if (controller + .activityRoom! + .isRoomAdmin) ...[ + if (!controller.isBotRoomMember) ElevatedButton( style: buttonStyle, - onPressed: - controller.enableButtons - ? controller - .confirmRoleSelection - : null, + onPressed: controller.playWithBot, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - controller.activityRoom - ?.isRoomAdmin ?? - true - ? L10n.of(context).start - : L10n.of(context) - .confirm, + L10n.of( + context, + ).playWithBot, ), ], ), ), + ElevatedButton( + style: buttonStyle, + onPressed: () { + NavigationUtil.goToSpaceRoute( + controller.activityRoom!.id, + ['invite'], + context, + ); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text( + L10n.of( + context, + ).inviteFriends, + ), + ], + ), + ), ], - ), - ), + ] else + ElevatedButton( + style: buttonStyle, + onPressed: controller.enableButtons + ? controller.confirmRoleSelection + : null, + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text( + controller + .activityRoom + ?.isRoomAdmin ?? + true + ? L10n.of(context).start + : L10n.of(context).confirm, + ), + ], + ), + ), + ], ), - ], + ), ), - ), + ], ), - ], + ), ), + ], + ), ), ); }, @@ -335,10 +325,7 @@ class ActivitySessionStartView extends StatelessWidget { class _ActivityStartButtons extends StatelessWidget { final ActivitySessionStartController controller; final ButtonStyle buttonStyle; - const _ActivityStartButtons( - this.controller, - this.buttonStyle, - ); + const _ActivityStartButtons(this.controller, this.buttonStyle); @override Widget build(BuildContext context) { @@ -368,16 +355,12 @@ class _ActivityStartButtons extends StatelessWidget { style: buttonStyle, onPressed: controller.courseParent?.canInvite ?? false ? () => context.push( - "/rooms/spaces/${controller.courseParent!.id}/invite", - ) + "/rooms/spaces/${controller.courseParent!.id}/invite", + ) : null, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - L10n.of(context).inviteFriendsToCourse, - ), - ], + children: [Text(L10n.of(context).inviteFriendsToCourse)], ), ), ElevatedButton( @@ -387,11 +370,7 @@ class _ActivityStartButtons extends StatelessWidget { ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - L10n.of(context).pickDifferentActivity, - ), - ], + children: [Text(L10n.of(context).pickDifferentActivity)], ), ), ] else if (joinedActivityRoom != null) ...[ @@ -406,9 +385,7 @@ class _ActivityStartButtons extends StatelessWidget { }, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).continueText), - ], + children: [Text(L10n.of(context).continueText)], ), ), ] else ...[ @@ -436,20 +413,12 @@ class _ActivityStartButtons extends StatelessWidget { ); if (!resp.isError) { - NavigationUtil.goToSpaceRoute( - resp.result, - [], - context, - ); + NavigationUtil.goToSpaceRoute(resp.result, [], context); } }, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - L10n.of(context).joinOpenSession, - ), - ], + children: [Text(L10n.of(context).joinOpenSession)], ), ), ], @@ -480,81 +449,78 @@ class _ActivityStatuses extends StatelessWidget { ), child: Column( children: [ - ...ActivitySummaryStatus.values.map( - (status) { - final entry = statuses[status]; - if (entry!.isEmpty) { - return const SizedBox.shrink(); - } + ...ActivitySummaryStatus.values.map((status) { + final entry = statuses[status]; + if (entry!.isEmpty) { + return const SizedBox.shrink(); + } - return Padding( - padding: const EdgeInsetsGeometry.symmetric( - horizontal: 20.0, - vertical: 16.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - status.label(L10n.of(context), entry.length), - style: theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + return Padding( + padding: const EdgeInsetsGeometry.symmetric( + horizontal: 20.0, + vertical: 16.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + status.label(L10n.of(context), entry.length), + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, ), ), - ...entry.entries.map((e) { - // if user is in the room, use the room info instead of the - // room summary response to get real-time activity roles info - final roomId = e.key; - final room = - Matrix.of(context).client.getRoomById(roomId); + ), + ...entry.entries.map((e) { + // if user is in the room, use the room info instead of the + // room summary response to get real-time activity roles info + final roomId = e.key; + final room = Matrix.of(context).client.getRoomById(roomId); - final activityPlan = - room?.activityPlan ?? e.value.activityPlan; + final activityPlan = + room?.activityPlan ?? e.value.activityPlan; - // If activity is completed, show all roles, even for users who have left the - // room (like the bot). Otherwise, show only joined users with roles - Map activityRoles = - status == ActivitySummaryStatus.completed - ? (e.value.activityRoles?.roles ?? {}) - : e.value.joinedUsersWithRoles; + // If activity is completed, show all roles, even for users who have left the + // room (like the bot). Otherwise, show only joined users with roles + Map activityRoles = + status == ActivitySummaryStatus.completed + ? (e.value.activityRoles?.roles ?? {}) + : e.value.joinedUsersWithRoles; - // If the user is in the activity room and it's not completed, use the room's - // state events to determine roles to update them in real-time - if (room?.assignedRoles != null && - status != ActivitySummaryStatus.completed) { - activityRoles = room!.assignedRoles!; - } + // If the user is in the activity room and it's not completed, use the room's + // state events to determine roles to update them in real-time + if (room?.assignedRoles != null && + status != ActivitySummaryStatus.completed) { + activityRoles = room!.assignedRoles!; + } - return ListTile( - title: OpenRolesIndicator( - roles: (activityPlan?.roles.values ?? []) - .sorted((a, b) => a.id.compareTo(b.id)) - .toList(), - assignedRoles: activityRoles.values.toList(), - size: 40.0, - spacing: 8.0, - space: space, - onUserTap: (user, context) { - showMemberActionsPopupMenu( - context: context, - user: user, - ); - }, - ), - trailing: space.isRoomAdmin - ? const Icon(Icons.arrow_forward) - : null, - onTap: space.isRoomAdmin ? () => onTap(roomId) : null, - ); - }), - ], - ), - ); - }, - ), + return ListTile( + title: OpenRolesIndicator( + roles: (activityPlan?.roles.values ?? []) + .sorted((a, b) => a.id.compareTo(b.id)) + .toList(), + assignedRoles: activityRoles.values.toList(), + size: 40.0, + spacing: 8.0, + space: space, + onUserTap: (user, context) { + showMemberActionsPopupMenu( + context: context, + user: user, + ); + }, + ), + trailing: space.isRoomAdmin + ? const Icon(Icons.arrow_forward) + : null, + onTap: space.isRoomAdmin ? () => onTap(roomId) : null, + ); + }), + ], + ), + ); + }), ], ), ); diff --git a/lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart b/lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart index 6a3ee5572..274bc9161 100644 --- a/lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart +++ b/lib/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart @@ -9,19 +9,14 @@ class BotJoinErrorDialog extends StatelessWidget { @override Widget build(BuildContext context) { return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), child: Container( width: 300.0, padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ - const BotFace( - width: 100, - expression: BotExpression.addled, - ), + const BotFace(width: 100, expression: BotExpression.addled), const SizedBox(height: 8), Text( L10n.of(context).botActivityJoinFailMessage, diff --git a/lib/pangea/activity_sessions/activity_summary_widget.dart b/lib/pangea/activity_sessions/activity_summary_widget.dart index 7ef14d940..345393658 100644 --- a/lib/pangea/activity_sessions/activity_summary_widget.dart +++ b/lib/pangea/activity_sessions/activity_summary_widget.dart @@ -114,9 +114,7 @@ class ActivitySummary extends StatelessWidget { style: theme.textTheme.bodyMedium, ), Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( spacing: 4.0, mainAxisSize: MainAxisSize.min, diff --git a/lib/pangea/activity_sessions/activity_user_summaries_widget.dart b/lib/pangea/activity_sessions/activity_user_summaries_widget.dart index 39db710db..9177cd2d4 100644 --- a/lib/pangea/activity_sessions/activity_user_summaries_widget.dart +++ b/lib/pangea/activity_sessions/activity_user_summaries_widget.dart @@ -17,10 +17,7 @@ import 'package:fluffychat/widgets/avatar.dart'; class ActivityUserSummaries extends StatelessWidget { final ChatController controller; - const ActivityUserSummaries({ - super.key, - required this.controller, - }); + const ActivityUserSummaries({super.key, required this.controller}); Room get room => controller.room; @@ -36,8 +33,10 @@ class ActivityUserSummaries extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Padding( - padding: - const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0), + padding: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 4.0, + ), child: Center( child: Material( color: Theme.of(context).colorScheme.surface.withAlpha(128), @@ -51,24 +50,15 @@ class ActivityUserSummaries extends StatelessWidget { spacing: 4.0, mainAxisSize: MainAxisSize.min, children: [ - Text( - L10n.of(context).activityFinishedMessage, - ), - Text( - summary.summary, - textAlign: TextAlign.center, - ), + Text(L10n.of(context).activityFinishedMessage), + Text(summary.summary, textAlign: TextAlign.center), ], ), ), ), ), ), - const Padding( - padding: EdgeInsets.symmetric( - vertical: 8.0, - ), - ), + const Padding(padding: EdgeInsets.symmetric(vertical: 8.0)), ButtonControlledCarouselView( summary: summary, controller: controller, @@ -88,11 +78,7 @@ class ButtonControlledCarouselView extends StatelessWidget { required this.controller, }); - void _scrollToUser( - ActivityRoleModel role, - int index, - double cardWidth, - ) { + void _scrollToUser(ActivityRoleModel role, int index, double cardWidth) { controller.activityController.highlightRole(role); final scrollController = controller.activityController.carouselController; @@ -123,8 +109,8 @@ class ButtonControlledCarouselView extends StatelessWidget { @override Widget build(BuildContext context) { final room = controller.room; - final superlatives = - room.activitySummary?.analytics?.generateSuperlatives(); + final superlatives = room.activitySummary?.analytics + ?.generateSuperlatives(); final availableRoles = room.activityPlan!.roles; final assignedRoles = room.assignedRoles ?? {}; final userSummaries = summary.participants @@ -156,8 +142,8 @@ class ButtonControlledCarouselView extends StatelessWidget { itemBuilder: (context, i) { final p = userSummaries[i]; final user = room.getParticipants().firstWhereOrNull( - (u) => u.id == p.participantId, - ); + (u) => u.id == p.participantId, + ); final userRole = assignedRoles.values.firstWhere( (role) => role.userId == p.participantId, ); @@ -193,9 +179,7 @@ class ButtonControlledCarouselView extends StatelessWidget { Flexible( child: Text( "${userRole.role ?? L10n.of(context).participant} | ${user?.calcDisplayname() ?? p.participantId.localpart}", - style: const TextStyle( - fontSize: 14.0, - ), + style: const TextStyle(fontSize: 14.0), overflow: TextOverflow.ellipsis, ), ), @@ -222,18 +206,14 @@ class ButtonControlledCarouselView extends StatelessWidget { children: [ Text( p.cefrLevel, - style: const TextStyle( - fontSize: 14.0, - ), + style: const TextStyle(fontSize: 14.0), ), //const SizedBox(width: 8), if (superlatives != null && (superlatives['vocab']!.contains( p.participantId, ))) ...[ - const SuperlativeTile( - icon: Symbols.dictionary, - ), + const SuperlativeTile(icon: Symbols.dictionary), ], if (superlatives != null && (superlatives['grammar']!.contains( @@ -247,9 +227,7 @@ class ButtonControlledCarouselView extends StatelessWidget { (superlatives['xp']!.contains( p.participantId, ))) ...[ - const SuperlativeTile( - icon: Icons.star, - ), + const SuperlativeTile(icon: Icons.star), ], if (p.superlatives.isNotEmpty) ...[ Text( @@ -273,7 +251,7 @@ class ButtonControlledCarouselView extends StatelessWidget { height: 125.0, child: ValueListenableBuilder( valueListenable: controller.activityController.highlightedRole, - builder: (context, highlightedRole, __) { + builder: (context, highlightedRole, _) { return ListView.builder( shrinkWrap: true, scrollDirection: Axis.horizontal, @@ -281,8 +259,8 @@ class ButtonControlledCarouselView extends StatelessWidget { itemBuilder: (context, index) { final p = userSummaries[index]; final user = room.getParticipants().firstWhereOrNull( - (u) => u.id == p.participantId, - ); + (u) => u.id == p.participantId, + ); final userRole = assignedRoles.values.firstWhere( (role) => role.userId == p.participantId, ); @@ -315,10 +293,7 @@ class ButtonControlledCarouselView extends StatelessWidget { class SuperlativeTile extends StatelessWidget { final IconData icon; - const SuperlativeTile({ - super.key, - required this.icon, - }); + const SuperlativeTile({super.key, required this.icon}); @override Widget build(BuildContext context) { @@ -327,12 +302,7 @@ class SuperlativeTile extends StatelessWidget { children: [ Icon(icon, size: 14, color: Theme.of(context).colorScheme.onSurface), const SizedBox(width: 2), - const Text( - "1st", - style: TextStyle( - fontSize: 14.0, - ), - ), + const Text("1st", style: TextStyle(fontSize: 14.0)), ], ); } @@ -340,9 +310,7 @@ class SuperlativeTile extends StatelessWidget { class _SummaryText extends StatefulWidget { final String text; - const _SummaryText({ - required this.text, - }); + const _SummaryText({required this.text}); @override State<_SummaryText> createState() => _SummaryTextState(); @@ -364,10 +332,7 @@ class _SummaryTextState extends State<_SummaryText> { thumbVisibility: true, child: SingleChildScrollView( controller: _scrollController, - child: Text( - widget.text, - style: const TextStyle(fontSize: 14.0), - ), + child: Text(widget.text, style: const TextStyle(fontSize: 14.0)), ), ); } diff --git a/lib/pangea/activity_suggestions/activity_suggestion_card.dart b/lib/pangea/activity_suggestions/activity_suggestion_card.dart index 51378853f..db02a6c57 100644 --- a/lib/pangea/activity_suggestions/activity_suggestion_card.dart +++ b/lib/pangea/activity_suggestions/activity_suggestion_card.dart @@ -36,9 +36,7 @@ class ActivitySuggestionCard extends StatelessWidget { child: ClipRRect( borderRadius: BorderRadius.circular(12.0), child: Container( - decoration: BoxDecoration( - color: theme.colorScheme.surfaceContainer, - ), + decoration: BoxDecoration(color: theme.colorScheme.surfaceContainer), child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -61,9 +59,7 @@ class ActivitySuggestionCard extends StatelessWidget { children: [ Text( activity.title, - style: TextStyle( - fontSize: fontSize, - ), + style: TextStyle(fontSize: fontSize), maxLines: 3, overflow: TextOverflow.ellipsis, ), diff --git a/lib/pangea/activity_summary/activity_summary_analytics_model.dart b/lib/pangea/activity_summary/activity_summary_analytics_model.dart index 32344a7c4..dc182bad7 100644 --- a/lib/pangea/activity_summary/activity_summary_analytics_model.dart +++ b/lib/pangea/activity_summary/activity_summary_analytics_model.dart @@ -79,14 +79,18 @@ class ActivitySummaryAnalyticsModel { for (final userId in userIds) { //vocab - final vocabCount = - uniqueConstructCountForUser(userId, ConstructTypeEnum.vocab); + final vocabCount = uniqueConstructCountForUser( + userId, + ConstructTypeEnum.vocab, + ); allVocabs[userId] = vocabCount; if (vocabCount > maxVocab) maxVocab = vocabCount; //grammar - final grammarCount = - uniqueConstructCountForUser(userId, ConstructTypeEnum.morph); + final grammarCount = uniqueConstructCountForUser( + userId, + ConstructTypeEnum.morph, + ); allGrammars[userId] = grammarCount; if (grammarCount > maxGrammar) maxGrammar = grammarCount; @@ -135,9 +139,8 @@ class ActivitySummaryAnalyticsModel { } Map toJson() => { - for (final entry in constructs.entries) - entry.key: entry.value.toJsonList(), - }; + for (final entry in constructs.entries) entry.key: entry.value.toJsonList(), + }; } class ConstructUsage { @@ -149,9 +152,9 @@ class ConstructUsage { void increment() => timesUsed++; Map toJson() => { - ...identifier.toJson(), - 'times_used': timesUsed, - }; + ...identifier.toJson(), + 'times_used': timesUsed, + }; } class UserConstructAnalytics { @@ -161,11 +164,11 @@ class UserConstructAnalytics { UserConstructAnalytics(this.userId) : usages = {}; /// Unique constructs of a given type - Set constructsOfType(ConstructTypeEnum type) => - usages.values - .map((u) => u.identifier) - .where((id) => id.type == type) - .toSet(); + Set constructsOfType(ConstructTypeEnum type) => usages + .values + .map((u) => u.identifier) + .where((id) => id.type == type) + .toSet(); void addUsage(ConstructIdentifier id) { usages[id.string] ??= ConstructUsage(id); diff --git a/lib/pangea/activity_summary/activity_summary_model.dart b/lib/pangea/activity_summary/activity_summary_model.dart index b3e350556..1805059e5 100644 --- a/lib/pangea/activity_summary/activity_summary_model.dart +++ b/lib/pangea/activity_summary/activity_summary_model.dart @@ -31,8 +31,9 @@ class ActivitySummaryModel { requestedAt: json['requested_at'] != null ? DateTime.parse(json['requested_at']) : null, - errorAt: - json['error_at'] != null ? DateTime.parse(json['error_at']) : null, + errorAt: json['error_at'] != null + ? DateTime.parse(json['error_at']) + : null, analytics: json['analytics'] != null ? ActivitySummaryAnalyticsModel.fromJson(json['analytics']) : null, diff --git a/lib/pangea/activity_summary/activity_summary_repo.dart b/lib/pangea/activity_summary/activity_summary_repo.dart index aadfedcb9..9d67950a2 100644 --- a/lib/pangea/activity_summary/activity_summary_repo.dart +++ b/lib/pangea/activity_summary/activity_summary_repo.dart @@ -68,10 +68,7 @@ class ActivitySummaryRepo { return ActivitySummaryResponseModel.fromJson(decodedBody); } - static void delete( - String roomId, - ActivityPlanModel activity, - ) async { + static void delete(String roomId, ActivityPlanModel activity) async { final storageKey = _storageKey(roomId, activity); _cache.remove(storageKey); } diff --git a/lib/pangea/activity_summary/activity_summary_request_model.dart b/lib/pangea/activity_summary/activity_summary_request_model.dart index 606e5a56b..e019697c0 100644 --- a/lib/pangea/activity_summary/activity_summary_request_model.dart +++ b/lib/pangea/activity_summary/activity_summary_request_model.dart @@ -44,10 +44,7 @@ class ContentFeedbackModel { final String feedback; final ActivitySummaryResponseModel content; - ContentFeedbackModel({ - required this.feedback, - required this.content, - }); + ContentFeedbackModel({required this.feedback, required this.content}); factory ContentFeedbackModel.fromJson(Map json) { return ContentFeedbackModel( @@ -57,10 +54,7 @@ class ContentFeedbackModel { } Map toJson() { - return { - 'feedback': feedback, - 'content': content.toJson(), - }; + return {'feedback': feedback, 'content': content.toJson()}; } } diff --git a/lib/pangea/activity_summary/activity_summary_response_model.dart b/lib/pangea/activity_summary/activity_summary_response_model.dart index 497fa1846..4393fbd69 100644 --- a/lib/pangea/activity_summary/activity_summary_response_model.dart +++ b/lib/pangea/activity_summary/activity_summary_response_model.dart @@ -16,8 +16,9 @@ class ParticipantSummaryModel { participantId: json['participant_id'] as String, feedback: json['feedback'] as String, cefrLevel: json['cefr_level'] as String, - superlatives: - (json['superlatives'] as List).map((e) => e as String).toList(), + superlatives: (json['superlatives'] as List) + .map((e) => e as String) + .toList(), ); } diff --git a/lib/pangea/analytics_data/analytics_data_service.dart b/lib/pangea/analytics_data/analytics_data_service.dart index 5e4a4f79b..b4362c156 100644 --- a/lib/pangea/analytics_data/analytics_data_service.dart +++ b/lib/pangea/analytics_data/analytics_data_service.dart @@ -28,10 +28,7 @@ class _AnalyticsClient { final Client client; final AnalyticsDatabase database; - _AnalyticsClient({ - required this.client, - required this.database, - }); + _AnalyticsClient({required this.client, required this.database}); } class AnalyticsStreamUpdate { @@ -124,8 +121,8 @@ class AnalyticsDataService { _invalidateCaches(); final analyticsUserId = await _analyticsClientGetter.database.getUserID(); - final lastUpdated = - await _analyticsClientGetter.database.getLastUpdated(); + final lastUpdated = await _analyticsClientGetter.database + .getLastUpdated(); if (analyticsUserId != client.userID || lastUpdated == null) { await _clearDatabase(); @@ -133,8 +130,9 @@ class AnalyticsDataService { } final resp = await client.getUserProfile(client.userID!); - final analyticsProfile = - AnalyticsProfileModel.fromJson(resp.additionalProperties); + final analyticsProfile = AnalyticsProfileModel.fromJson( + resp.additionalProperties, + ); _syncController?.dispose(); _syncController = AnalyticsSyncController( @@ -168,10 +166,12 @@ class AnalyticsDataService { } Future _initMergeTable() async { - final vocab = await _analyticsClientGetter.database - .getAggregatedConstructs(ConstructTypeEnum.vocab); - final morph = await _analyticsClientGetter.database - .getAggregatedConstructs(ConstructTypeEnum.morph); + final vocab = await _analyticsClientGetter.database.getAggregatedConstructs( + ConstructTypeEnum.vocab, + ); + final morph = await _analyticsClientGetter.database.getAggregatedConstructs( + ConstructTypeEnum.morph, + ); final blocked = blockedConstructs; _mergeTable.addConstructs(vocab, blocked); @@ -225,8 +225,8 @@ class AnalyticsDataService { await _ensureInitialized(); if (_cachedDerivedStats == null || _derivedCacheVersion != _cacheVersion) { - _cachedDerivedStats = - await _analyticsClientGetter.database.getDerivedStats(); + _cachedDerivedStats = await _analyticsClientGetter.database + .getDerivedStats(); _derivedCacheVersion = _cacheVersion; } @@ -318,8 +318,8 @@ class AnalyticsDataService { Future> getAggregatedConstructs( ConstructTypeEnum type, ) async { - final combined = - await _analyticsClientGetter.database.getAggregatedConstructs(type); + final combined = await _analyticsClientGetter.database + .getAggregatedConstructs(type); final stopwatch = Stopwatch()..start(); @@ -390,8 +390,9 @@ class AnalyticsDataService { AnalyticsUpdate update, ) async { final events = []; - final addedConstructs = - update.addedConstructs.where((c) => c.category != 'other').toList(); + final addedConstructs = update.addedConstructs + .where((c) => c.category != 'other') + .toList(); final updateIds = addedConstructs.map((c) => c.identifier).toList(); final prevData = await derivedData; @@ -401,13 +402,12 @@ class AnalyticsDataService { await _ensureInitialized(); final blocked = blockedConstructs; - final newUnusedConstructs = - updateIds.where((id) => !hasUsedConstruct(id)).toSet(); + final newUnusedConstructs = updateIds + .where((id) => !hasUsedConstruct(id)) + .toSet(); _mergeTable.addConstructsByUses(addedConstructs, blocked); - await _analyticsClientGetter.database.updateLocalAnalytics( - addedConstructs, - ); + await _analyticsClientGetter.database.updateLocalAnalytics(addedConstructs); final newConstructs = await getConstructUses(updateIds); @@ -443,7 +443,11 @@ class AnalyticsDataService { await MatrixState.pangeaController.userController.addXPOffset(offset); await updateXPOffset( MatrixState - .pangeaController.userController.publicProfile!.analytics.xpOffset!, + .pangeaController + .userController + .publicProfile! + .analytics + .xpOffset!, ); } @@ -465,13 +469,7 @@ class AnalyticsDataService { final prevLevel = prevConstruct.lemmaCategory; final newLevel = entry.value.lemmaCategory; if (newLevel.xpNeeded > prevLevel.xpNeeded) { - events.add( - ConstructLevelUpEvent( - entry.key, - newLevel, - update.targetID, - ), - ); + events.add(ConstructLevelUpEvent(entry.key, newLevel, update.targetID)); } } @@ -492,22 +490,18 @@ class AnalyticsDataService { _invalidateCaches(); final blocked = blockedConstructs; for (final event in events) { - _mergeTable.addConstructsByUses( - event.content.uses, - blocked, - ); + _mergeTable.addConstructsByUses(event.content.uses, blocked); } await _analyticsClientGetter.database.updateServerAnalytics(events); } - Future updateBlockedConstructs( - ConstructIdentifier constructId, - ) async { + Future updateBlockedConstructs(ConstructIdentifier constructId) async { await _ensureInitialized(); _mergeTable.removeConstruct(constructId); - final construct = - await _analyticsClientGetter.database.getConstructUse([constructId]); + final construct = await _analyticsClientGetter.database.getConstructUse([ + constructId, + ]); final derived = await derivedData; final newXP = derived.totalXP - construct.points; @@ -523,10 +517,7 @@ class AnalyticsDataService { _invalidateCaches(); updateDispatcher.sendConstructAnalyticsUpdate( - AnalyticsUpdate( - [], - blockedConstruct: constructId, - ), + AnalyticsUpdate([], blockedConstruct: constructId), ); } diff --git a/lib/pangea/analytics_data/analytics_database.dart b/lib/pangea/analytics_data/analytics_database.dart index 478f16155..91515bee2 100644 --- a/lib/pangea/analytics_data/analytics_database.dart +++ b/lib/pangea/analytics_data/analytics_database.dart @@ -122,12 +122,8 @@ class AnalyticsDatabase with DatabaseFileStorage { _lastEventTimestampBox = _collection.openBox( _lastEventTimestampBoxName, ); - _serverConstructsBox = _collection.openBox( - _serverConstructsBoxName, - ); - _localConstructsBox = _collection.openBox( - _localConstructsBoxName, - ); + _serverConstructsBox = _collection.openBox(_serverConstructsBoxName); + _localConstructsBox = _collection.openBox(_localConstructsBoxName); _aggregatedServerVocabConstructsBox = _collection.openBox( _aggregatedServerVocabConstructsBoxName, ); @@ -140,9 +136,7 @@ class AnalyticsDatabase with DatabaseFileStorage { _aggregatedLocalMorphConstructsBox = _collection.openBox( _aggregatedLocalMorphConstructsBoxName, ); - _derivedStatsBox = _collection.openBox( - _derivedStatsBoxName, - ); + _derivedStatsBox = _collection.openBox(_derivedStatsBoxName); } Future delete() async { @@ -185,8 +179,9 @@ class AnalyticsDatabase with DatabaseFileStorage { } Future getLastEventTimestamp() async { - final timestampString = - await _lastEventTimestampBox.get('last_event_timestamp'); + final timestampString = await _lastEventTimestampBox.get( + 'last_event_timestamp', + ); if (timestampString == null) return null; return DateTime.parse(timestampString); } @@ -195,9 +190,7 @@ class AnalyticsDatabase with DatabaseFileStorage { final raw = await _derivedStatsBox.get('derived_stats'); return raw == null ? DerivedAnalyticsDataModel() - : DerivedAnalyticsDataModel.fromJson( - Map.from(raw), - ); + : DerivedAnalyticsDataModel.fromJson(Map.from(raw)); } Future> getUses({ @@ -268,9 +261,7 @@ class AnalyticsDatabase with DatabaseFileStorage { for (final rawList in localValues) { if (rawList == null) continue; for (final raw in rawList) { - final use = OneConstructUse.fromJson( - Map.from(raw), - ); + final use = OneConstructUse.fromJson(Map.from(raw)); uses.add(use); } } @@ -283,11 +274,7 @@ class AnalyticsDatabase with DatabaseFileStorage { if (serverValues == null) return []; for (final entry in serverValues) { - uses.add( - OneConstructUse.fromJson( - Map.from(entry), - ), - ); + uses.add(OneConstructUse.fromJson(Map.from(entry))); } return uses; } @@ -309,9 +296,7 @@ class AnalyticsDatabase with DatabaseFileStorage { return [...serverKeys, ...localKeys]; } - Future getConstructUse( - List ids, - ) async { + Future getConstructUse(List ids) async { assert(ids.isNotEmpty); final ConstructUses construct = ConstructUses( @@ -332,16 +317,12 @@ class AnalyticsDatabase with DatabaseFileStorage { final serverRaw = await serverBox.get(key); if (serverRaw != null) { - server = ConstructUses.fromJson( - Map.from(serverRaw), - ); + server = ConstructUses.fromJson(Map.from(serverRaw)); } final localRaw = await localBox.get(key); if (localRaw != null) { - local = ConstructUses.fromJson( - Map.from(localRaw), - ); + local = ConstructUses.fromJson(Map.from(localRaw)); } if (server != null) construct.merge(server); @@ -370,9 +351,7 @@ class AnalyticsDatabase with DatabaseFileStorage { } /// Group uses by aggregate key - Map> _groupUses( - List uses, - ) { + Map> _groupUses(List uses) { final Map> grouped = {}; for (final u in uses) { final key = u.identifier.storageKey; @@ -438,10 +417,7 @@ class AnalyticsDatabase with DatabaseFileStorage { .map((e) => ConstructUses.fromJson(Map.from(e!))) .toList(); - final serverAgg = Map.fromIterables( - serverKeys, - serverConstructs, - ); + final serverAgg = Map.fromIterables(serverKeys, serverConstructs); if (localKeys.isEmpty) { combined = serverAgg; @@ -451,10 +427,7 @@ class AnalyticsDatabase with DatabaseFileStorage { .map((e) => ConstructUses.fromJson(Map.from(e!))) .toList(); - final localAgg = Map.fromIterables( - localKeys, - localConstructs, - ); + final localAgg = Map.fromIterables(localKeys, localConstructs); combined = Map.from(serverAgg); for (final entry in localAgg.entries) { @@ -472,19 +445,14 @@ class AnalyticsDatabase with DatabaseFileStorage { } stopwatch.stop(); - Logs().i( - "Combining aggregates took ${stopwatch.elapsedMilliseconds} ms", - ); + Logs().i("Combining aggregates took ${stopwatch.elapsedMilliseconds} ms"); return combined.values.toList(); } Future updateUserID(String userID) { return _transaction(() async { - await _lastEventTimestampBox.put( - 'user_id', - userID, - ); + await _lastEventTimestampBox.put('user_id', userID); }); } @@ -501,18 +469,12 @@ class AnalyticsDatabase with DatabaseFileStorage { return _transaction(() async { final stats = await getDerivedStats(); final updatedStats = stats.copyWith(offset: offset); - await _derivedStatsBox.put( - 'derived_stats', - updatedStats.toJson(), - ); + await _derivedStatsBox.put('derived_stats', updatedStats.toJson()); }); } Future updateDerivedStats(DerivedAnalyticsDataModel newStats) => - _derivedStatsBox.put( - 'derived_stats', - newStats.toJson(), - ); + _derivedStatsBox.put('derived_stats', newStats.toJson()); Future updateServerAnalytics( List events, @@ -600,9 +562,7 @@ class AnalyticsDatabase with DatabaseFileStorage { ); } - Future updateLocalAnalytics( - List uses, - ) async { + Future updateLocalAnalytics(List uses) async { if (uses.isEmpty) return; final stopwatch = Stopwatch()..start(); diff --git a/lib/pangea/analytics_data/analytics_database_builder.dart b/lib/pangea/analytics_data/analytics_database_builder.dart index 7f698aec8..bed8828d1 100644 --- a/lib/pangea/analytics_data/analytics_database_builder.dart +++ b/lib/pangea/analytics_data/analytics_database_builder.dart @@ -68,8 +68,9 @@ Future _constructDatabase(String name) async { // fix dlopen for old Android await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions(); // import the SQLite / SQLCipher shared objects / dynamic libraries - final factory = - createDatabaseFactoryFfi(ffiInit: SQfLiteEncryptionHelper.ffiInit); + final factory = createDatabaseFactoryFfi( + ffiInit: SQfLiteEncryptionHelper.ffiInit, + ); // required for [getDatabasesPath] databaseFactory = factory; diff --git a/lib/pangea/analytics_data/analytics_sync_controller.dart b/lib/pangea/analytics_data/analytics_sync_controller.dart index f895c5657..6d51507a6 100644 --- a/lib/pangea/analytics_data/analytics_sync_controller.dart +++ b/lib/pangea/analytics_data/analytics_sync_controller.dart @@ -16,10 +16,7 @@ class AnalyticsSyncController { StreamSubscription? _subscription; - AnalyticsSyncController({ - required this.client, - required this.dataService, - }); + AnalyticsSyncController({required this.client, required this.dataService}); void start() { _subscription ??= client.onSync.stream.listen(_onSync); @@ -34,11 +31,12 @@ class AnalyticsSyncController { final analyticsRoom = _getAnalyticsRoom(); if (analyticsRoom == null) return; - final events = - update.rooms?.join?[analyticsRoom.id]?.timeline?.events?.where( - (e) => - e.type == PangeaEventTypes.construct && e.senderId == client.userID, - ); + final events = update.rooms?.join?[analyticsRoom.id]?.timeline?.events + ?.where( + (e) => + e.type == PangeaEventTypes.construct && + e.senderId == client.userID, + ); if (events == null || events.isEmpty) return; @@ -67,7 +65,8 @@ class AnalyticsSyncController { final roomUpdate = update.rooms?.join?[analyticsRoomId]; if (roomUpdate == null) return false; - final hasAnalyticsEvent = roomUpdate.timeline?.events?.any( + final hasAnalyticsEvent = + roomUpdate.timeline?.events?.any( (e) => e.type == PangeaEventTypes.construct && e.senderId == client.userID, diff --git a/lib/pangea/analytics_data/analytics_update_dispatcher.dart b/lib/pangea/analytics_data/analytics_update_dispatcher.dart index acf7052fc..a89586944 100644 --- a/lib/pangea/analytics_data/analytics_update_dispatcher.dart +++ b/lib/pangea/analytics_data/analytics_update_dispatcher.dart @@ -11,10 +11,7 @@ class LevelUpdate { final int prevLevel; final int newLevel; - LevelUpdate({ - required this.prevLevel, - required this.newLevel, - }); + LevelUpdate({required this.prevLevel, required this.newLevel}); } class AnalyticsUpdate { @@ -22,11 +19,7 @@ class AnalyticsUpdate { final ConstructIdentifier? blockedConstruct; final String? targetID; - AnalyticsUpdate( - this.addedConstructs, { - this.blockedConstruct, - this.targetID, - }); + AnalyticsUpdate(this.addedConstructs, {this.blockedConstruct, this.targetID}); } class ConstructLevelUpdate { @@ -63,8 +56,10 @@ class AnalyticsUpdateDispatcher { StreamController.broadcast(); final StreamController> - _lemmaInfoUpdateStream = StreamController< - MapEntry>.broadcast(); + _lemmaInfoUpdateStream = + StreamController< + MapEntry + >.broadcast(); AnalyticsUpdateDispatcher(this.dataService); @@ -77,23 +72,18 @@ class AnalyticsUpdateDispatcher { _lemmaInfoUpdateStream.close(); } - Stream lemmaUpdateStream( - ConstructIdentifier constructId, - ) => + Stream lemmaUpdateStream(ConstructIdentifier constructId) => _lemmaInfoUpdateStream.stream .where((update) => update.key == constructId) .map((update) => update.value); - void sendActivityAnalyticsUpdate( - String? activityAnalytics, - ) => + void sendActivityAnalyticsUpdate(String? activityAnalytics) => activityAnalyticsStream.add(activityAnalytics); void sendLemmaInfoUpdate( ConstructIdentifier constructId, UserSetLemmaInfo lemmaInfo, - ) => - _lemmaInfoUpdateStream.add(MapEntry(constructId, lemmaInfo)); + ) => _lemmaInfoUpdateStream.add(MapEntry(constructId, lemmaInfo)); Future sendConstructAnalyticsUpdate( AnalyticsUpdate analyticsUpdate, @@ -129,10 +119,7 @@ class AnalyticsUpdateDispatcher { void _onLevelUp(final int lowerLevel, final int upperLevel) { levelUpdateStream.add( - LevelUpdate( - prevLevel: lowerLevel, - newLevel: upperLevel, - ), + LevelUpdate(prevLevel: lowerLevel, newLevel: upperLevel), ); } @@ -150,10 +137,7 @@ class AnalyticsUpdateDispatcher { } void _onXPGained(int points, String? targetID) { - final update = AnalyticsStreamUpdate( - points: points, - targetID: targetID, - ); + final update = AnalyticsStreamUpdate(points: points, targetID: targetID); constructUpdateStream.add(update); } @@ -172,9 +156,7 @@ class AnalyticsUpdateDispatcher { } void _onBlockedConstruct(ConstructIdentifier constructId) { - final update = AnalyticsStreamUpdate( - blockedConstruct: constructId, - ); + final update = AnalyticsStreamUpdate(blockedConstruct: constructId); constructUpdateStream.add(update); } diff --git a/lib/pangea/analytics_data/analytics_update_events.dart b/lib/pangea/analytics_data/analytics_update_events.dart index 2e7a02ce6..a202baa06 100644 --- a/lib/pangea/analytics_data/analytics_update_events.dart +++ b/lib/pangea/analytics_data/analytics_update_events.dart @@ -18,11 +18,7 @@ class ConstructLevelUpEvent extends AnalyticsUpdateEvent { final ConstructIdentifier constructId; final ConstructLevelEnum level; final String? targetID; - ConstructLevelUpEvent( - this.constructId, - this.level, - this.targetID, - ); + ConstructLevelUpEvent(this.constructId, this.level, this.targetID); } class XPGainedEvent extends AnalyticsUpdateEvent { diff --git a/lib/pangea/analytics_data/analytics_update_service.dart b/lib/pangea/analytics_data/analytics_update_service.dart index e0cd2d66c..f133d199b 100644 --- a/lib/pangea/analytics_data/analytics_update_service.dart +++ b/lib/pangea/analytics_data/analytics_update_service.dart @@ -48,14 +48,13 @@ class AnalyticsUpdateService { } Future onUpdateLanguages(LanguageUpdate update) async { - await sendLocalAnalyticsToAnalyticsRoom( - l2Override: update.prevTargetLang, - ); + await sendLocalAnalyticsToAnalyticsRoom(l2Override: update.prevTargetLang); await dataService.reinitialize(); final data = await dataService.derivedData; - MatrixState.pangeaController.userController - .updateAnalyticsProfile(level: data.level); + MatrixState.pangeaController.userController.updateAnalyticsProfile( + level: data.level, + ); } Future addAnalytics( @@ -64,10 +63,7 @@ class AnalyticsUpdateService { bool forceUpdate = false, }) async { await dataService.updateDispatcher.sendConstructAnalyticsUpdate( - AnalyticsUpdate( - newConstructs, - targetID: targetID, - ), + AnalyticsUpdate(newConstructs, targetID: targetID), ); final localConstructCount = await dataService.getLocalConstructCount(); @@ -101,9 +97,7 @@ class AnalyticsUpdateService { e: err, m: "Failed to update analytics", s: s, - data: { - "l2Override": l2Override, - }, + data: {"l2Override": l2Override}, ); } finally { _updateCompleter?.complete(); @@ -145,10 +139,7 @@ class AnalyticsUpdateService { final current = analyticsRoom.analyticsSettings; final blockedConstructs = current.blockedConstructs; final updated = current.copyWith( - blockedConstructs: { - ...blockedConstructs, - constructId, - }, + blockedConstructs: {...blockedConstructs, constructId}, ); await analyticsRoom.setAnalyticsSettings(updated); @@ -175,11 +166,7 @@ class AnalyticsUpdateService { await analyticsRoom.setUserSetLemmaInfo(constructId, updated); } catch (err, s) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: err, - data: userLemmaInfo.toJson(), - s: s, - ); + ErrorHandler.logError(e: err, data: userLemmaInfo.toJson(), s: s); } } } diff --git a/lib/pangea/analytics_data/analytics_updater_mixin.dart b/lib/pangea/analytics_data/analytics_updater_mixin.dart index d24628010..11b201e47 100644 --- a/lib/pangea/analytics_data/analytics_updater_mixin.dart +++ b/lib/pangea/analytics_data/analytics_updater_mixin.dart @@ -16,10 +16,11 @@ mixin AnalyticsUpdater on State { void initState() { super.initState(); final updater = Matrix.of(context).analyticsDataService.updateDispatcher; - _analyticsSubscription = - updater.constructUpdateStream.stream.listen(_onAnalyticsUpdate); - _constructLevelSubscription = - updater.constructLevelUpdateStream.stream.listen(_onConstructLevelUp); + _analyticsSubscription = updater.constructUpdateStream.stream.listen( + _onAnalyticsUpdate, + ); + _constructLevelSubscription = updater.constructLevelUpdateStream.stream + .listen(_onConstructLevelUp); } @override @@ -32,11 +33,9 @@ mixin AnalyticsUpdater on State { Future addAnalytics( List constructs, String? targetId, - ) => - Matrix.of(context).analyticsDataService.updateService.addAnalytics( - targetId, - constructs, - ); + ) => Matrix.of( + context, + ).analyticsDataService.updateService.addAnalytics(targetId, constructs); void _onAnalyticsUpdate(AnalyticsStreamUpdate update) { if (update.targetID != null) { diff --git a/lib/pangea/analytics_data/derived_analytics_data_model.dart b/lib/pangea/analytics_data/derived_analytics_data_model.dart index 384af243e..0d97a2736 100644 --- a/lib/pangea/analytics_data/derived_analytics_data_model.dart +++ b/lib/pangea/analytics_data/derived_analytics_data_model.dart @@ -7,10 +7,8 @@ class DerivedAnalyticsDataModel { final int _totalXP; final int offset; - DerivedAnalyticsDataModel({ - int totalXP = 0, - this.offset = 0, - }) : _totalXP = totalXP; + DerivedAnalyticsDataModel({int totalXP = 0, this.offset = 0}) + : _totalXP = totalXP; int get totalXP => _totalXP + offset; @@ -56,10 +54,7 @@ class DerivedAnalyticsDataModel { } else { ErrorHandler.logError( e: "Calculated level in Nan or Infinity", - data: { - "totalXP": totalXP, - "level": doubleScore, - }, + data: {"totalXP": totalXP, "level": doubleScore}, ); return 1; } @@ -72,9 +67,7 @@ class DerivedAnalyticsDataModel { xp += u.xp; } - return copyWith( - totalXP: xp, - ); + return copyWith(totalXP: xp); } DerivedAnalyticsDataModel merge(DerivedAnalyticsDataModel other) { @@ -84,10 +77,7 @@ class DerivedAnalyticsDataModel { ); } - DerivedAnalyticsDataModel copyWith({ - int? totalXP, - int? offset, - }) { + DerivedAnalyticsDataModel copyWith({int? totalXP, int? offset}) { return DerivedAnalyticsDataModel( totalXP: totalXP ?? this.totalXP, offset: offset ?? this.offset, @@ -95,14 +85,10 @@ class DerivedAnalyticsDataModel { } factory DerivedAnalyticsDataModel.fromJson(Map map) { - return DerivedAnalyticsDataModel( - totalXP: map['total_xp'] ?? 0, - ); + return DerivedAnalyticsDataModel(totalXP: map['total_xp'] ?? 0); } Map toJson() { - return { - 'total_xp': _totalXP, - }; + return {'total_xp': _totalXP}; } } diff --git a/lib/pangea/analytics_data/level_up_analytics_service.dart b/lib/pangea/analytics_data/level_up_analytics_service.dart index 532a5252c..9210bf802 100644 --- a/lib/pangea/analytics_data/level_up_analytics_service.dart +++ b/lib/pangea/analytics_data/level_up_analytics_service.dart @@ -44,10 +44,12 @@ class LevelUpAnalyticsService { final response = await ConstructRepo.generateConstructSummary(request); final summary = response.summary; - summary.levelVocabConstructs = - dataService.uniqueConstructsByType(ConstructTypeEnum.vocab); - summary.levelGrammarConstructs = - dataService.uniqueConstructsByType(ConstructTypeEnum.morph); + summary.levelVocabConstructs = dataService.uniqueConstructsByType( + ConstructTypeEnum.vocab, + ); + summary.levelGrammarConstructs = dataService.uniqueConstructsByType( + ConstructTypeEnum.morph, + ); return summary; } @@ -101,10 +103,7 @@ class LevelUpAnalyticsService { ErrorHandler.logError( e: e, s: s, - data: { - 'roomId': entry.key, - 'eventId': eventId, - }, + data: {'roomId': entry.key, 'eventId': eventId}, ); } } diff --git a/lib/pangea/analytics_details_popup/analytics_details_popup.dart b/lib/pangea/analytics_details_popup/analytics_details_popup.dart index 7bfa27c6a..962ba9e63 100644 --- a/lib/pangea/analytics_details_popup/analytics_details_popup.dart +++ b/lib/pangea/analytics_details_popup/analytics_details_popup.dart @@ -28,11 +28,7 @@ import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_reques import 'package:fluffychat/widgets/matrix.dart'; class ConstructAnalyticsView extends StatefulWidget { - const ConstructAnalyticsView({ - super.key, - required this.view, - this.construct, - }); + const ConstructAnalyticsView({super.key, required this.view, this.construct}); final ConstructTypeEnum view; final ConstructIdentifier? construct; @@ -82,10 +78,7 @@ class ConstructAnalyticsViewState extends State { } Future _setAnalyticsData() async { - final future = [ - _setMorphs(), - _setVocab(), - ]; + final future = [_setMorphs(), _setVocab()]; await Future.wait(future); } @@ -109,17 +102,16 @@ class ConstructAnalyticsViewState extends State { Future _setVocab() async { try { final analyticsService = Matrix.of(context).analyticsDataService; - final data = await analyticsService - .getAggregatedConstructs(ConstructTypeEnum.vocab); + final data = await analyticsService.getAggregatedConstructs( + ConstructTypeEnum.vocab, + ); vocab = data.values.toList(); - vocab!.sort( - (a, b) { - final normalizedA = removeDiacritics(a.lemma).toLowerCase(); - final normalizedB = removeDiacritics(b.lemma).toLowerCase(); - return normalizedA.compareTo(normalizedB); - }, - ); + vocab!.sort((a, b) { + final normalizedA = removeDiacritics(a.lemma).toLowerCase(); + final normalizedB = removeDiacritics(b.lemma).toLowerCase(); + return normalizedA.compareTo(normalizedB); + }); } finally { if (mounted) setState(() {}); } @@ -201,27 +193,26 @@ class ConstructAnalyticsViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ if (widget.construct == null) - LearningProgressIndicators( - selected: widget.view.indicator, - ), + LearningProgressIndicators(selected: widget.view.indicator), Expanded( child: widget.view == ConstructTypeEnum.morph ? widget.construct == null - ? MorphAnalyticsListView(controller: this) - : MorphDetailsView(constructId: widget.construct!) + ? MorphAnalyticsListView(controller: this) + : MorphDetailsView(constructId: widget.construct!) : widget.construct == null - ? VocabAnalyticsListView(controller: this) - : VocabDetailsView( - constructId: widget.construct!, - controller: this, - ), + ? VocabAnalyticsListView(controller: this) + : VocabDetailsView( + constructId: widget.construct!, + controller: this, + ), ), ], ), ), ), - floatingActionButton: - widget.construct == null ? _PracticeButton(view: widget.view) : null, + floatingActionButton: widget.construct == null + ? _PracticeButton(view: widget.view) + : null, ); } } @@ -233,10 +224,7 @@ class _PracticeButton extends StatelessWidget { void _showSnackbar(BuildContext context, String message) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - behavior: SnackBarBehavior.floating, - ), + SnackBar(content: Text(message), behavior: SnackBarBehavior.floating), ); } @@ -245,14 +233,13 @@ class _PracticeButton extends StatelessWidget { final analyticsService = Matrix.of(context).analyticsDataService; if (analyticsService.isInitializing) { return FloatingActionButton.extended( - onPressed: () => _showSnackbar( - context, - L10n.of(context).loadingPleaseWait, - ), + onPressed: () => + _showSnackbar(context, L10n.of(context).loadingPleaseWait), label: Text(view.practiceButtonText(context)), backgroundColor: Theme.of(context).colorScheme.surface, - foregroundColor: - Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5), + foregroundColor: Theme.of( + context, + ).colorScheme.onSurface.withValues(alpha: 0.5), ); } @@ -262,22 +249,17 @@ class _PracticeButton extends StatelessWidget { return FloatingActionButton.extended( onPressed: enabled ? () => context.go("/rooms/analytics/${view.name}/practice") - : () => _showSnackbar( - context, - L10n.of(context).notEnoughToPractice, - ), - backgroundColor: - enabled ? null : Theme.of(context).colorScheme.surfaceContainer, + : () => _showSnackbar(context, L10n.of(context).notEnoughToPractice), + backgroundColor: enabled + ? null + : Theme.of(context).colorScheme.surfaceContainer, foregroundColor: enabled ? null : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5), label: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon( - enabled ? Symbols.fitness_center : Icons.lock_outline, - size: 18, - ), + Icon(enabled ? Symbols.fitness_center : Icons.lock_outline, size: 18), const SizedBox(width: 4), Text(view.practiceButtonText(context)), ], diff --git a/lib/pangea/analytics_details_popup/analytics_details_usage_content.dart b/lib/pangea/analytics_details_popup/analytics_details_usage_content.dart index dd258718d..284c6e391 100644 --- a/lib/pangea/analytics_details_popup/analytics_details_usage_content.dart +++ b/lib/pangea/analytics_details_popup/analytics_details_usage_content.dart @@ -8,10 +8,7 @@ import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart'; class AnalyticsDetailsUsageContent extends StatelessWidget { final ConstructUses construct; - const AnalyticsDetailsUsageContent({ - required this.construct, - super.key, - }); + const AnalyticsDetailsUsageContent({required this.construct, super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart b/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart index 794cdbe5f..84c8bc369 100644 --- a/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart +++ b/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart @@ -14,10 +14,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class ConstructXPProgressBar extends StatelessWidget { final ConstructIdentifier construct; - const ConstructXPProgressBar({ - required this.construct, - super.key, - }); + const ConstructXPProgressBar({required this.construct, super.key}); @override Widget build(BuildContext context) { @@ -56,8 +53,9 @@ class ConstructXPProgressBar extends StatelessWidget { height: 20.0, widthPercent: progress, barColor: AppConfig.goldLight, - backgroundColor: - Theme.of(context).colorScheme.secondaryContainer, + backgroundColor: Theme.of( + context, + ).colorScheme.secondaryContainer, ), ], ); diff --git a/lib/pangea/analytics_details_popup/lemma_usage_dots.dart b/lib/pangea/analytics_details_popup/lemma_usage_dots.dart index 7ccd9849f..3063a2d61 100644 --- a/lib/pangea/analytics_details_popup/lemma_usage_dots.dart +++ b/lib/pangea/analytics_details_popup/lemma_usage_dots.dart @@ -29,13 +29,11 @@ class LemmaUsageDots extends StatelessWidget { // If the use type matches the given category, save to list // Usage with positive XP is saved as true, else false if (category == use.useType.skillsEnumType) { - useList.add( - switch (use.xp) { - > 0 => AppConfig.success, - < 0 => Colors.red, - _ => Colors.grey[400]!, - }, - ); + useList.add(switch (use.xp) { + > 0 => AppConfig.success, + < 0 => Colors.red, + _ => Colors.grey[400]!, + }); } } return useList; @@ -49,10 +47,7 @@ class LemmaUsageDots extends StatelessWidget { Container( width: 15.0, height: 15.0, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: color, shape: BoxShape.circle), ), ); } @@ -65,11 +60,7 @@ class LemmaUsageDots extends StatelessWidget { leading: Tooltip( triggerMode: TooltipTriggerMode.tap, message: tooltip, - child: Icon( - icon, - size: 24, - color: textColor.withValues(alpha: 0.7), - ), + child: Icon(icon, size: 24, color: textColor.withValues(alpha: 0.7)), ), title: dots.isEmpty ? Text( @@ -80,11 +71,7 @@ class LemmaUsageDots extends StatelessWidget { color: textColor.withAlpha(100), ), ) - : Wrap( - spacing: 3, - runSpacing: 5, - children: dots, - ), + : Wrap(spacing: 3, runSpacing: 5, children: dots), ); } } diff --git a/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart b/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart index f18e9aeda..761ffda8c 100644 --- a/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart +++ b/lib/pangea/analytics_details_popup/lemma_use_example_messages.dart @@ -6,6 +6,7 @@ import 'package:collection/collection.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/constructs/construct_level_enum.dart'; @@ -16,10 +17,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class LemmaUseExampleMessages extends StatelessWidget { final ConstructUses construct; - const LemmaUseExampleMessages({ - super.key, - required this.construct, - }); + const LemmaUseExampleMessages({super.key, required this.construct}); Future> _getExampleMessages() async { final List examples = []; @@ -62,7 +60,8 @@ class LemmaUseExampleMessages extends StatelessWidget { final PangeaMessageEvent pangeaMessageEvent = PangeaMessageEvent( event: event, timeline: timeline!, - ownMessage: event.senderId == + ownMessage: + event.senderId == MatrixState.pangeaController.matrixState.client.userID, ); final tokens = pangeaMessageEvent.messageDisplayRepresentation?.tokens; @@ -113,7 +112,8 @@ class LemmaUseExampleMessages extends StatelessWidget { text: TextSpan( style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * + fontSize: + AppSettings.fontSizeFactor.value * AppConfig.messageFontSize, ), children: example.textSpans, @@ -127,9 +127,7 @@ class LemmaUseExampleMessages extends StatelessWidget { return const Column( children: [ SizedBox(height: 10), - CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), + CircularProgressIndicator.adaptive(strokeWidth: 2), ], ); } @@ -177,34 +175,18 @@ class ExampleMessage { final gapSize = tokenIndex - globalTokenIndex; if (gapSize <= tokenWindowSize) { // if the gap is less than the window size, add all the gap tokens - tokenWindows.add( - TokenWindow( - globalTokenIndex, - tokenIndex, - false, - ), - ); + tokenWindows.add(TokenWindow(globalTokenIndex, tokenIndex, false)); } else { // otherwise, add the window size tokens preceding the bolded token tokenWindows.add( - TokenWindow( - tokenIndex - tokenWindowSize, - tokenIndex, - false, - ), + TokenWindow(tokenIndex - tokenWindowSize, tokenIndex, false), ); } } globalTokenIndex = tokenIndex; - tokenWindows.add( - TokenWindow( - tokenIndex, - tokenIndex + 1, - true, - ), - ); + tokenWindows.add(TokenWindow(tokenIndex, tokenIndex + 1, true)); globalTokenIndex = tokenIndex + 1; @@ -213,13 +195,7 @@ class ExampleMessage { // remaining tokens (up to the max window size) if (globalTokenIndex >= tokens.length) break; final endIndex = min(tokens.length, globalTokenIndex + tokenWindowSize); - tokenWindows.add( - TokenWindow( - globalTokenIndex, - endIndex, - false, - ), - ); + tokenWindows.add(TokenWindow(globalTokenIndex, endIndex, false)); break; } @@ -227,13 +203,7 @@ class ExampleMessage { final nextToken = _boldedTokens[i + 1]; final nextTokenIndex = tokens.indexOf(nextToken); final endIndex = min(nextTokenIndex, globalTokenIndex + tokenWindowSize); - tokenWindows.add( - TokenWindow( - globalTokenIndex, - endIndex, - false, - ), - ); + tokenWindows.add(TokenWindow(globalTokenIndex, endIndex, false)); globalTokenIndex = endIndex; } @@ -263,7 +233,8 @@ class ExampleMessage { ); tokenPointer = window.endTokenIndex; - characterPointer = tokens[window.endTokenIndex - 1].text.offset + + characterPointer = + tokens[window.endTokenIndex - 1].text.offset + tokens[window.endTokenIndex - 1].text.length; } diff --git a/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart b/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart index 9be8139e0..e2ccde7e7 100644 --- a/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart +++ b/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart @@ -21,10 +21,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class MorphAnalyticsListView extends StatelessWidget { final ConstructAnalyticsViewState controller; - const MorphAnalyticsListView({ - required this.controller, - super.key, - }); + const MorphAnalyticsListView({required this.controller, super.key}); @override Widget build(BuildContext context) { @@ -37,9 +34,7 @@ class MorphAnalyticsListView extends StatelessWidget { padding: padding, child: Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - DownloadAnalyticsButton(), - ], + children: [DownloadAnalyticsButton()], ), ), Expanded( @@ -57,23 +52,20 @@ class MorphAnalyticsListView extends StatelessWidget { // Morph feature boxes SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - final feature = controller.features[index]; - return feature.displayTags.isNotEmpty - ? Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: MorphFeatureBox( - morphFeature: feature.feature, - allTags: controller.morphs - .getDisplayTags(feature.feature) - .toSet(), - ), - ) - : const SizedBox.shrink(); - }, - childCount: controller.features.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final feature = controller.features[index]; + return feature.displayTags.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: MorphFeatureBox( + morphFeature: feature.feature, + allTags: controller.morphs + .getDisplayTags(feature.feature) + .toSet(), + ), + ) + : const SizedBox.shrink(); + }, childCount: controller.features.length), ), const SliverToBoxAdapter(child: SizedBox(height: 75.0)), ], @@ -140,31 +132,29 @@ class MorphFeatureBox extends StatelessWidget { alignment: WrapAlignment.center, spacing: 16.0, runSpacing: 16.0, - children: allTags.map( - (morphTag) { - final id = ConstructIdentifier( - lemma: morphTag, - type: ConstructTypeEnum.morph, - category: morphFeature, - ); + children: allTags.map((morphTag) { + final id = ConstructIdentifier( + lemma: morphTag, + type: ConstructTypeEnum.morph, + category: morphFeature, + ); - return FutureBuilder( - future: analyticsService.getConstructUse(id), - builder: (context, snapshot) => MorphTagChip( - morphFeature: morphFeature, - morphTag: morphTag, - constructAnalytics: snapshot.data, - onTap: () { - AnalyticsNavigationUtil.navigateToAnalytics( - context: context, - view: ProgressIndicatorEnum.morphsUsed, - construct: id, - ); - }, - ), - ); - }, - ).toList(), + return FutureBuilder( + future: analyticsService.getConstructUse(id), + builder: (context, snapshot) => MorphTagChip( + morphFeature: morphFeature, + morphTag: morphTag, + constructAnalytics: snapshot.data, + onTap: () { + AnalyticsNavigationUtil.navigateToAnalytics( + context: context, + view: ProgressIndicatorEnum.morphsUsed, + construct: id, + ); + }, + ), + ); + }).toList(), ), ), ], @@ -197,7 +187,7 @@ class MorphTagChip extends StatelessWidget { final theme = Theme.of(context); final unlocked = constructAnalytics != null && constructAnalytics!.numTotalUses > 0 || - Matrix.of(context).client.userID == Environment.supportUserId; + Matrix.of(context).client.userID == Environment.supportUserId; return Material( type: MaterialType.transparency, @@ -222,10 +212,7 @@ class MorphTagChip extends StatelessWidget { : null, color: unlocked ? null : theme.disabledColor, ), - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 8.0, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), child: Row( mainAxisSize: MainAxisSize.min, spacing: 8.0, @@ -245,10 +232,7 @@ class MorphTagChip extends StatelessWidget { morphTag: morphTag, ), ) - : const Icon( - Icons.lock, - color: Colors.white, - ), + : const Icon(Icons.lock, color: Colors.white), ), Flexible( child: Text( diff --git a/lib/pangea/analytics_details_popup/morph_details_view.dart b/lib/pangea/analytics_details_popup/morph_details_view.dart index ddfe948d4..bc2ca77b0 100644 --- a/lib/pangea/analytics_details_popup/morph_details_view.dart +++ b/lib/pangea/analytics_details_popup/morph_details_view.dart @@ -14,10 +14,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class MorphDetailsView extends StatelessWidget { final ConstructIdentifier constructId; - const MorphDetailsView({ - required this.constructId, - super.key, - }); + const MorphDetailsView({required this.constructId, super.key}); MorphFeaturesEnum get _morphFeature => MorphFeaturesEnumExtension.fromString(constructId.category); @@ -26,8 +23,9 @@ class MorphDetailsView extends StatelessWidget { @override Widget build(BuildContext context) { return FutureBuilder( - future: - Matrix.of(context).analyticsDataService.getConstructUse(constructId), + future: Matrix.of( + context, + ).analyticsDataService.getConstructUse(constructId), builder: (context, snapshot) { final construct = snapshot.data; final level = construct?.lemmaCategory ?? ConstructLevelEnum.seeds; @@ -57,9 +55,7 @@ class MorphDetailsView extends StatelessWidget { ConstructXPProgressBar(construct: construct.id), Padding( padding: const EdgeInsets.all(20.0), - child: AnalyticsDetailsUsageContent( - construct: construct, - ), + child: AnalyticsDetailsUsageContent(construct: construct), ), ], ], diff --git a/lib/pangea/analytics_details_popup/morph_meaning_widget.dart b/lib/pangea/analytics_details_popup/morph_meaning_widget.dart index d9c619b32..78f55ef3e 100644 --- a/lib/pangea/analytics_details_popup/morph_meaning_widget.dart +++ b/lib/pangea/analytics_details_popup/morph_meaning_widget.dart @@ -62,11 +62,13 @@ class MorphMeaningWidgetState extends State { } MorphInfoRequest get _request => MorphInfoRequest( - userL1: MatrixState.pangeaController.userController.userL1?.langCode ?? - LanguageKeys.defaultLanguage, - userL2: MatrixState.pangeaController.userController.userL2?.langCode ?? - LanguageKeys.defaultLanguage, - ); + userL1: + MatrixState.pangeaController.userController.userL1?.langCode ?? + LanguageKeys.defaultLanguage, + userL2: + MatrixState.pangeaController.userController.userL2?.langCode ?? + LanguageKeys.defaultLanguage, + ); Future _loadMorphMeaning() async { if (mounted) { @@ -178,15 +180,7 @@ class MorphEditView extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - "${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).whatIsMeaning( - getGrammarCopy( - category: morphFeature.name, - lemma: morphTag, - context: context, - ) ?? - morphTag, - '', - )}", + "${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).whatIsMeaning(getGrammarCopy(category: morphFeature.name, lemma: morphTag, context: context) ?? morphTag, '')}", textAlign: TextAlign.center, style: const TextStyle(fontStyle: FontStyle.italic), ), @@ -218,8 +212,8 @@ class MorphEditView extends StatelessWidget { ElevatedButton( onPressed: () => controller.text != meaning && controller.text.isNotEmpty - ? editMorphMeaning(controller.text) - : null, + ? editMorphMeaning(controller.text) + : null, style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart index 79dda8441..9ae81c3da 100644 --- a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart +++ b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart @@ -41,10 +41,9 @@ class VocabDetailsView extends StatelessWidget { if (resp != OkCancelResult.ok) return; final res = await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context) - .analyticsDataService - .updateService - .blockConstruct(constructId), + future: () => Matrix.of( + context, + ).analyticsDataService.updateService.blockConstruct(constructId), ); if (!res.isError) { @@ -63,8 +62,8 @@ class VocabDetailsView extends StatelessWidget { final Color textColor = (Theme.of(context).brightness != Brightness.light - ? level.color(context) - : level.darkColor(context)); + ? level.color(context) + : level.darkColor(context)); final forms = construct?.forms ?? []; final tokenText = PangeaTokenText.fromString(constructId.lemma); @@ -94,11 +93,13 @@ class VocabDetailsView extends StatelessWidget { MatrixState.pangeaController.userController.userL2Code!, construct: constructId, onClose: Navigator.of(context).pop, - onFlagTokenInfo: ( - LemmaInfoResponse lemmaInfo, - String phonetics, - ) => - controller.onFlagTokenInfo(token, lemmaInfo, phonetics), + onFlagTokenInfo: + (LemmaInfoResponse lemmaInfo, String phonetics) => + controller.onFlagTokenInfo( + token, + lemmaInfo, + phonetics, + ), reloadNotifier: controller.reloadNotifier, maxWidth: double.infinity, ), @@ -109,9 +110,7 @@ class VocabDetailsView extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: ConstructXPProgressBar( - construct: constructId, - ), + child: ConstructXPProgressBar(construct: constructId), ), Column( children: [ @@ -126,9 +125,7 @@ class VocabDetailsView extends StatelessWidget { ), ), ), - AnalyticsDetailsUsageContent( - construct: construct, - ), + AnalyticsDetailsUsageContent(construct: construct), ListTile( leading: Icon( Icons.delete_outline, @@ -176,9 +173,9 @@ class _VocabForms extends StatelessWidget { children: [ Text( L10n.of(context).formSectionHeader, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(width: 6.0), ...forms.mapIndexed( @@ -187,9 +184,9 @@ class _VocabForms extends StatelessWidget { children: [ WordTextWithAudioButton( text: form, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: textColor, - ), + style: Theme.of( + context, + ).textTheme.bodyLarge?.copyWith(color: textColor), uniqueID: "$form-$lemma-$i", langCode: MatrixState.pangeaController.userController.userL2Code!, diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart b/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart index 91f81c172..0c7b2a2c7 100644 --- a/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart +++ b/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart @@ -49,22 +49,19 @@ class VocabAnalyticsListTile extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ StreamBuilder( - stream: analyticsService.updateDispatcher - .lemmaUpdateStream(constructId), + stream: analyticsService.updateDispatcher.lemmaUpdateStream( + constructId, + ), builder: (context, snapshot) { - final emoji = snapshot.data?.emojis?.firstOrNull ?? + final emoji = + snapshot.data?.emojis?.firstOrNull ?? constructId.userSetEmoji; return Container( alignment: Alignment.center, height: (maxWidth - padding * 2) * 0.6, child: emoji != null - ? Text( - emoji, - style: const TextStyle( - fontSize: 22, - ), - ) + ? Text(emoji, style: const TextStyle(fontSize: 22)) : Text( "-", style: TextStyle( @@ -83,10 +80,7 @@ class VocabAnalyticsListTile extends StatelessWidget { child: ShrinkableText( text: constructId.lemma, maxWidth: maxWidth - padding * 2, - style: TextStyle( - fontSize: 16, - color: textColor, - ), + style: TextStyle(fontSize: 16, color: textColor), ), ), ], diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart b/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart index 541b3ff9c..9d392c51c 100644 --- a/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart +++ b/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart @@ -26,10 +26,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class VocabAnalyticsListView extends StatelessWidget { final ConstructAnalyticsViewState controller; - const VocabAnalyticsListView({ - super.key, - required this.controller, - }); + const VocabAnalyticsListView({super.key, required this.controller}); List? get _filteredVocab => controller.vocab?.where(_vocabFilter).toList(); @@ -51,8 +48,9 @@ class VocabAnalyticsListView extends StatelessWidget { } final normalizedLemma = removeDiacritics(use.lemma).toLowerCase(); - final normalizedSearch = - removeDiacritics(controller.searchController.text).toLowerCase(); + final normalizedSearch = removeDiacritics( + controller.searchController.text, + ).toLowerCase(); return normalizedLemma.contains(normalizedSearch); } @@ -62,7 +60,8 @@ class VocabAnalyticsListView extends StatelessWidget { final vocab = controller.vocab; final List filters = ConstructLevelEnum.values.reversed .map((constructLevelCategory) { - final int count = vocab + final int count = + vocab ?.where((e) => e.lemmaCategory == constructLevelCategory) .length ?? 0; @@ -76,8 +75,8 @@ class VocabAnalyticsListView extends StatelessWidget { shape: BoxShape.circle, color: controller.selectedConstructLevel == constructLevelCategory - ? constructLevelCategory.color(context).withAlpha(50) - : null, + ? constructLevelCategory.color(context).withAlpha(50) + : null, ), padding: const EdgeInsets.all(8.0), child: Badge( @@ -101,8 +100,9 @@ class VocabAnalyticsListView extends StatelessWidget { filters.add(const DownloadAnalyticsButton()); } - final constructParam = - GoRouterState.of(context).pathParameters['construct']; + final constructParam = GoRouterState.of( + context, + ).pathParameters['construct']; ConstructIdentifier? selectedConstruct; if (constructParam != null) { @@ -126,10 +126,8 @@ class VocabAnalyticsListView extends StatelessWidget { alignment: Alignment.center, child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), - transitionBuilder: (child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (child, animation) => + FadeTransition(opacity: animation, child: child), child: controller.isSearching ? Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -182,9 +180,7 @@ class VocabAnalyticsListView extends StatelessWidget { if (filteredVocab == null) const SliverFillRemaining( hasScrollBody: false, - child: Center( - child: CircularProgressIndicator.adaptive(), - ), + child: Center(child: CircularProgressIndicator.adaptive()), ) else filteredVocab.isEmpty @@ -203,38 +199,37 @@ class VocabAnalyticsListView extends StatelessWidget { : SliverGrid( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 100.0, - mainAxisExtent: 100.0, - crossAxisSpacing: 8.0, - mainAxisSpacing: 8.0, - ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final vocabItem = filteredVocab[index]; - return VocabAnalyticsListTile( - onTap: () { - TtsController.tryToSpeak( - vocabItem.id.lemma, - langCode: MatrixState.pangeaController - .userController.userL2Code!, - ); - AnalyticsNavigationUtil.navigateToAnalytics( - context: context, - view: ProgressIndicatorEnum.wordsUsed, - construct: vocabItem.id, - ); - }, - constructId: vocabItem.id, - textColor: Theme.of(context).brightness == - Brightness.light - ? vocabItem.lemmaCategory.darkColor(context) - : vocabItem.lemmaCategory.color(context), - level: vocabItem.lemmaCategory, - selected: vocabItem.id == selectedConstruct, - ); - }, - childCount: filteredVocab.length, - ), + maxCrossAxisExtent: 100.0, + mainAxisExtent: 100.0, + crossAxisSpacing: 8.0, + mainAxisSpacing: 8.0, + ), + delegate: SliverChildBuilderDelegate((context, index) { + final vocabItem = filteredVocab[index]; + return VocabAnalyticsListTile( + onTap: () { + TtsController.tryToSpeak( + vocabItem.id.lemma, + langCode: MatrixState + .pangeaController + .userController + .userL2Code!, + ); + AnalyticsNavigationUtil.navigateToAnalytics( + context: context, + view: ProgressIndicatorEnum.wordsUsed, + construct: vocabItem.id, + ); + }, + constructId: vocabItem.id, + textColor: + Theme.of(context).brightness == Brightness.light + ? vocabItem.lemmaCategory.darkColor(context) + : vocabItem.lemmaCategory.color(context), + level: vocabItem.lemmaCategory, + selected: vocabItem.id == selectedConstruct, + ); + }, childCount: filteredVocab.length), ), const SliverToBoxAdapter(child: SizedBox(height: 75.0)), ], diff --git a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart index ea0739580..5337a4abf 100644 --- a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart +++ b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart @@ -7,6 +7,7 @@ import 'package:intl/intl.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_downloads/analytics_summary_enum.dart'; @@ -28,9 +29,7 @@ import 'package:fluffychat/pangea/morphs/morph_repo.dart'; import 'package:fluffychat/widgets/matrix.dart'; class AnalyticsDownloadDialog extends StatefulWidget { - const AnalyticsDownloadDialog({ - super.key, - }); + const AnalyticsDownloadDialog({super.key}); @override AnalyticsDownloadDialogState createState() => AnalyticsDownloadDialogState(); @@ -73,13 +72,7 @@ class AnalyticsDownloadDialogState extends State { vocabSummary = await _getVocabAnalytics(); morphSummary = await _getMorphAnalytics(); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - "downloadType": _downloadType, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {"downloadType": _downloadType}); if (mounted) { setState(() { @@ -110,16 +103,8 @@ class AnalyticsDownloadDialogState extends State { "analytics_morph_${client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.csv"; final futures = [ - DownloadUtil.downloadFile( - vocabContent, - vocabFileName, - _downloadType, - ), - DownloadUtil.downloadFile( - morphContent, - morphFileName, - _downloadType, - ), + DownloadUtil.downloadFile(vocabContent, vocabFileName, _downloadType), + DownloadUtil.downloadFile(morphContent, morphFileName, _downloadType), ]; await Future.wait(futures); @@ -132,21 +117,11 @@ class AnalyticsDownloadDialogState extends State { final fileName = "analytics_${client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.xlsx"; - await DownloadUtil.downloadFile( - content, - fileName, - _downloadType, - ); + await DownloadUtil.downloadFile(content, fileName, _downloadType); } _downloaded = true; } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - "downloadType": _downloadType, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {"downloadType": _downloadType}); _error = L10n.of(context).errorDownloading; } finally { if (mounted) setState(() => _downloading = false); @@ -155,8 +130,9 @@ class AnalyticsDownloadDialogState extends State { Future> _getVocabAnalytics() async { final analyticsService = Matrix.of(context).analyticsDataService; - final aggregatedVocab = - await analyticsService.getAggregatedConstructs(ConstructTypeEnum.vocab); + final aggregatedVocab = await analyticsService.getAggregatedConstructs( + ConstructTypeEnum.vocab, + ); final uses = aggregatedVocab.values.toList(); final Map> lemmasToUses = {}; @@ -172,8 +148,9 @@ class AnalyticsDownloadDialogState extends State { final xp = uses.map((e) => e.points).reduce((a, total) => a + total); final exampleMessages = await _getExampleMessages(uses); - final allUses = - uses.map((u) => u.cappedUses).expand((element) => element); + final allUses = uses + .map((u) => u.cappedUses) + .expand((element) => element); int independantUseOccurrences = 0; int assistedUseOccurrences = 0; @@ -249,8 +226,9 @@ class AnalyticsDownloadDialogState extends State { ); final summary = AnalyticsSummaryModel( - morphFeature: MorphFeaturesEnumExtension.fromString(feature.feature) - .getDisplayCopy(context), + morphFeature: MorphFeaturesEnumExtension.fromString( + feature.feature, + ).getDisplayCopy(context), morphTag: tagCopy, xp: xp, forms: forms, @@ -269,8 +247,10 @@ class AnalyticsDownloadDialogState extends State { Future> _getExampleMessages( List constructUses, ) async { - final allUses = - constructUses.map((e) => e.cappedUses).expand((e) => e).toList(); + final allUses = constructUses + .map((e) => e.cappedUses) + .expand((e) => e) + .toList(); final List examples = []; for (final OneConstructUse use in allUses) { if (use.metadata.roomId == null) continue; @@ -330,16 +310,13 @@ class AnalyticsDownloadDialogState extends State { columnIndex: values.indexOf(key), ), ) - .value = TextCellValue(key.header(context)); + .value = TextCellValue( + key.header(context), + ); } final rows = entry.value - .map( - (summary) => _formatExcelRow( - summary, - entry.key, - ), - ) + .map((summary) => _formatExcelRow(summary, entry.key)) .toList(); for (int i = 0; i < rows.length; i++) { @@ -347,8 +324,11 @@ class AnalyticsDownloadDialogState extends State { for (int j = 0; j < row.length; j++) { final cell = row[j]; sheet - .cell(CellIndex.indexByColumnRow(rowIndex: i + 2, columnIndex: j)) - .value = cell; + .cell( + CellIndex.indexByColumnRow(rowIndex: i + 2, columnIndex: j), + ) + .value = + cell; } } } @@ -416,9 +396,7 @@ class AnalyticsDownloadDialogState extends State { Widget build(BuildContext context) { return Dialog( child: Container( - constraints: const BoxConstraints( - maxWidth: 400, - ), + constraints: const BoxConstraints(maxWidth: 400), padding: const EdgeInsets.symmetric(vertical: 20), child: Column( mainAxisSize: MainAxisSize.min, @@ -426,15 +404,18 @@ class AnalyticsDownloadDialogState extends State { Text( L10n.of(context).fileType, style: TextStyle( - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), ), Padding( padding: const EdgeInsets.all(8.0), child: SegmentedButton( selected: {_downloadType}, - onSelectionChanged: - _downloading ? null : (c) => _setDownloadType(c.first), + onSelectionChanged: _downloading + ? null + : (c) => _setDownloadType(c.first), segments: [ ButtonSegment( value: DownloadType.csv, @@ -490,9 +471,7 @@ class AnalyticsDownloadDialogState extends State { child: _error != null ? Padding( padding: const EdgeInsets.all(8.0), - child: ErrorIndicator( - message: _error!, - ), + child: ErrorIndicator(message: _error!), ) : const SizedBox(), ), diff --git a/lib/pangea/analytics_downloads/analytics_download_button.dart b/lib/pangea/analytics_downloads/analytics_download_button.dart index f78ec909e..8f1445a33 100644 --- a/lib/pangea/analytics_downloads/analytics_download_button.dart +++ b/lib/pangea/analytics_downloads/analytics_download_button.dart @@ -7,9 +7,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_downloads/analytics_dowload_dialog.dart'; class DownloadAnalyticsButton extends StatelessWidget { - const DownloadAnalyticsButton({ - super.key, - }); + const DownloadAnalyticsButton({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pangea/analytics_downloads/analytics_summary_enum.dart b/lib/pangea/analytics_downloads/analytics_summary_enum.dart index 59d9dfcfd..ef7a1fb4a 100644 --- a/lib/pangea/analytics_downloads/analytics_summary_enum.dart +++ b/lib/pangea/analytics_downloads/analytics_summary_enum.dart @@ -37,21 +37,21 @@ enum AnalyticsSummaryEnum { const AnalyticsSummaryEnum(); static List get vocabValues => [ - lemma, - xp, - forms, - exampleMessages, - independentUseOccurrences, - assistedUseOccurrences, - ]; + lemma, + xp, + forms, + exampleMessages, + independentUseOccurrences, + assistedUseOccurrences, + ]; static List get morphValues => [ - morphFeature, - morphTag, - xp, - forms, - exampleMessages, - independentUseOccurrences, - assistedUseOccurrences, - ]; + morphFeature, + morphTag, + xp, + forms, + exampleMessages, + independentUseOccurrences, + assistedUseOccurrences, + ]; } diff --git a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart index a176fff8d..bc3a05cd9 100644 --- a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart +++ b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart @@ -85,10 +85,7 @@ class SpaceAnalyticsSummaryModel { }); static SpaceAnalyticsSummaryModel emptyModel(String userID) { - return SpaceAnalyticsSummaryModel( - username: userID, - dataAvailable: false, - ); + return SpaceAnalyticsSummaryModel(username: userID, dataAvailable: false); } static SpaceAnalyticsSummaryModel fromEvents( @@ -189,8 +186,8 @@ class SpaceAnalyticsSummaryModel { } } - final totalXP = cleanedVocab.values - .fold(0, (sum, entry) => sum + entry.points) + + final totalXP = + cleanedVocab.values.fold(0, (sum, entry) => sum + entry.points) + cleanedMorph.values.fold(0, (sum, entry) => sum + entry.points); final level = DerivedAnalyticsDataModel.calculateLevelWithXp(totalXP); @@ -270,7 +267,8 @@ class SpaceAnalyticsSummaryModel { // if >= 80% correct original uses if (originalUsesCorrect.length + originalUsesIncorrect.length > 0) { - final percentCorrect = originalUsesCorrect.length / + final percentCorrect = + originalUsesCorrect.length / (originalUsesCorrect.length + originalUsesIncorrect.length); if (percentCorrect >= 0.8) { morphCorrectOriginal.add(entry.lemma); @@ -279,7 +277,8 @@ class SpaceAnalyticsSummaryModel { } if (systemUsesCorrect.length + systemUsesIncorrect.length > 0) { - final percentCorrectSystem = systemUsesCorrect.length / + final percentCorrectSystem = + systemUsesCorrect.length / (systemUsesCorrect.length + systemUsesIncorrect.length); if (percentCorrectSystem >= 0.8) { morphCorrectSystem.add(entry.lemma); diff --git a/lib/pangea/analytics_misc/analytics_navigation_util.dart b/lib/pangea/analytics_misc/analytics_navigation_util.dart index f6714618f..13a67fe3e 100644 --- a/lib/pangea/analytics_misc/analytics_navigation_util.dart +++ b/lib/pangea/analytics_misc/analytics_navigation_util.dart @@ -32,8 +32,10 @@ class AnalyticsNavigationUtil { } if (construct == null || - !{ProgressIndicatorEnum.wordsUsed, ProgressIndicatorEnum.morphsUsed} - .contains(view)) { + !{ + ProgressIndicatorEnum.wordsUsed, + ProgressIndicatorEnum.morphsUsed, + }.contains(view)) { context.go("/rooms/analytics/${view.route}"); return; } diff --git a/lib/pangea/analytics_misc/client_analytics_extension.dart b/lib/pangea/analytics_misc/client_analytics_extension.dart index ba2be21d1..4d171355c 100644 --- a/lib/pangea/analytics_misc/client_analytics_extension.dart +++ b/lib/pangea/analytics_misc/client_analytics_extension.dart @@ -45,15 +45,12 @@ extension AnalyticsClientExtension on Client { analyticsRoom.membership == Membership.invite) { debugger(when: kDebugMode); analyticsRoom.join().onError( - (error, stackTrace) => ErrorHandler.logError( - e: error, - s: stackTrace, - data: { - "langCode": lang!.langCodeShort, - "userIdParam": userIdParam, - }, - ), - ); + (error, stackTrace) => ErrorHandler.logError( + e: error, + s: stackTrace, + data: {"langCode": lang!.langCodeShort, "userIdParam": userIdParam}, + ), + ); return analyticsRoom; } return analyticsRoom; @@ -81,9 +78,7 @@ extension AnalyticsClientExtension on Client { initialState: [ StateEvent( type: EventTypes.RoomJoinRules, - content: { - ModelKey.joinRule: JoinRules.knock.name, - }, + content: {ModelKey.joinRule: JoinRules.knock.name}, ), ], ); @@ -97,11 +92,8 @@ extension AnalyticsClientExtension on Client { } /// Get all my analytics rooms - List get allMyAnalyticsRooms => rooms - .where( - (e) => e.isAnalyticsRoomOfUser(userID!), - ) - .toList(); + List get allMyAnalyticsRooms => + rooms.where((e) => e.isAnalyticsRoomOfUser(userID!)).toList(); /// Update the join rules of all analytics rooms to 'knock'. Future updateAnalyticsRoomJoinRules() async { @@ -123,9 +115,7 @@ extension AnalyticsClientExtension on Client { Future addAnalyticsRoomsToSpaces() async { if (userID == null || userID == BotName.byEnvironment) return; final spaces = rooms - .where( - (room) => room.isSpace && room.membership == Membership.join, - ) + .where((room) => room.isSpace && room.membership == Membership.join) .toList(); final Random random = Random(); diff --git a/lib/pangea/analytics_misc/construct_use_model.dart b/lib/pangea/analytics_misc/construct_use_model.dart index e3a3ddda5..cb298accf 100644 --- a/lib/pangea/analytics_misc/construct_use_model.dart +++ b/lib/pangea/analytics_misc/construct_use_model.dart @@ -21,18 +21,15 @@ class ConstructUses { required this.constructType, required this.lemma, required category, - }) : _category = category, - _uses = List.from(uses) { + }) : _category = category, + _uses = List.from(uses) { _sortUses(); } // Total points for all uses of this lemma int get points { return min( - _uses.fold( - 0, - (total, use) => total + use.xp, - ), + _uses.fold(0, (total, use) => total + use.xp), AnalyticsConstants.xpForFlower, ); } @@ -51,10 +48,10 @@ class ConstructUses { int get numTotalUses => _uses.length; ConstructIdentifier get id => ConstructIdentifier( - lemma: lemma, - type: constructType, - category: category, - ); + lemma: lemma, + type: constructType, + category: category, + ); /// Get the lemma category, based on points ConstructLevelEnum get lemmaCategory { @@ -80,10 +77,10 @@ class ConstructUses { } ConstructLevelEnum get constructLevel => switch (points) { - < AnalyticsConstants.xpForGreens => ConstructLevelEnum.seeds, - < AnalyticsConstants.xpForFlower => ConstructLevelEnum.greens, - _ => ConstructLevelEnum.flowers, - }; + < AnalyticsConstants.xpForGreens => ConstructLevelEnum.seeds, + < AnalyticsConstants.xpForFlower => ConstructLevelEnum.greens, + _ => ConstructLevelEnum.flowers, + }; List get forms => _uses.map((e) => e.form).whereType().toSet().toList(); diff --git a/lib/pangea/analytics_misc/constructs_model.dart b/lib/pangea/analytics_misc/constructs_model.dart index cb053585b..4ef803c23 100644 --- a/lib/pangea/analytics_misc/constructs_model.dart +++ b/lib/pangea/analytics_misc/constructs_model.dart @@ -17,9 +17,7 @@ import 'construct_type_enum.dart'; class ConstructAnalyticsModel { List uses; - ConstructAnalyticsModel({ - this.uses = const [], - }); + ConstructAnalyticsModel({this.uses = const []}); static const _usesKey = "uses"; @@ -37,13 +35,7 @@ class ConstructAnalyticsModel { try { uses.add(OneConstructUse.fromJson(useJson)); } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: { - "useJson": useJson, - }, - ); + ErrorHandler.logError(e: err, s: s, data: {"useJson": useJson}); continue; } } @@ -52,21 +44,15 @@ class ConstructAnalyticsModel { debugger(when: kDebugMode); ErrorHandler.logError( m: "Analytics room with non-list uses", - data: { - "usesKey": _usesKey, - }, + data: {"usesKey": _usesKey}, ); } - return ConstructAnalyticsModel( - uses: uses, - ); + return ConstructAnalyticsModel(uses: uses); } Map toJson() { - return { - _usesKey: uses.map((use) => use.toJson()).toList(), - }; + return {_usesKey: uses.map((use) => use.toJson()).toList()}; } } @@ -139,17 +125,17 @@ class OneConstructUse { } Map toJson() => { - 'useType': useType.string, - 'chatId': metadata.roomId, - 'timeStamp': metadata.timeStamp.toIso8601String(), - 'form': form, - 'msgId': metadata.eventId, - 'lemma': lemma, - 'constructType': constructType.string, - 'categories': category, - 'id': id, - 'xp': xp, - }; + 'useType': useType.string, + 'chatId': metadata.roomId, + 'timeStamp': metadata.timeStamp.toIso8601String(), + 'form': form, + 'msgId': metadata.eventId, + 'lemma': lemma, + 'constructType': constructType.string, + 'categories': category, + 'id': id, + 'xp': xp, + }; OneConstructUse copyWith({ String? lemma, @@ -188,8 +174,8 @@ class OneConstructUse { final String? category = categoryEntry is String ? categoryEntry : categoryEntry is List && categoryEntry.isNotEmpty - ? categoryEntry.first - : null; + ? categoryEntry.first + : null; return category ?? "Other"; } @@ -231,10 +217,10 @@ class OneConstructUse { } ConstructIdentifier get identifier => ConstructIdentifier( - lemma: lemma, - type: constructType, - category: category, - ); + lemma: lemma, + type: constructType, + category: category, + ); } class ConstructUseMetaData { diff --git a/lib/pangea/analytics_misc/example_message_util.dart b/lib/pangea/analytics_misc/example_message_util.dart index 6f7c73e2f..498c6129a 100644 --- a/lib/pangea/analytics_misc/example_message_util.dart +++ b/lib/pangea/analytics_misc/example_message_util.dart @@ -28,11 +28,11 @@ class _ExampleMessageResult { List toSpans() => displaySpans; AudioExampleMessage toAudioExampleMessage() => AudioExampleMessage( - tokens: includedTokens, - eventId: eventId, - roomId: roomId, - exampleMessage: ExampleMessageInfo(exampleMessage: displaySpans), - ); + tokens: includedTokens, + eventId: eventId, + roomId: roomId, + exampleMessage: ExampleMessageInfo(exampleMessage: displaySpans), + ); } class ExampleMessageUtil { @@ -220,8 +220,10 @@ class ExampleMessageUtil { ]; // Extract only the tokens that are included in the displayed text - final includedTokens = - tokens.sublist(firstIncludedTokenIndex, lastIncludedTokenIndex + 1); + final includedTokens = tokens.sublist( + firstIncludedTokenIndex, + lastIncludedTokenIndex + 1, + ); // Adjust target token index relative to the included tokens final adjustedTargetIndex = targetTokenIndex - firstIncludedTokenIndex; diff --git a/lib/pangea/analytics_misc/gain_points_animation.dart b/lib/pangea/analytics_misc/gain_points_animation.dart index 1580a73a8..fdad58fb8 100644 --- a/lib/pangea/analytics_misc/gain_points_animation.dart +++ b/lib/pangea/analytics_misc/gain_points_animation.dart @@ -51,43 +51,33 @@ class PointsGainedAnimationState extends State _progressAnimation = Tween( begin: 0.0, end: 3.0, - ).animate( - CurvedAnimation( - parent: _controller!, - curve: Curves.easeOut, - ), - ); + ).animate(CurvedAnimation(parent: _controller!, curve: Curves.easeOut)); _fadeAnimation = Tween( begin: 1.0, end: 0.0, - ).animate( - CurvedAnimation( - parent: _controller!, - curve: Curves.easeIn, - ), - ); + ).animate(CurvedAnimation(parent: _controller!, curve: Curves.easeIn)); initParticleTrajectories(); - _controller?.forward().then( - (_) { - if (!mounted) return; - MatrixState.pAnyState.closeOverlay("${widget.targetID}_points"); - }, - ); + _controller?.forward().then((_) { + if (!mounted) return; + MatrixState.pAnyState.closeOverlay("${widget.targetID}_points"); + }); } int get _points => widget.points.clamp(-25, 25); void initParticleTrajectories() { for (int i = 0; i < _points.abs(); i++) { - final angle = (i - _points.abs() / 2) / _points.abs() * (pi / 3) + + final angle = + (i - _points.abs() / 2) / _points.abs() * (pi / 3) + (_random.nextDouble() - 0.5) * pi / 6 + pi / 2; final speedMultiplier = 0.75 + _random.nextDouble() / 4; // Random speed multiplier. - final speed = _particleSpeed * + final speed = + _particleSpeed * speedMultiplier * (_points > 0 ? 2 : 1); // Exponential speed. _trajectories.add( @@ -127,9 +117,7 @@ class PointsGainedAnimationState extends State context, big: true, setColor: textColor == null, - existingStyle: TextStyle( - color: textColor, - ), + existingStyle: TextStyle(color: textColor), ), ); diff --git a/lib/pangea/analytics_misc/growth_animation.dart b/lib/pangea/analytics_misc/growth_animation.dart index 7cd42c4fa..263438507 100644 --- a/lib/pangea/analytics_misc/growth_animation.dart +++ b/lib/pangea/analytics_misc/growth_animation.dart @@ -57,14 +57,16 @@ class _GrowthAnimationState extends State _wiggleAmplitude = 4.0 + _random.nextDouble() * 4.0; _wiggleFrequency = 1.5 + _random.nextDouble() * 1.0; - _controller = AnimationController( - duration: const Duration(milliseconds: _durationMs), - vsync: this, - )..forward().then((_) { - if (mounted) { - MatrixState.pAnyState.closeOverlay(widget.targetID); - } - }); + _controller = + AnimationController( + duration: const Duration(milliseconds: _durationMs), + vsync: this, + ) + ..forward().then((_) { + if (mounted) { + MatrixState.pAnyState.closeOverlay(widget.targetID); + } + }); } @override @@ -85,7 +87,7 @@ class _GrowthAnimationState extends State final opacity = t < 0.5 ? t * 2 : (1.0 - t) * 2; final wiggle = sin(t * pi * _wiggleFrequency) * _wiggleAmplitude; return Transform.translate( - offset: Offset(_horizontalOffset! + wiggle, dy), + offset: Offset(_horizontalOffset + wiggle, dy), child: Opacity( opacity: opacity.clamp(0.0, 1.0), child: widget.level.icon(24), diff --git a/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart b/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart index 0719943e3..3ef9501e6 100644 --- a/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart +++ b/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart @@ -24,14 +24,14 @@ mixin LemmaEmojiSetter { } if (constructId.userSetEmoji == null) { - _getEmojiAnalytics( - constructId, - targetId: targetId, - ); + _getEmojiAnalytics(constructId, targetId: targetId); } await MatrixState - .pangeaController.matrixState.analyticsDataService.updateService + .pangeaController + .matrixState + .analyticsDataService + .updateService .setLemmaInfo(constructId, emoji: emoji); } @@ -67,8 +67,8 @@ mixin LemmaEmojiSetter { L10n.of(context).emojiSelectedSnackbar(constructId.lemma), textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Theme.of(context).colorScheme.surface, - ), + color: Theme.of(context).colorScheme.surface, + ), ), ), IconButton( @@ -107,9 +107,6 @@ mixin LemmaEmojiSetter { ]; MatrixState.pangeaController.matrixState.analyticsDataService.updateService - .addAnalytics( - targetId, - constructs, - ); + .addAnalytics(targetId, constructs); } } diff --git a/lib/pangea/analytics_misc/level_display_name.dart b/lib/pangea/analytics_misc/level_display_name.dart index 7fbe00d42..6c4c36920 100644 --- a/lib/pangea/analytics_misc/level_display_name.dart +++ b/lib/pangea/analytics_misc/level_display_name.dart @@ -17,13 +17,11 @@ class LevelDisplayName extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 0, - vertical: 2.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 2.0), child: FutureBuilder( - future: MatrixState.pangeaController.userController - .getPublicProfile(userId), + future: MatrixState.pangeaController.userController.getPublicProfile( + userId, + ), builder: (context, snapshot) { final analytics = snapshot.data?.analytics; return Row( @@ -48,17 +46,15 @@ class LevelDisplayName extends StatelessWidget { padding: const EdgeInsets.only(right: 4.0), child: Text( snapshot.data!.countryEmoji!, - style: textStyle ?? - const TextStyle( - fontSize: 16.0, - ), + style: textStyle ?? const TextStyle(fontSize: 16.0), ), ), if (analytics?.baseLanguage != null && analytics?.targetLanguage != null) Text( analytics!.baseLanguage!.langCodeShort.toUpperCase(), - style: textStyle ?? + style: + textStyle ?? TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary, @@ -73,22 +69,20 @@ class LevelDisplayName extends StatelessWidget { if (analytics?.targetLanguage != null) Text( analytics!.targetLanguage!.langCodeShort.toUpperCase(), - style: textStyle ?? + style: + textStyle ?? TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary, ), ), const SizedBox(width: 4.0), - if (analytics?.level != null) - Text( - "⭐", - style: textStyle, - ), + if (analytics?.level != null) Text("⭐", style: textStyle), if (analytics?.level != null) Text( "${analytics!.level!}", - style: textStyle ?? + style: + textStyle ?? TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary, diff --git a/lib/pangea/analytics_misc/level_up/level_up_banner.dart b/lib/pangea/analytics_misc/level_up/level_up_banner.dart index a242bac38..f026ad550 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_banner.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_banner.dart @@ -6,6 +6,7 @@ import 'package:audioplayers/audioplayers.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart'; @@ -30,7 +31,7 @@ class LevelUpUtil { ) async { // Remove delay since GetAnalyticsController._onLevelUp is already async final player = AudioPlayer(); - player.setVolume(AppConfig.volume); + player.setVolume(AppSettings.volume.value); // Wait for any existing snackbars to dismiss await _waitForSnackbars(context); @@ -122,12 +123,7 @@ class LevelUpBannerState extends State _slideAnimation = Tween( begin: const Offset(0, -1), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOut, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOut)); _slideController.forward(); @@ -158,17 +154,16 @@ class LevelUpBannerState extends State await showDialog( context: context, - builder: (context) => LevelUpPopup( - constructSummaryCompleter: _constructSummaryCompleter, - ), + builder: (context) => + LevelUpPopup(constructSummaryCompleter: _constructSummaryCompleter), ); } Future _loadConstructSummary() async { try { final analyticsRoom = await Matrix.of(context).client.getMyAnalyticsRoom( - MatrixState.pangeaController.userController.userL2!, - ); + MatrixState.pangeaController.userController.userL2!, + ); final timestamp = analyticsRoom!.lastLevelUpTimestamp; final analyticsService = Matrix.of(context).analyticsDataService; @@ -191,15 +186,15 @@ class LevelUpBannerState extends State final style = isColumnMode ? Theme.of(context).textTheme.titleLarge?.copyWith( - color: AppConfig.gold, - fontWeight: FontWeight.bold, - letterSpacing: 0.5, - ) + color: AppConfig.gold, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + ) : Theme.of(context).textTheme.bodyLarge?.copyWith( - color: AppConfig.gold, - fontWeight: FontWeight.bold, - letterSpacing: 0.5, - ); + color: AppConfig.gold, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + ); return SafeArea( child: Material( @@ -279,12 +274,14 @@ class LevelUpBannerState extends State padding: const EdgeInsets.all(4.0), ), onPressed: () { - MatrixState.pAnyState - .closeOverlay("level_up_notification"); + MatrixState.pAnyState.closeOverlay( + "level_up_notification", + ); }, constraints: const BoxConstraints(), - color: - Theme.of(context).colorScheme.onSurface, + color: Theme.of( + context, + ).colorScheme.onSurface, ), ), ), diff --git a/lib/pangea/analytics_misc/level_up/level_up_manager.dart b/lib/pangea/analytics_misc/level_up/level_up_manager.dart index b358d7ad2..61f0b6b5d 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_manager.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_manager.dart @@ -40,8 +40,8 @@ class LevelUpManager { final LanguageModel? l2 = MatrixState.pangeaController.userController.userL2; - final Room? analyticsRoom = - MatrixState.pangeaController.matrixState.client.analyticsRoomLocal(l2!); + final Room? analyticsRoom = MatrixState.pangeaController.matrixState.client + .analyticsRoomLocal(l2!); if (analyticsRoom != null) { final lastSummary = analyticsRoom.levelUpSummary; diff --git a/lib/pangea/analytics_misc/level_up/level_up_popup.dart b/lib/pangea/analytics_misc/level_up/level_up_popup.dart index 8795ab461..9e59e5bbb 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_popup.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_popup.dart @@ -26,10 +26,7 @@ import 'package:fluffychat/widgets/mxc_image.dart'; class LevelUpPopup extends StatefulWidget { final Completer constructSummaryCompleter; - const LevelUpPopup({ - required this.constructSummaryCompleter, - super.key, - }); + const LevelUpPopup({required this.constructSummaryCompleter, super.key}); @override State createState() => _LevelUpPopupState(); @@ -108,7 +105,7 @@ class _LevelUpPopupContentState extends State String language = MatrixState.pangeaController.userController.userL2Code?.toUpperCase() ?? - LanguageKeys.unknownLanguage; + LanguageKeys.unknownLanguage; ConstructSummary? _constructSummary; Object? _error; @@ -161,8 +158,8 @@ class _LevelUpPopupContentState extends State int get _startGrammar => LevelUpManager.instance.prevGrammar; int get _startVocab => LevelUpManager.instance.prevVocab; - get _endGrammar => LevelUpManager.instance.nextGrammar; - get _endVocab => LevelUpManager.instance.nextVocab; + int get _endGrammar => LevelUpManager.instance.nextGrammar; + int get _endVocab => LevelUpManager.instance.nextVocab; Future _loadConstructSummary() async { try { @@ -194,41 +191,41 @@ class _LevelUpPopupContentState extends State Widget build(BuildContext context) { final Animation vocabAnimation = IntTween(begin: _startVocab, end: _endVocab).animate( - CurvedAnimation( - parent: _controller, - curve: const Interval(0.0, 0.5, curve: Curves.easeInOutQuad), - ), - ); + CurvedAnimation( + parent: _controller, + curve: const Interval(0.0, 0.5, curve: Curves.easeInOutQuad), + ), + ); final Animation grammarAnimation = IntTween(begin: _startGrammar, end: _endGrammar).animate( - CurvedAnimation( - parent: _controller, - curve: const Interval(0.0, 0.5, curve: Curves.easeInOutQuad), - ), - ); + CurvedAnimation( + parent: _controller, + curve: const Interval(0.0, 0.5, curve: Curves.easeInOutQuad), + ), + ); - final Animation skillsOpacity = - Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation( - parent: _controller, - curve: const Interval(0.7, 1.0, curve: Curves.easeIn), - ), - ); + final Animation skillsOpacity = Tween(begin: 0.0, end: 1.0) + .animate( + CurvedAnimation( + parent: _controller, + curve: const Interval(0.7, 1.0, curve: Curves.easeIn), + ), + ); final Animation shrinkMultiplier = Tween(begin: 1.0, end: 0.3).animate( - CurvedAnimation( - parent: _controller, - curve: const Interval(0.7, 1.0, curve: Curves.easeInOut), - ), - ); + CurvedAnimation( + parent: _controller, + curve: const Interval(0.7, 1.0, curve: Curves.easeInOut), + ), + ); final colorScheme = Theme.of(context).colorScheme; final grammarVocabStyle = Theme.of(context).textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ); + fontWeight: FontWeight.bold, + color: colorScheme.primary, + ); final username = Matrix.of(context).client.userID?.split(':').first.substring(1) ?? ''; @@ -241,7 +238,7 @@ class _LevelUpPopupContentState extends State children: [ AnimatedBuilder( animation: _controller, - builder: (_, __) => Row( + builder: (_, _) => Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( @@ -274,7 +271,7 @@ class _LevelUpPopupContentState extends State // Progress bar + Level AnimatedBuilder( animation: _controller, - builder: (_, __) => Row( + builder: (_, _) => Row( children: [ const Expanded( child: LevelPopupProgressBar( @@ -283,17 +280,12 @@ class _LevelUpPopupContentState extends State ), ), const SizedBox(width: 8), - Text( - "⭐", - style: Theme.of(context).textTheme.titleLarge, - ), + Text("⭐", style: Theme.of(context).textTheme.titleLarge), Padding( padding: const EdgeInsets.all(8.0), child: AnimatedFlipCounter( value: displayedLevel, - textStyle: Theme.of(context) - .textTheme - .headlineMedium + textStyle: Theme.of(context).textTheme.headlineMedium ?.copyWith( fontWeight: FontWeight.bold, color: AppConfig.goldLight, @@ -310,7 +302,7 @@ class _LevelUpPopupContentState extends State // Vocab and grammar row AnimatedBuilder( animation: _controller, - builder: (_, __) => Row( + builder: (_, _) => Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Column( @@ -392,7 +384,7 @@ class _LevelUpPopupContentState extends State // Skills section AnimatedBuilder( animation: skillsOpacity, - builder: (_, __) => Opacity( + builder: (_, _) => Opacity( opacity: skillsOpacity.value, child: Column( crossAxisAlignment: CrossAxisAlignment.center, @@ -472,10 +464,7 @@ class _LevelUpPopupContentState extends State // chunk into rows of up to 4 final rows = >[ for (var i = 0; i < visibleSkills.length; i += itemsPerRow) - visibleSkills.sublist( - i, - min(i + itemsPerRow, visibleSkills.length), - ), + visibleSkills.sublist(i, min(i + itemsPerRow, visibleSkills.length)), ]; return Column( diff --git a/lib/pangea/analytics_misc/message_analytics_feedback.dart b/lib/pangea/analytics_misc/message_analytics_feedback.dart index f9efe40ef..237b893be 100644 --- a/lib/pangea/analytics_misc/message_analytics_feedback.dart +++ b/lib/pangea/analytics_misc/message_analytics_feedback.dart @@ -93,25 +93,21 @@ class MessageAnalyticsFeedbackState extends State } void _startTickerAnimations() { - _vocabTickerAnimation = IntTween( - begin: 0, - end: widget.newVocabConstructs, - ).animate( - CurvedAnimation( - parent: _tickerController, - curve: Curves.easeOutCubic, - ), - ); + _vocabTickerAnimation = IntTween(begin: 0, end: widget.newVocabConstructs) + .animate( + CurvedAnimation( + parent: _tickerController, + curve: Curves.easeOutCubic, + ), + ); - _grammarTickerAnimation = IntTween( - begin: 0, - end: widget.newGrammarConstructs, - ).animate( - CurvedAnimation( - parent: _tickerController, - curve: Curves.easeOutCubic, - ), - ); + _grammarTickerAnimation = + IntTween(begin: 0, end: widget.newGrammarConstructs).animate( + CurvedAnimation( + parent: _tickerController, + curve: Curves.easeOutCubic, + ), + ); setState(() {}); _tickerController.forward(); @@ -136,9 +132,7 @@ class MessageAnalyticsFeedbackState extends State builder: (context, child) { return Container( decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .surfaceContainer + color: Theme.of(context).colorScheme.surfaceContainer .withAlpha((_bubbleOpacityAnimation.value * 255).round()), borderRadius: const BorderRadius.only( topLeft: Radius.circular(16.0), @@ -240,28 +234,18 @@ class _AnimatedCounter extends StatelessWidget { final Animation? animation; final TextStyle? style; - const _AnimatedCounter({ - super.key, - required this.animation, - this.style, - }); + const _AnimatedCounter({super.key, required this.animation, this.style}); @override Widget build(BuildContext context) { if (animation == null) { - return Text( - "+ 0", - style: style, - ); + return Text("+ 0", style: style); } return AnimatedBuilder( animation: animation!, builder: (context, child) { - return Text( - "+ ${animation!.value}", - style: style, - ); + return Text("+ ${animation!.value}", style: style); }, ); } diff --git a/lib/pangea/analytics_misc/room_analytics_extension.dart b/lib/pangea/analytics_misc/room_analytics_extension.dart index 375bce4c0..de4fa816d 100644 --- a/lib/pangea/analytics_misc/room_analytics_extension.dart +++ b/lib/pangea/analytics_misc/room_analytics_extension.dart @@ -31,9 +31,7 @@ extension AnalyticsRoomExtension on Room { /// The [uses] parameter is a list of [OneConstructUse] objects representing the /// constructs to be sent. To prevent hitting the maximum event size, the events /// are chunked into smaller lists. Each chunk is sent as a separate event. - Future sendConstructsEvent( - List uses, - ) async { + Future sendConstructsEvent(List uses) async { // It's possible that the user has no info to send yet, but to prevent trying // to load the data over and over again, we'll sometimes send an empty event to // indicate that we have checked and there was no data. diff --git a/lib/pangea/analytics_misc/saved_analytics_extension.dart b/lib/pangea/analytics_misc/saved_analytics_extension.dart index 9e362baa8..f7457664a 100644 --- a/lib/pangea/analytics_misc/saved_analytics_extension.dart +++ b/lib/pangea/analytics_misc/saved_analytics_extension.dart @@ -34,12 +34,9 @@ extension SavedAnalyticsExtension on Room { ids.add(roomId); final syncFuture = client.waitForRoomInSync(id, join: true); - await client.setRoomStateWithKey( - id, - PangeaEventTypes.activityRoomIds, - "", - {ModelKey.roomIds: ids}, - ); + await client.setRoomStateWithKey(id, PangeaEventTypes.activityRoomIds, "", { + ModelKey.roomIds: ids, + }); final newLength = _activityRoomIds.length; if (newLength == prevLength) { await syncFuture; diff --git a/lib/pangea/analytics_misc/text_loading_shimmer.dart b/lib/pangea/analytics_misc/text_loading_shimmer.dart index f22a87062..28050360d 100644 --- a/lib/pangea/analytics_misc/text_loading_shimmer.dart +++ b/lib/pangea/analytics_misc/text_loading_shimmer.dart @@ -3,16 +3,13 @@ import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; class TextLoadingShimmer extends StatelessWidget { final double width; final double? height; - const TextLoadingShimmer({ - super.key, - this.width = 140.0, - this.height, - }); + const TextLoadingShimmer({super.key, this.width = 140.0, this.height}); @override Widget build(BuildContext context) { @@ -25,7 +22,8 @@ class TextLoadingShimmer extends StatelessWidget { color: Theme.of(context).colorScheme.primary, ), height: - height ?? (AppConfig.messageFontSize * AppConfig.fontSizeFactor), + height ?? + (AppConfig.messageFontSize * AppSettings.fontSizeFactor.value), width: width, ), ); diff --git a/lib/pangea/analytics_page/activity_archive.dart b/lib/pangea/analytics_page/activity_archive.dart index c76bdff96..89cfc1c88 100644 --- a/lib/pangea/analytics_page/activity_archive.dart +++ b/lib/pangea/analytics_page/activity_archive.dart @@ -20,24 +20,22 @@ import '../../config/themes.dart'; import '../../widgets/avatar.dart'; class ActivityArchive extends StatelessWidget { - const ActivityArchive({ - super.key, - }); + const ActivityArchive({super.key}); @override Widget build(BuildContext context) { return StreamBuilder( - stream: Matrix.of(context) - .analyticsDataService - .updateDispatcher - .activityAnalyticsStream - .stream, + stream: Matrix.of( + context, + ).analyticsDataService.updateDispatcher.activityAnalyticsStream.stream, builder: (context, _) { - final Room? analyticsRoom = - Matrix.of(context).client.analyticsRoomLocal(); + final Room? analyticsRoom = Matrix.of( + context, + ).client.analyticsRoomLocal(); final archive = analyticsRoom?.archivedActivities ?? []; - final selectedRoomId = - GoRouterState.of(context).pathParameters['roomid']; + final selectedRoomId = GoRouterState.of( + context, + ).pathParameters['roomid']; return Scaffold( body: SafeArea( child: Padding( @@ -95,17 +93,12 @@ class AnalyticsActivityItem extends StatelessWidget { Widget build(BuildContext context) { final objective = room.activityPlan?.learningObjective ?? ''; final cefrLevel = room.activitySummary?.summary?.participants - .firstWhereOrNull( - (p) => p.participantId == room.client.userID, - ) + .firstWhereOrNull((p) => p.participantId == room.client.userID) ?.cefrLevel; final theme = Theme.of(context); return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), child: Material( color: selected ? theme.colorScheme.secondaryContainer : null, borderRadius: BorderRadius.circular(AppConfig.borderRadius), diff --git a/lib/pangea/analytics_page/empty_analytics_page.dart b/lib/pangea/analytics_page/empty_analytics_page.dart index 852dfa679..ea5cadc40 100644 --- a/lib/pangea/analytics_page/empty_analytics_page.dart +++ b/lib/pangea/analytics_page/empty_analytics_page.dart @@ -19,9 +19,8 @@ class EmptyAnalyticsPage extends StatelessWidget { imageUrl: "${AppConfig.assetsBaseURL}/${AnalyticsPageConstants.dinoBotFileName}", errorWidget: (context, url, error) => const SizedBox(), - placeholder: (context, url) => const Center( - child: CircularProgressIndicator.adaptive(), - ), + placeholder: (context, url) => + const Center(child: CircularProgressIndicator.adaptive()), ), ), ), diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index e7f5f5fc8..42d3e452a 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -36,10 +36,7 @@ class SelectedMorphChoice { final MorphFeaturesEnum feature; final String tag; - const SelectedMorphChoice({ - required this.feature, - required this.tag, - }); + const SelectedMorphChoice({required this.feature, required this.tag}); } class VocabPracticeChoice { @@ -58,10 +55,7 @@ class _PracticeQueueEntry { final MessageActivityRequest request; final Completer completer; - _PracticeQueueEntry({ - required this.request, - required this.completer, - }); + _PracticeQueueEntry({required this.request, required this.completer}); } class SessionLoader extends AsyncLoader { @@ -77,10 +71,7 @@ class AnalyticsPractice extends StatefulWidget { static bool bypassExitConfirmation = true; final ConstructTypeEnum type; - const AnalyticsPractice({ - super.key, - required this.type, - }); + const AnalyticsPractice({super.key, required this.type}); @override AnalyticsPracticeState createState() => AnalyticsPracticeState(); @@ -91,7 +82,7 @@ class AnalyticsPracticeState extends State late final SessionLoader _sessionLoader; final ValueNotifier> - activityState = ValueNotifier(const AsyncState.idle()); + activityState = ValueNotifier(const AsyncState.idle()); final Queue<_PracticeQueueEntry> _queue = Queue(); @@ -125,7 +116,10 @@ class AnalyticsPracticeState extends State _sessionLoader = SessionLoader(type: widget.type); _startSession(); _languageStreamSubscription = MatrixState - .pangeaController.userController.languageStream.stream + .pangeaController + .userController + .languageStream + .stream .listen((_) => _onLanguageUpdate()); } @@ -146,10 +140,10 @@ class AnalyticsPracticeState extends State MultipleChoicePracticeActivityModel? get _currentActivity => activityState.value is AsyncLoaded - ? (activityState.value - as AsyncLoaded) - .value - : null; + ? (activityState.value + as AsyncLoaded) + .value + : null; bool get _isComplete => _sessionLoader.value?.isComplete ?? false; @@ -269,7 +263,10 @@ class AnalyticsPracticeState extends State try { _clearState(); await _analyticsService - .updateDispatcher.constructUpdateStream.stream.first + .updateDispatcher + .constructUpdateStream + .stream + .first .timeout(const Duration(seconds: 10)); await reloadSession(); } catch (e) { @@ -403,22 +400,16 @@ class AnalyticsPracticeState extends State } AnalyticsPractice.bypassExitConfirmation = true; if (!mounted) return; - activityState.value = - AsyncState.error(L10n.of(context).oopsSomethingWentWrong); + activityState.value = AsyncState.error( + L10n.of(context).oopsSomethingWentWrong, + ); return; } - Future _fillActivityQueue( - List requests, - ) async { + Future _fillActivityQueue(List requests) async { for (final request in requests) { final completer = Completer(); - _queue.add( - _PracticeQueueEntry( - request: request, - completer: completer, - ), - ); + _queue.add(_PracticeQueueEntry(request: request, completer: completer)); try { final res = await _fetchActivity(request); if (!mounted) return; @@ -434,10 +425,7 @@ class AnalyticsPracticeState extends State Future _fetchActivity( MessageActivityRequest req, ) async { - final result = await PracticeRepo.getPracticeActivity( - req, - messageInfo: {}, - ); + final result = await PracticeRepo.getPracticeActivity(req, messageInfo: {}); if (result.isError || result.result is! MultipleChoicePracticeActivityModel) { @@ -539,10 +527,7 @@ class AnalyticsPracticeState extends State final use = OneConstructUse( useType: ConstructUseTypeEnum.ignPA, constructType: widget.type, - metadata: ConstructUseMetaData( - roomId: null, - timeStamp: DateTime.now(), - ), + metadata: ConstructUseMetaData(roomId: null, timeStamp: DateTime.now()), category: token.pos, lemma: token.lemma.text, form: token.lemma.text, @@ -576,9 +561,7 @@ class AnalyticsPracticeState extends State } } - Future onSelectChoice( - String choiceContent, - ) async { + Future onSelectChoice(String choiceContent) async { if (_currentActivity == null) return; final activity = _currentActivity!; @@ -612,16 +595,19 @@ class AnalyticsPracticeState extends State final use = activity.constructUse(choiceContent); _sessionLoader.value!.submitAnswer(use); - await _analyticsService.updateService - .addAnalytics(choiceTargetId(choiceContent), [use]); + await _analyticsService.updateService.addAnalytics( + choiceTargetId(choiceContent), + [use], + ); if (!isCorrect) return; // For audio activities, check if all answers have been selected if (isAudioActivity) { final allAnswers = activity.multipleChoiceContent.answers; - final allSelected = allAnswers - .every((answer) => _selectedCorrectAnswers.contains(answer)); + final allSelected = allAnswers.every( + (answer) => _selectedCorrectAnswers.contains(answer), + ); if (!allSelected) { return; @@ -683,7 +669,8 @@ class AnalyticsPracticeState extends State /// Returns congratulations message based on performance String getCompletionMessage(BuildContext context) { final accuracy = _sessionLoader.value?.state.accuracy ?? 0; - final hasTimeBonus = (_sessionLoader.value?.state.elapsedSeconds ?? 0) <= + final hasTimeBonus = + (_sessionLoader.value?.state.elapsedSeconds ?? 0) <= AnalyticsPracticeConstants.timeForBonus; final hintsUsed = hintsUsedNotifier.value; diff --git a/lib/pangea/analytics_practice/analytics_practice_session_model.dart b/lib/pangea/analytics_practice/analytics_practice_session_model.dart index 9e6057b63..c7c58cfc3 100644 --- a/lib/pangea/analytics_practice/analytics_practice_session_model.dart +++ b/lib/pangea/analytics_practice/analytics_practice_session_model.dart @@ -10,9 +10,7 @@ import 'package:fluffychat/pangea/practice_activities/practice_target.dart'; class ExampleMessageInfo { final List exampleMessage; - const ExampleMessageInfo({ - required this.exampleMessage, - }); + const ExampleMessageInfo({required this.exampleMessage}); Map toJson() { final segments = >[]; @@ -26,9 +24,7 @@ class ExampleMessageInfo { } } - return { - 'segments': segments, - }; + return {'segments': segments}; } factory ExampleMessageInfo.fromJson(Map json) { @@ -67,10 +63,7 @@ class AudioExampleMessage { }); Map toJson() { - return { - 'eventId': eventId, - 'roomId': roomId, - }; + return {'eventId': eventId, 'roomId': roomId}; } factory AudioExampleMessage.fromJson(Map json) { @@ -97,11 +90,11 @@ class AnalyticsActivityTarget { }); Map toJson() => { - 'target': target.toJson(), - 'grammarErrorInfo': grammarErrorInfo?.toJson(), - 'exampleMessage': exampleMessage?.toJson(), - 'audioExampleMessage': audioExampleMessage?.toJson(), - }; + 'target': target.toJson(), + 'grammarErrorInfo': grammarErrorInfo?.toJson(), + 'exampleMessage': exampleMessage?.toJson(), + 'audioExampleMessage': audioExampleMessage?.toJson(), + }; factory AnalyticsActivityTarget.fromJson(Map json) => AnalyticsActivityTarget( @@ -135,19 +128,23 @@ class AnalyticsPracticeSessionModel { }) : state = state ?? const AnalyticsPracticeSessionState(); // Maximum activities to attempt (including skips) - int get _maxAttempts => (AnalyticsPracticeConstants.practiceGroupSize + - AnalyticsPracticeConstants.errorBufferSize) - .clamp(0, practiceTargets.length) - .toInt(); + int get _maxAttempts => + (AnalyticsPracticeConstants.practiceGroupSize + + AnalyticsPracticeConstants.errorBufferSize) + .clamp(0, practiceTargets.length) + .toInt(); - int get _completionGoal => AnalyticsPracticeConstants.practiceGroupSize - .clamp(0, practiceTargets.length); + int get _completionGoal => AnalyticsPracticeConstants.practiceGroupSize.clamp( + 0, + practiceTargets.length, + ); // Total attempted so far (completed + skipped) int get _totalAttempted => state.currentIndex + state.skippedActivities; bool get isComplete { - final complete = state.finished || + final complete = + state.finished || state.currentIndex >= _completionGoal || _totalAttempted >= _maxAttempts; return complete; @@ -155,8 +152,10 @@ class AnalyticsPracticeSessionModel { double get progress { final possibleCompletions = - (state.currentIndex + _maxAttempts - _totalAttempted) - .clamp(0, _completionGoal); + (state.currentIndex + _maxAttempts - _totalAttempted).clamp( + 0, + _completionGoal, + ); return possibleCompletions > 0 ? (state.currentIndex / possibleCompletions).clamp(0.0, 1.0) : 1.0; @@ -184,13 +183,11 @@ class AnalyticsPracticeSessionModel { void completeActivity() => state = state.copyWith(currentIndex: state.currentIndex + 1); - void incrementSkippedActivities() => state = state.copyWith( - skippedActivities: state.skippedActivities + 1, - ); + void incrementSkippedActivities() => + state = state.copyWith(skippedActivities: state.skippedActivities + 1); - void submitAnswer(OneConstructUse use) => state = state.copyWith( - completedUses: [...state.completedUses, use], - ); + void submitAnswer(OneConstructUse use) => + state = state.copyWith(completedUses: [...state.completedUses, use]); factory AnalyticsPracticeSessionModel.fromJson(Map json) { return AnalyticsPracticeSessionModel( @@ -201,9 +198,7 @@ class AnalyticsPracticeSessionModel { .toList(), userL1: json['userL1'] as String, userL2: json['userL2'] as String, - state: AnalyticsPracticeSessionState.fromJson( - json, - ), + state: AnalyticsPracticeSessionState.fromJson(json), ); } @@ -261,22 +256,22 @@ class AnalyticsPracticeSessionState { completedUses.where((use) => use.xp > 0).map(_bonusUse).toList(); List get allBonusUses => [ - if (_giveAccuracyBonus) ..._bonusUses, - if (_giveTimeBonus) ..._bonusUses, - ]; + if (_giveAccuracyBonus) ..._bonusUses, + if (_giveTimeBonus) ..._bonusUses, + ]; OneConstructUse _bonusUse(OneConstructUse originalUse) => OneConstructUse( - useType: ConstructUseTypeEnum.bonus, - constructType: originalUse.constructType, - metadata: ConstructUseMetaData( - roomId: originalUse.metadata.roomId, - timeStamp: DateTime.now(), - ), - category: originalUse.category, - lemma: originalUse.lemma, - form: originalUse.form, - xp: ConstructUseTypeEnum.bonus.pointValue, - ); + useType: ConstructUseTypeEnum.bonus, + constructType: originalUse.constructType, + metadata: ConstructUseMetaData( + roomId: originalUse.metadata.roomId, + timeStamp: DateTime.now(), + ), + category: originalUse.category, + lemma: originalUse.lemma, + form: originalUse.form, + xp: ConstructUseTypeEnum.bonus.pointValue, + ); AnalyticsPracticeSessionState copyWith({ List? completedUses, @@ -306,7 +301,8 @@ class AnalyticsPracticeSessionState { factory AnalyticsPracticeSessionState.fromJson(Map json) { return AnalyticsPracticeSessionState( - completedUses: (json['completedUses'] as List?) + completedUses: + (json['completedUses'] as List?) ?.map((e) => OneConstructUse.fromJson(e)) .whereType() .toList() ?? diff --git a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart index d50154085..41dc98542 100644 --- a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart +++ b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart @@ -37,7 +37,8 @@ class AnalyticsPracticeSessionRepo { final List targets = []; if (type == ConstructTypeEnum.vocab) { - const totalNeeded = AnalyticsPracticeConstants.practiceGroupSize + + const totalNeeded = + AnalyticsPracticeConstants.practiceGroupSize + AnalyticsPracticeConstants.errorBufferSize; final halfNeeded = (totalNeeded / 2).ceil(); @@ -79,7 +80,8 @@ class AnalyticsPracticeSessionRepo { (AnalyticsPracticeConstants.practiceGroupSize + AnalyticsPracticeConstants.errorBufferSize)) { final morphs = await _fetchMorphs(); - final remainingCount = (AnalyticsPracticeConstants.practiceGroupSize + + final remainingCount = + (AnalyticsPracticeConstants.practiceGroupSize + AnalyticsPracticeConstants.errorBufferSize) - targets.length; final morphEntries = morphs.take(remainingCount); @@ -118,7 +120,9 @@ class AnalyticsPracticeSessionRepo { static Future> _fetchVocab() async { final constructs = await MatrixState - .pangeaController.matrixState.analyticsDataService + .pangeaController + .matrixState + .analyticsDataService .getAggregatedConstructs(ConstructTypeEnum.vocab) .then((map) => map.values.toList()); @@ -148,9 +152,11 @@ class AnalyticsPracticeSessionRepo { } static Future> - _fetchAudio() async { + _fetchAudio() async { final constructs = await MatrixState - .pangeaController.matrixState.analyticsDataService + .pangeaController + .matrixState + .analyticsDataService .getAggregatedConstructs(ConstructTypeEnum.vocab) .then((map) => map.values.toList()); @@ -180,11 +186,11 @@ class AnalyticsPracticeSessionRepo { // Try to get an audio example message with token data for this lemma final audioExampleMessage = await ExampleMessageUtil.getAudioExampleMessage( - await MatrixState.pangeaController.matrixState.analyticsDataService - .getConstructUse(construct.id), - MatrixState.pangeaController.matrixState.client, - noBold: true, - ); + await MatrixState.pangeaController.matrixState.analyticsDataService + .getConstructUse(construct.id), + MatrixState.pangeaController.matrixState.client, + noBold: true, + ); // Only add to targets if we found an example message AND its eventId hasn't been used if (audioExampleMessage != null) { @@ -205,14 +211,18 @@ class AnalyticsPracticeSessionRepo { static Future> _fetchMorphs() async { final constructs = await MatrixState - .pangeaController.matrixState.analyticsDataService + .pangeaController + .matrixState + .analyticsDataService .getAggregatedConstructs(ConstructTypeEnum.morph) .then((map) => map.values.toList()); final morphInfoRequest = MorphInfoRequest( - userL1: MatrixState.pangeaController.userController.userL1?.langCode ?? + userL1: + MatrixState.pangeaController.userController.userL1?.langCode ?? LanguageKeys.defaultLanguage, - userL2: MatrixState.pangeaController.userController.userL2?.langCode ?? + userL2: + MatrixState.pangeaController.userController.userL2?.langCode ?? LanguageKeys.defaultLanguage, ); @@ -289,11 +299,7 @@ class AnalyticsPracticeSessionRepo { seenForms.add(form); final token = PangeaToken( - lemma: Lemma( - text: form, - saveVocab: true, - form: form, - ), + lemma: Lemma(text: form, saveVocab: true, form: form), text: PangeaTokenText.fromString(form), pos: 'other', morph: {feature: use.lemma}, @@ -314,16 +320,18 @@ class AnalyticsPracticeSessionRepo { static Future> _fetchErrors() async { final allRecentUses = await MatrixState - .pangeaController.matrixState.analyticsDataService + .pangeaController + .matrixState + .analyticsDataService .getUses( - count: 300, - filterCapped: false, - types: [ - ConstructUseTypeEnum.ga, - ConstructUseTypeEnum.corGE, - ConstructUseTypeEnum.incGE, - ], - ); + count: 300, + filterCapped: false, + types: [ + ConstructUseTypeEnum.ga, + ConstructUseTypeEnum.corGE, + ConstructUseTypeEnum.incGE, + ], + ); // Filter for grammar error uses final grammarErrorUses = allRecentUses @@ -419,9 +427,7 @@ class AnalyticsPracticeSessionRepo { .where( (token) => token.lemma.saveVocab && - choices.any( - (choice) => choice.contains(token.text.content), - ), + choices.any((choice) => choice.contains(token.text.content)), ) .toList(); @@ -437,8 +443,9 @@ class AnalyticsPracticeSessionRepo { category: firstToken.pos, ); - final hasRecentPractice = - recentlyPracticedConstructs.contains(tokenIdentifier); + final hasRecentPractice = recentlyPracticedConstructs.contains( + tokenIdentifier, + ); if (hasRecentPractice) continue; diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index ac863e992..9046f840a 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; @@ -48,7 +49,7 @@ class AnalyticsPracticeView extends StatelessWidget { Expanded( child: ValueListenableBuilder( valueListenable: controller.progressNotifier, - builder: (context, progress, __) { + builder: (context, progress, _) { return AnimatedProgressBar( height: 20.0, widthPercent: progress, @@ -60,7 +61,7 @@ class AnalyticsPracticeView extends StatelessWidget { //keep track of state to update timer ValueListenableBuilder( valueListenable: controller.sessionState, - builder: (context, state, __) { + builder: (context, state, _) { if (state is AsyncLoaded) { return PracticeTimerWidget( key: ValueKey(state.value.startedAt), @@ -76,20 +77,16 @@ class AnalyticsPracticeView extends StatelessWidget { ), ), body: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 8.0), child: MaxWidthBody( withScrolling: false, showBorder: false, child: ValueListenableBuilder( valueListenable: controller.sessionState, - builder: (context, state, __) { + builder: (context, state, _) { return switch (state) { AsyncError(:final error) => - ErrorIndicator( - message: error.toLocalizedString(context), - ), + ErrorIndicator(message: error.toLocalizedString(context)), AsyncLoaded(:final value) => value.isComplete ? CompletedActivitySessionView(state.value, controller) @@ -107,9 +104,7 @@ class AnalyticsPracticeView extends StatelessWidget { class _AnalyticsActivityView extends StatelessWidget { final AnalyticsPracticeState controller; - const _AnalyticsActivityView( - this.controller, - ); + const _AnalyticsActivityView(this.controller); @override Widget build(BuildContext context) { @@ -133,18 +128,17 @@ class _AnalyticsActivityView extends StatelessWidget { //per-activity instructions, add switch statement once there are more types const InstructionsInlineTooltip( instructionsEnum: InstructionsEnum.selectMeaning, - padding: EdgeInsets.symmetric( - vertical: 8.0, - ), + padding: EdgeInsets.symmetric(vertical: 8.0), ), SizedBox( height: 75.0, child: ValueListenableBuilder( valueListenable: controller.activityTarget, - builder: (context, target, __) { + builder: (context, target, _) { if (target == null) return const SizedBox.shrink(); - final isAudioActivity = target.target.activityType == + final isAudioActivity = + target.target.activityType == ActivityTypeEnum.lemmaAudio; final isVocabType = controller.widget.type == ConstructTypeEnum.vocab; @@ -163,9 +157,15 @@ class _AnalyticsActivityView extends StatelessWidget { if (isVocabType && !isAudioActivity) PhoneticTranscriptionWidget( text: target - .target.tokens.first.vocabConstructID.lemma, + .target + .tokens + .first + .vocabConstructID + .lemma, textLanguage: MatrixState - .pangeaController.userController.userL2!, + .pangeaController + .userController + .userL2!, style: const TextStyle(fontSize: 14.0), ), ], @@ -200,81 +200,75 @@ class _AnalyticsActivityView extends StatelessWidget { class _AnalyticsPracticeCenterContent extends StatelessWidget { final AnalyticsPracticeState controller; - const _AnalyticsPracticeCenterContent({ - required this.controller, - }); + const _AnalyticsPracticeCenterContent({required this.controller}); @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: controller.activityTarget, - builder: (context, target, __) => switch (target?.target.activityType) { + builder: (context, target, _) => switch (target?.target.activityType) { null => const SizedBox(), ActivityTypeEnum.grammarError => SingleChildScrollView( - child: ListenableBuilder( - listenable: Listenable.merge([ - controller.activityState, - controller.hintPressedNotifier, - ]), - builder: (context, __) { - final state = controller.activityState.value; - if (state - is! AsyncLoaded) { - return const SizedBox(); - } - final activity = state.value; - if (activity is! GrammarErrorPracticeActivityModel) { - return const SizedBox(); - } - return _ErrorBlankWidget( - key: ValueKey( - '${activity.eventID}_${activity.errorOffset}_${activity.errorLength}', - ), - activity: activity, - showTranslation: controller.hintPressedNotifier.value, - ); - }, - ), - ), - ActivityTypeEnum.grammarCategory => Center( - child: _ExampleMessageWidget( - controller.getExampleMessage(target!), - ), - ), - ActivityTypeEnum.lemmaAudio => ValueListenableBuilder( - valueListenable: controller.activityState, - builder: (context, state, __) => switch (state) { - AsyncLoaded( - value: final VocabAudioPracticeActivityModel activity - ) => - SizedBox( - height: 100.0, - child: Center( - child: AudioPlayerWidget( - null, - color: Theme.of(context).colorScheme.primary, - linkColor: Theme.of(context).colorScheme.secondary, - fontSize: - AppConfig.fontSizeFactor * AppConfig.messageFontSize, - eventId: '${activity.eventId}_practice', - roomId: activity.roomId!, - senderId: Matrix.of(context).client.userID!, - matrixFile: controller.getAudioFile(activity.eventId)!, - autoplay: true, - ), - ), + child: ListenableBuilder( + listenable: Listenable.merge([ + controller.activityState, + controller.hintPressedNotifier, + ]), + builder: (context, _) { + final state = controller.activityState.value; + if (state is! AsyncLoaded) { + return const SizedBox(); + } + final activity = state.value; + if (activity is! GrammarErrorPracticeActivityModel) { + return const SizedBox(); + } + return _ErrorBlankWidget( + key: ValueKey( + '${activity.eventID}_${activity.errorOffset}_${activity.errorLength}', ), - _ => const SizedBox(height: 100.0), + activity: activity, + showTranslation: controller.hintPressedNotifier.value, + ); }, ), - _ => SizedBox( - height: 100.0, - child: Center( - child: _ExampleMessageWidget( - controller.getExampleMessage(target!), + ), + ActivityTypeEnum.grammarCategory => Center( + child: _ExampleMessageWidget(controller.getExampleMessage(target!)), + ), + ActivityTypeEnum.lemmaAudio => ValueListenableBuilder( + valueListenable: controller.activityState, + builder: (context, state, _) => switch (state) { + AsyncLoaded( + value: final VocabAudioPracticeActivityModel activity, + ) => + SizedBox( + height: 100.0, + child: Center( + child: AudioPlayerWidget( + null, + color: Theme.of(context).colorScheme.primary, + linkColor: Theme.of(context).colorScheme.secondary, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, + eventId: '${activity.eventId}_practice', + roomId: activity.roomId!, + senderId: Matrix.of(context).client.userID!, + matrixFile: controller.getAudioFile(activity.eventId)!, + autoplay: true, + ), + ), ), - ), + _ => const SizedBox(height: 100.0), + }, + ), + _ => SizedBox( + height: 100.0, + child: Center( + child: _ExampleMessageWidget(controller.getExampleMessage(target!)), ), + ), }, ); } @@ -283,10 +277,7 @@ class _AnalyticsPracticeCenterContent extends StatelessWidget { class _AudioCompletionWidget extends StatelessWidget { final AnalyticsPracticeState controller; - const _AudioCompletionWidget({ - super.key, - required this.controller, - }); + const _AudioCompletionWidget({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -299,10 +290,7 @@ class _AudioCompletionWidget extends StatelessWidget { return Padding( padding: const EdgeInsets.all(16.0), child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Color.alphaBlend( Colors.white.withAlpha(180), @@ -314,7 +302,8 @@ class _AudioCompletionWidget extends StatelessWidget { text: TextSpan( style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: + AppSettings.fontSizeFactor.value * AppConfig.messageFontSize, ), children: exampleMessage, ), @@ -339,10 +328,7 @@ class _ExampleMessageWidget extends StatelessWidget { } return Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Color.alphaBlend( Colors.white.withAlpha(180), @@ -354,7 +340,9 @@ class _ExampleMessageWidget extends StatelessWidget { text: TextSpan( style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), children: snapshot.data!, ), @@ -374,7 +362,7 @@ class _HintsCounterBar extends StatelessWidget { Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: controller.hintsUsedNotifier, - builder: (context, hintsUsed, __) { + builder: (context, hintsUsed, _) { return Padding( padding: const EdgeInsets.only(top: 4.0), child: Row( @@ -410,7 +398,7 @@ class _HintSection extends StatelessWidget { controller.hintPressedNotifier, controller.hintsUsedNotifier, ]), - builder: (context, __) { + builder: (context, _) { final state = controller.activityState.value; if (state is! AsyncLoaded) { return const SizedBox.shrink(); @@ -422,9 +410,7 @@ class _HintSection extends StatelessWidget { final maxHintsReached = hintsUsed >= AnalyticsPracticeState.maxHints; return ConstrainedBox( - constraints: const BoxConstraints( - minHeight: 50.0, - ), + constraints: const BoxConstraints(minHeight: 50.0), child: Builder( builder: (context) { // For grammar category: fade out button and show hint content @@ -435,8 +421,9 @@ class _HintSection extends StatelessWidget { ? CrossFadeState.showSecond : CrossFadeState.showFirst, firstChild: HintButton( - onPressed: - maxHintsReached ? () {} : controller.onHintPressed, + onPressed: maxHintsReached + ? () {} + : controller.onHintPressed, depressed: maxHintsReached, ), secondChild: MorphMeaningWidget( @@ -464,9 +451,7 @@ class _HintSection extends StatelessWidget { class _WrongAnswerFeedback extends StatelessWidget { final AnalyticsPracticeState controller; - const _WrongAnswerFeedback({ - required this.controller, - }); + const _WrongAnswerFeedback({required this.controller}); @override Widget build(BuildContext context) { @@ -486,8 +471,9 @@ class _WrongAnswerFeedback extends StatelessWidget { } final activity = activityState.value; - final isWrongAnswer = - !activity.multipleChoiceContent.isCorrect(selectedChoice.tag); + final isWrongAnswer = !activity.multipleChoiceContent.isCorrect( + selectedChoice.tag, + ); if (!isWrongAnswer) { return const SizedBox.shrink(); @@ -543,8 +529,10 @@ class _ErrorBlankWidget extends StatelessWidget { trimmedBefore = true; } - final before = - chars.skip(beforeStart).take(errorOffset - beforeStart).toString(); + final before = chars + .skip(beforeStart) + .take(errorOffset - beforeStart) + .toString(); // ---------- AFTER ---------- int afterEnd = totalLength; @@ -568,10 +556,7 @@ class _ErrorBlankWidget extends StatelessWidget { final after = chars.skip(errorEnd).take(afterEnd - errorEnd).toString(); return Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Color.alphaBlend( Colors.white.withAlpha(180), @@ -586,7 +571,9 @@ class _ErrorBlankWidget extends StatelessWidget { text: TextSpan( style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), children: [ if (trimmedBefore) const TextSpan(text: '…'), @@ -619,7 +606,8 @@ class _ErrorBlankWidget extends StatelessWidget { activity.translation, style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryFixed, - fontSize: AppConfig.fontSizeFactor * + fontSize: + AppSettings.fontSizeFactor.value * AppConfig.messageFontSize, fontStyle: FontStyle.italic, ), @@ -669,10 +657,7 @@ class HintButton extends StatelessWidget { shape: BoxShape.circle, ), ), - const Icon( - Icons.lightbulb_outline, - size: 20, - ), + const Icon(Icons.lightbulb_outline, size: 20), ], ), ); @@ -682,23 +667,21 @@ class HintButton extends StatelessWidget { class _ActivityChoicesWidget extends StatelessWidget { final AnalyticsPracticeState controller; - const _ActivityChoicesWidget( - this.controller, - ); + const _ActivityChoicesWidget(this.controller); @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: controller.activityState, - builder: (context, state, __) { + builder: (context, state, _) { return switch (state) { AsyncLoading() => const Center( - child: SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator.adaptive(), - ), + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator.adaptive(), ), + ), AsyncError(:final error) => Column( mainAxisAlignment: MainAxisAlignment.center, @@ -716,7 +699,7 @@ class _ActivityChoicesWidget extends StatelessWidget { AsyncLoaded(:final value) => ValueListenableBuilder( valueListenable: controller.enableChoicesNotifier, - builder: (context, enabled, __) { + builder: (context, enabled, _) { final choices = controller.filteredChoices(value); final isAudioActivity = value.activityType == ActivityTypeEnum.lemmaAudio; @@ -725,7 +708,7 @@ class _ActivityChoicesWidget extends StatelessWidget { // For audio activities, use AnimatedSwitcher to fade between choices and example message return ValueListenableBuilder( valueListenable: controller.showingAudioCompletion, - builder: (context, showingCompletion, __) { + builder: (context, showingCompletion, _) { return AnimatedSwitcher( duration: const Duration(milliseconds: 500), layoutBuilder: (currentChild, previousChildren) { @@ -733,7 +716,7 @@ class _ActivityChoicesWidget extends StatelessWidget { alignment: Alignment.topCenter, children: [ ...previousChildren, - if (currentChild != null) currentChild, + ?currentChild, ], ); }, @@ -753,13 +736,12 @@ class _ActivityChoicesWidget extends StatelessWidget { .map( (choice) => _ChoiceCard( activity: value, - targetId: controller - .choiceTargetId(choice.choiceId), - choiceId: choice.choiceId, - onPressed: () => - controller.onSelectChoice( + targetId: controller.choiceTargetId( choice.choiceId, ), + choiceId: choice.choiceId, + onPressed: () => controller + .onSelectChoice(choice.choiceId), cardHeight: 48.0, choiceText: choice.choiceText, choiceEmoji: choice.choiceEmoji, @@ -784,9 +766,8 @@ class _ActivityChoicesWidget extends StatelessWidget { activity: value, targetId: controller.choiceTargetId(choice.choiceId), choiceId: choice.choiceId, - onPressed: () => controller.onSelectChoice( - choice.choiceId, - ), + onPressed: () => + controller.onSelectChoice(choice.choiceId), cardHeight: 60.0, choiceText: choice.choiceText, choiceEmoji: choice.choiceEmoji, @@ -798,11 +779,9 @@ class _ActivityChoicesWidget extends StatelessWidget { }, ), _ => Container( - constraints: const BoxConstraints(maxHeight: 400.0), - child: const Center( - child: CircularProgressIndicator.adaptive(), - ), - ), + constraints: const BoxConstraints(maxHeight: 400.0), + child: const Center(child: CircularProgressIndicator.adaptive()), + ), }; }, ); @@ -812,15 +791,13 @@ class _ActivityChoicesWidget extends StatelessWidget { class _AudioContinueButton extends StatelessWidget { final AnalyticsPracticeState controller; - const _AudioContinueButton({ - required this.controller, - }); + const _AudioContinueButton({required this.controller}); @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: controller.activityState, - builder: (context, state, __) { + builder: (context, state, _) { // Only show for audio activities if (state is! AsyncLoaded) { return const SizedBox.shrink(); @@ -833,7 +810,7 @@ class _AudioContinueButton extends StatelessWidget { return ValueListenableBuilder( valueListenable: controller.showingAudioCompletion, - builder: (context, showingCompletion, __) { + builder: (context, showingCompletion, _) { return Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( @@ -917,10 +894,7 @@ class _ChoiceCard extends StatelessWidget { height: cardHeight, isEnabled: enabled, shrinkWrap: shrinkWrap, - child: Text( - choiceText, - textAlign: TextAlign.center, - ), + child: Text(choiceText, textAlign: TextAlign.center), ); case ActivityTypeEnum.grammarCategory: diff --git a/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart b/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart index 230944160..60a38c82e 100644 --- a/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart +++ b/lib/pangea/analytics_practice/choice_cards/grammar_choice_card.dart @@ -34,13 +34,10 @@ class GrammarChoiceCard extends StatelessWidget { Widget build(BuildContext context) { final baseTextSize = (Theme.of(context).textTheme.titleMedium?.fontSize ?? 16) * - (height / 72.0).clamp(1.0, 1.4); + (height / 72.0).clamp(1.0, 1.4); final emojiSize = baseTextSize * 1.5; - final copy = getGrammarCopy( - category: feature.name, - lemma: tag, - context: context, - ) ?? + final copy = + getGrammarCopy(category: feature.name, lemma: tag, context: context) ?? tag; return GameChoiceCard( @@ -69,9 +66,7 @@ class GrammarChoiceCard extends StatelessWidget { copy, overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, - style: TextStyle( - fontSize: baseTextSize, - ), + style: TextStyle(fontSize: baseTextSize), ), ), ], diff --git a/lib/pangea/analytics_practice/choice_cards/meaning_choice_card.dart b/lib/pangea/analytics_practice/choice_cards/meaning_choice_card.dart index f51394054..1dbe54c93 100644 --- a/lib/pangea/analytics_practice/choice_cards/meaning_choice_card.dart +++ b/lib/pangea/analytics_practice/choice_cards/meaning_choice_card.dart @@ -30,7 +30,7 @@ class MeaningChoiceCard extends StatelessWidget { Widget build(BuildContext context) { final baseTextSize = (Theme.of(context).textTheme.titleMedium?.fontSize ?? 16) * - (height / 72.0).clamp(1.0, 1.4); + (height / 72.0).clamp(1.0, 1.4); final emojiSize = baseTextSize * 1.2; return GameChoiceCard( @@ -48,10 +48,7 @@ class MeaningChoiceCard extends StatelessWidget { width: height * .7, height: height, child: Center( - child: Text( - emoji!, - style: TextStyle(fontSize: emojiSize), - ), + child: Text(emoji!, style: TextStyle(fontSize: emojiSize)), ), ), Expanded( @@ -60,9 +57,7 @@ class MeaningChoiceCard extends StatelessWidget { overflow: TextOverflow.ellipsis, maxLines: 2, textAlign: TextAlign.left, - style: TextStyle( - fontSize: baseTextSize, - ), + style: TextStyle(fontSize: baseTextSize), ), ), ], @@ -75,10 +70,7 @@ class MeaningChoiceCard extends StatelessWidget { width: height * .7, height: height, child: Center( - child: Text( - emoji!, - style: TextStyle(fontSize: emojiSize), - ), + child: Text(emoji!, style: TextStyle(fontSize: emojiSize)), ), ), Expanded( @@ -87,9 +79,7 @@ class MeaningChoiceCard extends StatelessWidget { overflow: TextOverflow.ellipsis, maxLines: 2, textAlign: TextAlign.left, - style: TextStyle( - fontSize: baseTextSize, - ), + style: TextStyle(fontSize: baseTextSize), ), ), ], diff --git a/lib/pangea/analytics_practice/completed_activity_session_view.dart b/lib/pangea/analytics_practice/completed_activity_session_view.dart index 31df2f6ce..9d351110d 100644 --- a/lib/pangea/analytics_practice/completed_activity_session_view.dart +++ b/lib/pangea/analytics_practice/completed_activity_session_view.dart @@ -48,9 +48,9 @@ class CompletedActivitySessionView extends StatelessWidget { children: [ Text( controller.getCompletionMessage(context), - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), Expanded( @@ -87,20 +87,20 @@ class CompletedActivitySessionView extends StatelessWidget { widthPercent: snapshot.hasData ? snapshot.data!.levelProgress : 0.0, - backgroundColor: Theme.of(context) - .colorScheme - .surfaceContainerHighest, + backgroundColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, duration: const Duration(milliseconds: 500), ), ), ), Text( "+ ${session.state.allXPGained} XP", - style: - Theme.of(context).textTheme.titleLarge?.copyWith( - color: AppConfig.goldLight, - fontWeight: FontWeight.bold, - ), + style: Theme.of(context).textTheme.titleLarge + ?.copyWith( + color: AppConfig.goldLight, + fontWeight: FontWeight.bold, + ), ), ], ), @@ -115,14 +115,14 @@ class CompletedActivitySessionView extends StatelessWidget { markerWidth: 20.0, markerColor: AppConfig.success, backgroundColor: !accuracyAchievement - ? Theme.of(context) - .colorScheme - .surfaceContainerHighest + ? Theme.of( + context, + ).colorScheme.surfaceContainerHighest : Color.alphaBlend( AppConfig.goldLight.withValues(alpha: 0.3), - Theme.of(context) - .colorScheme - .surfaceContainerHighest, + Theme.of( + context, + ).colorScheme.surfaceContainerHighest, ), ), ), @@ -132,9 +132,7 @@ class CompletedActivitySessionView extends StatelessWidget { "${L10n.of(context).time}: ${_formatTime(elapsedSeconds)}", isAchievement: timeAchievement, achievementText: "+ ${session.state.timeBonusXP} XP", - child: TimeStarsWidget( - elapsedSeconds: elapsedSeconds, - ), + child: TimeStarsWidget(elapsedSeconds: elapsedSeconds), ), Column( children: [ @@ -149,11 +147,7 @@ class CompletedActivitySessionView extends StatelessWidget { onPressed: () => controller.reloadSession(), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - L10n.of(context).anotherRound, - ), - ], + children: [Text(L10n.of(context).anotherRound)], ), ), const SizedBox(height: 16), @@ -169,11 +163,7 @@ class CompletedActivitySessionView extends StatelessWidget { }, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - L10n.of(context).done, - ), - ], + children: [Text(L10n.of(context).done)], ), ), ], @@ -196,10 +186,7 @@ class CompletedActivitySessionView extends StatelessWidget { class TimeStarsWidget extends StatelessWidget { final int elapsedSeconds; - const TimeStarsWidget({ - required this.elapsedSeconds, - super.key, - }); + const TimeStarsWidget({required this.elapsedSeconds, super.key}); int get starCount { const timeForBonus = AnalyticsPracticeConstants.timeForBonus; diff --git a/lib/pangea/analytics_practice/grammar_error_practice_generator.dart b/lib/pangea/analytics_practice/grammar_error_practice_generator.dart index 9284a3c9f..762f8005a 100644 --- a/lib/pangea/analytics_practice/grammar_error_practice_generator.dart +++ b/lib/pangea/analytics_practice/grammar_error_practice_generator.dart @@ -5,9 +5,7 @@ import 'package:fluffychat/pangea/practice_activities/multiple_choice_activity_m import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; class GrammarErrorPracticeGenerator { - static Future get( - MessageActivityRequest req, - ) async { + static Future get(MessageActivityRequest req) async { assert( req.grammarErrorInfo != null, 'Grammar error info must be provided for grammar error practice', diff --git a/lib/pangea/analytics_practice/morph_category_activity_generator.dart b/lib/pangea/analytics_practice/morph_category_activity_generator.dart index 1a7069c8a..709dd7219 100644 --- a/lib/pangea/analytics_practice/morph_category_activity_generator.dart +++ b/lib/pangea/analytics_practice/morph_category_activity_generator.dart @@ -9,9 +9,7 @@ import 'package:fluffychat/pangea/practice_activities/practice_activity_model.da import 'package:fluffychat/widgets/matrix.dart'; class MorphCategoryActivityGenerator { - static Future get( - MessageActivityRequest req, - ) async { + static Future get(MessageActivityRequest req) async { if (req.target.morphFeature == null) { throw ArgumentError( "MorphCategoryActivityGenerator requires a targetMorphFeature", @@ -21,9 +19,7 @@ class MorphCategoryActivityGenerator { final feature = req.target.morphFeature!; final morphTag = req.target.tokens.first.getMorphTag(feature); if (morphTag == null) { - throw ArgumentError( - "Token does not have the specified morph feature", - ); + throw ArgumentError("Token does not have the specified morph feature"); } MorphFeaturesAndTags morphs = defaultMorphMapping; diff --git a/lib/pangea/analytics_practice/percent_marker_bar.dart b/lib/pangea/analytics_practice/percent_marker_bar.dart index 2bc8b63a0..469b7fa4b 100644 --- a/lib/pangea/analytics_practice/percent_marker_bar.dart +++ b/lib/pangea/analytics_practice/percent_marker_bar.dart @@ -31,8 +31,10 @@ class PercentMarkerBar extends StatelessWidget { final targetPosition = totalWidth * widthPercent.clamp(0.0, 1.0); // Calculate the start position, clamping to keep marker within bounds - final markerStart = - (targetPosition - halfMarker).clamp(0.0, totalWidth - markerWidth); + final markerStart = (targetPosition - halfMarker).clamp( + 0.0, + totalWidth - markerWidth, + ); return Stack( alignment: Alignment.centerLeft, @@ -45,7 +47,8 @@ class PercentMarkerBar extends StatelessWidget { width: constraints.maxWidth, decoration: BoxDecoration( borderRadius: BorderRadius.circular(height / 2), - color: backgroundColor ?? + color: + backgroundColor ?? Theme.of(context).colorScheme.secondaryContainer, ), ), diff --git a/lib/pangea/analytics_practice/practice_timer_widget.dart b/lib/pangea/analytics_practice/practice_timer_widget.dart index 005efe0cc..41c8a669e 100644 --- a/lib/pangea/analytics_practice/practice_timer_widget.dart +++ b/lib/pangea/analytics_practice/practice_timer_widget.dart @@ -84,9 +84,9 @@ class PracticeTimerWidgetState extends State { const SizedBox(width: 4.0), Text( _formatTime(_getCurrentSeconds()), - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), ), ], ); diff --git a/lib/pangea/analytics_practice/stat_card.dart b/lib/pangea/analytics_practice/stat_card.dart index 2c1f5d570..88f3430fe 100644 --- a/lib/pangea/analytics_practice/stat_card.dart +++ b/lib/pangea/analytics_practice/stat_card.dart @@ -35,17 +35,13 @@ class StatCard extends StatelessWidget { if (!isColumnMode) { titleStyle = theme.textTheme.bodyMedium; } - titleStyle = titleStyle?.copyWith( - fontWeight: FontWeight.bold, - ); + titleStyle = titleStyle?.copyWith(fontWeight: FontWeight.bold); TextStyle? achievementStyle = theme.textTheme.titleSmall; if (!isColumnMode) { achievementStyle = theme.textTheme.bodySmall; } - achievementStyle = achievementStyle?.copyWith( - fontWeight: FontWeight.bold, - ); + achievementStyle = achievementStyle?.copyWith(fontWeight: FontWeight.bold); return Container( decoration: BoxDecoration( @@ -59,20 +55,12 @@ class StatCard extends StatelessWidget { children: [ Row( children: [ - Icon( - icon, - ), + Icon(icon), const SizedBox(width: 8), - Text( - text, - style: titleStyle, - ), + Text(text, style: titleStyle), if (isAchievement) ...[ const Spacer(), - Text( - achievementText, - style: achievementStyle, - ), + Text(achievementText, style: achievementStyle), ], ], ), diff --git a/lib/pangea/analytics_practice/vocab_audio_activity_generator.dart b/lib/pangea/analytics_practice/vocab_audio_activity_generator.dart index bf3ee7770..f13839116 100644 --- a/lib/pangea/analytics_practice/vocab_audio_activity_generator.dart +++ b/lib/pangea/analytics_practice/vocab_audio_activity_generator.dart @@ -5,9 +5,7 @@ import 'package:fluffychat/pangea/practice_activities/multiple_choice_activity_m import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; class VocabAudioActivityGenerator { - static Future get( - MessageActivityRequest req, - ) async { + static Future get(MessageActivityRequest req) async { final token = req.target.tokens.first; final audioExample = req.audioExampleMessage; @@ -62,7 +60,8 @@ class VocabAudioActivityGenerator { ), roomId: audioExample?.roomId, eventId: audioExample?.eventId, - exampleMessage: audioExample?.exampleMessage ?? + exampleMessage: + audioExample?.exampleMessage ?? const ExampleMessageInfo(exampleMessage: []), ), ); diff --git a/lib/pangea/analytics_practice/vocab_meaning_activity_generator.dart b/lib/pangea/analytics_practice/vocab_meaning_activity_generator.dart index 7acc77b0d..14757ddf0 100644 --- a/lib/pangea/analytics_practice/vocab_meaning_activity_generator.dart +++ b/lib/pangea/analytics_practice/vocab_meaning_activity_generator.dart @@ -4,12 +4,11 @@ import 'package:fluffychat/pangea/practice_activities/multiple_choice_activity_m import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; class VocabMeaningActivityGenerator { - static Future get( - MessageActivityRequest req, - ) async { + static Future get(MessageActivityRequest req) async { final token = req.target.tokens.first; - final choices = - await LemmaActivityGenerator.lemmaActivityDistractors(token); + final choices = await LemmaActivityGenerator.lemmaActivityDistractors( + token, + ); if (!choices.contains(token.vocabConstructID)) { choices.add(token.vocabConstructID); diff --git a/lib/pangea/analytics_settings/analytics_settings_extension.dart b/lib/pangea/analytics_settings/analytics_settings_extension.dart index 7e7316ea9..63afccf49 100644 --- a/lib/pangea/analytics_settings/analytics_settings_extension.dart +++ b/lib/pangea/analytics_settings/analytics_settings_extension.dart @@ -16,9 +16,7 @@ extension AnalyticsSettingsRoomExtension on Room { Set get blockedConstructs => analyticsSettings.blockedConstructs; - Future setAnalyticsSettings( - AnalyticsSettingsModel settings, - ) async { + Future setAnalyticsSettings(AnalyticsSettingsModel settings) async { await client.setRoomStateWithKey( id, PangeaEventTypes.analyticsSettings, diff --git a/lib/pangea/analytics_settings/analytics_settings_model.dart b/lib/pangea/analytics_settings/analytics_settings_model.dart index 149964747..1566fabf5 100644 --- a/lib/pangea/analytics_settings/analytics_settings_model.dart +++ b/lib/pangea/analytics_settings/analytics_settings_model.dart @@ -3,9 +3,7 @@ import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; class AnalyticsSettingsModel { final Set blockedConstructs; - const AnalyticsSettingsModel({ - required this.blockedConstructs, - }); + const AnalyticsSettingsModel({required this.blockedConstructs}); AnalyticsSettingsModel copyWith({ Set? blockedConstructs, @@ -23,9 +21,7 @@ class AnalyticsSettingsModel { blockedConstructs.add(ConstructIdentifier.fromJson(lemma)); } } - return AnalyticsSettingsModel( - blockedConstructs: blockedConstructs, - ); + return AnalyticsSettingsModel(blockedConstructs: blockedConstructs); } Map toJson() { diff --git a/lib/pangea/analytics_summary/animated_progress_bar.dart b/lib/pangea/analytics_summary/animated_progress_bar.dart index eece5bf6d..354edd373 100644 --- a/lib/pangea/analytics_summary/animated_progress_bar.dart +++ b/lib/pangea/analytics_summary/animated_progress_bar.dart @@ -38,7 +38,8 @@ class AnimatedProgressBar extends StatelessWidget { borderRadius: const BorderRadius.all( Radius.circular(AppConfig.borderRadius), ), - color: backgroundColor ?? + color: + backgroundColor ?? Theme.of(context).colorScheme.secondaryContainer, ), ), diff --git a/lib/pangea/analytics_summary/learning_progress_bar.dart b/lib/pangea/analytics_summary/learning_progress_bar.dart index 2292c2b94..b841b9484 100644 --- a/lib/pangea/analytics_summary/learning_progress_bar.dart +++ b/lib/pangea/analytics_summary/learning_progress_bar.dart @@ -21,9 +21,7 @@ class LearningProgressBar extends StatelessWidget { return Container( alignment: Alignment.center, height: height, - child: const LinearProgressIndicator( - color: AppConfig.goldLight, - ), + child: const LinearProgressIndicator(color: AppConfig.goldLight), ); } diff --git a/lib/pangea/analytics_summary/learning_progress_indicator_button.dart b/lib/pangea/analytics_summary/learning_progress_indicator_button.dart index 1e4632e0b..999f95453 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicator_button.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicator_button.dart @@ -29,10 +29,9 @@ class HoverButton extends StatelessWidget { child: Container( decoration: BoxDecoration( color: hovered || selected - ? Theme.of(context) - .colorScheme - .primary - .withAlpha((hoverOpacity * 255).round()) + ? Theme.of(context).colorScheme.primary.withAlpha( + (hoverOpacity * 255).round(), + ) : Colors.transparent, borderRadius: borderRadius ?? BorderRadius.circular(36.0), ), diff --git a/lib/pangea/analytics_summary/learning_progress_indicators.dart b/lib/pangea/analytics_summary/learning_progress_indicators.dart index 7147d4540..c3e91e20e 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicators.dart @@ -82,13 +82,13 @@ class LearningProgressIndicators extends StatelessWidget { builder: (context, _) { final archivedActivitiesCount = analyticsRoom?.archivedActivitiesCount ?? - 0; + 0; return HoverButton( - selected: selected == + selected: + selected == ProgressIndicatorEnum.activities, onPressed: () { - AnalyticsNavigationUtil - .navigateToAnalytics( + AnalyticsNavigationUtil.navigateToAnalytics( context: context, view: ProgressIndicatorEnum.activities, ); @@ -107,9 +107,9 @@ class LearningProgressIndicators extends StatelessWidget { Icon( size: 18, Icons.radar, - color: Theme.of(context) - .colorScheme - .primary, + color: Theme.of( + context, + ).colorScheme.primary, weight: 1000, ), const SizedBox(width: 6.0), @@ -141,9 +141,9 @@ class LearningProgressIndicators extends StatelessWidget { .titleLarge ?.copyWith( fontWeight: FontWeight.bold, - color: Theme.of(context) - .colorScheme - .primary, + color: Theme.of( + context, + ).colorScheme.primary, ), textScaler: TextScaler.noScaling, ), @@ -157,9 +157,9 @@ class LearningProgressIndicators extends StatelessWidget { .titleLarge ?.copyWith( fontWeight: FontWeight.bold, - color: Theme.of(context) - .colorScheme - .primary, + color: Theme.of( + context, + ).colorScheme.primary, ), textScaler: TextScaler.noScaling, ), @@ -173,12 +173,11 @@ class LearningProgressIndicators extends StatelessWidget { builder: (context, hovered) { return Container( decoration: BoxDecoration( - color: (hovered && canSelect) || + color: + (hovered && canSelect) || (selected == ProgressIndicatorEnum.level) - ? Theme.of(context) - .colorScheme - .primary - .withAlpha((0.2 * 255).round()) + ? Theme.of(context).colorScheme.primary + .withAlpha((0.2 * 255).round()) : Colors.transparent, borderRadius: BorderRadius.circular(36.0), ), @@ -193,8 +192,7 @@ class LearningProgressIndicators extends StatelessWidget { child: GestureDetector( onTap: canSelect ? () { - AnalyticsNavigationUtil - .navigateToAnalytics( + AnalyticsNavigationUtil.navigateToAnalytics( context: context, view: ProgressIndicatorEnum.level, ); @@ -225,9 +223,9 @@ class LearningProgressIndicators extends StatelessWidget { .titleLarge ?.copyWith( fontWeight: FontWeight.bold, - color: Theme.of(context) - .colorScheme - .primary, + color: Theme.of( + context, + ).colorScheme.primary, ), ), ], diff --git a/lib/pangea/analytics_summary/level_analytics_details_content.dart b/lib/pangea/analytics_summary/level_analytics_details_content.dart index 61c316ff9..69ca9389b 100644 --- a/lib/pangea/analytics_summary/level_analytics_details_content.dart +++ b/lib/pangea/analytics_summary/level_analytics_details_content.dart @@ -13,9 +13,7 @@ import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; import 'package:fluffychat/widgets/matrix.dart'; class LevelAnalyticsDetailsContent extends StatelessWidget { - const LevelAnalyticsDetailsContent({ - super.key, - }); + const LevelAnalyticsDetailsContent({super.key}); @override Widget build(BuildContext context) { @@ -96,7 +94,8 @@ class LevelAnalyticsDetailsContent extends StatelessWidget { final use = uses[index]; String lemmaCopy = use.lemma; if (use.constructType == ConstructTypeEnum.morph) { - lemmaCopy = getGrammarCopy( + lemmaCopy = + getGrammarCopy( category: use.category, lemma: use.lemma, context: context, @@ -141,8 +140,9 @@ class LevelAnalyticsDetailsContent extends StatelessWidget { fontWeight: FontWeight.w900, fontSize: 14, height: 1, - color: - use.pointValueColor(context), + color: use.pointValueColor( + context, + ), ), ), ], diff --git a/lib/pangea/analytics_summary/progress_indicator.dart b/lib/pangea/analytics_summary/progress_indicator.dart index c690a3cbf..7a54b5955 100644 --- a/lib/pangea/analytics_summary/progress_indicator.dart +++ b/lib/pangea/analytics_summary/progress_indicator.dart @@ -32,15 +32,11 @@ class ProgressIndicatorBadge extends StatelessWidget { ), const SizedBox(width: 6.0), !loading - ? AnimatedFloatingNumber( - number: points, - ) + ? AnimatedFloatingNumber(number: points) : const SizedBox( height: 8, width: 8, - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), + child: CircularProgressIndicator.adaptive(strokeWidth: 2), ), ], ), @@ -52,10 +48,7 @@ class ProgressIndicatorBadge extends StatelessWidget { class AnimatedFloatingNumber extends StatefulWidget { final int number; - const AnimatedFloatingNumber({ - super.key, - required this.number, - }); + const AnimatedFloatingNumber({super.key, required this.number}); @override State createState() => AnimatedFloatingNumberState(); @@ -119,10 +112,7 @@ class AnimatedFloatingNumberState extends State position: _offsetAnim, child: FadeTransition( opacity: ReverseAnimation(_fadeAnim), - child: Text( - "$_floatingNumber", - style: indicatorStyle, - ), + child: Text("$_floatingNumber", style: indicatorStyle), ), ), Text( diff --git a/lib/pangea/authentication/p_login.dart b/lib/pangea/authentication/p_login.dart index b805ce72c..318431e4d 100644 --- a/lib/pangea/authentication/p_login.dart +++ b/lib/pangea/authentication/p_login.dart @@ -20,10 +20,7 @@ void pLoginAction({ await showFutureLoadingDialog( context: context, - future: () => _loginFuture( - controller: controller, - context: context, - ), + future: () => _loginFuture(controller: controller, context: context), onError: (e, s) { controller.setLoadingSignIn(false); return e is MatrixException @@ -65,14 +62,13 @@ Future _loginFuture({ final redirect = client.onLoginStateChanged.stream .where((state) => state == LoginState.loggedIn) .first - .then( - (_) { - final route = FluffyChatApp.router.state.fullPath; - if (route == null || !route.contains("/rooms")) { - context.go("/rooms"); - } - }, - ).timeout(const Duration(seconds: 30)); + .then((_) { + final route = FluffyChatApp.router.state.fullPath; + if (route == null || !route.contains("/rooms")) { + context.go("/rooms"); + } + }) + .timeout(const Duration(seconds: 30)); final loginRes = await client.login( LoginType.mLoginPassword, diff --git a/lib/pangea/authentication/p_logout.dart b/lib/pangea/authentication/p_logout.dart index fdc2a25a8..b7b197e0d 100644 --- a/lib/pangea/authentication/p_logout.dart +++ b/lib/pangea/authentication/p_logout.dart @@ -31,22 +31,20 @@ void pLogoutAction( final client = Matrix.of(context).client; // before wiping out locally cached construct data, save it to the server - await Matrix.of(context) - .analyticsDataService - .updateService - .sendLocalAnalyticsToAnalyticsRoom(); + await Matrix.of( + context, + ).analyticsDataService.updateService.sendLocalAnalyticsToAnalyticsRoom(); final redirect = client.onLoginStateChanged.stream .where((state) => state != LoginState.loggedIn) .first - .then( - (_) { - final route = FluffyChatApp.router.state.fullPath; - if (route == null || !route.contains("/home")) { - context.go("/home"); - } - }, - ).timeout(const Duration(seconds: 30)); + .then((_) { + final route = FluffyChatApp.router.state.fullPath; + if (route == null || !route.contains("/home")) { + context.go("/home"); + } + }) + .timeout(const Duration(seconds: 30)); await showFutureLoadingDialog( context: context, diff --git a/lib/pangea/bot/utils/bot_name.dart b/lib/pangea/bot/utils/bot_name.dart index 2596766cc..c53260c2e 100644 --- a/lib/pangea/bot/utils/bot_name.dart +++ b/lib/pangea/bot/utils/bot_name.dart @@ -4,7 +4,7 @@ class BotName { static String get byEnvironment => Environment.botName != null ? Environment.botName! : Environment.isStagingEnvironment - ? "@bot:staging.pangea.chat" - : "@bot:pangea.chat"; + ? "@bot:staging.pangea.chat" + : "@bot:pangea.chat"; static String get localBot => "@matrix-bot-test:staging.pangea.chat"; } diff --git a/lib/pangea/bot/utils/bot_room_extension.dart b/lib/pangea/bot/utils/bot_room_extension.dart index 830ca6a84..6a21e2cdc 100644 --- a/lib/pangea/bot/utils/bot_room_extension.dart +++ b/lib/pangea/bot/utils/bot_room_extension.dart @@ -47,11 +47,7 @@ extension BotRoomExtension on Room { ErrorHandler.logError( e: e, s: s, - data: { - 'roomId': id, - 'options': options.toJson(), - 'attempt': attempt, - }, + data: {'roomId': id, 'options': options.toJson(), 'attempt': attempt}, ); if (attempt == maxRetries) { diff --git a/lib/pangea/bot/utils/bot_style.dart b/lib/pangea/bot/utils/bot_style.dart index 8007660cf..95edf22eb 100644 --- a/lib/pangea/bot/utils/bot_style.dart +++ b/lib/pangea/bot/utils/bot_style.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; class BotStyle { @@ -15,8 +16,9 @@ class BotStyle { try { final TextStyle botStyle = TextStyle( fontWeight: bold ? FontWeight.w700 : null, - fontSize: AppConfig.messageFontSize * - AppConfig.fontSizeFactor * + fontSize: + AppConfig.messageFontSize * + AppSettings.fontSizeFactor.value * (big == true ? 1.2 : 1), fontStyle: italics ? FontStyle.italic : null, color: setColor ? Theme.of(context).colorScheme.primary : null, @@ -26,11 +28,7 @@ class BotStyle { return existingStyle?.merge(botStyle) ?? botStyle; } catch (err, stack) { - ErrorHandler.logError( - m: "error getting styles", - s: stack, - data: {}, - ); + ErrorHandler.logError(m: "error getting styles", s: stack, data: {}); return existingStyle ?? const TextStyle(); } } diff --git a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart index ea1a19f63..5293b9981 100644 --- a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart +++ b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart @@ -19,10 +19,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class BotChatSettingsDialog extends StatefulWidget { final Room room; - const BotChatSettingsDialog({ - required this.room, - super.key, - }); + const BotChatSettingsDialog({required this.room, super.key}); @override BotChatSettingsDialogState createState() => BotChatSettingsDialogState(); @@ -58,17 +55,14 @@ class BotChatSettingsDialogState extends State { await MatrixState.pangeaController.userController .updateProfile(update, waitForDataInSync: true) .timeout(const Duration(seconds: 15)); - await Matrix.of(context).client.updateBotOptions( - _userProfile.userSettings, - ); + await Matrix.of( + context, + ).client.updateBotOptions(_userProfile.userSettings); } catch (e, s) { ErrorHandler.logError( e: e, s: s, - data: { - 'roomId': widget.room.id, - 'model': _userProfile.toJson(), - }, + data: {'roomId': widget.room.id, 'model': _userProfile.toJson()}, ); } } @@ -159,7 +153,8 @@ class BotChatSettingsDialogState extends State { onChanged: _setVoice, value: _selectedVoice, language: _selectedLang, - enabled: !widget.room.isActivitySession || + enabled: + !widget.room.isActivitySession || (_selectedLang != null && _selectedLang == MatrixState.pangeaController.userController.userL2), diff --git a/lib/pangea/bot/widgets/bot_face_svg.dart b/lib/pangea/bot/widgets/bot_face_svg.dart index cdd46316f..d4b0af347 100644 --- a/lib/pangea/bot/widgets/bot_face_svg.dart +++ b/lib/pangea/bot/widgets/bot_face_svg.dart @@ -91,12 +91,15 @@ class BotFaceState extends State { Future _loadRiveFile() async { if (!widget.useRive) return; - final riveFile = - await RiveFile.asset('assets/pangea/bot_faces/pangea_bot.riv'); + final riveFile = await RiveFile.asset( + 'assets/pangea/bot_faces/pangea_bot.riv', + ); final artboard = riveFile.mainArtboard; - _controller = - StateMachineController.fromArtboard(artboard, 'BotIconStateMachine'); + _controller = StateMachineController.fromArtboard( + artboard, + 'BotIconStateMachine', + ); if (_controller != null) { artboard.addController(_controller!); @@ -119,10 +122,7 @@ class BotFaceState extends State { width: widget.width, height: widget.width, child: _artboard != null - ? Rive( - artboard: _artboard!, - fit: BoxFit.cover, - ) + ? Rive(artboard: _artboard!, fit: BoxFit.cover) : CachedNetworkImage( imageUrl: svgURL, placeholder: (context, url) => const CircularProgressIndicator(), diff --git a/lib/pangea/bot/widgets/bot_settings_language_icon.dart b/lib/pangea/bot/widgets/bot_settings_language_icon.dart index 837bba59f..c8735315e 100644 --- a/lib/pangea/bot/widgets/bot_settings_language_icon.dart +++ b/lib/pangea/bot/widgets/bot_settings_language_icon.dart @@ -10,10 +10,7 @@ import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; class BotSettingsLanguageIcon extends StatelessWidget { final User user; - const BotSettingsLanguageIcon({ - super.key, - required this.user, - }); + const BotSettingsLanguageIcon({super.key, required this.user}); @override Widget build(BuildContext context) { @@ -31,10 +28,10 @@ class BotSettingsLanguageIcon extends StatelessWidget { borderRadius: BorderRadius.circular(32.0), onTap: room.isRoomAdmin ? () => showMemberActionsPopupMenu( - context: context, - user: user, - room: room, - ) + context: context, + user: user, + room: room, + ) : null, child: Container( decoration: BoxDecoration( diff --git a/lib/pangea/chat/constants/default_power_level.dart b/lib/pangea/chat/constants/default_power_level.dart index 1e84aca80..30f1c1f4f 100644 --- a/lib/pangea/chat/constants/default_power_level.dart +++ b/lib/pangea/chat/constants/default_power_level.dart @@ -4,88 +4,75 @@ import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; class RoomDefaults { static StateEvent defaultPowerLevels(String userID) => StateEvent( - type: EventTypes.RoomPowerLevels, - stateKey: '', - content: { - "ban": 50, - "kick": 50, - "invite": 50, - "redact": 50, - "events": { - PangeaEventTypes.activityPlan: 0, - PangeaEventTypes.activityRole: 0, - PangeaEventTypes.activitySummary: 0, - "m.room.power_levels": 100, - "m.room.pinned_events": 50, - }, - "events_default": 0, - "state_default": 50, - "users": { - userID: 100, - }, - "users_default": 0, - "notifications": { - "room": 50, - }, - }, - ); + type: EventTypes.RoomPowerLevels, + stateKey: '', + content: { + "ban": 50, + "kick": 50, + "invite": 50, + "redact": 50, + "events": { + PangeaEventTypes.activityPlan: 0, + PangeaEventTypes.activityRole: 0, + PangeaEventTypes.activitySummary: 0, + "m.room.power_levels": 100, + "m.room.pinned_events": 50, + }, + "events_default": 0, + "state_default": 50, + "users": {userID: 100}, + "users_default": 0, + "notifications": {"room": 50}, + }, + ); static StateEvent restrictedPowerLevels(String userID) => StateEvent( - type: EventTypes.RoomPowerLevels, - stateKey: '', - content: { - "ban": 50, - "kick": 50, - "invite": 50, - "redact": 50, - "events": { - PangeaEventTypes.activityPlan: 50, - PangeaEventTypes.activityRole: 0, - PangeaEventTypes.activitySummary: 0, - "m.room.power_levels": 100, - "m.room.pinned_events": 50, - }, - "events_default": 50, - "state_default": 50, - "users": { - userID: 100, - }, - "users_default": 0, - "notifications": { - "room": 50, - }, - }, - ); + type: EventTypes.RoomPowerLevels, + stateKey: '', + content: { + "ban": 50, + "kick": 50, + "invite": 50, + "redact": 50, + "events": { + PangeaEventTypes.activityPlan: 50, + PangeaEventTypes.activityRole: 0, + PangeaEventTypes.activitySummary: 0, + "m.room.power_levels": 100, + "m.room.pinned_events": 50, + }, + "events_default": 50, + "state_default": 50, + "users": {userID: 100}, + "users_default": 0, + "notifications": {"room": 50}, + }, + ); static StateEvent defaultSpacePowerLevels( String userID, { int spaceChild = 50, - }) => - StateEvent( - type: EventTypes.RoomPowerLevels, - stateKey: '', - content: { - "ban": 50, - "kick": 50, - "invite": 50, - "redact": 50, - "events": { - PangeaEventTypes.courseUser: 0, - "m.room.power_levels": 100, - "m.room.join_rules": 100, - "m.space.child": spaceChild, - }, - "events_default": 0, - "state_default": 50, - "users": { - userID: 100, - }, - "users_default": 0, - "notifications": { - "room": 50, - }, - }, - ); + }) => StateEvent( + type: EventTypes.RoomPowerLevels, + stateKey: '', + content: { + "ban": 50, + "kick": 50, + "invite": 50, + "redact": 50, + "events": { + PangeaEventTypes.courseUser: 0, + "m.room.power_levels": 100, + "m.room.join_rules": 100, + "m.space.child": spaceChild, + }, + "events_default": 0, + "state_default": 50, + "users": {userID: 100}, + "users_default": 0, + "notifications": {"room": 50}, + }, + ); static Visibility spaceChildVisibility = Visibility.private; } diff --git a/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart b/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart index a88c25f34..653fb8b81 100644 --- a/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart +++ b/lib/pangea/chat/utils/unlocked_morphs_snackbar.dart @@ -59,10 +59,7 @@ class ConstructNotificationUtil { final bool result = OverlayUtil.showOverlay( overlayKey: "${construct.string}_snackbar", context: context, - child: ConstructNotificationOverlay( - construct: construct, - copy: copy, - ), + child: ConstructNotificationOverlay(construct: construct, copy: copy), transformTargetId: "", position: OverlayPositionEnum.top, backDropToDismiss: false, @@ -103,7 +100,8 @@ class ConstructNotificationOverlay extends StatefulWidget { } class ConstructNotificationOverlayState - extends State with TickerProviderStateMixin { + extends State + with TickerProviderStateMixin { AnimationController? _controller; Animation? _animation; @@ -115,10 +113,7 @@ class ConstructNotificationOverlayState vsync: this, ); - _animation = CurvedAnimation( - parent: _controller!, - curve: Curves.easeInOut, - ); + _animation = CurvedAnimation(parent: _controller!, curve: Curves.easeInOut); _controller!.forward().then((_) { OverlayUtil.showOverlay( @@ -247,8 +242,8 @@ class ConstructNotificationOverlayState : const Size(22.0, 22.0), morphFeature: MorphFeaturesEnumExtension.fromString( - widget.construct.category, - ), + widget.construct.category, + ), morphTag: widget.construct.lemma, ), ], @@ -271,9 +266,7 @@ class ConstructNotificationOverlayState ), ), onPressed: _showDetails, - child: Text( - L10n.of(context).details, - ), + child: Text(L10n.of(context).details), ) : SizedBox( width: 32.0, @@ -284,8 +277,9 @@ class ConstructNotificationOverlayState Icons.info_outline, ), style: IconButton.styleFrom( - padding: - const EdgeInsets.all(4.0), + padding: const EdgeInsets.all( + 4.0, + ), ), onPressed: _showDetails, constraints: const BoxConstraints(), @@ -300,9 +294,7 @@ class ConstructNotificationOverlayState child: Tooltip( message: L10n.of(context).close, child: IconButton( - icon: const Icon( - Icons.close, - ), + icon: const Icon(Icons.close), style: IconButton.styleFrom( padding: const EdgeInsets.all(4.0), ), diff --git a/lib/pangea/chat/widgets/chat_floating_action_button.dart b/lib/pangea/chat/widgets/chat_floating_action_button.dart index 88122aa3c..b29f5ee59 100644 --- a/lib/pangea/chat/widgets/chat_floating_action_button.dart +++ b/lib/pangea/chat/widgets/chat_floating_action_button.dart @@ -5,10 +5,7 @@ import 'package:fluffychat/pangea/choreographer/choreographer_has_error_button.d class ChatFloatingActionButton extends StatelessWidget { final ChatController controller; - const ChatFloatingActionButton({ - super.key, - required this.controller, - }); + const ChatFloatingActionButton({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -17,14 +14,12 @@ class ChatFloatingActionButton extends StatelessWidget { } return ListenableBuilder( - listenable: Listenable.merge( - [ - controller.choreographer.errorService, - controller.choreographer.itController.open, - controller.scrollController, - controller.scrollableNotifier, - ], - ), + listenable: Listenable.merge([ + controller.choreographer.errorService, + controller.choreographer.itController.open, + controller.scrollController, + controller.scrollableNotifier, + ]), builder: (context, _) { if (controller.scrollController.hasClients && controller.scrollController.position.pixels > 0) { diff --git a/lib/pangea/chat/widgets/chat_input_bar.dart b/lib/pangea/chat/widgets/chat_input_bar.dart index 599c25abb..1938ffbd7 100644 --- a/lib/pangea/chat/widgets/chat_input_bar.dart +++ b/lib/pangea/chat/widgets/chat_input_bar.dart @@ -29,7 +29,7 @@ class ChatInputBar extends StatelessWidget { children: [ ValueListenableBuilder( valueListenable: controller.choreographer.itController.open, - builder: (context, open, __) { + builder: (context, open, _) { return open ? Container( constraints: const BoxConstraints( @@ -69,9 +69,7 @@ class ChatInputBar extends StatelessWidget { child: Material( clipBehavior: Clip.hardEdge, color: theme.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + borderRadius: const BorderRadius.all(Radius.circular(24)), child: controller.room.isAbandonedDMRoom == true ? _AbandonedDMContent(controller: controller) : Column( @@ -79,9 +77,7 @@ class ChatInputBar extends StatelessWidget { children: [ ITBar(choreographer: controller.choreographer), ReplyDisplay(controller), - PangeaChatInputRow( - controller: controller, - ), + PangeaChatInputRow(controller: controller), ChatEmojiPicker(controller), ], ), @@ -95,9 +91,7 @@ class ChatInputBar extends StatelessWidget { class _AbandonedDMContent extends StatelessWidget { final ChatController controller; - const _AbandonedDMContent({ - required this.controller, - }); + const _AbandonedDMContent({required this.controller}); @override Widget build(BuildContext context) { @@ -106,32 +100,18 @@ class _AbandonedDMContent extends StatelessWidget { children: [ TextButton.icon( style: TextButton.styleFrom( - padding: const EdgeInsets.all( - 16, - ), + padding: const EdgeInsets.all(16), foregroundColor: Theme.of(context).colorScheme.error, ), - icon: const Icon( - Icons.archive_outlined, - ), + icon: const Icon(Icons.archive_outlined), onPressed: controller.leaveChat, - label: Text( - L10n.of(context).leave, - ), + label: Text(L10n.of(context).leave), ), TextButton.icon( - style: TextButton.styleFrom( - padding: const EdgeInsets.all( - 16, - ), - ), - icon: const Icon( - Icons.forum_outlined, - ), + style: TextButton.styleFrom(padding: const EdgeInsets.all(16)), + icon: const Icon(Icons.forum_outlined), onPressed: controller.recreateChat, - label: Text( - L10n.of(context).reopenChat, - ), + label: Text(L10n.of(context).reopenChat), ), ], ); diff --git a/lib/pangea/chat/widgets/chat_input_bar_header.dart b/lib/pangea/chat/widgets/chat_input_bar_header.dart index cc3408790..762be951f 100644 --- a/lib/pangea/chat/widgets/chat_input_bar_header.dart +++ b/lib/pangea/chat/widgets/chat_input_bar_header.dart @@ -21,21 +21,13 @@ class ChatInputBarHeader extends StatelessWidget { } return Container( - margin: EdgeInsets.only( - bottom: 10, - left: padding, - right: padding, - ), + margin: EdgeInsets.only(bottom: 10, left: padding, right: padding), constraints: const BoxConstraints( maxWidth: FluffyThemes.columnWidth * 2.4, ), child: Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ - ChatFloatingActionButton( - controller: controller, - ), - ], + children: [ChatFloatingActionButton(controller: controller)], ), ); } diff --git a/lib/pangea/chat/widgets/icon_rain.dart b/lib/pangea/chat/widgets/icon_rain.dart index b6dd09aff..18890b992 100644 --- a/lib/pangea/chat/widgets/icon_rain.dart +++ b/lib/pangea/chat/widgets/icon_rain.dart @@ -215,13 +215,11 @@ class _AnimatedFallingIconState extends State<_AnimatedFallingIcon> @override void initState() { super.initState(); - _controller = AnimationController( - duration: widget.duration, - vsync: this, - ); - _animation = Tween(begin: -40, end: widget.maxHeight + 40).animate( - CurvedAnimation(parent: _controller, curve: Curves.easeIn), - ); + _controller = AnimationController(duration: widget.duration, vsync: this); + _animation = Tween( + begin: -40, + end: widget.maxHeight + 40, + ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeIn)); _controller.forward().then((_) => widget.onComplete()); } @@ -237,7 +235,8 @@ class _AnimatedFallingIconState extends State<_AnimatedFallingIcon> animation: _animation, builder: (context, child) { final progress = _controller.value; - final sway = widget.swayAmplitude * + final sway = + widget.swayAmplitude * sin( 2 * pi * widget.swayFrequency * progress + widget.startX * 2 * pi, ); diff --git a/lib/pangea/chat/widgets/pangea_chat_input_row.dart b/lib/pangea/chat/widgets/pangea_chat_input_row.dart index 6c9f08c8a..1543e9232 100644 --- a/lib/pangea/chat/widgets/pangea_chat_input_row.dart +++ b/lib/pangea/chat/widgets/pangea_chat_input_row.dart @@ -1,12 +1,15 @@ import 'package:flutter/material.dart'; import 'package:animations/animations.dart'; +import 'package:emoji_picker_flutter/locales/default_emoji_set_locale.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/input_bar.dart'; +import 'package:fluffychat/pages/chat/recording_input_row.dart'; +import 'package:fluffychat/pages/chat/recording_view_model.dart'; import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; import 'package:fluffychat/pangea/choreographer/choreo_constants.dart'; import 'package:fluffychat/pangea/choreographer/choreographer_send_button.dart'; @@ -19,10 +22,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class PangeaChatInputRow extends StatelessWidget { final ChatController controller; - const PangeaChatInputRow({ - required this.controller, - super.key, - }); + const PangeaChatInputRow({required this.controller, super.key}); LanguageModel? get activel1 => controller.pangeaController.userController.userL1; @@ -43,217 +43,311 @@ class PangeaChatInputRow extends StatelessWidget { .link, child: Container( decoration: const BoxDecoration( - borderRadius: BorderRadius.all( - Radius.circular(8.0), - ), + borderRadius: BorderRadius.all(Radius.circular(8.0)), ), - child: Row( - key: MatrixState.pAnyState - .layerLinkAndKey(ChoreoConstants.inputTransformTargetKey) - .key, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox(width: 4), - ValueListenableBuilder( - valueListenable: controller.sendController, - builder: (context, text, __) { - final isBotDM = controller.room.isBotDM; - return AnimatedContainer( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - height: height, - width: text.text.isEmpty && - !controller.choreographer.itController.open.value - ? height - : 0, - alignment: Alignment.center, - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), - child: PopupMenuButton( - useRootNavigator: true, - icon: const Icon(Icons.add_outlined), - onSelected: controller.onAddPopupMenuButtonSelected, - itemBuilder: (BuildContext context) => - >[ - if (!isBotDM) - PopupMenuItem( - value: 'file', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.green, - foregroundColor: Colors.white, - child: Icon(Icons.attachment_outlined), - ), - title: Text(L10n.of(context).sendFile), - contentPadding: const EdgeInsets.all(0), - ), - ), - PopupMenuItem( - value: 'image', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.blue, - foregroundColor: Colors.white, - child: Icon(Icons.image_outlined), - ), - title: Text(L10n.of(context).sendImage), - contentPadding: const EdgeInsets.all(0), + child: RecordingViewModel( + builder: (context, recordingViewModel) { + if (recordingViewModel.isRecording) { + return RecordingInputRow( + state: recordingViewModel, + onSend: controller.onVoiceMessageSend, + ); + } + return Row( + key: MatrixState.pAnyState + .layerLinkAndKey(ChoreoConstants.inputTransformTargetKey) + .key, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(width: 4), + ValueListenableBuilder( + valueListenable: controller.sendController, + builder: (context, text, _) { + final isBotDM = controller.room.isBotDM; + return AnimatedContainer( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + height: height, + width: + text.text.isEmpty && + !controller + .choreographer + .itController + .open + .value + ? height + : 0, + alignment: Alignment.center, + clipBehavior: Clip.hardEdge, + decoration: const BoxDecoration(), + child: PopupMenuButton( + useRootNavigator: true, + icon: const Icon(Icons.add_outlined), + onSelected: controller.onAddPopupMenuButtonSelected, + itemBuilder: (BuildContext context) => + >[ + PopupMenuItem( + value: AddPopupMenuActions.poll, + child: ListTile( + leading: CircleAvatar( + backgroundColor: theme + .colorScheme + .onPrimaryContainer, + foregroundColor: + theme.colorScheme.primaryContainer, + child: const Icon(Icons.poll_outlined), + ), + title: Text(L10n.of(context).startPoll), + contentPadding: const EdgeInsets.all(0), + ), + ), + if (!isBotDM) + PopupMenuItem( + value: AddPopupMenuActions.file, + child: ListTile( + leading: const CircleAvatar( + backgroundColor: Colors.green, + foregroundColor: Colors.white, + child: Icon( + Icons.attachment_outlined, + ), + ), + title: Text(L10n.of(context).sendFile), + contentPadding: const EdgeInsets.all(0), + ), + ), + PopupMenuItem( + value: AddPopupMenuActions.image, + child: ListTile( + leading: const CircleAvatar( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + child: Icon(Icons.image_outlined), + ), + title: Text(L10n.of(context).sendImage), + contentPadding: const EdgeInsets.all(0), + ), + ), + if (!isBotDM) + PopupMenuItem( + value: AddPopupMenuActions.image, + child: ListTile( + leading: CircleAvatar( + backgroundColor: theme + .colorScheme + .onPrimaryContainer, + foregroundColor: theme + .colorScheme + .primaryContainer, + child: const Icon( + Icons.photo_outlined, + ), + ), + title: Text(L10n.of(context).sendImage), + contentPadding: const EdgeInsets.all(0), + ), + ), + if (PlatformInfos.isMobile) + PopupMenuItem( + value: AddPopupMenuActions.photoCamera, + child: ListTile( + leading: const CircleAvatar( + backgroundColor: Colors.purple, + foregroundColor: Colors.white, + child: Icon( + Icons.camera_alt_outlined, + ), + ), + title: Text( + L10n.of(context).openCamera, + ), + contentPadding: const EdgeInsets.all(0), + ), + ), + if (!isBotDM) + if (PlatformInfos.isMobile) + PopupMenuItem( + value: AddPopupMenuActions.videoCamera, + child: ListTile( + leading: const CircleAvatar( + backgroundColor: Colors.red, + foregroundColor: Colors.white, + child: Icon( + Icons.videocam_outlined, + ), + ), + title: Text( + L10n.of(context).openVideoCamera, + ), + contentPadding: const EdgeInsets.all( + 0, + ), + ), + ), + if (!isBotDM) + if (PlatformInfos.isMobile) + PopupMenuItem( + value: AddPopupMenuActions.location, + child: ListTile( + leading: const CircleAvatar( + backgroundColor: Colors.brown, + foregroundColor: Colors.white, + child: Icon( + Icons.gps_fixed_outlined, + ), + ), + title: Text( + L10n.of(context).shareLocation, + ), + contentPadding: const EdgeInsets.all( + 0, + ), + ), + ), + ], + ), + ); + }, + ), + if (FluffyThemes.isColumnMode(context)) + Container( + height: height, + width: height, + alignment: Alignment.center, + child: IconButton( + tooltip: L10n.of(context).emojis, + icon: PageTransitionSwitcher( + transitionBuilder: + ( + Widget child, + Animation primaryAnimation, + Animation secondaryAnimation, + ) { + return SharedAxisTransition( + animation: primaryAnimation, + secondaryAnimation: secondaryAnimation, + transitionType: + SharedAxisTransitionType.scaled, + fillColor: Colors.transparent, + child: child, + ); + }, + child: Icon( + controller.showEmojiPicker + ? Icons.keyboard + : Icons.add_reaction_outlined, + key: ValueKey(controller.showEmojiPicker), ), ), - if (PlatformInfos.isMobile) - PopupMenuItem( - value: 'camera', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.purple, - foregroundColor: Colors.white, - child: Icon(Icons.camera_alt_outlined), - ), - title: Text(L10n.of(context).openCamera), - contentPadding: const EdgeInsets.all(0), - ), + onPressed: controller.emojiPickerAction, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 0.0), + child: InputBar( + room: controller.room, + minLines: 1, + maxLines: 8, + autofocus: !PlatformInfos.isMobile, + keyboardType: TextInputType.multiline, + textInputAction: + AppSettings.sendOnEnter.value == true && + PlatformInfos.isMobile + ? TextInputAction.send + : null, + onSubmitted: (_) => controller.onInputBarSubmitted(), + onSubmitImage: controller.sendImageFromClipBoard, + focusNode: controller.inputFocus, + controller: controller.sendController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.only( + left: 6.0, + right: 6.0, + bottom: 6.0, + top: 3.0, ), - if (!isBotDM) - if (PlatformInfos.isMobile) - PopupMenuItem( - value: 'camera-video', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.red, - foregroundColor: Colors.white, - child: Icon(Icons.videocam_outlined), - ), - title: Text(L10n.of(context).openVideoCamera), - contentPadding: const EdgeInsets.all(0), - ), + disabledBorder: InputBorder.none, + hintMaxLines: 1, + border: InputBorder.none, + enabledBorder: InputBorder.none, + filled: false, + ), + onChanged: controller.onInputBarChanged, + choreographer: controller.choreographer, + showNextMatch: controller.showNextMatch, + onFeedbackSubmitted: (feedback) => controller + .onRequestWritingAssistance(feedback: feedback), + suggestionEmojis: + getDefaultEmojiLocale( + AppSettings + .emojiSuggestionLocale + .value + .isNotEmpty + ? Locale( + AppSettings.emojiSuggestionLocale.value, + ) + : Localizations.localeOf(context), + ).fold( + [], + (emojis, category) => + emojis..addAll(category.emoji), ), - if (!isBotDM) - if (PlatformInfos.isMobile) - PopupMenuItem( - value: 'location', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.brown, - foregroundColor: Colors.white, - child: Icon(Icons.gps_fixed_outlined), - ), - title: Text(L10n.of(context).shareLocation), - contentPadding: const EdgeInsets.all(0), - ), - ), - ], - ), - ); - }, - ), - if (FluffyThemes.isColumnMode(context)) - Container( - height: height, - width: height, - alignment: Alignment.center, - child: IconButton( - tooltip: L10n.of(context).emojis, - icon: PageTransitionSwitcher( - transitionBuilder: ( - Widget child, - Animation primaryAnimation, - Animation secondaryAnimation, - ) { - return SharedAxisTransition( - animation: primaryAnimation, - secondaryAnimation: secondaryAnimation, - transitionType: SharedAxisTransitionType.scaled, - fillColor: Colors.transparent, - child: child, - ); - }, - child: Icon( - controller.showEmojiPicker - ? Icons.keyboard - : Icons.add_reaction_outlined, - key: ValueKey(controller.showEmojiPicker), ), ), - onPressed: controller.emojiPickerAction, ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 0.0), - child: InputBar( - room: controller.room, - minLines: 1, - maxLines: 8, - autofocus: !PlatformInfos.isMobile, - keyboardType: TextInputType.multiline, - textInputAction: AppConfig.sendOnEnter == true && - PlatformInfos.isMobile - ? TextInputAction.send - : null, - onSubmitted: (_) => controller.onInputBarSubmitted(), - onSubmitImage: controller.sendImageFromClipBoard, - focusNode: controller.inputFocus, - controller: controller.sendController, - decoration: const InputDecoration( - contentPadding: EdgeInsets.only( - left: 6.0, - right: 6.0, - bottom: 6.0, - top: 3.0, - ), - disabledBorder: InputBorder.none, - hintMaxLines: 1, - border: InputBorder.none, - enabledBorder: InputBorder.none, - filled: false, - ), - onChanged: controller.onInputBarChanged, + StartIGCButton( + key: ValueKey(controller.choreographer), + onPressed: () => + controller.onRequestWritingAssistance(manual: true), choreographer: controller.choreographer, - showNextMatch: controller.showNextMatch, - onFeedbackSubmitted: (feedback) => controller - .onRequestWritingAssistance(feedback: feedback), + initialState: state, + initialForegroundColor: state.stateColor(context), + initialBackgroundColor: state.backgroundColor(context), ), - ), - ), - StartIGCButton( - key: ValueKey(controller.choreographer), - onPressed: () => - controller.onRequestWritingAssistance(manual: true), - choreographer: controller.choreographer, - initialState: state, - initialForegroundColor: state.stateColor(context), - initialBackgroundColor: state.backgroundColor(context), - ), - ValueListenableBuilder( - valueListenable: controller.sendController, - builder: (context, text, __) { - return Container( - height: height, - width: height, - alignment: Alignment.center, - child: PlatformInfos.platformCanRecord && - text.text.isEmpty && - !controller.choreographer.itController.open.value - ? FloatingActionButton.small( - tooltip: L10n.of(context).voiceMessage, - onPressed: controller.voiceMessageAction, - elevation: 0, - heroTag: null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(height), - ), - backgroundColor: theme.bubbleColor, - foregroundColor: theme.onBubbleColor, - child: const Icon(Icons.mic_none_outlined), - ) - : ChoreographerSendButton( - controller: controller, - ), - ); - }, - ), - ], + ValueListenableBuilder( + valueListenable: controller.sendController, + builder: (context, text, _) { + return Container( + height: height, + width: height, + alignment: Alignment.center, + child: + PlatformInfos.platformCanRecord && + text.text.isEmpty && + !controller + .choreographer + .itController + .open + .value + ? IconButton( + tooltip: L10n.of(context).voiceMessage, + onPressed: () => ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text( + L10n.of( + context, + ).longPressToRecordVoiceMessage, + ), + ), + ), + onLongPress: () => recordingViewModel + .startRecording(controller.room), + style: IconButton.styleFrom( + backgroundColor: theme.bubbleColor, + foregroundColor: theme.onBubbleColor, + ), + icon: const Icon(Icons.mic_none_outlined), + ) + : ChoreographerSendButton(controller: controller), + ); + }, + ), + ], + ); + }, ), ), ), diff --git a/lib/pages/chat/events/room_creation_state_event.dart b/lib/pangea/chat/widgets/room_creation_state_event.dart similarity index 79% rename from lib/pages/chat/events/room_creation_state_event.dart rename to lib/pangea/chat/widgets/room_creation_state_event.dart index 8c4c3519e..f964c5b54 100644 --- a/lib/pages/chat/events/room_creation_state_event.dart +++ b/lib/pangea/chat/widgets/room_creation_state_event.dart @@ -12,15 +12,10 @@ import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; -// #Pangea -// class RoomCreationStateEvent extends StatelessWidget { class RoomCreationStateEvent extends StatefulWidget { - // Pangea# final Event event; - const RoomCreationStateEvent({required this.event, super.key}); - // #Pangea @override State createState() => RoomCreationStateEventState(); } @@ -36,14 +31,14 @@ class RoomCreationStateEventState extends State { @override void initState() { super.initState(); - _memberSubscription = event.room.client.onRoomState.stream.where( - (u) { - return u.roomId == event.room.id && - u.state.type == EventTypes.RoomMember; - }, - ).listen((_) { - if (_members > 1) setState(() {}); - }); + _memberSubscription = event.room.client.onRoomState.stream + .where((u) { + return u.roomId == event.room.id && + u.state.type == EventTypes.RoomMember; + }) + .listen((_) { + if (_members > 1) setState(() {}); + }); } @override @@ -51,7 +46,6 @@ class RoomCreationStateEventState extends State { _memberSubscription?.cancel(); super.dispose(); } - // Pangea# @override Widget build(BuildContext context) { @@ -61,13 +55,9 @@ class RoomCreationStateEventState extends State { final roomName = event.room.getLocalizedDisplayname(matrixLocals); return Padding( padding: const EdgeInsets.only(bottom: 32.0), - // #Pangea - // child: Center( - // child: ConstrainedBox( child: Column( children: [ ConstrainedBox( - // Pangea# constraints: const BoxConstraints(maxWidth: 256), child: Material( color: theme.colorScheme.surfaceContainer, @@ -75,16 +65,14 @@ class RoomCreationStateEventState extends State { child: Padding( padding: const EdgeInsets.all(16.0), child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ Avatar( mxContent: event.room.avatar, name: roomName, size: Avatar.defaultSize * 2, - // #Pangea userId: event.room.directChatMatrixID, useRive: true, - // Pangea# ), Text( roomName, @@ -102,28 +90,18 @@ class RoomCreationStateEventState extends State { ), ), ), - // #Pangea const SizedBox(height: 16.0), const InstructionsInlineTooltip( instructionsEnum: InstructionsEnum.clickMessage, - padding: EdgeInsets.only( - left: 16.0, - right: 16.0, - top: 16.0, - ), + padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0), animate: false, ), if (_members <= 1 && InstructionsEnum.clickMessage.isToggledOff) const InstructionsInlineTooltip( instructionsEnum: InstructionsEnum.emptyChatWarning, - padding: EdgeInsets.only( - left: 16.0, - right: 16.0, - top: 16.0, - ), + padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0), animate: false, ), - // Pangea# ], ), ); diff --git a/lib/pangea/chat_list/utils/app_version_util.dart b/lib/pangea/chat_list/utils/app_version_util.dart index cdd6242ce..a94d37a56 100644 --- a/lib/pangea/chat_list/utils/app_version_util.dart +++ b/lib/pangea/chat_list/utils/app_version_util.dart @@ -25,9 +25,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class AppVersionUtil { static final GetStorage _versionBox = GetStorage("version_storage"); - static Future _getAppVersion( - String accessToken, - ) async { + static Future _getAppVersion(String accessToken) async { final packageInfo = await PackageInfo.fromPlatform(); final currentVersion = packageInfo.version; final currentBuildNumber = packageInfo.buildNumber; @@ -110,9 +108,11 @@ class AppVersionUtil { // If a part of the current version is greater than the // remote version, then the current version is newer than // the remote version. - for (int i = 0; - i < min(currentVersionParts.length, remoteVersionParts.length); - i++) { + for ( + int i = 0; + i < min(currentVersionParts.length, remoteVersionParts.length); + i++ + ) { if (currentVersionParts[i] < remoteVersionParts[i]) { isOlderCurrentVersion = true; isDifferentVersion = true; diff --git a/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart b/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart index aad62e677..980380c0f 100644 --- a/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart +++ b/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart @@ -33,10 +33,12 @@ Future showInviteDialog(Room room, BuildContext context) async { constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256), child: Text( room.isSpace - ? L10n.of(context) - .invitedToSpace(room.name, room.creatorId ?? "???") - : L10n.of(context) - .invitedToChat(room.name, room.creatorId ?? "???"), + ? L10n.of( + context, + ).invitedToSpace(room.name, room.creatorId ?? "???") + : L10n.of( + context, + ).invitedToChat(room.name, room.creatorId ?? "???"), textAlign: TextAlign.center, ), ), @@ -87,10 +89,7 @@ Future showInviteDialog(Room room, BuildContext context) async { } // ignore: curly_braces_in_flow_control_structures -void chatListHandleSpaceTap( - BuildContext context, - Room space, -) { +void chatListHandleSpaceTap(BuildContext context, Room space) { void setActiveSpaceAndCloseChat() { context.go("/rooms/spaces/${space.id}/details"); } @@ -114,9 +113,8 @@ void chatListHandleSpaceTap( //else confirm you want to join //can we tell whether space or chat? final rooms = Matrix.of(context).client.rooms.where( - (element) => - element.isSpace && element.membership == Membership.join, - ); + (element) => element.isSpace && element.membership == Membership.join, + ); final justInputtedCode = SpaceCodeRepo.recentCode; if (rooms.any((s) => s.spaceChildren.any((c) => c.roomId == space.id))) { autoJoin(space); diff --git a/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart b/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart index 3b86069b6..4b1264f05 100644 --- a/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart +++ b/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/course_chats/open_roles_indicator.dart'; @@ -25,12 +25,12 @@ class ChatListItemSubtitle extends StatelessWidget { !event.redacted && event.type == EventTypes.Message && event.messageType == MessageTypes.Text && - !(AppConfig.renderHtml && !event.redacted && event.isRichMessage); + !(AppSettings.renderHtml.value && + !event.redacted && + event.isRichMessage); } - Future _getPangeaMessageEvent( - final Event event, - ) async { + Future _getPangeaMessageEvent(final Event event) async { final Timeline timeline = event.room.timeline != null ? event.room.timeline! : await event.room.getTimeline(); @@ -79,7 +79,8 @@ class ChatListItemSubtitle extends StatelessWidget { hideEdit: true, plaintextBody: true, removeMarkdown: true, - withSenderNamePrefix: !event.room.isDirectChat || + withSenderNamePrefix: + !event.room.isDirectChat || event.room.directChatMatrixID != event.room.lastEvent?.senderId, ), builder: (context, snapshot) { diff --git a/lib/pangea/chat_list/widgets/chat_list_view_body_wrapper.dart b/lib/pangea/chat_list/widgets/chat_list_view_body_wrapper.dart index a721c5921..25e57ea8b 100644 --- a/lib/pangea/chat_list/widgets/chat_list_view_body_wrapper.dart +++ b/lib/pangea/chat_list/widgets/chat_list_view_body_wrapper.dart @@ -11,10 +11,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class ChatListViewBodyWrapper extends StatefulWidget { final ChatListController controller; - const ChatListViewBodyWrapper({ - required this.controller, - super.key, - }); + const ChatListViewBodyWrapper({required this.controller, super.key}); @override State createState() => diff --git a/lib/pangea/chat_list/widgets/pangea_chat_list_header.dart b/lib/pangea/chat_list/widgets/pangea_chat_list_header.dart index a951ef459..2cdc1ed46 100644 --- a/lib/pangea/chat_list/widgets/pangea_chat_list_header.dart +++ b/lib/pangea/chat_list/widgets/pangea_chat_list_header.dart @@ -23,62 +23,59 @@ class PangeaChatListHeader extends StatelessWidget final theme = Theme.of(context); return SliverList( - delegate: SliverChildListDelegate( - [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - const LearningProgressIndicators(), - AnimatedSize( - duration: FluffyThemes.animationDuration, - child: showSearch - ? TextField( - controller: controller.searchController, - focusNode: controller.searchFocusNode, - textInputAction: TextInputAction.search, - onChanged: (text) => controller.onSearchEnter( - text, - globalSearch: globalSearch, + delegate: SliverChildListDelegate([ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const LearningProgressIndicators(), + AnimatedSize( + duration: FluffyThemes.animationDuration, + child: showSearch + ? TextField( + controller: controller.searchController, + focusNode: controller.searchFocusNode, + textInputAction: TextInputAction.search, + onChanged: (text) => controller.onSearchEnter( + text, + globalSearch: globalSearch, + ), + decoration: InputDecoration( + filled: true, + fillColor: theme.colorScheme.secondaryContainer, + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(99), ), - decoration: InputDecoration( - filled: true, - fillColor: theme.colorScheme.secondaryContainer, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(99), - ), - contentPadding: EdgeInsets.zero, - hintText: L10n.of(context).search, - hintStyle: TextStyle( - color: theme.colorScheme.onPrimaryContainer, - fontWeight: FontWeight.normal, - ), - floatingLabelBehavior: FloatingLabelBehavior.never, - prefixIcon: controller.isSearchMode - ? IconButton( - tooltip: L10n.of(context).cancel, - icon: const Icon(Icons.close_outlined), - onPressed: controller.cancelSearch, + contentPadding: EdgeInsets.zero, + hintText: L10n.of(context).search, + hintStyle: TextStyle( + color: theme.colorScheme.onPrimaryContainer, + fontWeight: FontWeight.normal, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + prefixIcon: controller.isSearchMode + ? IconButton( + tooltip: L10n.of(context).cancel, + icon: const Icon(Icons.close_outlined), + onPressed: controller.cancelSearch, + color: theme.colorScheme.onPrimaryContainer, + ) + : IconButton( + onPressed: controller.startSearch, + icon: Icon( + Icons.search_outlined, color: theme.colorScheme.onPrimaryContainer, - ) - : IconButton( - onPressed: controller.startSearch, - icon: Icon( - Icons.search_outlined, - color: - theme.colorScheme.onPrimaryContainer, - ), ), - ), - ) - : const SizedBox.shrink(), - ), - ], - ), + ), + ), + ) + : const SizedBox.shrink(), + ), + ], ), - ], - ), + ), + ]), ); } diff --git a/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart b/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart index b2894fc27..b7bb3fb28 100644 --- a/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart +++ b/lib/pangea/chat_list/widgets/public_room_bottom_sheet.dart @@ -16,7 +16,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class PublicRoomBottomSheet extends StatefulWidget { final String? roomAlias; final BuildContext outerContext; - final PublicRoomsChunk? chunk; + final PublishedRoomsChunk? chunk; final List? via; PublicRoomBottomSheet({ @@ -32,11 +32,12 @@ class PublicRoomBottomSheet extends StatefulWidget { static Future show({ required BuildContext context, String? roomAlias, - PublicRoomsChunk? chunk, + PublishedRoomsChunk? chunk, List? via, }) async { - final room = MatrixState.pangeaController.matrixState.client - .getRoomById(chunk!.roomId); + final room = MatrixState.pangeaController.matrixState.client.getRoomById( + chunk!.roomId, + ); if (room != null && room.membership == Membership.join) { context.go("/rooms/spaces/${room.id}/details"); @@ -60,7 +61,7 @@ class PublicRoomBottomSheet extends StatefulWidget { class PublicRoomBottomSheetState extends State { BuildContext get outerContext => widget.outerContext; - PublicRoomsChunk? get chunk => widget.chunk; + PublishedRoomsChunk? get chunk => widget.chunk; String? get roomAlias => widget.roomAlias; List? get via => widget.via; @@ -129,26 +130,22 @@ class PublicRoomBottomSheetState extends State { await showFutureLoadingDialog( context: context, - future: () async => client.knockRoom( - roomAlias ?? chunk!.roomId, - via: via, - ), + future: () async => + client.knockRoom(roomAlias ?? chunk!.roomId, via: via), onSuccess: () => L10n.of(context).knockSpaceSuccess, delay: false, ); } - bool testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias; + bool testRoom(PublishedRoomsChunk r) => r.canonicalAlias == roomAlias; - Future search() async { + Future search() async { final chunk = this.chunk; if (chunk != null) return chunk; final query = await Matrix.of(outerContext).client.queryPublicRooms( - server: roomAlias!.domain, - filter: PublicRoomQueryFilter( - genericSearchTerm: roomAlias, - ), - ); + server: roomAlias!.domain, + filter: PublicRoomQueryFilter(genericSearchTerm: roomAlias), + ); if (!query.chunk.any(testRoom)) { throw (L10n.of(outerContext).noRoomsFound); } @@ -174,7 +171,7 @@ class PublicRoomBottomSheetState extends State { ), ], ), - body: FutureBuilder( + body: FutureBuilder( future: search(), builder: (context, snapshot) { return Padding( @@ -227,10 +224,12 @@ class PublicRoomBottomSheetState extends State { child: Text( chunk?.topic ?? (chunk?.roomType != 'm.space' - ? L10n.of(context) - .noChatDescriptionYet - : L10n.of(context) - .noSpaceDescriptionYet), + ? L10n.of( + context, + ).noChatDescriptionYet + : L10n.of( + context, + ).noSpaceDescriptionYet), softWrap: true, textAlign: TextAlign.start, maxLines: null, @@ -265,12 +264,13 @@ class PublicRoomBottomSheetState extends State { enabledBorder: InputBorder.none, errorBorder: InputBorder.none, disabledBorder: InputBorder.none, - hintText: - L10n.of(context).enterSpaceCode, + hintText: L10n.of( + context, + ).enterSpaceCode, contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + horizontal: 16.0, + ), hintStyle: TextStyle( color: Theme.of(context).hintColor, ), @@ -281,9 +281,9 @@ class PublicRoomBottomSheetState extends State { decoration: BoxDecoration( border: Border( left: BorderSide( - color: Theme.of(context) - .colorScheme - .outline, + color: Theme.of( + context, + ).colorScheme.outline, ), ), ), @@ -311,10 +311,7 @@ class PublicRoomBottomSheetState extends State { spacing: 8.0, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Symbols.door_open, - size: 20.0, - ), + const Icon(Symbols.door_open, size: 20.0), Text(L10n.of(context).askToJoin), ], ), diff --git a/lib/pangea/chat_settings/models/bot_options_model.dart b/lib/pangea/chat_settings/models/bot_options_model.dart index 8800212b7..7592cc9e7 100644 --- a/lib/pangea/chat_settings/models/bot_options_model.dart +++ b/lib/pangea/chat_settings/models/bot_options_model.dart @@ -60,7 +60,7 @@ class BotOptionsModel { this.textAdventureGameMasterInstructions, }); - factory BotOptionsModel.fromJson(json) { + factory BotOptionsModel.fromJson(Map json) { final genderEntry = json[ModelKey.targetGender]; Map targetGenders = {}; if (genderEntry is Map) { @@ -84,10 +84,8 @@ class BotOptionsModel { languageLevel: json[ModelKey.languageLevel] is int ? LanguageLevelTypeEnum.fromInt(json[ModelKey.languageLevel]) : json[ModelKey.languageLevel] is String - ? LanguageLevelTypeEnum.fromString( - json[ModelKey.languageLevel], - ) - : LanguageLevelTypeEnum.a1, + ? LanguageLevelTypeEnum.fromString(json[ModelKey.languageLevel]) + : LanguageLevelTypeEnum.a1, safetyModeration: json[ModelKey.safetyModeration] ?? true, mode: json[ModelKey.mode] ?? BotMode.discussion, targetLanguage: json[ModelKey.targetLanguage], @@ -150,11 +148,7 @@ class BotOptionsModel { return data; } catch (e, s) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: e, - s: s, - data: data, - ); + ErrorHandler.logError(e: e, s: s, data: data); return data; } } @@ -185,7 +179,8 @@ class BotOptionsModel { mode: mode ?? this.mode, discussionTopic: discussionTopic ?? this.discussionTopic, discussionKeywords: discussionKeywords ?? this.discussionKeywords, - discussionTriggerReactionEnabled: discussionTriggerReactionEnabled ?? + discussionTriggerReactionEnabled: + discussionTriggerReactionEnabled ?? this.discussionTriggerReactionEnabled, discussionTriggerReactionKey: discussionTriggerReactionKey ?? this.discussionTriggerReactionKey, @@ -196,7 +191,7 @@ class BotOptionsModel { customTriggerReactionKey ?? this.customTriggerReactionKey, textAdventureGameMasterInstructions: textAdventureGameMasterInstructions ?? - this.textAdventureGameMasterInstructions, + this.textAdventureGameMasterInstructions, targetLanguage: targetLanguage ?? this.targetLanguage, targetVoice: targetVoice ?? this.targetVoice, userGenders: userGenders ?? this.userGenders, diff --git a/lib/pangea/chat_settings/pages/chat_details_button_row.dart b/lib/pangea/chat_settings/pages/chat_details_button_row.dart index 731032817..25fe876d5 100644 --- a/lib/pangea/chat_settings/pages/chat_details_button_row.dart +++ b/lib/pangea/chat_settings/pages/chat_details_button_row.dart @@ -37,10 +37,7 @@ class ChatDetailsButtonRowState extends State { @override void initState() { super.initState(); - notificationChangeSub ??= Matrix.of(context) - .client - .onSync - .stream + notificationChangeSub ??= Matrix.of(context).client.onSync.stream .where( (syncUpdate) => syncUpdate.accountData?.any( @@ -48,9 +45,7 @@ class ChatDetailsButtonRowState extends State { ) ?? false, ) - .listen( - (u) => setState(() {}), - ); + .listen((u) => setState(() {})); } @override @@ -71,11 +66,10 @@ class ChatDetailsButtonRowState extends State { title: l10n.permissions, icon: const Icon(Icons.edit_attributes_outlined, size: 30.0), onPressed: () { - NavigationUtil.goToSpaceRoute( - room.id, - ['details', 'permissions'], - context, - ); + NavigationUtil.goToSpaceRoute(room.id, [ + 'details', + 'permissions', + ], context); }, enabled: room.isRoomAdmin, visible: !room.isDirectChat, @@ -112,9 +106,7 @@ class ChatDetailsButtonRowState extends State { room.id, ['details', 'invite'], context, - queryParams: { - 'filter': filter, - }, + queryParams: {'filter': filter}, ); }, enabled: room.canInvite, @@ -190,11 +182,9 @@ class ChatDetailsButtonRowState extends State { @override Widget build(BuildContext context) { - final buttons = _buttons(context) - .where( - (button) => button.visible, - ) - .toList(); + final buttons = _buttons( + context, + ).where((button) => button.visible).toList(); return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -205,10 +195,12 @@ class ChatDetailsButtonRowState extends State { final mini = fullButtonCapacity < 4; - final List mainViewButtons = - buttons.where((button) => button.showInMainView).toList(); - final List otherButtons = - buttons.where((button) => !button.showInMainView).toList(); + final List mainViewButtons = buttons + .where((button) => button.showInMainView) + .toList(); + final List otherButtons = buttons + .where((button) => !button.showInMainView) + .toList(); return Row( spacing: FluffyThemes.isColumnMode(context) ? 12.0 : 0.0, diff --git a/lib/pangea/chat_settings/pages/chat_details_content.dart b/lib/pangea/chat_settings/pages/chat_details_content.dart index b97eb0cfb..c71edb338 100644 --- a/lib/pangea/chat_settings/pages/chat_details_content.dart +++ b/lib/pangea/chat_settings/pages/chat_details_content.dart @@ -58,18 +58,14 @@ class ChatDetailsContent extends StatelessWidget { ), ), if (!room.isDirectChat && - room.canChangeStateEvent( - EventTypes.RoomAvatar, - )) + room.canChangeStateEvent(EventTypes.RoomAvatar)) Positioned( bottom: 0, right: 0, child: FloatingActionButton.small( onPressed: controller.setAvatarAction, heroTag: null, - child: const Icon( - Icons.camera_alt_outlined, - ), + child: const Icon(Icons.camera_alt_outlined), ), ), ], @@ -83,23 +79,22 @@ class ChatDetailsContent extends StatelessWidget { TextButton.icon( onPressed: room.isDirectChat ? null - : () => room.canChangeStateEvent( - EventTypes.RoomName, - ) - ? controller.setDisplaynameAction() - : FluffyShare.share( - displayname, - context, - copyOnly: true, - ), + : () => + room.canChangeStateEvent( + EventTypes.RoomName, + ) + ? controller.setDisplaynameAction() + : FluffyShare.share( + displayname, + context, + copyOnly: true, + ), icon: Icon( room.isDirectChat ? Icons.chat_bubble_outline - : room.canChangeStateEvent( - EventTypes.RoomName, - ) - ? Icons.edit_outlined - : Icons.copy_outlined, + : room.canChangeStateEvent(EventTypes.RoomName) + ? Icons.edit_outlined + : Icons.copy_outlined, size: 16, ), style: TextButton.styleFrom( @@ -120,17 +115,12 @@ class ChatDetailsContent extends StatelessWidget { onPressed: room.isDirectChat || !room.canInvite ? null : () => NavigationUtil.goToSpaceRoute( - controller.roomId, - ['details', 'invite'], - context, - queryParams: { - 'filter': 'participants', - }, - ), - icon: const Icon( - Icons.group_outlined, - size: 14, - ), + controller.roomId, + ['details', 'invite'], + context, + queryParams: {'filter': 'participants'}, + ), + icon: const Icon(Icons.group_outlined, size: 14), style: TextButton.styleFrom( foregroundColor: theme.colorScheme.secondary, disabledForegroundColor: @@ -171,8 +161,8 @@ class ChatDetailsContent extends StatelessWidget { child: SelectableLinkify( text: room.topic.isEmpty ? room.isSpace - ? L10n.of(context).noSpaceDescriptionYet - : L10n.of(context).noChatDescriptionYet + ? L10n.of(context).noSpaceDescriptionYet + : L10n.of(context).noChatDescriptionYet : room.topic, options: const LinkifyOptions(humanize: false), linkStyle: const TextStyle( @@ -195,10 +185,7 @@ class ChatDetailsContent extends StatelessWidget { ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: ChatDetailsButtonRow( - controller: controller, - room: room, - ), + child: ChatDetailsButtonRow(controller: controller, room: room), ), ], ); @@ -210,9 +197,7 @@ class ChatDetailsContent extends StatelessWidget { children: [ const InstructionsInlineTooltip( instructionsEnum: InstructionsEnum.chatParticipantTooltip, - padding: EdgeInsets.only( - bottom: 16.0, - ), + padding: EdgeInsets.only(bottom: 16.0), ), RoomParticipantsSection(room: room), ], diff --git a/lib/pangea/chat_settings/pages/edit_course.dart b/lib/pangea/chat_settings/pages/edit_course.dart index c8a024ba1..5ba7dc627 100644 --- a/lib/pangea/chat_settings/pages/edit_course.dart +++ b/lib/pangea/chat_settings/pages/edit_course.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; import 'package:matrix/matrix.dart'; @@ -150,7 +151,7 @@ class EditCourseController extends State { final picked = await selectFiles( context, allowMultiple: false, - type: FileSelectorType.images, + type: FileType.image, ); final pickedFile = picked.firstOrNull; if (pickedFile == null) return; @@ -171,18 +172,16 @@ class EditCourseController extends State { title: Text(L10n.of(context).editing), ), body: StreamBuilder( - stream: Matrix.of(context).client.onRoomState.stream.where( - (u) => u.roomId == widget.roomId, - ), + stream: Matrix.of( + context, + ).client.onRoomState.stream.where((u) => u.roomId == widget.roomId), builder: (context, snapshot) { return SafeArea( child: Container( alignment: Alignment.topCenter, padding: const EdgeInsets.all(16.0), child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 600, - ), + constraints: const BoxConstraints(maxWidth: 600), child: _room == null || !_room!.isSpace ? Center(child: Text(L10n.of(context).noRoomsFound)) : Column( @@ -212,8 +211,8 @@ class EditCourseController extends State { name: _room?.name, borderRadius: BorderRadius.circular( - 0.0, - ), + 0.0, + ), size: 200.0, ), ), @@ -234,8 +233,9 @@ class EditCourseController extends State { controller: _titleController, decoration: InputDecoration( border: OutlineInputBorder( - borderRadius: - BorderRadius.circular(4.0), + borderRadius: BorderRadius.circular( + 4.0, + ), ), hintText: L10n.of(context).courseTitle, ), @@ -244,8 +244,9 @@ class EditCourseController extends State { controller: _descController, decoration: InputDecoration( border: OutlineInputBorder( - borderRadius: - BorderRadius.circular(4.0), + borderRadius: BorderRadius.circular( + 4.0, + ), ), hintText: L10n.of(context).courseDesc, ), diff --git a/lib/pangea/chat_settings/pages/pangea_chat_access_settings.dart b/lib/pangea/chat_settings/pages/pangea_chat_access_settings.dart index ff33177e8..a70fd6f72 100644 --- a/lib/pangea/chat_settings/pages/pangea_chat_access_settings.dart +++ b/lib/pangea/chat_settings/pages/pangea_chat_access_settings.dart @@ -29,14 +29,13 @@ class PangeaChatAccessSettingsPageView extends StatelessWidget { body: MaxWidthBody( showBorder: false, child: StreamBuilder( - stream: room.client.onRoomState.stream - .where((update) => update.roomId == controller.room.id), + stream: room.client.onRoomState.stream.where( + (update) => update.roomId == controller.room.id, + ), builder: (context, snapshot) { return Container( width: 400.0, - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: FutureBuilder( future: room.client.getRoomVisibilityOnDirectory(room.id), builder: (context, snapshot) { @@ -111,11 +110,7 @@ class ChatAccessTitle extends StatelessWidget { final IconData icon; final String title; - const ChatAccessTitle({ - super.key, - required this.icon, - required this.title, - }); + const ChatAccessTitle({super.key, required this.icon, required this.title}); @override Widget build(BuildContext context) { @@ -123,10 +118,7 @@ class ChatAccessTitle extends StatelessWidget { final isColumnMode = FluffyThemes.isColumnMode(context); return Row( children: [ - Icon( - icon, - size: isColumnMode ? 32.0 : 24.0, - ), + Icon(icon, size: isColumnMode ? 32.0 : 24.0), SizedBox(width: isColumnMode ? 32.0 : 16.0), Flexible( child: Text( @@ -207,8 +199,8 @@ class ChatAccessTile extends StatelessWidget { description != null ? Text(description!) : descriptionWidget != null - ? descriptionWidget! - : const SizedBox.shrink(), + ? descriptionWidget! + : const SizedBox.shrink(), ], ), ), diff --git a/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart b/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart index bdf7e050c..6588362bd 100644 --- a/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart +++ b/lib/pangea/chat_settings/pages/pangea_invitation_selection.dart @@ -91,13 +91,15 @@ class PangeaInvitationSelectionController void initState() { super.initState(); - _room?.requestParticipants( - [Membership.join, Membership.invite, Membership.knock], - false, - true, - ).then((_) { - if (mounted) setState(() {}); - }); + _room + ?.requestParticipants( + [Membership.join, Membership.invite, Membership.knock], + false, + true, + ) + .then((_) { + if (mounted) setState(() {}); + }); if (widget.initialFilter != null && availableFilters.contains(widget.initialFilter)) { @@ -167,14 +169,11 @@ class PangeaInvitationSelectionController (f) => switch (f) { InvitationFilter.space => spaceParent != null, InvitationFilter.contacts => true, - InvitationFilter.invited => participants?.any( - (u) => u.membership == Membership.invite, - ) ?? - false, - InvitationFilter.knocking => participants?.any( - (u) => u.membership == Membership.knock, - ) ?? - false, + InvitationFilter.invited => + participants?.any((u) => u.membership == Membership.invite) ?? + false, + InvitationFilter.knocking => + participants?.any((u) => u.membership == Membership.knock) ?? false, InvitationFilter.public => true, InvitationFilter.participants => true, }, @@ -186,21 +185,21 @@ class PangeaInvitationSelectionController } List get _membershipOrder => [ - Membership.join, - Membership.invite, - Membership.knock, - Membership.leave, - Membership.ban, - ]; + Membership.join, + Membership.invite, + Membership.knock, + Membership.leave, + Membership.ban, + ]; String? membershipCopy(Membership? membership) => switch (membership) { - Membership.ban => L10n.of(context).banned, - Membership.invite => L10n.of(context).invited, - Membership.join => null, - Membership.knock => L10n.of(context).knocking, - Membership.leave => L10n.of(context).leftTheChat, - null => null, - }; + Membership.ban => L10n.of(context).banned, + Membership.invite => L10n.of(context).invited, + Membership.join => null, + Membership.knock => L10n.of(context).knocking, + Membership.leave => L10n.of(context).leftTheChat, + null => null, + }; int _sortUsers(User a, User b) { // sort yourself to the top @@ -256,17 +255,15 @@ class PangeaInvitationSelectionController case InvitationFilter.contacts: contacts = getContacts(context); case InvitationFilter.invited: - contacts = participants - ?.where( - (u) => u.membership == Membership.invite, - ) + contacts = + participants + ?.where((u) => u.membership == Membership.invite) .toList() ?? []; case InvitationFilter.knocking: - contacts = participants - ?.where( - (u) => u.membership == Membership.knock, - ) + contacts = + participants + ?.where((u) => u.membership == Membership.knock) .toList() ?? []; default: @@ -323,11 +320,7 @@ class PangeaInvitationSelectionController await _room!.addJoinCode(); if (mounted) setState(() {}); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'roomId': _room!.id}, - ); + ErrorHandler.logError(e: e, s: s, data: {'roomId': _room!.id}); } } @@ -343,9 +336,9 @@ class PangeaInvitationSelectionController try { response = await matrix.client.searchUser(text, limit: 100); } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text((e).toLocalizedString(context))), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text((e).toLocalizedString(context)))); return; } finally { setState(() => loading = false); @@ -367,8 +360,7 @@ class PangeaInvitationSelectionController ); } - final participants = this - .participants + final participants = this.participants ?.where( (user) => [Membership.join, Membership.invite].contains(user.membership), @@ -457,9 +449,9 @@ class PangeaInvitationSelectionController ), ); } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(result.error.toString())), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(result.error.toString()))); } }); } diff --git a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart index 0b050a1ed..ca55851db 100644 --- a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart +++ b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart @@ -31,13 +31,12 @@ class PangeaInvitationSelectionView extends StatelessWidget { @override Widget build(BuildContext context) { - final room = - Matrix.of(context).client.getRoomById(controller.widget.roomId); + final room = Matrix.of( + context, + ).client.getRoomById(controller.widget.roomId); if (room == null) { return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).oopsSomethingWentWrong), - ), + appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)), body: Center( child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), ), @@ -50,19 +49,13 @@ class PangeaInvitationSelectionView extends StatelessWidget { final doneButton = ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: theme.colorScheme.primaryContainer, - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), ), child: Row( spacing: 12.0, mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.check, - color: theme.colorScheme.onPrimaryContainer, - ), + Icon(Icons.check, color: theme.colorScheme.onPrimaryContainer), Text( L10n.of(context).done, style: TextStyle( @@ -154,32 +147,34 @@ class PangeaInvitationSelectionView extends StatelessWidget { .where((update) => update.roomId == room.id) .rateLimit(const Duration(seconds: 1)), builder: (context, snapshot) { - final participants = - room.getParticipants().map((user) => user.id).toSet(); + final participants = room + .getParticipants() + .map((user) => user.id) + .toSet(); return controller.filter == InvitationFilter.public ? controller.foundProfiles.isEmpty - ? Padding( - padding: const EdgeInsets.all(24.0), - child: Text( - room.isSpace - ? L10n.of(context).publicInviteDescSpace - : L10n.of(context).publicInviteDescChat, - ), - ) - : ListView.builder( - itemCount: controller.foundProfiles.length, - itemBuilder: (BuildContext context, int i) => - _InviteContactListTile( - profile: controller.foundProfiles[i], - isMember: participants.contains( - controller.foundProfiles[i].userId, + ? Padding( + padding: const EdgeInsets.all(24.0), + child: Text( + room.isSpace + ? L10n.of(context).publicInviteDescSpace + : L10n.of(context).publicInviteDescChat, ), - onTap: () => controller.inviteAction( - controller.foundProfiles[i].userId, - ), - controller: controller, - ), - ) + ) + : ListView.builder( + itemCount: controller.foundProfiles.length, + itemBuilder: (BuildContext context, int i) => + _InviteContactListTile( + profile: controller.foundProfiles[i], + isMember: participants.contains( + controller.foundProfiles[i].userId, + ), + onTap: () => controller.inviteAction( + controller.foundProfiles[i].userId, + ), + controller: controller, + ), + ) : ListView.builder( itemCount: contacts.length + 2, itemBuilder: (BuildContext context, int i) { @@ -206,7 +201,9 @@ class PangeaInvitationSelectionView extends StatelessWidget { ), subtitle: Text( L10n.of(context).countParticipants( - controller.spaceParent!.summary + controller + .spaceParent! + .summary .mJoinedMemberCount ?? 1, ), @@ -237,10 +234,9 @@ class PangeaInvitationSelectionView extends StatelessWidget { "${AppConfig.assetsBaseURL}/${RoomSettingsConstants.referFriendAsset}", errorWidget: (context, url, error) => const SizedBox(), - placeholder: (context, url) => - const Center( - child: CircularProgressIndicator - .adaptive(), + placeholder: (context, url) => const Center( + child: + CircularProgressIndicator.adaptive(), ), ), ), @@ -250,15 +246,15 @@ class PangeaInvitationSelectionView extends StatelessWidget { user: contacts[i], profile: Profile( avatarUrl: contacts[i].avatarUrl, - displayName: contacts[i].displayName ?? + displayName: + contacts[i].displayName ?? contacts[i].id.localpart ?? L10n.of(context).user, userId: contacts[i].id, ), isMember: participants.contains(contacts[i].id), - onTap: () => controller.inviteAction( - contacts[i].id, - ), + onTap: () => + controller.inviteAction(contacts[i].id), controller: controller, ); }, @@ -314,41 +310,36 @@ class PangeaInvitationSelectionView extends StatelessWidget { "$initialUrl/#/join_with_link?${SpaceConstants.classCode}=${room.classCode}"; } - await Clipboard.setData( - ClipboardData(text: toCopy), - ); - ScaffoldMessenger.of( - context, - ).showSnackBar( + await Clipboard.setData(ClipboardData(text: toCopy)); + ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - L10n.of(context).copiedToClipboard, - ), + content: Text(L10n.of(context).copiedToClipboard), ), ); }, itemBuilder: (BuildContext context) => >[ - PopupMenuItem( - value: 0, - child: ListTile( - leading: const Icon(Icons.share_outlined), - title: Text(L10n.of(context).shareSpaceLink), - contentPadding: const EdgeInsets.all(0), - ), - ), - PopupMenuItem( - value: 1, - child: ListTile( - leading: const Icon(Icons.share_outlined), - title: Text( - L10n.of(context) - .shareInviteCode(room.classCode!), + PopupMenuItem( + value: 0, + child: ListTile( + leading: const Icon(Icons.share_outlined), + title: Text(L10n.of(context).shareSpaceLink), + contentPadding: const EdgeInsets.all(0), + ), ), - contentPadding: const EdgeInsets.all(0), - ), - ), - ], + PopupMenuItem( + value: 1, + child: ListTile( + leading: const Icon(Icons.share_outlined), + title: Text( + L10n.of( + context, + ).shareInviteCode(room.classCode!), + ), + contentPadding: const EdgeInsets.all(0), + ), + ), + ], ), ), room.classCode != null @@ -392,26 +383,21 @@ class _InviteContactListTile extends StatelessWidget { final String? permissionBatch = participant == null ? null : participant.powerLevel >= 100 - ? L10n.of(context).admin - : participant.powerLevel >= 50 - ? L10n.of(context).moderator - : null; + ? L10n.of(context).admin + : participant.powerLevel >= 50 + ? L10n.of(context).moderator + : null; return ListTile( onTap: participant != null - ? () => showMemberActionsPopupMenu( - context: context, - user: participant, - ) + ? () => + showMemberActionsPopupMenu(context: context, user: participant) : null, leading: Avatar( mxContent: profile.avatarUrl, name: profile.displayName, presenceUserId: profile.userId, - onTap: () => UserDialog.show( - context: context, - profile: profile, - ), + onTap: () => UserDialog.show(context: context, profile: profile), ), title: Text( profile.displayName ?? profile.userId.localpart ?? l10n.user, @@ -425,9 +411,7 @@ class _InviteContactListTile extends StatelessWidget { const SizedBox(height: 2.0), Text( profile.userId, - style: const TextStyle( - fontSize: 12.0, - ), + style: const TextStyle(fontSize: 12.0), maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -450,34 +434,29 @@ class _InviteContactListTile extends StatelessWidget { ), ) : permissionBatch != null - ? Container( - margin: const EdgeInsets.only(right: 12.0), - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - decoration: BoxDecoration( - color: participant!.powerLevel >= 100 - ? theme.colorScheme.tertiary - : theme.colorScheme.tertiaryContainer, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ), - child: Text( - permissionBatch, - style: theme.textTheme.labelSmall?.copyWith( - color: participant.powerLevel >= 100 - ? theme.colorScheme.onTertiary - : theme.colorScheme.onTertiaryContainer, - ), - ), - ) - : TextButton.icon( - onPressed: isMember ? null : onTap, - label: Text(isMember ? l10n.participant : l10n.invite), - icon: Icon(isMember ? Icons.check : Icons.add), + ? Container( + margin: const EdgeInsets.only(right: 12.0), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: participant!.powerLevel >= 100 + ? theme.colorScheme.tertiary + : theme.colorScheme.tertiaryContainer, + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), + child: Text( + permissionBatch, + style: theme.textTheme.labelSmall?.copyWith( + color: participant.powerLevel >= 100 + ? theme.colorScheme.onTertiary + : theme.colorScheme.onTertiaryContainer, ), + ), + ) + : TextButton.icon( + onPressed: isMember ? null : onTap, + label: Text(isMember ? l10n.participant : l10n.invite), + icon: Icon(isMember ? Icons.check : Icons.add), + ), ); } } diff --git a/lib/pangea/chat_settings/pages/pangea_room_details.dart b/lib/pangea/chat_settings/pages/pangea_room_details.dart index c0573ed5c..8631c7b85 100644 --- a/lib/pangea/chat_settings/pages/pangea_room_details.dart +++ b/lib/pangea/chat_settings/pages/pangea_room_details.dart @@ -19,9 +19,7 @@ class PangeaRoomDetailsView extends StatelessWidget { final room = Matrix.of(context).client.getRoomById(controller.roomId!); if (room == null || room.membership == Membership.leave) { return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).oopsSomethingWentWrong), - ), + appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)), body: Center( child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), ), @@ -29,15 +27,17 @@ class PangeaRoomDetailsView extends StatelessWidget { } return StreamBuilder( - stream: room.client.onRoomState.stream - .where((update) => update.roomId == room.id), + stream: room.client.onRoomState.stream.where( + (update) => update.roomId == room.id, + ), builder: (context, snapshot) { return SafeArea( child: Scaffold( appBar: room.isSpace ? null : AppBar( - leading: controller.widget.embeddedCloseButton ?? + leading: + controller.widget.embeddedCloseButton ?? const Center(child: BackButton()), ), body: Padding( diff --git a/lib/pangea/chat_settings/pages/room_details_buttons.dart b/lib/pangea/chat_settings/pages/room_details_buttons.dart index c47f64734..e3138cb16 100644 --- a/lib/pangea/chat_settings/pages/room_details_buttons.dart +++ b/lib/pangea/chat_settings/pages/room_details_buttons.dart @@ -71,10 +71,9 @@ class RoomDetailsButton extends StatelessWidget { height: height, decoration: BoxDecoration( color: hovered || selected - ? Theme.of(context) - .colorScheme - .primaryContainer - .withAlpha(200) + ? Theme.of( + context, + ).colorScheme.primaryContainer.withAlpha(200) : Colors.transparent, borderRadius: BorderRadius.circular(8), ), diff --git a/lib/pangea/chat_settings/pages/room_participants_widget.dart b/lib/pangea/chat_settings/pages/room_participants_widget.dart index ba6484e8b..59600aae4 100644 --- a/lib/pangea/chat_settings/pages/room_participants_widget.dart +++ b/lib/pangea/chat_settings/pages/room_participants_widget.dart @@ -16,10 +16,7 @@ import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; class RoomParticipantsSection extends StatelessWidget { final Room room; - const RoomParticipantsSection({ - required this.room, - super.key, - }); + const RoomParticipantsSection({required this.room, super.key}); final double _width = 100.0; @@ -69,20 +66,18 @@ class RoomParticipantsSection extends StatelessWidget { ? MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( - onTap: () => NavigationUtil.goToSpaceRoute( - room.id, - ['details', 'invite'], - context, - ), + onTap: () => NavigationUtil.goToSpaceRoute(room.id, [ + 'details', + 'invite', + ], context), child: HoverBuilder( builder: (context, hovered) { return Container( decoration: BoxDecoration( color: hovered - ? Theme.of(context) - .colorScheme - .primary - .withAlpha(50) + ? Theme.of( + context, + ).colorScheme.primary.withAlpha(50) : Colors.transparent, borderRadius: BorderRadius.circular(8), ), @@ -116,8 +111,8 @@ class RoomParticipantsSection extends StatelessWidget { final permissionBatch = user.powerLevel >= 100 ? L10n.of(context).admin : user.powerLevel >= 50 - ? L10n.of(context).moderator - : ''; + ? L10n.of(context).moderator + : ''; final membershipBatch = switch (user.membership) { Membership.ban => null, @@ -165,10 +160,7 @@ class RoomParticipantsSection extends StatelessWidget { ), ) else - SizedBox( - height: _width, - width: _width, - ), + SizedBox(height: _width, width: _width), Builder( builder: (context) { return MouseRegion( @@ -235,31 +227,29 @@ class RoomParticipantsSection extends StatelessWidget { ), ) : permissionBatch.isNotEmpty - ? Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 4, - ), - decoration: BoxDecoration( - color: user.powerLevel >= 100 - ? theme.colorScheme.tertiary - : theme.colorScheme.tertiaryContainer, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ), - child: Text( - permissionBatch, - style: - theme.textTheme.labelSmall?.copyWith( - color: user.powerLevel >= 100 - ? theme.colorScheme.onTertiary - : theme.colorScheme - .onTertiaryContainer, - ), - ), - ) - : null, + ? Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 4, + ), + decoration: BoxDecoration( + color: user.powerLevel >= 100 + ? theme.colorScheme.tertiary + : theme.colorScheme.tertiaryContainer, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + ), + child: Text( + permissionBatch, + style: theme.textTheme.labelSmall?.copyWith( + color: user.powerLevel >= 100 + ? theme.colorScheme.onTertiary + : theme.colorScheme.onTertiaryContainer, + ), + ), + ) + : null, ), ], ), diff --git a/lib/pangea/chat_settings/pages/space_details_button_row.dart b/lib/pangea/chat_settings/pages/space_details_button_row.dart index 2ca0c62b8..2d404b176 100644 --- a/lib/pangea/chat_settings/pages/space_details_button_row.dart +++ b/lib/pangea/chat_settings/pages/space_details_button_row.dart @@ -38,10 +38,7 @@ class SpaceDetailsButtonRowState extends State { @override void initState() { super.initState(); - notificationChangeSub ??= Matrix.of(context) - .client - .onSync - .stream + notificationChangeSub ??= Matrix.of(context).client.onSync.stream .where( (syncUpdate) => syncUpdate.accountData?.any( @@ -49,9 +46,7 @@ class SpaceDetailsButtonRowState extends State { ) ?? false, ) - .listen( - (u) => setState(() {}), - ); + .listen((u) => setState(() {})); } @override @@ -67,11 +62,7 @@ class SpaceDetailsButtonRowState extends State { @override Widget build(BuildContext context) { - final buttons = widget.buttons - .where( - (button) => button.visible, - ) - .toList(); + final buttons = widget.buttons.where((button) => button.visible).toList(); return LayoutBuilder( builder: (context, constraints) { @@ -80,10 +71,12 @@ class SpaceDetailsButtonRowState extends State { final mini = fullButtonCapacity < 4; - final List mainViewButtons = - buttons.where((button) => button.showInMainView).toList(); - final List otherButtons = - buttons.where((button) => !button.showInMainView).toList(); + final List mainViewButtons = buttons + .where((button) => button.showInMainView) + .toList(); + final List otherButtons = buttons + .where((button) => !button.showInMainView) + .toList(); return Row( spacing: FluffyThemes.isColumnMode(context) ? 12.0 : 0.0, diff --git a/lib/pangea/chat_settings/pages/space_details_content.dart b/lib/pangea/chat_settings/pages/space_details_content.dart index b57233edd..65976c4da 100644 --- a/lib/pangea/chat_settings/pages/space_details_content.dart +++ b/lib/pangea/chat_settings/pages/space_details_content.dart @@ -38,9 +38,7 @@ enum SpaceSettingsTabs { more; static SpaceSettingsTabs? fromString(String value) { - return SpaceSettingsTabs.values.firstWhereOrNull( - (e) => e.name == value, - ); + return SpaceSettingsTabs.values.firstWhereOrNull((e) => e.name == value); } } @@ -48,11 +46,7 @@ class SpaceDetailsContent extends StatelessWidget { final ChatDetailsController controller; final Room room; - const SpaceDetailsContent( - this.controller, - this.room, { - super.key, - }); + const SpaceDetailsContent(this.controller, this.room, {super.key}); SpaceSettingsTabs tab(BuildContext context) { final defaultTab = FluffyThemes.isColumnMode(context) @@ -111,9 +105,7 @@ class SpaceDetailsContent extends StatelessWidget { if (room.getParticipants([Membership.knock]).isEmpty) { filter = room.pangeaSpaceParents.isNotEmpty ? 'space' : 'contacts'; } - context.go( - '/rooms/spaces/${room.id}/details/invite?filter=$filter', - ); + context.go('/rooms/spaces/${room.id}/details/invite?filter=$filter'); }, enabled: room.canInvite, showInMainView: false, @@ -216,10 +208,8 @@ class SpaceDetailsContent extends StatelessWidget { description: l10n.createGroupChatDesc, icon: const Icon(Symbols.chat_add_on, size: 30.0), onPressed: controller.addGroupChat, - enabled: room.isRoomAdmin && - room.canChangeStateEvent( - EventTypes.SpaceChild, - ), + enabled: + room.isRoomAdmin && room.canChangeStateEvent(EventTypes.SpaceChild), showInMainView: false, ), ButtonDetails( @@ -250,10 +240,7 @@ class SpaceDetailsContent extends StatelessWidget { ButtonDetails( title: l10n.delete, description: l10n.deleteDesc, - icon: const Icon( - Icons.delete_outline, - size: 30.0, - ), + icon: const Icon(Icons.delete_outline, size: 30.0), onPressed: () => DeleteSpaceDialog.show(room, context), enabled: room.isRoomAdmin, showInMainView: false, @@ -351,9 +338,7 @@ class SpaceDetailsContent extends StatelessWidget { ); case SpaceSettingsTabs.course: return SingleChildScrollView( - child: CourseSettings( - controller: controller, - ), + child: CourseSettings(controller: controller), ); case SpaceSettingsTabs.participants: return SingleChildScrollView( @@ -374,16 +359,12 @@ class SpaceDetailsContent extends StatelessWidget { ); case SpaceSettingsTabs.analytics: return SingleChildScrollView( - child: Center( - child: SpaceAnalytics(roomId: room.id), - ), + child: Center(child: SpaceAnalytics(roomId: room.id)), ); case SpaceSettingsTabs.more: - final buttons = _buttons(context) - .where( - (b) => !b.showInMainView && b.visible, - ) - .toList(); + final buttons = _buttons( + context, + ).where((b) => !b.showInMainView && b.visible).toList(); return SingleChildScrollView( child: Column( diff --git a/lib/pangea/chat_settings/utils/bot_client_extension.dart b/lib/pangea/chat_settings/utils/bot_client_extension.dart index c1ad861b7..307dc1400 100644 --- a/lib/pangea/chat_settings/utils/bot_client_extension.dart +++ b/lib/pangea/chat_settings/utils/bot_client_extension.dart @@ -19,35 +19,37 @@ extension BotClientExtension on Client { // All 2-member rooms with the bot List get _targetBotChats => rooms.where((r) { - return - // bot settings exist - r.botOptions != null && - // there is no activity plan - r.activityPlan == null && - // it's just the bot and one other user in the room - r.summary.mJoinedMemberCount == 2 && - r.getParticipants().any((u) => u.id == BotName.byEnvironment); - }).toList(); + return + // bot settings exist + r.botOptions != null && + // there is no activity plan + r.activityPlan == null && + // it's just the bot and one other user in the room + r.summary.mJoinedMemberCount == 2 && + r.getParticipants().any((u) => u.id == BotName.byEnvironment); + }).toList(); Future startChatWithBot() => startDirectChat( - BotName.byEnvironment, - preset: CreateRoomPreset.trustedPrivateChat, - initialState: [ - StateEvent( - content: BotOptionsModel( - mode: BotMode.directChat, - targetLanguage: - MatrixState.pangeaController.userController.userL2?.langCode, - languageLevel: MatrixState.pangeaController.userController.profile - .userSettings.cefrLevel, - ).toJson(), - type: PangeaEventTypes.botOptions, - ), - RoomDefaults.defaultPowerLevels( - userID!, - ), - ], - ); + BotName.byEnvironment, + preset: CreateRoomPreset.trustedPrivateChat, + initialState: [ + StateEvent( + content: BotOptionsModel( + mode: BotMode.directChat, + targetLanguage: + MatrixState.pangeaController.userController.userL2?.langCode, + languageLevel: MatrixState + .pangeaController + .userController + .profile + .userSettings + .cefrLevel, + ).toJson(), + type: PangeaEventTypes.botOptions, + ), + RoomDefaults.defaultPowerLevels(userID!), + ], + ); Future updateBotOptions(UserSettings userSettings) async { final targetBotRooms = [..._targetBotChats]; @@ -69,8 +71,9 @@ extension BotClientExtension on Client { continue; } - final updatedGenders = - Map.from(botOptions.userGenders); + final updatedGenders = Map.from( + botOptions.userGenders, + ); if (updatedGenders[userID] != gender) { updatedGenders[userID!] = gender; diff --git a/lib/pangea/chat_settings/utils/delete_room.dart b/lib/pangea/chat_settings/utils/delete_room.dart index 4cf22806a..7cd772dfc 100644 --- a/lib/pangea/chat_settings/utils/delete_room.dart +++ b/lib/pangea/chat_settings/utils/delete_room.dart @@ -11,17 +11,11 @@ extension on Api { // Response 200 OK format: { message: "Deleted" }. // Requester must be member of the room and have the highest power level of the room to perform this request. Future delete(String roomId) async { - final requestUri = Uri( - path: '_synapse/client/pangea/v1/delete_room', - ); + final requestUri = Uri(path: '_synapse/client/pangea/v1/delete_room'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; request.headers['authorization'] = 'Bearer ${bearerToken!}'; - request.bodyBytes = utf8.encode( - jsonEncode({ - 'room_id': roomId, - }), - ); + request.bodyBytes = utf8.encode(jsonEncode({'room_id': roomId})); final response = await httpClient.send(request); if (response.statusCode != 200) { throw Exception('http error response'); @@ -34,8 +28,8 @@ extension DeleteRoom on Room { await client.delete(id); } - Future> getSpaceChildrenToDelete() async { - final List rooms = []; + Future> getSpaceChildrenToDelete() async { + final List rooms = []; String? nextBatch; int calls = 0; @@ -51,9 +45,7 @@ extension DeleteRoom on Room { } return rooms - .where( - (r) => r.roomType != PangeaRoomTypes.analytics && r.roomId != id, - ) + .where((r) => r.roomType != PangeaRoomTypes.analytics && r.roomId != id) .toList(); } } diff --git a/lib/pangea/chat_settings/utils/room_summary_extension.dart b/lib/pangea/chat_settings/utils/room_summary_extension.dart index 4315a11a7..37ff4e6c8 100644 --- a/lib/pangea/chat_settings/utils/room_summary_extension.dart +++ b/lib/pangea/chat_settings/utils/room_summary_extension.dart @@ -16,9 +16,7 @@ extension RoomSummaryExtension on Api { Future getRoomSummaries(List roomIds) async { final requestUri = Uri( path: '/_synapse/client/unstable/org.pangea/room_preview', - queryParameters: { - 'rooms': roomIds.join(","), - }, + queryParameters: {'rooms': roomIds.join(",")}, ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; @@ -147,8 +145,9 @@ class RoomSummaryResponse { json[EventTypes.RoomJoinRules]?['default']?['content']?['join_rule']; JoinRules? joinRule; if (joinRulesString != null && joinRulesString is String) { - joinRule = JoinRules.values - .singleWhereOrNull((element) => element.text == joinRulesString); + joinRule = JoinRules.values.singleWhereOrNull( + (element) => element.text == joinRulesString, + ); } final displayName = diff --git a/lib/pangea/chat_settings/widgets/chat_context_menu_action.dart b/lib/pangea/chat_settings/widgets/chat_context_menu_action.dart index f8efca23d..387de07aa 100644 --- a/lib/pangea/chat_settings/widgets/chat_context_menu_action.dart +++ b/lib/pangea/chat_settings/widgets/chat_context_menu_action.dart @@ -84,9 +84,7 @@ void chatContextMenuAction( ), const SizedBox(width: 12), Expanded( - child: Text( - l10n.goToCourse(space.getLocalizedDisplayname()), - ), + child: Text(l10n.goToCourse(space.getLocalizedDisplayname())), ), ], ), @@ -123,9 +121,7 @@ void chatContextMenuAction( : Icons.mark_as_unread_outlined, ), const SizedBox(width: 12), - Text( - room.markedUnread ? l10n.markAsRead : l10n.markAsUnread, - ), + Text(room.markedUnread ? l10n.markAsRead : l10n.markAsUnread), ], ), ), @@ -139,9 +135,7 @@ void chatContextMenuAction( room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined, ), const SizedBox(width: 12), - Text( - room.isFavourite ? l10n.unpin : l10n.pin, - ), + Text(room.isFavourite ? l10n.unpin : l10n.pin), ], ), ), @@ -152,13 +146,9 @@ void chatContextMenuAction( child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Icon( - Icons.stop_circle_outlined, - ), + const Icon(Icons.stop_circle_outlined), const SizedBox(width: 12), - Text( - l10n.endActivity, - ), + Text(l10n.endActivity), ], ), ), @@ -175,9 +165,7 @@ void chatContextMenuAction( const SizedBox(width: 12), Text( room.membership == Membership.invite ? l10n.delete : l10n.leave, - style: TextStyle( - color: theme.colorScheme.onErrorContainer, - ), + style: TextStyle(color: theme.colorScheme.onErrorContainer), ), ], ), @@ -195,9 +183,7 @@ void chatContextMenuAction( const SizedBox(width: 12), Text( l10n.delete, - style: TextStyle( - color: theme.colorScheme.onErrorContainer, - ), + style: TextStyle(color: theme.colorScheme.onErrorContainer), ), ], ), @@ -236,6 +222,15 @@ void chatContextMenuAction( ), ); return; + case ChatContextAction.block: + final inviteEvent = room.getState( + EventTypes.RoomMember, + room.client.userID!, + ); + context.go( + '/rooms/settings/security/ignorelist', + extra: inviteEvent?.senderId, + ); case ChatContextAction.leave: final confirmed = await showOkCancelAlertDialog( context: context, diff --git a/lib/pangea/chat_settings/widgets/delete_space_dialog.dart b/lib/pangea/chat_settings/widgets/delete_space_dialog.dart index 2b16d1d17..0d3937f8d 100644 --- a/lib/pangea/chat_settings/widgets/delete_space_dialog.dart +++ b/lib/pangea/chat_settings/widgets/delete_space_dialog.dart @@ -14,16 +14,10 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; class DeleteSpaceDialog extends StatefulWidget { final Room space; - const DeleteSpaceDialog({ - super.key, - required this.space, - }); + const DeleteSpaceDialog({super.key, required this.space}); - static Future show( - Room room, - BuildContext context, - ) async { - final resp = await showDialog?>( + static Future show(Room room, BuildContext context) async { + final resp = await showDialog?>( context: context, builder: (_) => DeleteSpaceDialog(space: room), ); @@ -40,7 +34,7 @@ class DeleteSpaceDialog extends StatefulWidget { static Future _deleteSpace( Room space, - List rooms, + List rooms, ) async { final List> futures = []; for (final room in rooms) { @@ -66,8 +60,8 @@ class DeleteSpaceDialog extends StatefulWidget { } class DeleteSpaceDialogState extends State { - List _rooms = []; - final List _roomsToDelete = []; + List _rooms = []; + final List _roomsToDelete = []; bool _loadingRooms = true; String? _roomLoadError; @@ -93,13 +87,7 @@ class DeleteSpaceDialogState extends State { _rooms = await widget.space.getSpaceChildrenToDelete(); } catch (e, s) { _roomLoadError = L10n.of(context).errorLoadingSpaceChildren; - ErrorHandler.logError( - e: e, - s: s, - data: { - "roomID": widget.space.id, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {"roomID": widget.space.id}); } finally { setState(() { _loadingRooms = false; @@ -107,10 +95,7 @@ class DeleteSpaceDialogState extends State { } } - void _onRoomSelected( - bool? selected, - SpaceRoomsChunk room, - ) { + void _onRoomSelected(bool? selected, SpaceRoomsChunk$2 room) { if (selected == null || (selected && _roomsToDelete.contains(room)) || (!selected && !_roomsToDelete.contains(room))) { @@ -134,7 +119,7 @@ class DeleteSpaceDialogState extends State { }); } - List get _selectableRooms { + List get _selectableRooms { return _rooms.where((chunk) { final room = widget.space.client.getRoomById(chunk.roomId); return room != null && @@ -147,16 +132,11 @@ class DeleteSpaceDialogState extends State { Widget build(BuildContext context) { return Dialog( shape: RoundedRectangleBorder( - side: BorderSide( - color: Theme.of(context).colorScheme.error, - ), + side: BorderSide(color: Theme.of(context).colorScheme.error), borderRadius: BorderRadius.circular(32.0), ), child: Container( - constraints: const BoxConstraints( - maxWidth: 400, - maxHeight: 600, - ), + constraints: const BoxConstraints(maxWidth: 400, maxHeight: 600), padding: const EdgeInsets.symmetric(vertical: 20), child: Column( mainAxisSize: MainAxisSize.min, @@ -164,8 +144,8 @@ class DeleteSpaceDialogState extends State { Text( L10n.of(context).areYouSure, style: Theme.of(context).textTheme.titleLarge?.copyWith( - color: Theme.of(context).colorScheme.error, - ), + color: Theme.of(context).colorScheme.error, + ), ), Padding( padding: const EdgeInsets.symmetric( @@ -210,7 +190,8 @@ class DeleteSpaceDialogState extends State { children: [ if (_selectableRooms.length > 1) CheckboxListTile( - value: _roomsToDelete.length == + value: + _roomsToDelete.length == _selectableRooms.length, onChanged: (_) => _toggleSelectAll(), title: Text( @@ -226,13 +207,16 @@ class DeleteSpaceDialogState extends State { itemBuilder: (context, index) { final chunk = _rooms[index]; - final room = widget.space.client - .getRoomById(chunk.roomId); - final isMember = room != null && + final room = widget.space.client.getRoomById( + chunk.roomId, + ); + final isMember = + room != null && room.membership == Membership.join && room.isRoomAdmin; - final displayname = chunk.name ?? + final displayname = + chunk.name ?? chunk.canonicalAlias ?? L10n.of(context).emptyChat; @@ -243,7 +227,7 @@ class DeleteSpaceDialogState extends State { value: _roomsToDelete.contains(chunk), onChanged: isMember ? (value) => - _onRoomSelected(value, chunk) + _onRoomSelected(value, chunk) : null, title: Text(displayname), controlAffinity: diff --git a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart index cf0b5d11d..9f0cbdb87 100644 --- a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart +++ b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart @@ -23,9 +23,7 @@ class LanguageLevelDropdown extends StatelessWidget { alignedDropdown: true, child: DropdownButtonFormField( itemHeight: null, - decoration: InputDecoration( - labelText: l10n.cefrLevelLabel, - ), + decoration: InputDecoration(labelText: l10n.cefrLevelLabel), selectedItemBuilder: (context) => LanguageLevelTypeEnum.values .map((levelOption) => Text(levelOption.title(context))) .toList(), @@ -38,8 +36,9 @@ class LanguageLevelDropdown extends StatelessWidget { } : null, initialValue: initialLevel, - items: LanguageLevelTypeEnum.values - .map((LanguageLevelTypeEnum levelOption) { + items: LanguageLevelTypeEnum.values.map(( + LanguageLevelTypeEnum levelOption, + ) { return DropdownMenuItem( value: levelOption, child: Padding( diff --git a/lib/pangea/chat_settings/widgets/room_capacity_button.dart b/lib/pangea/chat_settings/widgets/room_capacity_button.dart index 7397325ac..161b90946 100644 --- a/lib/pangea/chat_settings/widgets/room_capacity_button.dart +++ b/lib/pangea/chat_settings/widgets/room_capacity_button.dart @@ -12,11 +12,7 @@ class RoomCapacityButton extends StatefulWidget { final Room room; final ChatDetailsController? controller; - const RoomCapacityButton({ - super.key, - required this.room, - this.controller, - }); + const RoomCapacityButton({super.key, required this.room, this.controller}); @override RoomCapacityButtonState createState() => RoomCapacityButtonState(); @@ -90,9 +86,7 @@ class RoomCapacityButtonState extends State { ); if (success.error == null) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(L10n.of(context).chatCapacityHasBeenChanged), - ), + SnackBar(content: Text(L10n.of(context).chatCapacityHasBeenChanged)), ); setState(() {}); } diff --git a/lib/pangea/choreographer/assistance_state_enum.dart b/lib/pangea/choreographer/assistance_state_enum.dart index 1680bb972..1a4f95539 100644 --- a/lib/pangea/choreographer/assistance_state_enum.dart +++ b/lib/pangea/choreographer/assistance_state_enum.dart @@ -15,7 +15,7 @@ enum AssistanceStateEnum { complete, error; - Color stateColor(context) { + Color stateColor(BuildContext context) { switch (this) { case AssistanceStateEnum.noSub: case AssistanceStateEnum.noMessage: @@ -30,7 +30,7 @@ enum AssistanceStateEnum { } } - Color sendButtonColor(context) { + Color sendButtonColor(BuildContext context) { switch (this) { case AssistanceStateEnum.noMessage: case AssistanceStateEnum.fetched: @@ -46,17 +46,18 @@ enum AssistanceStateEnum { } bool get allowsFeedback => switch (this) { - AssistanceStateEnum.notFetched => true, - _ => false, - }; + AssistanceStateEnum.notFetched => true, + _ => false, + }; Color backgroundColor(BuildContext context) => switch (this) { - AssistanceStateEnum.noSub || - AssistanceStateEnum.noMessage || - AssistanceStateEnum.fetched || - AssistanceStateEnum.complete || - AssistanceStateEnum.error => - Theme.of(context).colorScheme.surfaceContainerHighest, - _ => Theme.of(context).colorScheme.primaryContainer, - }; + AssistanceStateEnum.noSub || + AssistanceStateEnum.noMessage || + AssistanceStateEnum.fetched || + AssistanceStateEnum.complete || + AssistanceStateEnum.error => Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + _ => Theme.of(context).colorScheme.primaryContainer, + }; } diff --git a/lib/pangea/choreographer/choreo_edit_model.dart b/lib/pangea/choreographer/choreo_edit_model.dart index 6669dffa9..6ce869120 100644 --- a/lib/pangea/choreographer/choreo_edit_model.dart +++ b/lib/pangea/choreographer/choreo_edit_model.dart @@ -9,11 +9,7 @@ class ChoreoEditModel { final String insert; /// Normal constructor created from preexisting ChoreoEdit values - const ChoreoEditModel({ - this.offset = 0, - this.length = 0, - this.insert = "", - }); + const ChoreoEditModel({this.offset = 0, this.length = 0, this.insert = ""}); /// Constructor that determines and saves /// edits differentiating originalText and editedText @@ -30,11 +26,7 @@ class ChoreoEditModel { final length = _lastDifference(originalText, editedText, offset) + 1 - offset; final insert = _insertion(originalText, editedText, offset, length); - return ChoreoEditModel( - offset: offset, - length: length, - insert: insert, - ); + return ChoreoEditModel(offset: offset, length: length, insert: insert); } factory ChoreoEditModel.fromJson(Map json) { diff --git a/lib/pangea/choreographer/choreo_record_model.dart b/lib/pangea/choreographer/choreo_record_model.dart index 502977e06..cc02fd108 100644 --- a/lib/pangea/choreographer/choreo_record_model.dart +++ b/lib/pangea/choreographer/choreo_record_model.dart @@ -96,11 +96,7 @@ class ChoreoRecordModel { length = textBefore.length - offset; } - textAfter = textBefore.replaceRange( - offset, - offset + length, - insert, - ); + textAfter = textBefore.replaceRange(offset, offset + length, insert); final edits = ChoreoEditModel.fromText( originalText: currentEdit, @@ -143,23 +139,24 @@ class ChoreoRecordModel { Map toJson() { final data = {}; data[_stepsKey] = jsonEncode(choreoSteps.map((e) => e.toJson()).toList()); - data[_openMatchesKey] = - jsonEncode(openMatches.map((e) => e.toJson()).toList()); + data[_openMatchesKey] = jsonEncode( + openMatches.map((e) => e.toJson()).toList(), + ); data[_originalTextKey] = originalText; return data; } bool get includedIT => choreoSteps.any((step) { - return step.acceptedOrIgnoredMatch?.status == - PangeaMatchStatusEnum.accepted && - (step.acceptedOrIgnoredMatch?.isOutOfTargetMatch ?? false); - }); + return step.acceptedOrIgnoredMatch?.status == + PangeaMatchStatusEnum.accepted && + (step.acceptedOrIgnoredMatch?.isOutOfTargetMatch ?? false); + }); bool get includedIGC => choreoSteps.any((step) { - return step.acceptedOrIgnoredMatch?.status == - PangeaMatchStatusEnum.accepted && - (step.acceptedOrIgnoredMatch?.isGrammarMatch ?? false); - }); + return step.acceptedOrIgnoredMatch?.status == + PangeaMatchStatusEnum.accepted && + (step.acceptedOrIgnoredMatch?.isGrammarMatch ?? false); + }); bool endedWithIT(String sent) { return includedIT && stepText() == sent; diff --git a/lib/pangea/choreographer/choreographer.dart b/lib/pangea/choreographer/choreographer.dart index 500a70139..285e3ba73 100644 --- a/lib/pangea/choreographer/choreographer.dart +++ b/lib/pangea/choreographer/choreographer.dart @@ -57,9 +57,7 @@ class Choreographer extends ChangeNotifier { StreamSubscription? _acceptedContinuanceSub; StreamSubscription? _updatedMatchSub; - Choreographer( - this.inputFocus, - ) { + Choreographer(this.inputFocus) { _initialize(); } @@ -70,10 +68,10 @@ class Choreographer extends ChangeNotifier { String get currentText => textController.text; ChoreoRecordModel get _record => _choreoRecord ??= ChoreoRecordModel( - originalText: textController.text, - choreoSteps: [], - openMatches: [], - ); + originalText: textController.text, + choreoSteps: [], + openMatches: [], + ); bool _backoffRequest(DateTime? error, int backoffSeconds) { if (error == null) return false; @@ -106,22 +104,29 @@ class Choreographer extends ChangeNotifier { ); _languageSub ??= MatrixState - .pangeaController.userController.languageStream.stream + .pangeaController + .userController + .languageStream + .stream .listen((update) { - clear(); - }); + clear(); + }); _settingsUpdateSub ??= MatrixState - .pangeaController.userController.settingsUpdateStream.stream + .pangeaController + .userController + .settingsUpdateStream + .stream .listen((_) { - notifyListeners(); - }); + notifyListeners(); + }); _acceptedContinuanceSub ??= itController.acceptedContinuanceStream.stream .listen(_onAcceptContinuance); - _updatedMatchSub ??= - igcController.matchUpdateStream.stream.listen(_onUpdateMatch); + _updatedMatchSub ??= igcController.matchUpdateStream.stream.listen( + _onUpdateMatch, + ); } void clear() { @@ -161,7 +166,7 @@ class Choreographer extends ChangeNotifier { super.dispose(); } - void onPaste(value) => _record.pastedStrings.add(value); + void onPaste(String value) => _record.pastedStrings.add(value); void onClickSend() { if (assistanceState == AssistanceStateEnum.fetched) { @@ -239,9 +244,7 @@ class Choreographer extends ChangeNotifier { textController.editType = EditTypeEnum.keyboard; } - Future requestWritingAssistance({ - bool manual = false, - }) async { + Future requestWritingAssistance({bool manual = false}) async { if (assistanceState != AssistanceStateEnum.notFetched) return; final SubscriptionStatus canSendStatus = MatrixState.pangeaController.subscriptionController.subscriptionStatus; @@ -261,10 +264,7 @@ class Choreographer extends ChangeNotifier { _resetDebounceTimer(); _startLoading(); - await igcController.getIGCTextData( - textController.text, - [], - ); + await igcController.getIGCTextData(textController.text, []); // init choreo record to record the original text before any matches are applied _choreoRecord ??= ChoreoRecordModel( @@ -277,10 +277,7 @@ class Choreographer extends ChangeNotifier { await igcController.acceptNormalizationMatches(); } else { // trigger a re-render of the text field to show IGC matches - textController.setSystemText( - textController.text, - EditTypeEnum.igc, - ); + textController.setSystemText(textController.text, EditTypeEnum.igc); } _stopLoading(); @@ -311,19 +308,20 @@ class Choreographer extends ChangeNotifier { if (l1LangCode != null && l2LangCode != null && !_backoffRequest(_lastTokensError, _tokenErrorBackoff)) { - final res = await TokensRepo.get( - MatrixState.pangeaController.userController.accessToken, - TokensRequestModel( - fullText: message, - senderL1: l1LangCode, - senderL2: l2LangCode, - ), - ).timeout( - const Duration(seconds: 10), - onTimeout: () { - return Result.error("Token request timed out"); - }, - ); + final res = + await TokensRepo.get( + MatrixState.pangeaController.userController.accessToken, + TokensRequestModel( + fullText: message, + senderL1: l1LangCode, + senderL2: l2LangCode, + ), + ).timeout( + const Duration(seconds: 10), + onTimeout: () { + return Result.error("Token request timed out"); + }, + ); if (res.isError) { _lastTokensError = DateTime.now(); @@ -374,10 +372,7 @@ class Choreographer extends ChangeNotifier { igcController.clear(); itMatch.setStatus(PangeaMatchStatusEnum.accepted); - _record.addRecord( - "", - match: itMatch.updatedMatch, - ); + _record.addRecord("", match: itMatch.updatedMatch); _setChoreoMode(ChoreoModeEnum.it); textController.setSystemText("", EditTypeEnum.it); @@ -423,19 +418,13 @@ class Choreographer extends ChangeNotifier { } void _onUpdateMatch(PangeaMatchState match) { - textController.setSystemText( - igcController.currentText!, - EditTypeEnum.igc, - ); + textController.setSystemText(igcController.currentText!, EditTypeEnum.igc); switch (match.updatedMatch.status) { case PangeaMatchStatusEnum.accepted: case PangeaMatchStatusEnum.automatic: case PangeaMatchStatusEnum.ignored: - _record.addRecord( - textController.text, - match: match.updatedMatch, - ); + _record.addRecord(textController.text, match: match.updatedMatch); case PangeaMatchStatusEnum.undo: _record.choreoSteps.removeWhere( (step) => diff --git a/lib/pangea/choreographer/choreographer_send_button.dart b/lib/pangea/choreographer/choreographer_send_button.dart index b367ee482..69a6c6a15 100644 --- a/lib/pangea/choreographer/choreographer_send_button.dart +++ b/lib/pangea/choreographer/choreographer_send_button.dart @@ -6,10 +6,7 @@ import 'package:fluffychat/pangea/choreographer/choreographer_state_extension.da class ChoreographerSendButton extends StatelessWidget { final ChatController controller; - const ChoreographerSendButton({ - super.key, - required this.controller, - }); + const ChoreographerSendButton({super.key, required this.controller}); Future _onPressed(BuildContext context) async { controller.choreographer.onClickSend(); @@ -20,14 +17,15 @@ class ChoreographerSendButton extends StatelessWidget { Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: controller.choreographer.isFetching, - builder: (context, fetching, __) { + builder: (context, fetching, _) { return Container( height: 56, alignment: Alignment.center, child: IconButton( icon: const Icon(Icons.send_outlined), - color: controller.choreographer.assistanceState - .sendButtonColor(context), + color: controller.choreographer.assistanceState.sendButtonColor( + context, + ), onPressed: fetching ? null : () => _onPressed(context), tooltip: L10n.of(context).send, ), diff --git a/lib/pangea/choreographer/igc/autocorrect_popup.dart b/lib/pangea/choreographer/igc/autocorrect_popup.dart index dc49d053a..e4f4e332f 100644 --- a/lib/pangea/choreographer/igc/autocorrect_popup.dart +++ b/lib/pangea/choreographer/igc/autocorrect_popup.dart @@ -25,10 +25,7 @@ class AutocorrectPopup extends StatelessWidget { spacing: 8.0, children: [ Text(originalText), - InkWell( - onTap: onUndo, - child: const Icon(Icons.replay, size: 12), - ), + InkWell(onTap: onUndo, child: const Icon(Icons.replay, size: 12)), ], ), ), diff --git a/lib/pangea/choreographer/igc/autocorrect_span.dart b/lib/pangea/choreographer/igc/autocorrect_span.dart index 5517172d1..02607f997 100644 --- a/lib/pangea/choreographer/igc/autocorrect_span.dart +++ b/lib/pangea/choreographer/igc/autocorrect_span.dart @@ -13,33 +13,33 @@ class AutocorrectSpan extends WidgetSpan { required VoidCallback onUndo, required TextStyle style, }) : super( - alignment: PlaceholderAlignment.middle, - child: CompositedTransformTarget( - link: MatrixState.pAnyState.layerLinkAndKey(transformTargetId).link, - child: Builder( - builder: (context) { - return RichText( - key: MatrixState.pAnyState - .layerLinkAndKey(transformTargetId) - .key, - text: TextSpan( - text: currentText, - style: style, - recognizer: TapGestureRecognizer() - ..onTap = () { - OverlayUtil.showOverlay( - context: context, - child: AutocorrectPopup( - originalText: originalText, - onUndo: onUndo, - ), - transformTargetId: transformTargetId, - ); - }, - ), - ); - }, - ), - ), - ); + alignment: PlaceholderAlignment.middle, + child: CompositedTransformTarget( + link: MatrixState.pAnyState.layerLinkAndKey(transformTargetId).link, + child: Builder( + builder: (context) { + return RichText( + key: MatrixState.pAnyState + .layerLinkAndKey(transformTargetId) + .key, + text: TextSpan( + text: currentText, + style: style, + recognizer: TapGestureRecognizer() + ..onTap = () { + OverlayUtil.showOverlay( + context: context, + child: AutocorrectPopup( + originalText: originalText, + onUndo: onUndo, + ), + transformTargetId: transformTargetId, + ); + }, + ), + ); + }, + ), + ), + ); } diff --git a/lib/pangea/choreographer/igc/igc_controller.dart b/lib/pangea/choreographer/igc/igc_controller.dart index 7ca41e5c8..5387d87f3 100644 --- a/lib/pangea/choreographer/igc/igc_controller.dart +++ b/lib/pangea/choreographer/igc/igc_controller.dart @@ -39,12 +39,12 @@ class IgcController { String? get currentText => _currentText; List get openMatches => _openMatches; - List get recentAutomaticCorrections => - _closedMatches.reversed - .takeWhile( - (m) => m.updatedMatch.status == PangeaMatchStatusEnum.automatic, - ) - .toList(); + List get recentAutomaticCorrections => _closedMatches + .reversed + .takeWhile( + (m) => m.updatedMatch.status == PangeaMatchStatusEnum.automatic, + ) + .toList(); List get openAutomaticMatches => _openMatches .where((match) => match.updatedMatch.match.isNormalizationError()) @@ -52,8 +52,9 @@ class IgcController { PangeaMatchState? get currentlyOpenMatch { final RegExp pattern = RegExp(r'span_card_overlay_.+'); - final String? matchingKey = - MatrixState.pAnyState.getMatchingOverlayKeys(pattern).firstOrNull; + final String? matchingKey = MatrixState.pAnyState + .getMatchingOverlayKeys(pattern) + .firstOrNull; if (matchingKey == null) return null; final parts = matchingKey.split('_'); @@ -72,14 +73,13 @@ class IgcController { IGCRequestModel _igcRequest( String text, List prevMessages, - ) => - IGCRequestModel( - fullText: text, - userId: MatrixState.pangeaController.userController.client.userID!, - enableIGC: true, - enableIT: true, - prevMessages: prevMessages, - ); + ) => IGCRequestModel( + fullText: text, + userId: MatrixState.pangeaController.userController.client.userID!, + enableIGC: true, + enableIT: true, + prevMessages: prevMessages, + ); void dispose() { matchUpdateStream.close(); @@ -125,10 +125,7 @@ class IgcController { _openMatches.add(matchState); } - void updateMatch( - PangeaMatchState match, - PangeaMatchStatusEnum status, - ) { + void updateMatch(PangeaMatchState match, PangeaMatchStatusEnum status) { PangeaMatchState updated; switch (status) { case PangeaMatchStatusEnum.accepted: @@ -151,9 +148,8 @@ class IgcController { ) { final PangeaMatchState openMatch = _openMatches.firstWhere( (m) => m.originalMatch == matchState.originalMatch, - orElse: () => throw StateError( - 'No open match found while updating match.', - ), + orElse: () => + throw StateError('No open match found while updating match.'), ); matchState.setStatus(status); @@ -165,9 +161,7 @@ class IgcController { case PangeaMatchStatusEnum.automatic: final choice = matchState.updatedMatch.match.selectedChoice; if (choice == null) { - throw ArgumentError( - 'acceptMatch called with a null selectedChoice.', - ); + throw ArgumentError('acceptMatch called with a null selectedChoice.'); } _applyReplacement( matchState.updatedMatch.match.offset, @@ -191,9 +185,8 @@ class IgcController { ) { final closedMatch = _closedMatches.firstWhere( (m) => m.originalMatch == matchState.originalMatch, - orElse: () => throw StateError( - 'No closed match found while updating match.', - ), + orElse: () => + throw StateError('No closed match found while updating match.'), ); matchState.setStatus(status); @@ -201,9 +194,7 @@ class IgcController { final selectedValue = matchState.updatedMatch.match.selectedChoice?.value; if (selectedValue == null) { - throw StateError( - 'Cannot update match without a selectedChoice value.', - ); + throw StateError('Cannot update match without a selectedChoice value.'); } final replacement = matchState.originalMatch.match.fullText.characters @@ -257,11 +248,7 @@ class IgcController { } }); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {"currentText": currentText}, - ); + ErrorHandler.logError(e: e, s: s, data: {"currentText": currentText}); if (!completer.isCompleted) completer.complete(); } @@ -271,11 +258,7 @@ class IgcController { /// Applies a text replacement to [_currentText] and adjusts match offsets. /// /// Called internally when a correction is accepted or undone. - void _applyReplacement( - int offset, - int length, - String replacement, - ) { + void _applyReplacement(int offset, int length, String replacement) { if (_currentText == null) { throw StateError('_applyReplacement called with null _currentText'); } @@ -357,21 +340,22 @@ class IgcController { _isFetching = true; _lastRequest = request; - final res = await IgcRepo.get( - MatrixState.pangeaController.userController.accessToken, - request, - ).timeout( - const Duration(seconds: 10), - onTimeout: () { - return Result.error( - TimeoutException( - request.feedback.isNotEmpty - ? 'IGC feedback request timed out' - : 'IGC request timed out', - ), + final res = + await IgcRepo.get( + MatrixState.pangeaController.userController.accessToken, + request, + ).timeout( + const Duration(seconds: 10), + onTimeout: () { + return Result.error( + TimeoutException( + request.feedback.isNotEmpty + ? 'IGC feedback request timed out' + : 'IGC request timed out', + ), + ); + }, ); - }, - ); if (res.isError) { debugPrint('IgcRepo.get error: ${res.asError}'); diff --git a/lib/pangea/choreographer/igc/igc_repo.dart b/lib/pangea/choreographer/igc/igc_repo.dart index 998a1faee..749b92ef9 100644 --- a/lib/pangea/choreographer/igc/igc_repo.dart +++ b/lib/pangea/choreographer/igc/igc_repo.dart @@ -17,10 +17,7 @@ class _IgcCacheItem { final Future data; final DateTime timestamp; - const _IgcCacheItem({ - required this.data, - required this.timestamp, - }); + const _IgcCacheItem({required this.data, required this.timestamp}); } class _IgnoredMatchCacheItem { @@ -41,10 +38,7 @@ class _IgnoredMatchCacheItem { @override int get hashCode => spanText.hashCode; - _IgnoredMatchCacheItem({ - required this.match, - required this.timestamp, - }); + _IgnoredMatchCacheItem({required this.match, required this.timestamp}); } class IgcRepo { @@ -66,10 +60,7 @@ class IgcRepo { } debugPrint('[IgcRepo.get] cache MISS, fetching from server...'); - final future = _fetch( - accessToken, - igcRequest: igcRequest, - ); + final future = _fetch(accessToken, igcRequest: igcRequest); _setCached(igcRequest, future); return _getResult(igcRequest, future); } @@ -93,8 +84,9 @@ class IgcRepo { ); } - final Map json = - jsonDecode(utf8.decode(res.bodyBytes).toString()); + final Map json = jsonDecode( + utf8.decode(res.bodyBytes).toString(), + ); return IGCResponseModel.fromJson(json); } @@ -108,23 +100,17 @@ class IgcRepo { return Result.value(res); } catch (e, s) { _igcCache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } - static Future? _getCached( - IGCRequestModel request, - ) { + static Future? _getCached(IGCRequestModel request) { final cacheKeys = [..._igcCache.keys]; for (final key in cacheKeys) { - if (_igcCache[key]! - .timestamp - .isBefore(DateTime.now().subtract(_cacheDuration))) { + if (_igcCache[key]!.timestamp.isBefore( + DateTime.now().subtract(_cacheDuration), + )) { _igcCache.remove(key); } } @@ -135,11 +121,10 @@ class IgcRepo { static void _setCached( IGCRequestModel request, Future response, - ) => - _igcCache[request.hashCode.toString()] = _IgcCacheItem( - data: response, - timestamp: DateTime.now(), - ); + ) => _igcCache[request.hashCode.toString()] = _IgcCacheItem( + data: response, + timestamp: DateTime.now(), + ); static void ignore(PangeaMatch match) { _setCachedIgnoredSpan(match); @@ -150,9 +135,7 @@ class IgcRepo { return cached != null; } - static PangeaMatch? _getCachedIgnoredSpan( - PangeaMatch match, - ) { + static PangeaMatch? _getCachedIgnoredSpan(PangeaMatch match) { final cacheKeys = [..._ignoredMatchCache.keys]; for (final key in cacheKeys) { final entry = _ignoredMatchCache[key]!; @@ -168,9 +151,7 @@ class IgcRepo { return _ignoredMatchCache[cacheEntry.hashCode.toString()]?.match; } - static void _setCachedIgnoredSpan( - PangeaMatch match, - ) { + static void _setCachedIgnoredSpan(PangeaMatch match) { final cacheEntry = _IgnoredMatchCacheItem( match: match, timestamp: DateTime.now(), diff --git a/lib/pangea/choreographer/igc/igc_request_model.dart b/lib/pangea/choreographer/igc/igc_request_model.dart index 1d3ddb7f2..5b17ea9f8 100644 --- a/lib/pangea/choreographer/igc/igc_request_model.dart +++ b/lib/pangea/choreographer/igc/igc_request_model.dart @@ -16,7 +16,12 @@ class IGCRequestModel with BaseRequestModel { @override String get userCefr => MatrixState - .pangeaController.userController.profile.userSettings.cefrLevel.string; + .pangeaController + .userController + .profile + .userSettings + .cefrLevel + .string; @override String get userL1 => MatrixState.pangeaController.userController.userL1Code!; @@ -36,15 +41,14 @@ class IGCRequestModel with BaseRequestModel { /// Creates a copy of this request with optional feedback. IGCRequestModel copyWithFeedback( List> newFeedback, - ) => - IGCRequestModel( - fullText: fullText, - enableIGC: enableIGC, - enableIT: enableIT, - userId: userId, - prevMessages: prevMessages, - feedback: newFeedback, - ); + ) => IGCRequestModel( + fullText: fullText, + enableIGC: enableIGC, + enableIT: enableIT, + userId: userId, + prevMessages: prevMessages, + feedback: newFeedback, + ); Map toJson() { final json = { @@ -54,8 +58,9 @@ class IGCRequestModel with BaseRequestModel { ModelKey.enableIT: enableIT, ModelKey.enableIGC: enableIGC, ModelKey.userId: userId, - ModelKey.prevMessages: - jsonEncode(prevMessages.map((x) => x.toJson()).toList()), + ModelKey.prevMessages: jsonEncode( + prevMessages.map((x) => x.toJson()).toList(), + ), }; if (feedback.isNotEmpty) { json[ModelKey.feedback] = feedback.map((f) => f.toJson()).toList(); @@ -84,14 +89,14 @@ class IGCRequestModel with BaseRequestModel { @override int get hashCode => Object.hash( - fullText.trim(), - userL1, - userL2, - enableIT, - enableIGC, - userId, - _feedbackHash, - ); + fullText.trim(), + userL1, + userL2, + enableIT, + enableIGC, + userId, + _feedbackHash, + ); } /// Previous text/audio message sent in chat @@ -117,10 +122,10 @@ class PreviousMessage { ); Map toJson() => { - ModelKey.prevContent: content, - ModelKey.prevSender: sender, - ModelKey.prevTimestamp: timestamp.toIso8601String(), - }; + ModelKey.prevContent: content, + ModelKey.prevSender: sender, + ModelKey.prevTimestamp: timestamp.toIso8601String(), + }; @override bool operator ==(Object other) { @@ -135,10 +140,6 @@ class PreviousMessage { @override int get hashCode { - return Object.hash( - content, - sender, - timestamp, - ); + return Object.hash(content, sender, timestamp); } } diff --git a/lib/pangea/choreographer/igc/igc_response_model.dart b/lib/pangea/choreographer/igc/igc_response_model.dart index 2feb383fb..9aa4e9236 100644 --- a/lib/pangea/choreographer/igc/igc_response_model.dart +++ b/lib/pangea/choreographer/igc/igc_response_model.dart @@ -31,14 +31,14 @@ class IGCResponseModel { return IGCResponseModel( matches: json["matches"] != null ? (json["matches"] as Iterable) - .map( - (e) => PangeaMatch.fromJson( - e as Map, - fullText: originalInput, - ), - ) - .toList() - .cast() + .map( + (e) => PangeaMatch.fromJson( + e as Map, + fullText: originalInput, + ), + ) + .toList() + .cast() : [], originalInput: originalInput, fullTextCorrection: json["full_text_correction"], @@ -51,13 +51,13 @@ class IGCResponseModel { } Map toJson() => { - "original_input": originalInput, - "full_text_correction": fullTextCorrection, - // Serialize as flat SpanData objects matching server's SpanDataV2 schema - "matches": matches.map((e) => e.match.toJson()).toList(), - ModelKey.userL1: userL1, - ModelKey.userL2: userL2, - ModelKey.enableIT: enableIT, - ModelKey.enableIGC: enableIGC, - }; + "original_input": originalInput, + "full_text_correction": fullTextCorrection, + // Serialize as flat SpanData objects matching server's SpanDataV2 schema + "matches": matches.map((e) => e.match.toJson()).toList(), + ModelKey.userL1: userL1, + ModelKey.userL2: userL2, + ModelKey.enableIT: enableIT, + ModelKey.enableIGC: enableIGC, + }; } diff --git a/lib/pangea/choreographer/igc/pangea_match_model.dart b/lib/pangea/choreographer/igc/pangea_match_model.dart index e76400813..0292560f6 100644 --- a/lib/pangea/choreographer/igc/pangea_match_model.dart +++ b/lib/pangea/choreographer/igc/pangea_match_model.dart @@ -7,10 +7,7 @@ class PangeaMatch { final SpanData match; final PangeaMatchStatusEnum status; - const PangeaMatch({ - required this.match, - required this.status, - }); + const PangeaMatch({required this.match, required this.status}); /// Parse PangeaMatch from JSON. /// @@ -20,21 +17,16 @@ class PangeaMatch { /// /// [fullText] is passed to SpanData as fallback when the span JSON doesn't /// contain full_text (e.g., when using original_input from parent response). - factory PangeaMatch.fromJson( - Map json, { - String? fullText, - }) { + factory PangeaMatch.fromJson(Map json, {String? fullText}) { // Check if this is V1 format (has "match" wrapper) or V2 format (flat SpanData) final bool isV1Format = json[_matchKey] is Map; - final Map spanJson = - isV1Format ? json[_matchKey] as Map : json; + final Map spanJson = isV1Format + ? json[_matchKey] as Map + : json; return PangeaMatch( - match: SpanData.fromJson( - spanJson, - parentFullText: fullText, - ), + match: SpanData.fromJson(spanJson, parentFullText: fullText), // V1 format may have status; V2 format always defaults to open status: isV1Format && json[_statusKey] != null ? PangeaMatchStatusEnum.fromString(json[_statusKey] as String) @@ -43,9 +35,9 @@ class PangeaMatch { } Map toJson() => { - _matchKey: match.toJson(), - _statusKey: status.name, - }; + _matchKey: match.toJson(), + _statusKey: status.name, + }; static const _matchKey = "match"; static const _statusKey = "status"; diff --git a/lib/pangea/choreographer/igc/pangea_match_state_model.dart b/lib/pangea/choreographer/igc/pangea_match_state_model.dart index 7fb0fcc7d..3418c1a79 100644 --- a/lib/pangea/choreographer/igc/pangea_match_state_model.dart +++ b/lib/pangea/choreographer/igc/pangea_match_state_model.dart @@ -11,16 +11,13 @@ class PangeaMatchState { required PangeaMatch original, required SpanData match, required PangeaMatchStatusEnum status, - }) : _original = original, - _match = match, - _status = status; + }) : _original = original, + _match = match, + _status = status; PangeaMatch get originalMatch => _original; - PangeaMatch get updatedMatch => PangeaMatch( - match: _match, - status: _status, - ); + PangeaMatch get updatedMatch => PangeaMatch(match: _match, status: _status); void setStatus(PangeaMatchStatusEnum status) { _status = status; @@ -44,9 +41,7 @@ class PangeaMatchState { throw Exception('No choices available to select best choice from.'); } selectChoice( - updatedMatch.match.choices!.indexWhere( - (c) => c.isBestCorrection, - ), + updatedMatch.match.choices!.indexWhere((c) => c.isBestCorrection), ); } diff --git a/lib/pangea/choreographer/igc/replacement_type_enum.dart b/lib/pangea/choreographer/igc/replacement_type_enum.dart index 827d587cb..65272ed15 100644 --- a/lib/pangea/choreographer/igc/replacement_type_enum.dart +++ b/lib/pangea/choreographer/igc/replacement_type_enum.dart @@ -34,7 +34,6 @@ enum ReplacementTypeEnum { conditional, // If clauses, would/could infinitiveGerund, // Infinitive vs gerund usage modal, // Modal verb usage (can/could/should/must) - // === SURFACE-LEVEL CORRECTIONS (auto-applied) === punct, diacritics, @@ -46,7 +45,6 @@ enum ReplacementTypeEnum { l1Interference, // L1 patterns bleeding through incorrectly collocation, // Wrong word pairing (e.g., "do a mistake" → "make a mistake") semanticConfusion, // Similar meanings, wrong choice (e.g., "see/watch/look") - // === HIGHER-LEVEL SUGGESTIONS === transcription, style, @@ -210,7 +208,6 @@ extension SpanDataTypeEnumExt on ReplacementTypeEnum { 'correction' => 'subjectVerbAgreement', // Legacy fallback 'grammar' => 'subjectVerbAgreement', // Legacy fallback 'word_choice' => 'semanticConfusion', // Legacy fallback - // Snake_case to camelCase conversions - grammar types 'did_you_mean' => 'didYouMean', 'verb_conjugation' => 'verbConjugation', @@ -249,8 +246,9 @@ extension SpanDataTypeEnumExt on ReplacementTypeEnum { return L10n.of(context).practiceDefaultPrompt; case ReplacementTypeEnum.itStart: return L10n.of(context).needsItMessage( - MatrixState.pangeaController.userController.userL2 - ?.getDisplayName(context) ?? + MatrixState.pangeaController.userController.userL2?.getDisplayName( + context, + ) ?? L10n.of(context).targetLanguage, ); // All grammar types and other corrections use the same default prompt diff --git a/lib/pangea/choreographer/igc/span_card.dart b/lib/pangea/choreographer/igc/span_card.dart index 3b8ca6715..52b926cb7 100644 --- a/lib/pangea/choreographer/igc/span_card.dart +++ b/lib/pangea/choreographer/igc/span_card.dart @@ -58,19 +58,14 @@ class SpanCardState extends State { void _updateMatch(PangeaMatchStatusEnum status) { try { - widget.choreographer.igcController.updateMatch( - widget.match, - status, - ); + widget.choreographer.igcController.updateMatch(widget.match, status); widget.showNextMatch(); } catch (e, s) { ErrorHandler.logError( e: e, s: s, level: SentryLevel.warning, - data: { - "match": widget.match.toJson(), - }, + data: {"match": widget.match.toJson()}, ); widget.choreographer.clearMatches(e); return; @@ -111,10 +106,7 @@ class SpanCardState extends State { ), const Flexible( child: Center( - child: BotFace( - width: 32.0, - expression: BotExpression.idle, - ), + child: BotFace(width: 32.0, expression: BotExpression.idle), ), ), IconButton( @@ -154,12 +146,12 @@ class SpanCardState extends State { widget.match.updatedMatch.match.selectedChoiceIndex, id: widget.match.hashCode.toString(), langCode: MatrixState - .pangeaController.userController.userL2Code!, + .pangeaController + .userController + .userL2Code!, ), const SizedBox(), - _SpanCardFeedback( - widget.match.updatedMatch.match, - ), + _SpanCardFeedback(widget.match.updatedMatch.match), ], ), ), @@ -190,9 +182,7 @@ class _SpanCardFeedback extends StatelessWidget { final defaultContent = Text( prompt, - style: BotStyle.text(context).copyWith( - fontStyle: FontStyle.italic, - ), + style: BotStyle.text(context).copyWith(fontStyle: FontStyle.italic), ); return Column( @@ -223,9 +213,7 @@ class _SpanCardButtons extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - decoration: BoxDecoration( - color: Theme.of(context).cardColor, - ), + decoration: BoxDecoration(color: Theme.of(context).cardColor), padding: const EdgeInsets.only(top: 12.0), child: Row( spacing: 10.0, @@ -235,13 +223,12 @@ class _SpanCardButtons extends StatelessWidget { opacity: 0.8, child: TextButton( style: TextButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.primary.withAlpha(25), + backgroundColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(25), ), onPressed: onIgnore, - child: Center( - child: Text(L10n.of(context).ignoreInThisText), - ), + child: Center(child: Text(L10n.of(context).ignoreInThisText)), ), ), ), @@ -251,9 +238,10 @@ class _SpanCardButtons extends StatelessWidget { child: TextButton( onPressed: selectedChoice != null ? onAccept : null, style: TextButton.styleFrom( - backgroundColor: (selectedChoice?.color ?? - Theme.of(context).colorScheme.primary) - .withAlpha(50), + backgroundColor: + (selectedChoice?.color ?? + Theme.of(context).colorScheme.primary) + .withAlpha(50), side: selectedChoice != null ? BorderSide( color: selectedChoice!.color, diff --git a/lib/pangea/choreographer/igc/span_data_model.dart b/lib/pangea/choreographer/igc/span_data_model.dart index fea744140..8861c6ba5 100644 --- a/lib/pangea/choreographer/igc/span_data_model.dart +++ b/lib/pangea/choreographer/igc/span_data_model.dart @@ -64,7 +64,7 @@ class SpanData { json['type'] ?? json['type_name'] ?? json['typeName']; final String? typeString = rawType is Map ? (rawType['type_name'] ?? rawType['type'] ?? rawType['typeName']) - as String? + as String? : rawType as String?; // Try to get fullText from span JSON, fall back to parent's original_input @@ -83,7 +83,8 @@ class SpanData { offset: json['offset'] as int, length: json['length'] as int, fullText: fullText, - type: SpanDataTypeEnumExt.fromString(typeString) ?? + type: + SpanDataTypeEnumExt.fromString(typeString) ?? ReplacementTypeEnum.other, rule: json['rule'] != null ? Rule.fromJson(json['rule'] as Map) @@ -122,9 +123,7 @@ class SpanData { offset >= this.offset && offset <= this.offset + length; SpanChoice? get bestChoice { - return choices?.firstWhereOrNull( - (choice) => choice.isBestCorrection, - ); + return choices?.firstWhereOrNull((choice) => choice.isBestCorrection); } int get selectedChoiceIndex { @@ -167,9 +166,7 @@ class SpanData { } final correctChoice = choices - ?.firstWhereOrNull( - (c) => c.isBestCorrection, - ) + ?.firstWhereOrNull((c) => c.isBestCorrection) ?.value; final l2Code = @@ -255,22 +252,20 @@ class SpanChoice { value: json['value'] as String, type: json['type'] != null ? SpanChoiceTypeEnum.values.firstWhereOrNull( - (element) => element.name == json['type'], - ) ?? - SpanChoiceTypeEnum.suggestion + (element) => element.name == json['type'], + ) ?? + SpanChoiceTypeEnum.suggestion : SpanChoiceTypeEnum.suggestion, feedback: json['feedback'], selected: json['selected'] ?? false, - timestamp: - json['timestamp'] != null ? DateTime.parse(json['timestamp']) : null, + timestamp: json['timestamp'] != null + ? DateTime.parse(json['timestamp']) + : null, ); } Map toJson() { - final Map data = { - 'value': value, - 'type': type.name, - }; + final Map data = {'value': value, 'type': type.name}; // V2 format: use selected_at instead of separate selected + timestamp if (selected && timestamp != null) { @@ -321,17 +316,12 @@ class SpanChoice { class Rule { final String id; - const Rule({ - required this.id, - }); + const Rule({required this.id}); - factory Rule.fromJson(Map json) => Rule( - id: json['id'] as String, - ); + factory Rule.fromJson(Map json) => + Rule(id: json['id'] as String); - Map toJson() => { - 'id': id, - }; + Map toJson() => {'id': id}; @override bool operator ==(Object other) { diff --git a/lib/pangea/choreographer/igc/span_data_repo.dart b/lib/pangea/choreographer/igc/span_data_repo.dart index 7c07825bc..c19a9aafe 100644 --- a/lib/pangea/choreographer/igc/span_data_repo.dart +++ b/lib/pangea/choreographer/igc/span_data_repo.dart @@ -15,10 +15,7 @@ class _SpanDetailsCacheItem { final Future data; final DateTime timestamp; - const _SpanDetailsCacheItem({ - required this.data, - required this.timestamp, - }); + const _SpanDetailsCacheItem({required this.data, required this.timestamp}); } class SpanDataRepo { @@ -34,10 +31,7 @@ class SpanDataRepo { return _getResult(request, cached); } - final future = _fetch( - accessToken, - request: request, - ); + final future = _fetch(accessToken, request: request); _setCached(request, future); return _getResult(request, future); } @@ -75,18 +69,12 @@ class SpanDataRepo { return Result.value(res); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } - static Future? _getCached( - SpanDetailsRequest request, - ) { + static Future? _getCached(SpanDetailsRequest request) { final cacheKeys = [..._cache.keys]; for (final key in cacheKeys) { if (DateTime.now().difference(_cache[key]!.timestamp) >= _cacheDuration) { diff --git a/lib/pangea/choreographer/igc/span_data_request.dart b/lib/pangea/choreographer/igc/span_data_request.dart index 50fd6441b..88b80f139 100644 --- a/lib/pangea/choreographer/igc/span_data_request.dart +++ b/lib/pangea/choreographer/igc/span_data_request.dart @@ -17,12 +17,12 @@ class SpanDetailsRequest { }); Map toJson() => { - ModelKey.userL1: userL1, - ModelKey.userL2: userL2, - ModelKey.enableIT: enableIT, - ModelKey.enableIGC: enableIGC, - 'span': span.toJson(), - }; + ModelKey.userL1: userL1, + ModelKey.userL2: userL2, + ModelKey.enableIT: enableIT, + ModelKey.enableIGC: enableIGC, + 'span': span.toJson(), + }; @override bool operator ==(Object other) { diff --git a/lib/pangea/choreographer/igc/start_igc_button.dart b/lib/pangea/choreographer/igc/start_igc_button.dart index 34db5f2d3..1340e899d 100644 --- a/lib/pangea/choreographer/igc/start_igc_button.dart +++ b/lib/pangea/choreographer/igc/start_igc_button.dart @@ -41,26 +41,25 @@ class _StartIGCButtonState extends State @override void initState() { super.initState(); - _spinController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 300), - )..addStatusListener((status) { - if (status == AnimationStatus.completed) { - if (_shouldStop) { - _spinController?.stop(); - _spinController?.value = 0; - } else { - _spinController?.forward(from: 0); + _spinController = + AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + )..addStatusListener((status) { + if (status == AnimationStatus.completed) { + if (_shouldStop) { + _spinController?.stop(); + _spinController?.value = 0; + } else { + _spinController?.forward(from: 0); + } } - } - }); + }); - _rotation = Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation( - parent: _spinController!, - curve: Curves.linear, - ), - ); + _rotation = Tween( + begin: 0.0, + end: 1.0, + ).animate(CurvedAnimation(parent: _spinController!, curve: Curves.linear)); _colorController = AnimationController( vsync: this, @@ -137,10 +136,10 @@ class _StartIGCButtonState extends State onTap: enableFeedback ? widget.onPressed : null, onLongPress: enableFeedback ? () => showDialog( - context: context, - builder: (c) => const SettingsLearning(), - barrierDismissible: false, - ) + context: context, + builder: (c) => const SettingsLearning(), + barrierDismissible: false, + ) : null, child: Stack( alignment: Alignment.center, @@ -175,11 +174,7 @@ class _StartIGCButtonState extends State color: _backgroundColor.value, ), ), - Icon( - size: 16, - Icons.check, - color: _iconColor.value, - ), + Icon(size: 16, Icons.check, color: _iconColor.value), ], ), ), diff --git a/lib/pangea/choreographer/igc/text_normalization_util.dart b/lib/pangea/choreographer/igc/text_normalization_util.dart index 08364c3e7..b08a491c7 100644 --- a/lib/pangea/choreographer/igc/text_normalization_util.dart +++ b/lib/pangea/choreographer/igc/text_normalization_util.dart @@ -29,11 +29,7 @@ String normalizeString(String input, String languageCode) { // Step 5: Normalize whitespace (collapse multiple spaces, trim) return normalized.replaceAll(RegExp(r'\s+'), '').trim(); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'input': input}, - ); + ErrorHandler.logError(e: e, s: s, data: {'input': input}); return input; } } diff --git a/lib/pangea/choreographer/it/completed_it_step_model.dart b/lib/pangea/choreographer/it/completed_it_step_model.dart index e1af2afff..5eafc6f05 100644 --- a/lib/pangea/choreographer/it/completed_it_step_model.dart +++ b/lib/pangea/choreographer/it/completed_it_step_model.dart @@ -7,10 +7,7 @@ class CompletedITStepModel { final List continuances; final int chosen; - const CompletedITStepModel( - this.continuances, { - required this.chosen, - }); + const CompletedITStepModel(this.continuances, {required this.chosen}); Map toJson() { final Map data = {}; @@ -24,10 +21,7 @@ class CompletedITStepModel { for (final Map continuance in json['continuances']) { continuances.add(ContinuanceModel.fromJson(continuance)); } - return CompletedITStepModel( - continuances, - chosen: json['chosen'], - ); + return CompletedITStepModel(continuances, chosen: json['chosen']); } ContinuanceModel? get chosenContinuance { diff --git a/lib/pangea/choreographer/it/contextual_definition_repo.dart b/lib/pangea/choreographer/it/contextual_definition_repo.dart index 8d6a5a616..3050d28b2 100644 --- a/lib/pangea/choreographer/it/contextual_definition_repo.dart +++ b/lib/pangea/choreographer/it/contextual_definition_repo.dart @@ -23,11 +23,7 @@ class ContextualDefinitionRepo { return Result.value(await cached); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } @@ -59,20 +55,13 @@ class ContextualDefinitionRepo { final ContextualDefinitionResponseModel response = ContextualDefinitionResponseModel.fromJson( - jsonDecode( - utf8.decode(res.bodyBytes).toString(), - ), - ); + jsonDecode(utf8.decode(res.bodyBytes).toString()), + ); if (response.text.isEmpty) { ErrorHandler.logError( - e: Exception( - "empty text in contextual definition response", - ), - data: { - "request": request.toJson(), - "accessToken": accessToken, - }, + e: Exception("empty text in contextual definition response"), + data: {"request": request.toJson(), "accessToken": accessToken}, ); } @@ -88,23 +77,16 @@ class ContextualDefinitionRepo { return Result.value(res); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } - static Future? _getCached( - ContextualDefinitionRequestModel request, - ) => + static Future? _getCached(ContextualDefinitionRequestModel request) => _cache[request.hashCode.toString()]; static void _setCached( ContextualDefinitionRequestModel request, Future response, - ) => - _cache[request.hashCode.toString()] = response; + ) => _cache[request.hashCode.toString()] = response; } diff --git a/lib/pangea/choreographer/it/contextual_definition_request_model.dart b/lib/pangea/choreographer/it/contextual_definition_request_model.dart index 71376d9e7..a160d5ec3 100644 --- a/lib/pangea/choreographer/it/contextual_definition_request_model.dart +++ b/lib/pangea/choreographer/it/contextual_definition_request_model.dart @@ -16,12 +16,12 @@ class ContextualDefinitionRequestModel { }); Map toJson() => { - ModelKey.fullText: fullText, - ModelKey.word: word, - ModelKey.lang: feedbackLang, - ModelKey.fullTextLang: fullTextLang, - ModelKey.wordLang: wordLang, - }; + ModelKey.fullText: fullText, + ModelKey.word: word, + ModelKey.lang: feedbackLang, + ModelKey.fullTextLang: fullTextLang, + ModelKey.wordLang: wordLang, + }; @override bool operator ==(Object other) => diff --git a/lib/pangea/choreographer/it/contextual_definition_response_model.dart b/lib/pangea/choreographer/it/contextual_definition_response_model.dart index 7b3cd613a..fb6e2a032 100644 --- a/lib/pangea/choreographer/it/contextual_definition_response_model.dart +++ b/lib/pangea/choreographer/it/contextual_definition_response_model.dart @@ -5,6 +5,5 @@ class ContextualDefinitionResponseModel { factory ContextualDefinitionResponseModel.fromJson( Map json, - ) => - ContextualDefinitionResponseModel(text: json["response"]); + ) => ContextualDefinitionResponseModel(text: json["response"]); } diff --git a/lib/pangea/choreographer/it/gold_route_tracker_model.dart b/lib/pangea/choreographer/it/gold_route_tracker_model.dart index dfb2d61c3..47a7a9be4 100644 --- a/lib/pangea/choreographer/it/gold_route_tracker_model.dart +++ b/lib/pangea/choreographer/it/gold_route_tracker_model.dart @@ -5,7 +5,7 @@ class GoldRouteTrackerModel { final List continuances; const GoldRouteTrackerModel(this.continuances, String originalText) - : _originalText = originalText; + : _originalText = originalText; ContinuanceModel? currentContinuance({ required String currentText, diff --git a/lib/pangea/choreographer/it/it_bar.dart b/lib/pangea/choreographer/it/it_bar.dart index fc856954b..d9d1915bf 100644 --- a/lib/pangea/choreographer/it/it_bar.dart +++ b/lib/pangea/choreographer/it/it_bar.dart @@ -21,10 +21,7 @@ import '../../common/widgets/choice_array.dart'; class ITBar extends StatefulWidget { final Choreographer choreographer; - const ITBar({ - super.key, - required this.choreographer, - }); + const ITBar({super.key, required this.choreographer}); @override ITBarState createState() => ITBarState(); @@ -128,9 +125,7 @@ class ITBarState extends State with SingleTickerProviderStateMixin { e: e, s: s, level: SentryLevel.warning, - data: { - "index": index, - }, + data: {"index": index}, ); widget.choreographer.itController.closeIT(); return; @@ -158,9 +153,7 @@ class ITBarState extends State with SingleTickerProviderStateMixin { e: e, s: s, level: SentryLevel.warning, - data: { - "index": index, - }, + data: {"index": index}, ); widget.choreographer.itController.closeIT(); } @@ -229,22 +222,20 @@ class ITBarState extends State with SingleTickerProviderStateMixin { IconButton( onPressed: widget.choreographer.itController.closeIT, - icon: const Icon( - Icons.close, - size: 20, - ), + icon: const Icon(Icons.close, size: 20), ), ], ) : ValueListenableBuilder( valueListenable: widget.choreographer.itController.currentITStep, - builder: (context, step, __) { + builder: (context, step, _) { return step == null ? CircularProgressIndicator( strokeWidth: 2.0, - color: - Theme.of(context).colorScheme.primary, + color: Theme.of( + context, + ).colorScheme.primary, ) : _ITChoices( continuances: step.continuances, @@ -287,7 +278,7 @@ class _ITBarHeader extends StatelessWidget { Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: editing, - builder: (context, isEditing, __) { + builder: (context, isEditing, _) { return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -326,16 +317,15 @@ class _ITBarHeader extends StatelessWidget { padding: const EdgeInsets.only(left: 8.0, right: 8.0), child: ValueListenableBuilder( valueListenable: progress, - builder: (context, value, __) => AnimatedProgressBar( + builder: (context, value, _) => AnimatedProgressBar( height: 20.0, widthPercent: value, - backgroundColor: Theme.of(context) - .colorScheme - .surfaceContainerHighest, - barColor: Theme.of(context) - .colorScheme - .primary - .withAlpha(180), + backgroundColor: Theme.of( + context, + ).colorScheme.surfaceContainerHighest, + barColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(180), ), ), ), @@ -366,7 +356,7 @@ class _ITBarHeader extends StatelessWidget { ? const SizedBox(height: 24.0) : ValueListenableBuilder( valueListenable: sourceText, - builder: (context, text, __) { + builder: (context, text, _) { return Container( padding: const EdgeInsets.only(top: 8.0), constraints: const BoxConstraints(minHeight: 24.0), diff --git a/lib/pangea/choreographer/it/it_controller.dart b/lib/pangea/choreographer/it/it_controller.dart index 34efef30d..d8f51dc50 100644 --- a/lib/pangea/choreographer/it/it_controller.dart +++ b/lib/pangea/choreographer/it/it_controller.dart @@ -147,7 +147,8 @@ class ITController { chosen: chosenIndex, ), ); - final progress = (_goldRouteTracker!.continuances.indexWhere( + final progress = + (_goldRouteTracker!.continuances.indexWhere( (c) => c.text == _currentITStep.value!.continuances[chosenIndex].text, diff --git a/lib/pangea/choreographer/it/it_feedback_card.dart b/lib/pangea/choreographer/it/it_feedback_card.dart index 39ffbba8d..75df42b79 100644 --- a/lib/pangea/choreographer/it/it_feedback_card.dart +++ b/lib/pangea/choreographer/it/it_feedback_card.dart @@ -16,10 +16,7 @@ import '../../common/widgets/card_error_widget.dart'; class ITFeedbackCard extends StatelessWidget { final FullTextTranslationRequestModel req; - const ITFeedbackCard( - this.req, { - super.key, - }); + const ITFeedbackCard(this.req, {super.key}); Future> _getFeedback() { return FullTextTranslationRepo.get( @@ -47,25 +44,11 @@ class ITFeedbackCard extends StatelessWidget { spacing: 10, alignment: WrapAlignment.center, children: [ - Text( - req.text, - style: BotStyle.text(context), - ), - Text( - "≈", - style: BotStyle.text(context), - ), + Text(req.text, style: BotStyle.text(context)), + Text("≈", style: BotStyle.text(context)), snapshot.hasData - ? Text( - snapshot.data!.result!, - style: BotStyle.text(context), - ) - : TextLoadingShimmer( - width: min( - 140, - 10.0 * req.text.length, - ), - ), + ? Text(snapshot.data!.result!, style: BotStyle.text(context)) + : TextLoadingShimmer(width: min(140, 10.0 * req.text.length)), ], ), ); diff --git a/lib/pangea/choreographer/it/it_repo.dart b/lib/pangea/choreographer/it/it_repo.dart index 7c36b33dc..04ccb5c77 100644 --- a/lib/pangea/choreographer/it/it_repo.dart +++ b/lib/pangea/choreographer/it/it_repo.dart @@ -15,19 +15,14 @@ class _ITCacheItem { final Future response; final DateTime timestamp; - const _ITCacheItem({ - required this.response, - required this.timestamp, - }); + const _ITCacheItem({required this.response, required this.timestamp}); } class ITRepo { static final Map _cache = {}; static const Duration _cacheDuration = Duration(minutes: 10); - static Future> get( - ITRequestModel request, - ) { + static Future> get(ITRequestModel request) { final cached = _getCached(request); if (cached != null) { return _getResult(request, cached); @@ -38,15 +33,15 @@ class ITRepo { return _getResult(request, future); } - static Future _fetch( - ITRequestModel request, - ) async { + static Future _fetch(ITRequestModel request) async { final Requests req = Requests( choreoApiKey: Environment.choreoApiKey, accessToken: MatrixState.pangeaController.userController.accessToken, ); - final Response res = - await req.post(url: PApiUrls.firstStep, body: request.toJson()); + final Response res = await req.post( + url: PApiUrls.firstStep, + body: request.toJson(), + ); if (res.statusCode != 200) { throw Exception('Failed to load interactive translation'); @@ -65,18 +60,12 @@ class ITRepo { return Result.value(res); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } - static Future? _getCached( - ITRequestModel request, - ) { + static Future? _getCached(ITRequestModel request) { final cacheKeys = [..._cache.keys]; for (final key in cacheKeys) { if (DateTime.now().difference(_cache[key]!.timestamp) >= _cacheDuration) { diff --git a/lib/pangea/choreographer/it/it_request_model.dart b/lib/pangea/choreographer/it/it_request_model.dart index b5baf2604..5e70a65b4 100644 --- a/lib/pangea/choreographer/it/it_request_model.dart +++ b/lib/pangea/choreographer/it/it_request_model.dart @@ -21,29 +21,29 @@ class ITRequestModel { required this.goldContinuances, }); - factory ITRequestModel.fromJson(json) => ITRequestModel( - text: json[ModelKey.text], - customInput: json['custom_input'], - sourceLangCode: json[ModelKey.srcLang], - targetLangCode: json[ModelKey.tgtLang], - goldTranslation: json[ModelKey.goldTranslation], - goldContinuances: json['gold_continuances'] != null - ? (json['gold_continuances']) - .map((e) => ContinuanceModel.fromJson(e)) - .toList() - : null, - ); + factory ITRequestModel.fromJson(Map json) => ITRequestModel( + text: json[ModelKey.text], + customInput: json['custom_input'], + sourceLangCode: json[ModelKey.srcLang], + targetLangCode: json[ModelKey.tgtLang], + goldTranslation: json[ModelKey.goldTranslation], + goldContinuances: json['gold_continuances'] != null + ? (json['gold_continuances']) + .map((e) => ContinuanceModel.fromJson(e)) + .toList() + : null, + ); Map toJson() => { - ModelKey.text: text, - 'custom_input': customInput, - ModelKey.srcLang: sourceLangCode, - ModelKey.tgtLang: targetLangCode, - ModelKey.goldTranslation: goldTranslation, - 'gold_continuances': goldContinuances != null - ? List.from(goldContinuances!.map((e) => e.toJson())) - : null, - }; + ModelKey.text: text, + 'custom_input': customInput, + ModelKey.srcLang: sourceLangCode, + ModelKey.tgtLang: targetLangCode, + ModelKey.goldTranslation: goldTranslation, + 'gold_continuances': goldContinuances != null + ? List.from(goldContinuances!.map((e) => e.toJson())) + : null, + }; @override bool operator ==(Object other) { diff --git a/lib/pangea/choreographer/it/it_shimmer.dart b/lib/pangea/choreographer/it/it_shimmer.dart index 33ab1ba28..06912b0ed 100644 --- a/lib/pangea/choreographer/it/it_shimmer.dart +++ b/lib/pangea/choreographer/it/it_shimmer.dart @@ -28,10 +28,7 @@ class ItShimmer extends StatelessWidget { onPressed: null, child: const Text( " ", // 10 spaces - style: TextStyle( - color: Colors.transparent, - fontSize: 16, - ), + style: TextStyle(color: Colors.transparent, fontSize: 16), ), ), ); diff --git a/lib/pangea/choreographer/it/it_step_model.dart b/lib/pangea/choreographer/it/it_step_model.dart index e3e72200a..34f98b42c 100644 --- a/lib/pangea/choreographer/it/it_step_model.dart +++ b/lib/pangea/choreographer/it/it_step_model.dart @@ -33,14 +33,14 @@ class ITStepModel { ...responseModel.continuances .where((c) => c.text.toLowerCase() != goldCont.text.toLowerCase()) .map((e) { - //we only want one green choice and for that to be our gold - if (e.level == ChoreoConstants.levelThresholdForGreen) { - return e.copyWith( - level: ChoreoConstants.levelThresholdForYellow, - ); - } - return e; - }), + //we only want one green choice and for that to be our gold + if (e.level == ChoreoConstants.levelThresholdForGreen) { + return e.copyWith( + level: ChoreoConstants.levelThresholdForYellow, + ); + } + return e; + }), goldCont, ]; continuances.shuffle(); @@ -49,16 +49,10 @@ class ITStepModel { } } - return ITStepModel( - continuances: continuances, - isFinal: isFinal, - ); + return ITStepModel(continuances: continuances, isFinal: isFinal); } - ITStepModel copyWith({ - List? continuances, - bool? isFinal, - }) { + ITStepModel copyWith({List? continuances, bool? isFinal}) { return ITStepModel( continuances: continuances ?? this.continuances, isFinal: isFinal ?? this.isFinal, diff --git a/lib/pangea/choreographer/it/word_data_card.dart b/lib/pangea/choreographer/it/word_data_card.dart index ae8c1ab57..a4359c657 100644 --- a/lib/pangea/choreographer/it/word_data_card.dart +++ b/lib/pangea/choreographer/it/word_data_card.dart @@ -29,7 +29,8 @@ class WordDataCard extends StatelessWidget { word: word, fullTextLang: langCode, wordLang: langCode, - feedbackLang: MatrixState.pangeaController.userController.userL1Code ?? + feedbackLang: + MatrixState.pangeaController.userController.userL1Code ?? LanguageKeys.defaultLanguage, ); diff --git a/lib/pangea/choreographer/text_editing/edit_type_enum.dart b/lib/pangea/choreographer/text_editing/edit_type_enum.dart index 8ef9eaedc..c3b6a7d1e 100644 --- a/lib/pangea/choreographer/text_editing/edit_type_enum.dart +++ b/lib/pangea/choreographer/text_editing/edit_type_enum.dart @@ -1,7 +1 @@ -enum EditTypeEnum { - igc, - it, - itDismissed, - keyboard, - other, -} +enum EditTypeEnum { igc, it, itDismissed, keyboard, other } diff --git a/lib/pangea/choreographer/text_editing/pangea_text_controller.dart b/lib/pangea/choreographer/text_editing/pangea_text_controller.dart index 73b0e1d98..4a8920c74 100644 --- a/lib/pangea/choreographer/text_editing/pangea_text_controller.dart +++ b/lib/pangea/choreographer/text_editing/pangea_text_controller.dart @@ -20,19 +20,17 @@ class PangeaTextController extends TextEditingController { EditTypeEnum editType = EditTypeEnum.keyboard; String _currentText = ''; - PangeaTextController({ - required this.choreographer, - }) { + PangeaTextController({required this.choreographer}) { addListener(_onTextChanged); } bool get exceededMaxLength => text.length >= ChoreoConstants.maxLength; TextStyle _underlineStyle(Color color) => TextStyle( - decoration: TextDecoration.underline, - decorationColor: color, - decorationThickness: 5, - ); + decoration: TextDecoration.underline, + decorationColor: color, + decorationThickness: 5, + ); Color _underlineColor(PangeaMatch match) { // Automatic corrections use primary color @@ -87,9 +85,7 @@ class PangeaTextController extends TextEditingController { e: e, s: s, level: SentryLevel.warning, - data: { - "match": match.toJson(), - }, + data: {"match": match.toJson()}, ); MatrixState.pAnyState.closeOverlay(); choreographer.clearMatches(e); @@ -128,16 +124,13 @@ class PangeaTextController extends TextEditingController { } TextSpan _buildPaywallSpan(TextStyle? style) => TextSpan( - text: text, - style: style?.merge( - _underlineStyle(const Color.fromARGB(187, 132, 96, 224)), - ), - ); + text: text, + style: style?.merge( + _underlineStyle(const Color.fromARGB(187, 132, 96, 224)), + ), + ); - InlineSpan _buildMatchSpan( - PangeaMatchState match, - TextStyle style, - ) { + InlineSpan _buildMatchSpan(PangeaMatchState match, TextStyle style) { final span = choreographer.igcController.currentText!.characters .getRange( match.updatedMatch.match.offset, @@ -162,25 +155,22 @@ class PangeaTextController extends TextEditingController { style: style, ); } else { - return TextSpan( - text: span, - style: style, - ); + return TextSpan(text: span, style: style); } } /// Returns a list of [TextSpan]s used to display the text in the input field /// with the appropriate styling for each error match. - List _buildTokenSpan({ - TextStyle? defaultStyle, - }) { - final textSpanMatches = [ - ...choreographer.igcController.openMatches, - ...choreographer.igcController.recentAutomaticCorrections, - ]..sort( - (a, b) => - a.updatedMatch.match.offset.compareTo(b.updatedMatch.match.offset), - ); + List _buildTokenSpan({TextStyle? defaultStyle}) { + final textSpanMatches = + [ + ...choreographer.igcController.openMatches, + ...choreographer.igcController.recentAutomaticCorrections, + ]..sort( + (a, b) => a.updatedMatch.match.offset.compareTo( + b.updatedMatch.match.offset, + ), + ); final currentText = choreographer.igcController.currentText!; final spans = []; diff --git a/lib/pangea/common/config/environment.dart b/lib/pangea/common/config/environment.dart index 53388f2dc..dc5ae9cc7 100644 --- a/lib/pangea/common/config/environment.dart +++ b/lib/pangea/common/config/environment.dart @@ -32,16 +32,22 @@ class Environment { appConfigOverride?.synapseURL ?? dotenv.env['SYNAPSE_URL']; if (homeServerFromSynapseURL != null) { if (homeServerFromSynapseURL.startsWith("http://")) { - homeServerFromSynapseURL = - homeServerFromSynapseURL.replaceFirst("http://", ""); + homeServerFromSynapseURL = homeServerFromSynapseURL.replaceFirst( + "http://", + "", + ); } if (homeServerFromSynapseURL.startsWith("https://")) { - homeServerFromSynapseURL = - homeServerFromSynapseURL.replaceFirst("https://", ""); + homeServerFromSynapseURL = homeServerFromSynapseURL.replaceFirst( + "https://", + "", + ); } if (homeServerFromSynapseURL.startsWith("matrix.")) { - homeServerFromSynapseURL = - homeServerFromSynapseURL.replaceFirst("matrix.", ""); + homeServerFromSynapseURL = homeServerFromSynapseURL.replaceFirst( + "matrix.", + "", + ); } } return appConfigOverride?.homeServer ?? @@ -65,7 +71,8 @@ class Environment { } static String get cmsApi { - final envEntry = dotenv.env['CMS_API'] ?? + final envEntry = + dotenv.env['CMS_API'] ?? appConfigOverride?.choreoApi ?? dotenv.env['CHOREO_API']; if (envEntry == null) { @@ -152,11 +159,7 @@ class Environment { final override = AppConfigOverride.fromJson(entry); overrides.add(override); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: entry, - ); + ErrorHandler.logError(e: e, s: s, data: entry); continue; } } @@ -169,11 +172,7 @@ class Environment { try { return AppConfigOverride.fromJson(entry); } catch (e) { - ErrorHandler.logError( - e: e, - s: StackTrace.current, - data: entry, - ); + ErrorHandler.logError(e: e, s: StackTrace.current, data: entry); return null; } } diff --git a/lib/pangea/common/controllers/base_controller.dart b/lib/pangea/common/controllers/base_controller.dart index 51a66dd00..411c1f5a8 100644 --- a/lib/pangea/common/controllers/base_controller.dart +++ b/lib/pangea/common/controllers/base_controller.dart @@ -8,11 +8,11 @@ class BaseController { stateStream = _stateListener.stream.asBroadcastStream(); } - dispose() { + void dispose() { _stateListener.close(); } - setState(T data) { + void setState(T data) { _stateListener.add(data); } } diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 969dcb522..4d75abbd5 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -6,7 +6,6 @@ import 'package:get_storage/get_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; @@ -67,7 +66,7 @@ class PangeaController { subscriptionController.reinitialize(); StyleSettingsRepo.settings(userID!).then((settings) { - AppConfig.fontSizeFactor = settings.fontSizeFactor; + AppSettings.fontSizeFactor.setItem(settings.fontSizeFactor); AppConfig.useActivityImageAsChatBackground = settings.useActivityImageBackground; }); @@ -103,28 +102,24 @@ class PangeaController { } Sentry.configureScope( - (scope) => scope.setUser( - SentryUser( - id: userID, - name: userID, - ), - ), + (scope) => scope.setUser(SentryUser(id: userID, name: userID)), ); GoogleAnalytics.analyticsUserUpdate(userID); } void _registerSubscriptions() { _languageSubscription?.cancel(); - _languageSubscription = - userController.languageStream.stream.listen(_onLanguageUpdate); + _languageSubscription = userController.languageStream.stream.listen( + _onLanguageUpdate, + ); _settingsSubscription?.cancel(); - _settingsSubscription = userController.settingsUpdateStream.stream.listen( - (update) async { - await matrixState.client.updateBotOptions(update.userSettings); - await userController.updatePublicProfile(); - }, - ); + _settingsSubscription = userController.settingsUpdateStream.stream.listen(( + update, + ) async { + await matrixState.client.updateBotOptions(update.userSettings); + await userController.updatePublicProfile(); + }); _joinSpaceSubscription?.cancel(); _joinSpaceSubscription ??= matrixState.client.onSync.stream @@ -139,18 +134,6 @@ class PangeaController { futures.add(GetStorage(key).erase()); } - if (AppConfig.showedActivityMenu) { - futures.add( - SharedPreferences.getInstance().then((prefs) async { - AppConfig.showedActivityMenu = false; - prefs.setBool( - SettingKeys.showedActivityMenu, - AppConfig.showedActivityMenu, - ); - }), - ); - } - await Future.wait(futures); } @@ -182,8 +165,9 @@ class PangeaController { } await _clearCache(exclude: exclude); - await matrixState.client - .updateBotOptions(userController.profile.userSettings); + await matrixState.client.updateBotOptions( + userController.profile.userSettings, + ); await userController.updatePublicProfile(); } diff --git a/lib/pangea/common/models/base_request_model.dart b/lib/pangea/common/models/base_request_model.dart index 2ffd77a27..f3f48f75e 100644 --- a/lib/pangea/common/models/base_request_model.dart +++ b/lib/pangea/common/models/base_request_model.dart @@ -16,12 +16,17 @@ mixin BaseRequestModel { /// Convert to JSON map with common fields Map toBaseJson() => { - ModelKey.userL1: userL1, - ModelKey.userL2: userL2, - ModelKey.cefrLevel: userCefr, - ModelKey.userGender: MatrixState - .pangeaController.userController.profile.userSettings.gender.string, - }; + ModelKey.userL1: userL1, + ModelKey.userL2: userL2, + ModelKey.cefrLevel: userCefr, + ModelKey.userGender: MatrixState + .pangeaController + .userController + .profile + .userSettings + .gender + .string, + }; /// Injects user context (CEFR level, gender) into a request body. /// Safely handles cases where MatrixState is not yet initialized. diff --git a/lib/pangea/common/models/llm_feedback_model.dart b/lib/pangea/common/models/llm_feedback_model.dart index 29e0a83ee..cd43942b3 100644 --- a/lib/pangea/common/models/llm_feedback_model.dart +++ b/lib/pangea/common/models/llm_feedback_model.dart @@ -19,7 +19,7 @@ class LLMFeedbackModel { }); Map toJson() => { - ModelKey.feedback: feedback, - ModelKey.content: contentToJson(content), - }; + ModelKey.feedback: feedback, + ModelKey.content: contentToJson(content), + }; } diff --git a/lib/pangea/common/network/requests.dart b/lib/pangea/common/network/requests.dart index 7481db2fd..e0c6e0982 100644 --- a/lib/pangea/common/network/requests.dart +++ b/lib/pangea/common/network/requests.dart @@ -11,10 +11,7 @@ class Requests { late String? accessToken; late String? choreoApiKey; - Requests({ - this.accessToken, - this.choreoApiKey, - }); + Requests({this.accessToken, this.choreoApiKey}); Future post({ required String url, @@ -36,17 +33,16 @@ class Requests { } Future get({required String url}) async { - final http.Response response = - await http.get(Uri.parse(url), headers: _headers); + final http.Response response = await http.get( + Uri.parse(url), + headers: _headers, + ); handleError(response); return response; } - void addBreadcrumb( - http.Response response, { - Map? body, - }) { + void addBreadcrumb(http.Response response, {Map? body}) { debugPrint("Error - code: ${response.statusCode}"); debugPrint("api: ${response.request?.url}"); debugPrint("request body: $body"); @@ -57,15 +53,10 @@ class Requests { statusCode: response.statusCode, ), ); - Sentry.addBreadcrumb( - Breadcrumb(data: {"body": body}), - ); + Sentry.addBreadcrumb(Breadcrumb(data: {"body": body})); } - void handleError( - http.Response response, { - Map? body, - }) { + void handleError(http.Response response, {Map? body}) { if (response.statusCode == 401) { final responseBody = jsonDecode(utf8.decode(response.bodyBytes)); if (responseBody['detail'] == 'No active subscription found') { @@ -79,7 +70,7 @@ class Requests { } } - get _headers { + Map get _headers { final Map headers = { "Content-Type": "application/json", "Accept": "application/json", diff --git a/lib/pangea/common/utils/any_state_holder.dart b/lib/pangea/common/utils/any_state_holder.dart index a2f8d4c61..2e2d69980 100644 --- a/lib/pangea/common/utils/any_state_holder.dart +++ b/lib/pangea/common/utils/any_state_holder.dart @@ -32,8 +32,9 @@ class PangeaAnyState { Sentry.addBreadcrumb(Breadcrumb(data: _layerLinkAndKeys)); throw Exception("layerLinkAndKey with null for $transformTargetId"); } else { - _layerLinkAndKeys[transformTargetId] = - LayerLinkAndKey(transformTargetId); + _layerLinkAndKeys[transformTargetId] = LayerLinkAndKey( + transformTargetId, + ); } } @@ -66,10 +67,7 @@ class PangeaAnyState { ), ); - Overlay.of( - context, - rootOverlay: rootOverlay, - ).insert(entry); + Overlay.of(context, rootOverlay: rootOverlay).insert(entry); return true; } @@ -77,31 +75,20 @@ class PangeaAnyState { void closeOverlay([String? overlayKey]) { final entry = overlayKey != null ? entries.firstWhereOrNull((element) => element.key == overlayKey) - : entries.lastWhereOrNull( - (element) => element.canPop, - ); + : entries.lastWhereOrNull((element) => element.canPop); if (entry != null) { try { entry.entry.remove(); entry.entry.dispose(); } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: { - "overlay": entry, - }, - ); + ErrorHandler.logError(e: err, s: s, data: {"overlay": entry}); } entries.remove(entry); } } - void closeAllOverlays({ - RegExp? filter, - force = false, - }) { + void closeAllOverlays({RegExp? filter, force = false}) { List shouldRemove = List.from(entries); if (!force) { shouldRemove = shouldRemove.where((element) => element.canPop).toList(); @@ -120,13 +107,7 @@ class PangeaAnyState { shouldRemove[i].entry.remove(); shouldRemove[i].entry.dispose(); } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: { - "overlay": shouldRemove[i], - }, - ); + ErrorHandler.logError(e: err, s: s, data: {"overlay": shouldRemove[i]}); } entries.remove(shouldRemove[i]); @@ -134,8 +115,9 @@ class PangeaAnyState { } RenderBox? getRenderBox(String key) { - final box = layerLinkAndKey(key).key.currentContext?.findRenderObject() - as RenderBox?; + final box = + layerLinkAndKey(key).key.currentContext?.findRenderObject() + as RenderBox?; return box?.hasSize == true ? box : null; } @@ -166,10 +148,10 @@ class LayerLinkAndKey { } Map toJson() => { - "key": key.toString(), - "link": link.toString(), - "transformTargetId": transformTargetId, - }; + "key": key.toString(), + "link": link.toString(), + "transformTargetId": transformTargetId, + }; @override operator ==(Object other) => diff --git a/lib/pangea/common/utils/async_state.dart b/lib/pangea/common/utils/async_state.dart index 908f9e021..725c9b06b 100644 --- a/lib/pangea/common/utils/async_state.dart +++ b/lib/pangea/common/utils/async_state.dart @@ -101,11 +101,7 @@ abstract class AsyncLoader { } if (e is! HttpException) { - ErrorHandler.logError( - e: e, - s: s, - data: {}, - ); + ErrorHandler.logError(e: e, s: s, data: {}); } } } diff --git a/lib/pangea/common/utils/error_handler.dart b/lib/pangea/common/utils/error_handler.dart index bde0e3db8..9018ff5f2 100644 --- a/lib/pangea/common/utils/error_handler.dart +++ b/lib/pangea/common/utils/error_handler.dart @@ -12,7 +12,8 @@ import 'package:fluffychat/utils/platform_infos.dart'; class PangeaWarningError implements Exception { final String message; - PangeaWarningError(message) : message = "Pangea Warning Error: $message"; + PangeaWarningError(String message) + : message = "Pangea Warning Error: $message"; @override String toString() => message; @@ -22,18 +23,16 @@ class ErrorHandler { ErrorHandler(); static Future initialize() async { - await SentryFlutter.init( - (options) { - options.dsn = Environment.sentryDsn; - options.tracesSampleRate = 0.1; - options.debug = kDebugMode; - options.environment = kDebugMode - ? "debug" - : Environment.isStagingEnvironment - ? "staging" - : "productionC"; - }, - ); + await SentryFlutter.init((options) { + options.dsn = Environment.sentryDsn; + options.tracesSampleRate = 0.1; + options.debug = kDebugMode; + options.environment = kDebugMode + ? "debug" + : Environment.isStagingEnvironment + ? "staging" + : "productionC"; + }); // Error handling FlutterError.onError = (FlutterErrorDetails details) async { @@ -46,16 +45,12 @@ class ErrorHandler { }; PlatformDispatcher.instance.onError = (exception, stack) { - logError( - e: exception, - s: stack, - data: {}, - ); + logError(e: exception, s: stack, data: {}); return true; }; } - static logError({ + static Future logError({ Object? e, StackTrace? s, String? m, @@ -90,12 +85,7 @@ class ErrorCopy { late String body; int? errorCode; - ErrorCopy( - this.context, { - this.error, - String? title, - String? body, - }) { + ErrorCopy(this.context, {this.error, String? title, String? body}) { if (title != null) this.title = title; if (body != null) this.body = body; if (title == null || body == null) setCopy(); @@ -156,11 +146,7 @@ class ErrorCopy { body = l10n.errorPleaseRefresh; } } catch (e, s) { - ErrorHandler.logError( - e: s, - s: s, - data: {}, - ); + ErrorHandler.logError(e: s, s: s, data: {}); _setDefaults(); } } diff --git a/lib/pangea/common/utils/firebase_analytics.dart b/lib/pangea/common/utils/firebase_analytics.dart index fefe6308a..8d8fc59c0 100644 --- a/lib/pangea/common/utils/firebase_analytics.dart +++ b/lib/pangea/common/utils/firebase_analytics.dart @@ -40,85 +40,79 @@ class GoogleAnalytics { debugPrint(" Storage Bucket: ${app.options.storageBucket}"); } - static analyticsUserUpdate(String? userID) { + static void analyticsUserUpdate(String? userID) { debugPrint("user update $userID"); analytics?.setUserId(id: userID); } - static updateUserSubscriptionStatus(bool subscribed) { - analytics?.setUserProperty( - name: 'subscribed', - value: "$subscribed", - ); + static void updateUserSubscriptionStatus(bool subscribed) { + analytics?.setUserProperty(name: 'subscribed', value: "$subscribed"); } - static logEvent(String name, {parameters}) { + static void logEvent(String name, {parameters}) { debugPrint("event: $name - parameters: $parameters"); analytics?.logEvent(name: name, parameters: parameters); } - static login(String type, String? userID) { + static void login(String type, String? userID) { logEvent('login', parameters: {'method': type}); analyticsUserUpdate(userID); } - static signUp(String type) { + static void signUp(String type) { logEvent('sign_up', parameters: {'method': type}); } - static logout() { + static void logout() { logEvent('logout'); analyticsUserUpdate(null); } - static createClass(String className, String classCode) { + static void createClass(String className, String classCode) { logEvent( 'create_class', parameters: {'name': className, 'group_id': classCode}, ); } - static createChat(String newChatRoomId) { + static void createChat(String newChatRoomId) { logEvent('create_chat', parameters: {"chat_id": newChatRoomId}); } - static addParent(String chatRoomId, String classCode) { + static void addParent(String chatRoomId, String classCode) { logEvent( 'add_room_to_class', parameters: {"chat_id": chatRoomId, 'group_id': classCode}, ); } - static removeChatFromClass(String chatRoomId, String classCode) { + static void removeChatFromClass(String chatRoomId, String classCode) { logEvent( 'remove_room_from_class', parameters: {"chat_id": chatRoomId, 'group_id': classCode}, ); } - static joinClass(String classCode) { + static void joinClass(String classCode) { logEvent('join_group', parameters: {'group_id': classCode}); } - static sendMessage(String chatRoomId, String classCode) { + static void sendMessage(String chatRoomId, String classCode) { logEvent( 'sent_message', - parameters: { - "chat_id": chatRoomId, - 'group_id': classCode, - }, + parameters: {"chat_id": chatRoomId, 'group_id': classCode}, ); } - static contextualRequest() { + static void contextualRequest() { logEvent('context_request'); } - static messageTranslate() { + static void messageTranslate() { logEvent('message_translate'); } - static beginPurchaseSubscription( + static void beginPurchaseSubscription( SubscriptionDetails details, BuildContext context, ) { @@ -135,7 +129,7 @@ class GoogleAnalytics { 'price': details.price, 'item_category': "subscription", 'quantity': 1, - } + }, ], }, ); diff --git a/lib/pangea/common/utils/overlay.dart b/lib/pangea/common/utils/overlay.dart index d7ecad999..10d2fa6df 100644 --- a/lib/pangea/common/utils/overlay.dart +++ b/lib/pangea/common/utils/overlay.dart @@ -21,11 +21,7 @@ import '../../../config/themes.dart'; import '../../../widgets/matrix.dart'; import 'error_handler.dart'; -enum OverlayPositionEnum { - transform, - centered, - top, -} +enum OverlayPositionEnum { transform, centered, top } class OverlayUtil { static bool showOverlay({ @@ -72,15 +68,18 @@ class OverlayUtil { ), ), Positioned( - top: (position == OverlayPositionEnum.centered || + top: + (position == OverlayPositionEnum.centered || position == OverlayPositionEnum.top) ? 0 : null, - right: (position == OverlayPositionEnum.centered || + right: + (position == OverlayPositionEnum.centered || position == OverlayPositionEnum.top) ? 0 : null, - left: (position == OverlayPositionEnum.centered || + left: + (position == OverlayPositionEnum.centered || position == OverlayPositionEnum.top) ? 0 : null, @@ -111,16 +110,12 @@ class OverlayUtil { ); } catch (err, stack) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: err, - s: stack, - data: {}, - ); + ErrorHandler.logError(e: err, s: stack, data: {}); return false; } } - static showPositionedCard({ + static void showPositionedCard({ required BuildContext context, required Widget cardToShow, required String transformTargetId, @@ -138,8 +133,8 @@ class OverlayUtil { Alignment? followerAnchor, }) { try { - final LayerLinkAndKey layerLinkAndKey = - MatrixState.pAnyState.layerLinkAndKey(transformTargetId); + final LayerLinkAndKey layerLinkAndKey = MatrixState.pAnyState + .layerLinkAndKey(transformTargetId); if (layerLinkAndKey.key.currentContext == null) { debugPrint("layerLinkAndKey.key.currentContext is null"); return; @@ -151,30 +146,34 @@ class OverlayUtil { bool hasTopOverflow = false; if (targetRenderBox != null && targetRenderBox.hasSize) { - final Offset transformTargetOffset = - (targetRenderBox).localToGlobal(Offset.zero); + final Offset transformTargetOffset = (targetRenderBox).localToGlobal( + Offset.zero, + ); final Size transformTargetSize = targetRenderBox.size; final columnWidth = FluffyThemes.isColumnMode(context) ? FluffyThemes.columnWidth + FluffyThemes.navRailWidth : 0; - final horizontalMidpoint = (transformTargetOffset.dx - columnWidth) + + final horizontalMidpoint = + (transformTargetOffset.dx - columnWidth) + (transformTargetSize.width / 2); final halfMaxWidth = maxWidth / 2; final hasLeftOverflow = (horizontalMidpoint - halfMaxWidth) < 10; - final hasRightOverflow = (horizontalMidpoint + halfMaxWidth) > - (MediaQuery.of(context).size.width - columnWidth - 10); + final hasRightOverflow = + (horizontalMidpoint + halfMaxWidth) > + (MediaQuery.widthOf(context) - columnWidth - 10); hasTopOverflow = maxHeight + kToolbarHeight > transformTargetOffset.dy; double xOffset = 0; - MediaQuery.of(context).size.width - (horizontalMidpoint + halfMaxWidth); + MediaQuery.widthOf(context) - (horizontalMidpoint + halfMaxWidth); if (hasLeftOverflow) { xOffset = (horizontalMidpoint - halfMaxWidth - 10) * -1; } else if (hasRightOverflow) { - xOffset = (MediaQuery.of(context).size.width - columnWidth) - + xOffset = + (MediaQuery.of(context).size.width - columnWidth) - (horizontalMidpoint + halfMaxWidth + 10); } offset = Offset(xOffset, 0); @@ -204,20 +203,18 @@ class OverlayUtil { closePrevOverlay: closePrevOverlay, offset: offset, overlayKey: overlayKey, - targetAnchor: targetAnchor ?? + targetAnchor: + targetAnchor ?? (hasTopOverflow ? Alignment.bottomCenter : Alignment.topCenter), - followerAnchor: followerAnchor ?? + followerAnchor: + followerAnchor ?? (hasTopOverflow ? Alignment.topCenter : Alignment.bottomCenter), onDismiss: onDismiss, ignorePointer: ignorePointer, ); } catch (err, stack) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: err, - s: stack, - data: {}, - ); + ErrorHandler.logError(e: err, s: stack, data: {}); } } @@ -290,9 +287,7 @@ class OverlayUtil { closePrevOverlay: false, canPop: false, overlayKey: "star_rain_level_up", - child: const StarRainWidget( - overlayKey: "star_rain_level_up", - ), + child: const StarRainWidget(overlayKey: "star_rain_level_up"), ); } @@ -306,10 +301,7 @@ class OverlayUtil { followerAnchor: Alignment.bottomCenter, targetAnchor: Alignment.bottomCenter, context: context, - child: PointsGainedAnimation( - points: points, - targetID: targetId, - ), + child: PointsGainedAnimation(points: points, targetID: targetId), transformTargetId: targetId, closePrevOverlay: false, backDropToDismiss: false, @@ -330,10 +322,7 @@ class OverlayUtil { followerAnchor: Alignment.topCenter, targetAnchor: Alignment.topCenter, context: context, - child: GrowthAnimation( - targetID: overlayKey, - level: level, - ), + child: GrowthAnimation(targetID: overlayKey, level: level), transformTargetId: targetId, closePrevOverlay: false, backDropToDismiss: false, diff --git a/lib/pangea/common/utils/p_vguard.dart b/lib/pangea/common/utils/p_vguard.dart index c977618c1..afecbb9d4 100644 --- a/lib/pangea/common/utils/p_vguard.dart +++ b/lib/pangea/common/utils/p_vguard.dart @@ -20,8 +20,9 @@ class PAuthGaurd { return Matrix.of(context).client.isLogged() ? '/rooms' : null; } - final isLogged = - Matrix.of(context).widget.clients.any((client) => client.isLogged()); + final isLogged = Matrix.of( + context, + ).widget.clients.any((client) => client.isLogged()); if (!isLogged) return null; // If user hasn't set their L2, @@ -39,8 +40,9 @@ class PAuthGaurd { return Matrix.of(context).client.isLogged() ? null : '/home'; } - final isLogged = - Matrix.of(context).widget.clients.any((client) => client.isLogged()); + final isLogged = Matrix.of( + context, + ).widget.clients.any((client) => client.isLogged()); if (!isLogged) { return '/home'; } @@ -60,9 +62,9 @@ class PAuthGaurd { return Matrix.of(context).client.isLogged() ? null : '/home'; } - final isLogged = Matrix.of(context).widget.clients.any( - (client) => client.isLogged(), - ); + final isLogged = Matrix.of( + context, + ).widget.clients.any((client) => client.isLogged()); if (!isLogged) { return '/home'; } diff --git a/lib/pangea/common/utils/play_click_sound.dart b/lib/pangea/common/utils/play_click_sound.dart index 2edb51bde..7787103bf 100644 --- a/lib/pangea/common/utils/play_click_sound.dart +++ b/lib/pangea/common/utils/play_click_sound.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'package:audioplayers/audioplayers.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; class ClickPlayer { late AudioPlayer _player; @@ -10,7 +10,7 @@ class ClickPlayer { ClickPlayer() { _player = AudioPlayer(); _player.setPlayerMode(PlayerMode.lowLatency); - _player.setVolume(min(0.5, AppConfig.volume)); + _player.setVolume(min(0.5, AppSettings.volume.value)); } Future play() async { diff --git a/lib/pangea/common/widgets/anchored_overlay_widget.dart b/lib/pangea/common/widgets/anchored_overlay_widget.dart index 60fd2dc9a..e20ef580d 100644 --- a/lib/pangea/common/widgets/anchored_overlay_widget.dart +++ b/lib/pangea/common/widgets/anchored_overlay_widget.dart @@ -54,10 +54,11 @@ class _AnchoredOverlayWidgetState extends State { @override Widget build(BuildContext context) { - final leftPosition = (widget.anchorRect.left + - (widget.anchorRect.width / 2) - - (overlayWidth / 2)) - .clamp(8.0, MediaQuery.sizeOf(context).width - overlayWidth - 8.0); + final leftPosition = + (widget.anchorRect.left + + (widget.anchorRect.width / 2) - + (overlayWidth / 2)) + .clamp(8.0, MediaQuery.sizeOf(context).width - overlayWidth - 8.0); return AnimatedOpacity( opacity: _visible ? 1.0 : 0.0, @@ -91,10 +92,7 @@ class _AnchoredOverlayWidgetState extends State { child: Material( color: Colors.transparent, elevation: 4, - child: SizedBox( - width: overlayWidth, - child: widget.child, - ), + child: SizedBox(width: overlayWidth, child: widget.child), ), ), ], diff --git a/lib/pangea/common/widgets/card_error_widget.dart b/lib/pangea/common/widgets/card_error_widget.dart index 43f7c107a..88df1caf3 100644 --- a/lib/pangea/common/widgets/card_error_widget.dart +++ b/lib/pangea/common/widgets/card_error_widget.dart @@ -6,10 +6,7 @@ import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart'; class CardErrorWidget extends StatelessWidget { final String error; - const CardErrorWidget( - this.error, { - super.key, - }); + const CardErrorWidget(this.error, {super.key}); @override Widget build(BuildContext context) { @@ -28,10 +25,7 @@ class CardErrorWidget extends StatelessWidget { spacing: 12.0, mainAxisSize: MainAxisSize.min, children: [ - const BotFace( - width: 50.0, - expression: BotExpression.addled, - ), + const BotFace(width: 50.0, expression: BotExpression.addled), Flexible( child: Text( error, diff --git a/lib/pangea/common/widgets/card_header.dart b/lib/pangea/common/widgets/card_header.dart index 3999c0a40..b9ab8974b 100644 --- a/lib/pangea/common/widgets/card_header.dart +++ b/lib/pangea/common/widgets/card_header.dart @@ -5,10 +5,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import '../../bot/widgets/bot_face_svg.dart'; class CardHeader extends StatelessWidget { - const CardHeader( - this.text, { - super.key, - }); + const CardHeader(this.text, {super.key}); final String text; @@ -21,10 +18,7 @@ class CardHeader extends StatelessWidget { child: Row( spacing: 12.0, children: [ - const BotFace( - width: 50.0, - expression: BotExpression.addled, - ), + const BotFace(width: 50.0, expression: BotExpression.addled), Expanded( child: Text( text, diff --git a/lib/pangea/common/widgets/choice_animation.dart b/lib/pangea/common/widgets/choice_animation.dart index 616a74ede..eafd2a5b0 100644 --- a/lib/pangea/common/widgets/choice_animation.dart +++ b/lib/pangea/common/widgets/choice_animation.dart @@ -82,25 +82,19 @@ class ChoiceAnimationWidgetState extends State return widget.isCorrect == null ? widget.child : widget.isCorrect == true - ? AnimatedBuilder( - animation: _animation, - builder: (context, child) { - return Transform.scale( - scale: _animation.value, - child: child, - ); - }, - child: widget.child, - ) - : AnimatedBuilder( - animation: _animation, - builder: (context, child) { - return Transform.rotate( - angle: _animation.value, - child: child, - ); - }, - child: widget.child, - ); + ? AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return Transform.scale(scale: _animation.value, child: child); + }, + child: widget.child, + ) + : AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return Transform.rotate(angle: _animation.value, child: child); + }, + child: widget.child, + ); } } diff --git a/lib/pangea/common/widgets/choice_array.dart b/lib/pangea/common/widgets/choice_array.dart index d0c109259..660b87e5a 100644 --- a/lib/pangea/common/widgets/choice_array.dart +++ b/lib/pangea/common/widgets/choice_array.dart @@ -76,11 +76,7 @@ class ChoicesArray extends StatelessWidget { } class Choice { - Choice({ - this.color, - required this.text, - this.isGold = false, - }); + Choice({this.color, required this.text, this.isGold = false}); final Color? color; final String text; @@ -142,11 +138,9 @@ class ChoiceItem extends StatelessWidget { ), child: TextButton( style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), - backgroundColor: entry.value.color?.withAlpha(50) ?? + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + backgroundColor: + entry.value.color?.withAlpha(50) ?? theme.colorScheme.primary.withAlpha(10), textStyle: BotStyle.text(context), shape: RoundedRectangleBorder( @@ -161,9 +155,7 @@ class ChoiceItem extends StatelessWidget { getDisplayCopy != null ? getDisplayCopy!(entry.value.text) : entry.value.text, - style: BotStyle.text(context).copyWith( - fontSize: fontSize, - ), + style: BotStyle.text(context).copyWith(fontSize: fontSize), textAlign: TextAlign.center, ), ), diff --git a/lib/pangea/common/widgets/content_loading_indicator.dart b/lib/pangea/common/widgets/content_loading_indicator.dart index 15655d9ee..3c7047cf9 100644 --- a/lib/pangea/common/widgets/content_loading_indicator.dart +++ b/lib/pangea/common/widgets/content_loading_indicator.dart @@ -3,10 +3,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; class ContentLoadingIndicator extends StatelessWidget { - const ContentLoadingIndicator({ - super.key, - this.height, - }); + const ContentLoadingIndicator({super.key, this.height}); final double? height; diff --git a/lib/pangea/common/widgets/customized_svg.dart b/lib/pangea/common/widgets/customized_svg.dart index ad8c3dc89..b70f75c59 100644 --- a/lib/pangea/common/widgets/customized_svg.dart +++ b/lib/pangea/common/widgets/customized_svg.dart @@ -108,16 +108,10 @@ class _CustomizedSvgState extends State { final response = await http.get(Uri.parse(widget.svgUrl)); if (response.statusCode != 200) { final e = Exception('Failed to load SVG: ${response.statusCode}'); - ErrorHandler.logError( - e: e, - data: { - "svgUrl": widget.svgUrl, - }, - ); - await _svgStorage.write( - widget.svgUrl, - {'timestamp': DateTime.now().millisecondsSinceEpoch}, - ); + ErrorHandler.logError(e: e, data: {"svgUrl": widget.svgUrl}); + await _svgStorage.write(widget.svgUrl, { + 'timestamp': DateTime.now().millisecondsSinceEpoch, + }); throw e; } @@ -144,8 +138,9 @@ class _CustomizedSvgState extends State { cachedSvgEntry is Map && cachedSvgEntry['svg'] is String && cachedSvgEntry['timestamp'] is int && - DateTime.fromMillisecondsSinceEpoch(cachedSvgEntry['timestamp']) - .isAfter(DateTime.now().subtract(const Duration(days: 1)))) { + DateTime.fromMillisecondsSinceEpoch( + cachedSvgEntry['timestamp'], + ).isAfter(DateTime.now().subtract(const Duration(days: 1)))) { return _modifySVG(cachedSvgEntry['svg'] as String); } return null; @@ -158,15 +153,10 @@ class _CustomizedSvgState extends State { return SizedBox( width: widget.width, height: widget.height, - child: const Center( - child: CircularProgressIndicator(), - ), + child: const Center(child: CircularProgressIndicator()), ); } else { - return SizedBox( - width: widget.width, - height: widget.height, - ); + return SizedBox(width: widget.width, height: widget.height); } } else if (_hasError || _svgContent == null) { return widget.errorIcon; diff --git a/lib/pangea/common/widgets/dropdown_text_button.dart b/lib/pangea/common/widgets/dropdown_text_button.dart index ce3f5e6f4..abab1cd43 100644 --- a/lib/pangea/common/widgets/dropdown_text_button.dart +++ b/lib/pangea/common/widgets/dropdown_text_button.dart @@ -14,23 +14,12 @@ class DropdownTextButton extends StatelessWidget { Widget build(BuildContext context) { return Container( color: isSelected - ? Theme.of(context) - .colorScheme - .primary - .withAlpha(20) // Highlight selected + ? Theme.of(context).colorScheme.primary.withAlpha( + 20, + ) // Highlight selected : Colors.transparent, - padding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 12, - ), - child: Row( - children: [ - Text( - text, - overflow: TextOverflow.clip, - ), - ], - ), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), + child: Row(children: [Text(text, overflow: TextOverflow.clip)]), ); } } @@ -38,21 +27,13 @@ class DropdownTextButton extends StatelessWidget { class CustomDropdownTextButton extends StatelessWidget { final String text; - const CustomDropdownTextButton({ - required this.text, - super.key, - }); + const CustomDropdownTextButton({required this.text, super.key}); @override Widget build(BuildContext context) { return Row( children: [ - Expanded( - child: Text( - text, - overflow: TextOverflow.clip, - ), - ), + Expanded(child: Text(text, overflow: TextOverflow.clip)), Icon( Icons.arrow_drop_down, color: Theme.of(context).colorScheme.onSurfaceVariant, diff --git a/lib/pangea/common/widgets/error_indicator.dart b/lib/pangea/common/widgets/error_indicator.dart index 8e9f30397..fa74eb2c5 100644 --- a/lib/pangea/common/widgets/error_indicator.dart +++ b/lib/pangea/common/widgets/error_indicator.dart @@ -26,20 +26,13 @@ class ErrorIndicator extends StatelessWidget { ), const SizedBox(width: 8), Flexible( - child: Text( - message, - style: style, - textAlign: TextAlign.center, - ), + child: Text(message, style: style, textAlign: TextAlign.center), ), ], ); if (onTap != null) { - return TextButton( - onPressed: onTap, - child: content, - ); + return TextButton(onPressed: onTap, child: content); } return content; diff --git a/lib/pangea/common/widgets/feedback_dialog.dart b/lib/pangea/common/widgets/feedback_dialog.dart index 3ec11dee5..84bbe9a67 100644 --- a/lib/pangea/common/widgets/feedback_dialog.dart +++ b/lib/pangea/common/widgets/feedback_dialog.dart @@ -40,21 +40,13 @@ class _FeedbackDialogState extends State { mainAxisSize: MainAxisSize.min, children: [ const Center( - child: BotFace( - width: 50.0, - expression: BotExpression.addled, - ), - ), - Text( - L10n.of(context).feedbackDialogDesc, - textAlign: TextAlign.center, + child: BotFace(width: 50.0, expression: BotExpression.addled), ), + Text(L10n.of(context).feedbackDialogDesc, textAlign: TextAlign.center), if (widget.extraContent != null) widget.extraContent!, TextFormField( controller: _feedbackController, - decoration: InputDecoration( - hintText: L10n.of(context).feedbackHint, - ), + decoration: InputDecoration(hintText: L10n.of(context).feedbackHint), keyboardType: TextInputType.multiline, onFieldSubmitted: _feedbackController.text.isNotEmpty ? (value) => widget.onSubmit(value) @@ -74,9 +66,7 @@ class _FeedbackDialogState extends State { ), child: Container( width: 325.0, - constraints: const BoxConstraints( - maxHeight: 600.0, - ), + constraints: const BoxConstraints(maxHeight: 600.0), padding: const EdgeInsets.all(12.0), child: Column( spacing: 20.0, @@ -91,9 +81,7 @@ class _FeedbackDialogState extends State { ), Expanded( child: Container( - constraints: const BoxConstraints( - minHeight: 40.0, - ), + constraints: const BoxConstraints(minHeight: 40.0), alignment: Alignment.center, child: Text( widget.title, @@ -108,9 +96,7 @@ class _FeedbackDialogState extends State { const SizedBox( width: 40.0, height: 40.0, - child: Center( - child: Icon(Icons.flag_outlined), - ), + child: Center(child: Icon(Icons.flag_outlined)), ), ], ), @@ -122,13 +108,12 @@ class _FeedbackDialogState extends State { builder: (context, value, _) { final isNotEmpty = value.text.isNotEmpty; return ElevatedButton( - onPressed: - isNotEmpty ? () => widget.onSubmit(value.text) : null, + onPressed: isNotEmpty + ? () => widget.onSubmit(value.text) + : null, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).feedbackButton), - ], + children: [Text(L10n.of(context).feedbackButton)], ), ); }, diff --git a/lib/pangea/common/widgets/feedback_response_dialog.dart b/lib/pangea/common/widgets/feedback_response_dialog.dart index e411116d1..986ad0903 100644 --- a/lib/pangea/common/widgets/feedback_response_dialog.dart +++ b/lib/pangea/common/widgets/feedback_response_dialog.dart @@ -49,34 +49,21 @@ class FeedbackResponseDialog extends StatelessWidget { const SizedBox( width: 40.0, height: 40.0, - child: Center( - child: Icon(Icons.flag_outlined), - ), + child: Center(child: Icon(Icons.flag_outlined)), ), ], ), ), Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Column( spacing: 20.0, mainAxisSize: MainAxisSize.min, children: [ - const BotFace( - width: 60.0, - expression: BotExpression.idle, - ), - Text( - feedback, - textAlign: TextAlign.center, - ), + const BotFace(width: 60.0, expression: BotExpression.idle), + Text(feedback, textAlign: TextAlign.center), if (description != null) - Text( - description!, - textAlign: TextAlign.center, - ), + Text(description!, textAlign: TextAlign.center), const SizedBox.shrink(), ], ), diff --git a/lib/pangea/common/widgets/full_width_dialog.dart b/lib/pangea/common/widgets/full_width_dialog.dart index aeaa6d416..e3fb69d65 100644 --- a/lib/pangea/common/widgets/full_width_dialog.dart +++ b/lib/pangea/common/widgets/full_width_dialog.dart @@ -25,17 +25,15 @@ class FullWidthDialog extends StatelessWidget { duration: FluffyThemes.animationDuration, child: ConstrainedBox( constraints: isColumnMode - ? BoxConstraints( - maxWidth: maxWidth, - maxHeight: maxHeight, - ) + ? BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight) : BoxConstraints( - maxWidth: MediaQuery.of(context).size.width, - maxHeight: MediaQuery.of(context).size.height, + maxWidth: MediaQuery.widthOf(context), + maxHeight: MediaQuery.heightOf(context), ), child: ClipRRect( - borderRadius: - isColumnMode ? BorderRadius.circular(20.0) : BorderRadius.zero, + borderRadius: isColumnMode + ? BorderRadius.circular(20.0) + : BorderRadius.zero, child: dialogContent, ), ), @@ -44,14 +42,8 @@ class FullWidthDialog extends StatelessWidget { return BackdropFilter( filter: ImageFilter.blur(sigmaX: 2.5, sigmaY: 2.5), child: isColumnMode - ? Dialog( - backgroundColor: backgroundColor, - child: content, - ) - : Dialog.fullscreen( - backgroundColor: backgroundColor, - child: content, - ), + ? Dialog(backgroundColor: backgroundColor, child: content) + : Dialog.fullscreen(backgroundColor: backgroundColor, child: content), ); } } diff --git a/lib/pangea/common/widgets/overlay_container.dart b/lib/pangea/common/widgets/overlay_container.dart index fa09779be..65949595f 100644 --- a/lib/pangea/common/widgets/overlay_container.dart +++ b/lib/pangea/common/widgets/overlay_container.dart @@ -35,9 +35,7 @@ class OverlayContainer extends StatelessWidget { width: 2, color: borderColor ?? Theme.of(context).colorScheme.primary, ), - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), ), constraints: BoxConstraints( maxWidth: maxWidth, diff --git a/lib/pangea/common/widgets/pressable_button.dart b/lib/pangea/common/widgets/pressable_button.dart index 04fc8ffd7..9b69fb4a2 100644 --- a/lib/pangea/common/widgets/pressable_button.dart +++ b/lib/pangea/common/widgets/pressable_button.dart @@ -12,7 +12,7 @@ class PressableButton extends StatefulWidget { final bool depressed; final Color color; final Widget Function(BuildContext context, bool depressed, Color shadowColor) - builder; + builder; final void Function()? onPressed; final Stream? triggerAnimation; @@ -59,8 +59,10 @@ class PressableButtonState extends State duration: const Duration(milliseconds: 100), vsync: this, ); - _tweenAnimation = - Tween(begin: 0, end: widget.buttonHeight).animate(_controller); + _tweenAnimation = Tween( + begin: 0, + end: widget.buttonHeight, + ).animate(_controller); if (!_depressed) { _triggerAnimationSubscription = widget.triggerAnimation?.listen((_) { @@ -139,9 +141,7 @@ class PressableButtonState extends State @override Widget build(BuildContext context) { final shadowColor = Color.alphaBlend( - Colors.black.withAlpha( - (255 * widget.colorFactor).round(), - ), + Colors.black.withAlpha((255 * widget.colorFactor).round()), widget.color, ); diff --git a/lib/pangea/common/widgets/share_room_button.dart b/lib/pangea/common/widgets/share_room_button.dart index b698ef703..c3a390cf9 100644 --- a/lib/pangea/common/widgets/share_room_button.dart +++ b/lib/pangea/common/widgets/share_room_button.dart @@ -13,10 +13,7 @@ import 'package:fluffychat/pangea/spaces/space_constants.dart'; class ShareRoomButton extends StatelessWidget { final Room room; - const ShareRoomButton({ - super.key, - required this.room, - }); + const ShareRoomButton({super.key, required this.room}); @override Widget build(BuildContext context) { @@ -32,21 +29,16 @@ class ShareRoomButton extends StatelessWidget { final spaceCode = room.classCode!; String toCopy = spaceCode; if (value == 0) { - final String initialUrl = - kIsWeb ? html.window.origin! : Environment.frontendURL; + final String initialUrl = kIsWeb + ? html.window.origin! + : Environment.frontendURL; toCopy = "$initialUrl/#/join_with_link?${SpaceConstants.classCode}=${room.classCode}"; } await Clipboard.setData(ClipboardData(text: toCopy)); - ScaffoldMessenger.of( - context, - ).showSnackBar( - SnackBar( - content: Text( - L10n.of(context).copiedToClipboard, - ), - ), + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(L10n.of(context).copiedToClipboard)), ); }, itemBuilder: (BuildContext context) => >[ @@ -60,9 +52,7 @@ class ShareRoomButton extends StatelessWidget { PopupMenuItem( value: 1, child: ListTile( - title: Text( - L10n.of(context).shareInviteCode(room.classCode!), - ), + title: Text(L10n.of(context).shareInviteCode(room.classCode!)), contentPadding: const EdgeInsets.all(0), ), ), diff --git a/lib/pangea/common/widgets/shimmer_background.dart b/lib/pangea/common/widgets/shimmer_background.dart index 66a6bafc9..d1eb34a6f 100644 --- a/lib/pangea/common/widgets/shimmer_background.dart +++ b/lib/pangea/common/widgets/shimmer_background.dart @@ -31,20 +31,12 @@ class _ShimmerBackgroundState extends State @override void initState() { super.initState(); - _controller = AnimationController( - duration: pulseDuration, - vsync: this, - ); + _controller = AnimationController(duration: pulseDuration, vsync: this); _animation = Tween( begin: 0.0, end: 0.3, - ).animate( - CurvedAnimation( - parent: _controller, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); if (widget.enabled) { _startPulsing(); @@ -110,8 +102,9 @@ class _ShimmerBackgroundState extends State borderRadius: borderRadius, child: Container( decoration: BoxDecoration( - color: widget.shimmerColor - .withValues(alpha: _animation.value), + color: widget.shimmerColor.withValues( + alpha: _animation.value, + ), borderRadius: borderRadius, ), ), diff --git a/lib/pangea/common/widgets/shrinkable_text.dart b/lib/pangea/common/widgets/shrinkable_text.dart index 8a3177ed2..a62b3cbb1 100644 --- a/lib/pangea/common/widgets/shrinkable_text.dart +++ b/lib/pangea/common/widgets/shrinkable_text.dart @@ -24,10 +24,7 @@ class ShrinkableText extends StatelessWidget { child: FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, - child: Text( - text, - style: style, - ), + child: Text(text, style: style), ), ); }, diff --git a/lib/pangea/common/widgets/transparent_backdrop.dart b/lib/pangea/common/widgets/transparent_backdrop.dart index 7103d1e0e..adcedde88 100644 --- a/lib/pangea/common/widgets/transparent_backdrop.dart +++ b/lib/pangea/common/widgets/transparent_backdrop.dart @@ -28,19 +28,12 @@ class TransparentBackdrop extends StatelessWidget { backgroundColor?.withAlpha((0.8 * 255).round()) ?? Colors.transparent; return TweenAnimationBuilder( - tween: Tween( - begin: animateBackground ? 0.0 : 1.0, - end: 1.0, - ), + tween: Tween(begin: animateBackground ? 0.0 : 1.0, end: 1.0), duration: animateBackground ? backgroundAnimationDuration : Duration.zero, builder: (context, t, child) { return Material( borderOnForeground: false, - color: Color.lerp( - Colors.transparent, - targetColor, - t, - ), + color: Color.lerp(Colors.transparent, targetColor, t), clipBehavior: Clip.antiAlias, child: InkWell( hoverColor: Colors.transparent, diff --git a/lib/pangea/common/widgets/tutorial_overlay_message.dart b/lib/pangea/common/widgets/tutorial_overlay_message.dart index aed582a2c..dbc5c1c53 100644 --- a/lib/pangea/common/widgets/tutorial_overlay_message.dart +++ b/lib/pangea/common/widgets/tutorial_overlay_message.dart @@ -5,10 +5,7 @@ import 'package:fluffychat/config/app_config.dart'; class TutorialOverlayMessage extends StatelessWidget { final String message; - const TutorialOverlayMessage( - this.message, { - super.key, - }); + const TutorialOverlayMessage(this.message, {super.key}); @override Widget build(BuildContext context) { @@ -36,8 +33,8 @@ class TutorialOverlayMessage extends StatelessWidget { child: Text( message, style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurface, - ), + color: Theme.of(context).colorScheme.onSurface, + ), textAlign: TextAlign.center, ), ), diff --git a/lib/pangea/common/widgets/url_image_widget.dart b/lib/pangea/common/widgets/url_image_widget.dart index 30bff2b9b..6236b42da 100644 --- a/lib/pangea/common/widgets/url_image_widget.dart +++ b/lib/pangea/common/widgets/url_image_widget.dart @@ -54,15 +54,13 @@ class ImageByUrl extends StatelessWidget { height: width, fit: BoxFit.cover, imageUrl: imageUrl.toString(), - placeholder: ( - context, - url, - ) => - Shimmer.fromColors( - baseColor: - Theme.of(context).colorScheme.primary.withAlpha(20), - highlightColor: - Theme.of(context).colorScheme.primary.withAlpha(50), + placeholder: (context, url) => Shimmer.fromColors( + baseColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(20), + highlightColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(50), child: Container( width: width, height: width, @@ -71,11 +69,7 @@ class ImageByUrl extends StatelessWidget { ), ), ), - errorWidget: ( - context, - url, - error, - ) => + errorWidget: (context, url, error) => replacement ?? const SizedBox(), httpHeaders: headers, imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, diff --git a/lib/pangea/common/widgets/word_audio_button.dart b/lib/pangea/common/widgets/word_audio_button.dart index 385290e38..0adba8318 100644 --- a/lib/pangea/common/widgets/word_audio_button.dart +++ b/lib/pangea/common/widgets/word_audio_button.dart @@ -43,10 +43,10 @@ class WordAudioButtonState extends State { @override void initState() { super.initState(); - _loadingChoreoSubscription = - TtsController.loadingChoreoStream.stream.listen((val) { - if (mounted) setState(() => _isLoading = val); - }); + _loadingChoreoSubscription = TtsController.loadingChoreoStream.stream + .listen((val) { + if (mounted) setState(() => _isLoading = val); + }); } @override @@ -77,12 +77,14 @@ class WordAudioButtonState extends State { .key, opacity: widget.isSelected || _isPlaying ? 1 : widget.baseOpacity, child: Tooltip( - message: - _isPlaying ? L10n.of(context).stop : L10n.of(context).playAudio, + message: _isPlaying + ? L10n.of(context).stop + : L10n.of(context).playAudio, child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( - onTap: widget.callbackOverride ?? + onTap: + widget.callbackOverride ?? () async { if (_isPlaying) { await TtsController.stop(); @@ -111,9 +113,7 @@ class WordAudioButtonState extends State { ? const SizedBox( width: 16, height: 16, - child: CircularProgressIndicator( - strokeWidth: 3, - ), + child: CircularProgressIndicator(strokeWidth: 3), ) : Icon( _isPlaying ? Icons.pause_outlined : Icons.volume_up, diff --git a/lib/pangea/constructs/construct_form.dart b/lib/pangea/constructs/construct_form.dart index 50f62d47d..05f37d167 100644 --- a/lib/pangea/constructs/construct_form.dart +++ b/lib/pangea/constructs/construct_form.dart @@ -7,10 +7,7 @@ class ConstructForm { /// The constructIdenfifier final ConstructIdentifier cId; - ConstructForm({ - required this.form, - required this.cId, - }); + ConstructForm({required this.form, required this.cId}); @override bool operator ==(Object other) { @@ -30,9 +27,6 @@ class ConstructForm { } Map toJson() { - return { - 'form': form, - 'cId': cId.toJson(), - }; + return {'form': form, 'cId': cId.toJson()}; } } diff --git a/lib/pangea/constructs/construct_identifier.dart b/lib/pangea/constructs/construct_identifier.dart index 63943c074..e0c240621 100644 --- a/lib/pangea/constructs/construct_identifier.dart +++ b/lib/pangea/constructs/construct_identifier.dart @@ -39,11 +39,7 @@ class ConstructIdentifier { debugger(when: kDebugMode); ErrorHandler.logError( e: Exception("Morph feature not found"), - data: { - "category": category, - "lemma": lemma, - "type": type, - }, + data: {"category": category, "lemma": lemma, "type": type}, ); } } @@ -88,11 +84,7 @@ class ConstructIdentifier { } Map toJson() { - return { - 'lemma': lemma, - 'type': type.string, - 'cat': category, - }; + return {'lemma': lemma, 'type': type.string, 'cat': category}; } // override operator == and hashCode @@ -132,11 +124,7 @@ class ConstructIdentifier { if (type == null) return null; - return ConstructIdentifier( - lemma: lemma, - type: type, - category: category, - ); + return ConstructIdentifier(lemma: lemma, type: type, category: category); } bool get isContentWord => @@ -147,10 +135,10 @@ class ConstructIdentifier { partOfSpeech: category, lemmaLang: MatrixState.pangeaController.userController.userL2?.langCodeShort ?? - LanguageKeys.defaultLanguage, + LanguageKeys.defaultLanguage, userL1: MatrixState.pangeaController.userController.userL1?.langCodeShort ?? - LanguageKeys.defaultLanguage, + LanguageKeys.defaultLanguage, lemma: lemma, messageInfo: messageInfo, ); @@ -158,11 +146,10 @@ class ConstructIdentifier { /// [lemmmaLang] if not set, assumed to be userL2 Future> getLemmaInfo( Map messageInfo, - ) => - LemmaInfoRepo.get( - MatrixState.pangeaController.userController.accessToken, - lemmaInfoRequest(messageInfo), - ); + ) => LemmaInfoRepo.get( + MatrixState.pangeaController.userController.accessToken, + lemmaInfoRequest(messageInfo), + ); String? get userSetEmoji => _userLemmaInfo.emojis?.firstOrNull; @@ -182,26 +169,17 @@ class ConstructIdentifier { final typeName = parts[1]; final category = parts[2]; - final type = ConstructTypeEnum.values.firstWhereOrNull( - (e) => e.name == typeName, - ) ?? + final type = + ConstructTypeEnum.values.firstWhereOrNull((e) => e.name == typeName) ?? ConstructTypeEnum.vocab; - return ConstructIdentifier( - lemma: lemma, - type: type, - category: category, - ); + return ConstructIdentifier(lemma: lemma, type: type, category: category); } PangeaToken get asToken => PangeaToken( - lemma: Lemma( - text: lemma, - saveVocab: true, - form: lemma, - ), - pos: category, - text: PangeaTokenText.fromString(lemma), - morph: {}, - ); + lemma: Lemma(text: lemma, saveVocab: true, form: lemma), + pos: category, + text: PangeaTokenText.fromString(lemma), + morph: {}, + ); } diff --git a/lib/pangea/constructs/construct_level_enum.dart b/lib/pangea/constructs/construct_level_enum.dart index 1fc003cf2..6e973c3df 100644 --- a/lib/pangea/constructs/construct_level_enum.dart +++ b/lib/pangea/constructs/construct_level_enum.dart @@ -4,21 +4,13 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart'; import 'package:fluffychat/pangea/common/widgets/customized_svg.dart'; -enum ConstructLevelEnum { - flowers, - greens, - seeds, -} +enum ConstructLevelEnum { flowers, greens, seeds } extension ConstructLevelEnumExt on ConstructLevelEnum { Color color(BuildContext context) { switch (this) { case ConstructLevelEnum.flowers: - return Color.lerp( - AppConfig.primaryColor, - Colors.white, - 0.6, - ) ?? + return Color.lerp(AppConfig.primaryColor, Colors.white, 0.6) ?? AppConfig.primaryColor; case ConstructLevelEnum.greens: return Color.lerp(AppConfig.success, Colors.white, 0.6) ?? @@ -31,11 +23,7 @@ extension ConstructLevelEnumExt on ConstructLevelEnum { Color darkColor(BuildContext context) { switch (this) { case ConstructLevelEnum.flowers: - return Color.lerp( - AppConfig.primaryColor, - Colors.white, - 0.3, - ) ?? + return Color.lerp(AppConfig.primaryColor, Colors.white, 0.3) ?? AppConfig.primaryColor; case ConstructLevelEnum.greens: return Color.lerp(AppConfig.success, Colors.black, 0.3) ?? @@ -90,15 +78,10 @@ extension ConstructLevelEnumExt on ConstructLevelEnum { } Widget icon([double? size]) => CustomizedSvg( - svgUrl: _svgURL, - colorReplacements: const {}, - errorIcon: Text( - emoji, - style: TextStyle( - fontSize: size ?? 24, - ), - ), - width: size, - height: size, - ); + svgUrl: _svgURL, + colorReplacements: const {}, + errorIcon: Text(emoji, style: TextStyle(fontSize: size ?? 24)), + width: size, + height: size, + ); } diff --git a/lib/pangea/constructs/construct_repo.dart b/lib/pangea/constructs/construct_repo.dart index 8fe3bb917..bedafc4a9 100644 --- a/lib/pangea/constructs/construct_repo.dart +++ b/lib/pangea/constructs/construct_repo.dart @@ -97,14 +97,10 @@ class ConstructSummaryRequest { class ConstructSummaryResponse { final ConstructSummary summary; - ConstructSummaryResponse({ - required this.summary, - }); + ConstructSummaryResponse({required this.summary}); Map toJson() { - return { - 'summary': summary.toJson(), - }; + return {'summary': summary.toJson()}; } factory ConstructSummaryResponse.fromJson(Map json) { @@ -122,8 +118,10 @@ class ConstructRepo { choreoApiKey: Environment.choreoApiKey, accessToken: MatrixState.pangeaController.userController.accessToken, ); - final Response res = - await req.post(url: PApiUrls.constructSummary, body: request.toJson()); + final Response res = await req.post( + url: PApiUrls.constructSummary, + body: request.toJson(), + ); final decodedBody = jsonDecode(utf8.decode(res.bodyBytes)); final response = ConstructSummaryResponse.fromJson(decodedBody); return response; diff --git a/lib/pangea/course_chats/activity_template_chat_list_item.dart b/lib/pangea/course_chats/activity_template_chat_list_item.dart index 6396be97f..c589a5d82 100644 --- a/lib/pangea/course_chats/activity_template_chat_list_item.dart +++ b/lib/pangea/course_chats/activity_template_chat_list_item.dart @@ -26,10 +26,7 @@ class ActivityTemplateChatListItem extends StatelessWidget { Widget build(BuildContext context) { final activity = sessions.first.activity; return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), child: Column( spacing: 10.0, mainAxisSize: MainAxisSize.min, @@ -43,9 +40,7 @@ class ActivityTemplateChatListItem extends StatelessWidget { leading: ImageByUrl( imageUrl: activity.imageURL, width: Avatar.defaultSize, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 2, - ), + borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), replacement: Avatar( name: activity.title, borderRadius: BorderRadius.circular( @@ -66,53 +61,44 @@ class ActivityTemplateChatListItem extends StatelessWidget { ), ), ), - ...sessions.map( - (e) { - return Padding( - padding: const EdgeInsets.only( - top: 4.0, - bottom: 4.0, - right: 4.0, - left: 14.0, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: OpenRolesIndicator( - roles: e.activity.roles.values.toList(), - assignedRoles: e.assignedRoles, - space: space, + ...sessions.map((e) { + return Padding( + padding: const EdgeInsets.only( + top: 4.0, + bottom: 4.0, + right: 4.0, + left: 14.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: OpenRolesIndicator( + roles: e.activity.roles.values.toList(), + assignedRoles: e.assignedRoles, + space: space, + ), + ), + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 24.0), + child: ElevatedButton( + onPressed: () => showFutureLoadingDialog( + context: context, + future: () => joinActivity(e), + ), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.only(left: 8.0, right: 8.0), + ), + child: Text( + L10n.of(context).join, + style: const TextStyle(fontSize: 12), ), ), - ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 24.0, - ), - child: ElevatedButton( - onPressed: () => showFutureLoadingDialog( - context: context, - future: () => joinActivity(e), - ), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.only( - left: 8.0, - right: 8.0, - ), - ), - child: Text( - L10n.of(context).join, - style: const TextStyle( - fontSize: 12, - ), - ), - ), - ), - ], - ), - ); - }, - ), + ), + ], + ), + ); + }), ], ), ); diff --git a/lib/pangea/course_chats/course_chats_page.dart b/lib/pangea/course_chats/course_chats_page.dart index a26eeb56d..f3f7c6142 100644 --- a/lib/pangea/course_chats/course_chats_page.dart +++ b/lib/pangea/course_chats/course_chats_page.dart @@ -49,7 +49,7 @@ class CourseChatsController extends State String get roomId => widget.roomId; Room? get room => widget.client.getRoomById(widget.roomId); - List? discoveredChildren; + List? discoveredChildren; StreamSubscription? _roomSubscription; String? _nextBatch; bool noMoreRooms = false; @@ -57,9 +57,7 @@ class CourseChatsController extends State @override void initState() { - loadHierarchy(reload: true).then( - (_) => _joinDefaultChats(), - ); + loadHierarchy(reload: true).then((_) => _joinDefaultChats()); // Listen for changes to the activeSpace's hierarchy, // and reload the hierarchy when they come through @@ -99,9 +97,7 @@ class CourseChatsController extends State room?.spaceChildren.map((c) => c.roomId).whereType().toSet() ?? {}; - List get joinedRooms => Matrix.of(context) - .client - .rooms + List get joinedRooms => Matrix.of(context).client.rooms .where((room) => childrenIds.contains(room.id)) .where((room) => !room.isHiddenRoom) .toList(); @@ -109,7 +105,7 @@ class CourseChatsController extends State List joinedActivities() => joinedRooms.where((r) => r.isActivitySession).toList(); - List get discoveredGroupChats => (discoveredChildren ?? []) + List get discoveredGroupChats => (discoveredChildren ?? []) .where( (chunk) => chunk.roomType == null || @@ -174,34 +170,37 @@ class CourseChatsController extends State Future _joinDefaultChats() async { if (discoveredChildren == null) return; - final found = List.from(discoveredChildren!); + final found = List.from(discoveredChildren!); final List joinFutures = []; for (final chunk in found) { if (chunk.canonicalAlias == null) continue; final alias = chunk.canonicalAlias!; - final isDefaultChat = (alias.localpart ?? '') - .startsWith(SpaceConstants.announcementsChatAlias) || - (alias.localpart ?? '') - .startsWith(SpaceConstants.introductionChatAlias); + final isDefaultChat = + (alias.localpart ?? '').startsWith( + SpaceConstants.announcementsChatAlias, + ) || + (alias.localpart ?? '').startsWith( + SpaceConstants.introductionChatAlias, + ); if (!isDefaultChat) continue; joinFutures.add( - widget.client.joinRoom(alias).then((_) { - discoveredChildren?.remove(chunk); - }).catchError((e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'alias': alias, - 'spaceId': widget.roomId, - }, - ); - return null; - }), + widget.client + .joinRoom(alias) + .then((_) { + discoveredChildren?.remove(chunk); + }) + .catchError((e, s) { + ErrorHandler.logError( + e: e, + s: s, + data: {'alias': alias, 'spaceId': widget.roomId}, + ); + return null; + }), ); } @@ -210,7 +209,7 @@ class CourseChatsController extends State } } - Future loadHierarchy({reload = false}) async { + Future loadHierarchy({bool reload = false}) async { final room = widget.client.getRoomById(widget.roomId); if (room == null) return; @@ -269,10 +268,10 @@ class CourseChatsController extends State // Failsafe to prevent too many calls to the server in a row int callsToServer = 0; - List? currentHierarchy = + List? currentHierarchy = discoveredChildren == null || reload - ? null - : List.from(discoveredChildren!); + ? null + : List.from(discoveredChildren!); String? currentNextBatch = reload ? null : _nextBatch; // Makes repeated calls to the server until 10 new visible rooms have @@ -308,10 +307,7 @@ class CourseChatsController extends State // if rooms have earlier been loaded for this space, add those // previously loaded rooms to the front of the response list - response.rooms.insertAll( - 0, - currentHierarchy ?? [], - ); + response.rooms.insertAll(0, currentHierarchy ?? []); // finally, set the response to the last response for this space // and set the current next batch token @@ -351,13 +347,13 @@ class CourseChatsController extends State inviteEvent == null ? L10n.of(context).inviteForMe : inviteEvent.content.tryGet('reason') ?? - L10n.of(context).youInvitedBy( - room - .unsafeGetUserFromMemoryOrFallback( - inviteEvent.senderId, - ) - .calcDisplayname(i18n: matrixLocals), - ), + L10n.of(context).youInvitedBy( + room + .unsafeGetUserFromMemoryOrFallback( + inviteEvent.senderId, + ) + .calcDisplayname(i18n: matrixLocals), + ), textAlign: TextAlign.center, ), ), @@ -399,10 +395,7 @@ class CourseChatsController extends State return; case InviteAction.block: final userId = inviteEvent?.senderId; - context.go( - '/rooms/settings/security/ignorelist', - extra: userId, - ); + context.go('/rooms/settings/security/ignorelist', extra: userId); return; } if (!mounted) return; @@ -423,9 +416,7 @@ class CourseChatsController extends State if (room.membership == Membership.ban) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(L10n.of(context).youHaveBeenBannedFromThisChat), - ), + SnackBar(content: Text(L10n.of(context).youHaveBeenBannedFromThisChat)), ); return; } @@ -443,15 +434,13 @@ class CourseChatsController extends State NavigationUtil.goToSpaceRoute(room.id, [], context); } - void joinChildRoom(SpaceRoomsChunk item) async { + void joinChildRoom(SpaceRoomsChunk$2 item) async { final space = widget.client.getRoomById(widget.roomId); final roomId = await PublicRoomBottomSheet.show( context: context, chunk: item, via: space?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == item.roomId, - ) + .firstWhereOrNull((child) => child.roomId == item.roomId) ?.via, ); if (mounted && roomId != null) { @@ -483,9 +472,7 @@ class CourseChatsController extends State via: widget.client .getRoomById(widget.roomId) ?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == roomId, - ) + .firstWhereOrNull((child) => child.roomId == roomId) ?.via, ); @@ -501,17 +488,15 @@ class CourseChatsController extends State context.go("/rooms/spaces/${widget.roomId}/$roomId"); } - bool _includeSpaceChild( - Room space, - SpaceRoomsChunk hierarchyMember, - ) { + bool _includeSpaceChild(Room space, SpaceRoomsChunk$2 hierarchyMember) { if (!mounted) return false; final bool isAnalyticsRoom = hierarchyMember.roomType == PangeaRoomTypes.analytics; - final bool isMember = [Membership.join, Membership.invite].contains( - widget.client.getRoomById(hierarchyMember.roomId)?.membership, - ); + final bool isMember = [ + Membership.join, + Membership.invite, + ].contains(widget.client.getRoomById(hierarchyMember.roomId)?.membership); final bool isSuggested = space.spaceChildSuggestionStatus[hierarchyMember.roomId] ?? true; @@ -519,11 +504,11 @@ class CourseChatsController extends State return !isAnalyticsRoom && (isMember || isSuggested); } - List _filterHierarchyResponse( + List _filterHierarchyResponse( Room space, - List hierarchyResponse, + List hierarchyResponse, ) { - final List filteredChildren = []; + final List filteredChildren = []; for (final child in hierarchyResponse) { if (child.roomId == widget.roomId) { continue; @@ -558,27 +543,22 @@ class CourseChatsController extends State } final joinedRooms = joinUpdate?.entries - .where( - (e) => childrenIds.contains(e.key), - ) + .where((e) => childrenIds.contains(e.key)) .map((e) => e.value.timeline?.events) .whereType>(); final invitedRooms = inviteUpdate?.entries - .where( - (e) => childrenIds.contains(e.key), - ) + .where((e) => childrenIds.contains(e.key)) .map((e) => e.value.inviteState) .whereType>(); final leftRooms = leaveUpdate?.entries - .where( - (e) => childrenIds.contains(e.key), - ) + .where((e) => childrenIds.contains(e.key)) .map((e) => e.value.timeline?.events) .whereType>(); - final bool hasJoinedRoom = joinedRooms?.any( + final bool hasJoinedRoom = + joinedRooms?.any( (events) => events.any( (e) => e.senderId == widget.client.userID && @@ -587,7 +567,8 @@ class CourseChatsController extends State ) ?? false; - final bool hasLeftRoom = leftRooms?.any( + final bool hasLeftRoom = + leftRooms?.any( (events) => events.any( (e) => e.senderId == widget.client.userID && @@ -604,21 +585,16 @@ class CourseChatsController extends State final leaveTimeline = leaveUpdate?[widget.roomId]?.timeline?.events; if (joinTimeline == null && leaveTimeline == null) return false; - final bool hasJoinUpdate = joinTimeline?.any( - (event) => event.type == EventTypes.SpaceChild, - ) ?? + final bool hasJoinUpdate = + joinTimeline?.any((event) => event.type == EventTypes.SpaceChild) ?? false; - final bool hasLeaveUpdate = leaveTimeline?.any( - (event) => event.type == EventTypes.SpaceChild, - ) ?? + final bool hasLeaveUpdate = + leaveTimeline?.any((event) => event.type == EventTypes.SpaceChild) ?? false; return hasJoinUpdate || hasLeaveUpdate; } - int _sortSpaceChildren( - SpaceRoomsChunk a, - SpaceRoomsChunk b, - ) { + int _sortSpaceChildren(SpaceRoomsChunk$2 a, SpaceRoomsChunk$2 b) { final bool aIsSpace = a.roomType == 'm.space'; final bool bIsSpace = b.roomType == 'm.space'; @@ -635,9 +611,7 @@ class CourseChatsController extends State return !room!.dismissedDefaultChat(type) && !room!.hasDefaultChat(type); } - Future dismissDefaultChatCreation( - CourseDefaultChatsEnum type, - ) async { + Future dismissDefaultChatCreation(CourseDefaultChatsEnum type) async { if (room == null) { throw Exception("Room is null"); } @@ -651,9 +625,7 @@ class CourseChatsController extends State await room!.setCourseChatsSettings(settings); } - Future createDefaultChat( - CourseDefaultChatsEnum type, - ) async { + Future createDefaultChat(CourseDefaultChatsEnum type) async { if (room == null) { throw Exception("Room is null"); } diff --git a/lib/pangea/course_chats/course_chats_settings_model.dart b/lib/pangea/course_chats/course_chats_settings_model.dart index a05af31c6..fa05467a9 100644 --- a/lib/pangea/course_chats/course_chats_settings_model.dart +++ b/lib/pangea/course_chats/course_chats_settings_model.dart @@ -8,9 +8,9 @@ class CourseChatsSettingsModel { }); Map toJson() => { - 'dismissed_intro_chat': dismissedIntroChat, - 'dismissed_announcements_chat': dismissedAnnouncementsChat, - }; + 'dismissed_intro_chat': dismissedIntroChat, + 'dismissed_announcements_chat': dismissedAnnouncementsChat, + }; factory CourseChatsSettingsModel.fromJson(Map json) { return CourseChatsSettingsModel( @@ -22,10 +22,9 @@ class CourseChatsSettingsModel { CourseChatsSettingsModel copyWith({ bool? dismissedIntroChat, bool? dismissedAnnouncementsChat, - }) => - CourseChatsSettingsModel( - dismissedIntroChat: dismissedIntroChat ?? this.dismissedIntroChat, - dismissedAnnouncementsChat: - dismissedAnnouncementsChat ?? this.dismissedAnnouncementsChat, - ); + }) => CourseChatsSettingsModel( + dismissedIntroChat: dismissedIntroChat ?? this.dismissedIntroChat, + dismissedAnnouncementsChat: + dismissedAnnouncementsChat ?? this.dismissedAnnouncementsChat, + ); } diff --git a/lib/pangea/course_chats/course_chats_view.dart b/lib/pangea/course_chats/course_chats_view.dart index ce4b41310..136c69a62 100644 --- a/lib/pangea/course_chats/course_chats_view.dart +++ b/lib/pangea/course_chats/course_chats_view.dart @@ -22,21 +22,13 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; class CourseChatsView extends StatelessWidget { final CourseChatsController controller; - const CourseChatsView( - this.controller, { - super.key, - }); + const CourseChatsView(this.controller, {super.key}); @override Widget build(BuildContext context) { final room = controller.room; if (room == null) { - return const Center( - child: Icon( - Icons.search_outlined, - size: 80, - ), - ); + return const Center(child: Icon(Icons.search_outlined, size: 80)); } return StreamBuilder( @@ -48,21 +40,20 @@ class CourseChatsView extends StatelessWidget { final joinedSessions = controller.joinedActivities(); final discoveredGroupChats = controller.discoveredGroupChats; - final discoveredSessions = - controller.discoveredActivities().entries.toList(); + final discoveredSessions = controller + .discoveredActivities() + .entries + .toList(); final isColumnMode = FluffyThemes.isColumnMode(context); return Padding( padding: isColumnMode - ? const EdgeInsets.only( - top: 12.0, - left: 8.0, - right: 8.0, - ) + ? const EdgeInsets.only(top: 12.0, left: 8.0, right: 8.0) : const EdgeInsets.all(0.0), child: ListView.builder( shrinkWrap: true, - itemCount: joinedChats.length + + itemCount: + joinedChats.length + joinedSessions.length + discoveredGroupChats.length + discoveredSessions.length + @@ -81,10 +72,7 @@ class CourseChatsView extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ LearningProgressIndicators(), - Icon( - Icons.chat_bubble_outline, - size: 30.0, - ), + Icon(Icons.chat_bubble_outline, size: 30.0), SizedBox(height: 12.0), ], ), @@ -167,10 +155,7 @@ class CourseChatsView extends StatelessWidget { return joinedSessions.isEmpty ? const SizedBox() : Padding( - padding: const EdgeInsets.only( - top: 20.0, - bottom: 4.0, - ), + padding: const EdgeInsets.only(top: 20.0, bottom: 4.0), child: Text( L10n.of(context).myActivities, style: const TextStyle(fontSize: 12.0), @@ -204,10 +189,7 @@ class CourseChatsView extends StatelessWidget { return discoveredSessions.isEmpty ? const SizedBox() : Padding( - padding: const EdgeInsets.only( - top: 20.0, - bottom: 4.0, - ), + padding: const EdgeInsets.only(top: 20.0, bottom: 4.0), child: Text( L10n.of(context).openToJoin, style: const TextStyle(fontSize: 12.0), @@ -239,8 +221,9 @@ class CourseChatsView extends StatelessWidget { vertical: 2.0, ), child: TextButton( - onPressed: - controller.isLoading ? null : controller.loadHierarchy, + onPressed: controller.isLoading + ? null + : controller.loadHierarchy, child: controller.isLoading ? LinearProgressIndicator( borderRadius: BorderRadius.circular( diff --git a/lib/pangea/course_chats/course_default_chats_enum.dart b/lib/pangea/course_chats/course_default_chats_enum.dart index e9e335fbd..a0de4f5fc 100644 --- a/lib/pangea/course_chats/course_default_chats_enum.dart +++ b/lib/pangea/course_chats/course_default_chats_enum.dart @@ -7,31 +7,33 @@ enum CourseDefaultChatsEnum { announcements; String get alias => switch (this) { - CourseDefaultChatsEnum.introductions => - SpaceConstants.introductionChatAlias, - CourseDefaultChatsEnum.announcements => - SpaceConstants.announcementsChatAlias, - }; + CourseDefaultChatsEnum.introductions => + SpaceConstants.introductionChatAlias, + CourseDefaultChatsEnum.announcements => + SpaceConstants.announcementsChatAlias, + }; String title(L10n l10n) => switch (this) { - CourseDefaultChatsEnum.introductions => l10n.introductions, - CourseDefaultChatsEnum.announcements => l10n.announcements, - }; + CourseDefaultChatsEnum.introductions => l10n.introductions, + CourseDefaultChatsEnum.announcements => l10n.announcements, + }; String creationTitle(L10n l10n) => switch (this) { - CourseDefaultChatsEnum.introductions => l10n.introChatTitle, - CourseDefaultChatsEnum.announcements => l10n.announcementsChatTitle, - }; + CourseDefaultChatsEnum.introductions => l10n.introChatTitle, + CourseDefaultChatsEnum.announcements => l10n.announcementsChatTitle, + }; String creationDesc(L10n l10n) => switch (this) { - CourseDefaultChatsEnum.introductions => l10n.introChatDesc, - CourseDefaultChatsEnum.announcements => l10n.announcementsChatDesc, - }; + CourseDefaultChatsEnum.introductions => l10n.introChatDesc, + CourseDefaultChatsEnum.announcements => l10n.announcementsChatDesc, + }; dynamic powerLevels(String userID) => switch (this) { - CourseDefaultChatsEnum.introductions => - RoomDefaults.defaultPowerLevels(userID), - CourseDefaultChatsEnum.announcements => - RoomDefaults.restrictedPowerLevels(userID), - }; + CourseDefaultChatsEnum.introductions => RoomDefaults.defaultPowerLevels( + userID, + ), + CourseDefaultChatsEnum.announcements => RoomDefaults.restrictedPowerLevels( + userID, + ), + }; } diff --git a/lib/pangea/course_chats/extended_space_rooms_chunk.dart b/lib/pangea/course_chats/extended_space_rooms_chunk.dart index e3adbd56a..fd50ad059 100644 --- a/lib/pangea/course_chats/extended_space_rooms_chunk.dart +++ b/lib/pangea/course_chats/extended_space_rooms_chunk.dart @@ -4,7 +4,7 @@ import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart'; class ExtendedSpaceRoomsChunk { - final SpaceRoomsChunk chunk; + final SpaceRoomsChunk$2 chunk; final List assignedRoles; final ActivityPlanModel activity; diff --git a/lib/pangea/course_chats/open_roles_indicator.dart b/lib/pangea/course_chats/open_roles_indicator.dart index e90e7095b..4739903f0 100644 --- a/lib/pangea/course_chats/open_roles_indicator.dart +++ b/lib/pangea/course_chats/open_roles_indicator.dart @@ -37,21 +37,25 @@ class OpenRolesIndicator extends StatelessWidget { spacing: spacing ?? 2.0, children: [ ...roles.map((role) { - final assigned = - assignedRoles.firstWhereOrNull((r) => r.id == role.id); + final assigned = assignedRoles.firstWhereOrNull( + (r) => r.id == role.id, + ); final user = assigned != null - ? roomParticipants - .firstWhereOrNull((p) => p.id == assigned.userId) ?? - spaceParticipants - .firstWhereOrNull((p) => p.id == assigned.userId) + ? roomParticipants.firstWhereOrNull( + (p) => p.id == assigned.userId, + ) ?? + spaceParticipants.firstWhereOrNull( + (p) => p.id == assigned.userId, + ) : null; if (assigned != null) { return Builder( builder: (context) => Avatar( mxContent: user?.avatarUrl, - name: user?.calcDisplayname() ?? + name: + user?.calcDisplayname() ?? assigned.userId.localpart ?? assigned.userId, size: size ?? 16, diff --git a/lib/pangea/course_chats/unjoined_chat_list_item.dart b/lib/pangea/course_chats/unjoined_chat_list_item.dart index c95407c1a..3ddeec3bb 100644 --- a/lib/pangea/course_chats/unjoined_chat_list_item.dart +++ b/lib/pangea/course_chats/unjoined_chat_list_item.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; class UnjoinedChatListItem extends StatelessWidget { - final SpaceRoomsChunk chunk; + final SpaceRoomsChunk$2 chunk; final VoidCallback onTap; const UnjoinedChatListItem({ super.key, @@ -21,10 +21,7 @@ class UnjoinedChatListItem extends StatelessWidget { final displayname = chunk.name ?? chunk.canonicalAlias ?? L10n.of(context).emptyChat; return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), child: Material( borderRadius: BorderRadius.circular(AppConfig.borderRadius), clipBehavior: Clip.hardEdge, @@ -35,14 +32,11 @@ class UnjoinedChatListItem extends StatelessWidget { leading: Avatar( mxContent: chunk.avatarUrl, name: displayname, - userId: Matrix.of(context) - .client - .getRoomById(chunk.roomId) - ?.directChatMatrixID, + userId: Matrix.of( + context, + ).client.getRoomById(chunk.roomId)?.directChatMatrixID, borderRadius: chunk.roomType == 'm.space' - ? BorderRadius.circular( - AppConfig.borderRadius / 2, - ) + ? BorderRadius.circular(AppConfig.borderRadius / 2) : null, ), title: Row( @@ -62,17 +56,12 @@ class UnjoinedChatListItem extends StatelessWidget { ), ), const SizedBox(width: 4), - const Icon( - Icons.people_outlined, - size: 14, - ), + const Icon(Icons.people_outlined, size: 14), ], ), subtitle: Text( chunk.topic ?? - L10n.of(context).countParticipants( - chunk.numJoinedMembers, - ), + L10n.of(context).countParticipants(chunk.numJoinedMembers), maxLines: 1, overflow: TextOverflow.ellipsis, ), diff --git a/lib/pangea/course_creation/course_info_chip_widget.dart b/lib/pangea/course_creation/course_info_chip_widget.dart index 51daf3d2b..991dc64b9 100644 --- a/lib/pangea/course_creation/course_info_chip_widget.dart +++ b/lib/pangea/course_creation/course_info_chip_widget.dart @@ -28,16 +28,8 @@ class CourseInfoChip extends StatelessWidget { spacing: 4.0, mainAxisSize: MainAxisSize.min, children: [ - Icon( - icon, - size: iconSize, - ), - Text( - text, - style: TextStyle( - fontSize: fontSize, - ), - ), + Icon(icon, size: iconSize), + Text(text, style: TextStyle(fontSize: fontSize)), ], ), ); diff --git a/lib/pangea/course_creation/course_invite_page.dart b/lib/pangea/course_creation/course_invite_page.dart index 2af4659e8..1f50e8e67 100644 --- a/lib/pangea/course_creation/course_invite_page.dart +++ b/lib/pangea/course_creation/course_invite_page.dart @@ -54,12 +54,12 @@ class CourseInvitePageController extends State final spaceId = await widget.courseCreationCompleter!.future; final room = Matrix.of(context).client.getRoomById(spaceId); if (room == null || room.coursePlan == null) { - await Matrix.of(context).client.onRoomState.stream.firstWhere((event) { - return event.roomId == spaceId && - event.state.type == PangeaEventTypes.coursePlan; - }).timeout( - const Duration(seconds: 10), - ); + await Matrix.of(context).client.onRoomState.stream + .firstWhere((event) { + return event.roomId == spaceId && + event.state.type == PangeaEventTypes.coursePlan; + }) + .timeout(const Duration(seconds: 10)); } return spaceId; } @@ -76,9 +76,7 @@ class CourseInvitePageController extends State child: Center( child: Container( padding: const EdgeInsets.all(20.0), - constraints: const BoxConstraints( - maxWidth: 750, - ), + constraints: const BoxConstraints(maxWidth: 750), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -97,10 +95,7 @@ class CourseInvitePageController extends State spacing: 10.0, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.map_outlined, - size: 40.0, - ), + const Icon(Icons.map_outlined, size: 40.0), Flexible( child: Text( course!.title, @@ -121,8 +116,8 @@ class CourseInvitePageController extends State ), ) : loadingCourse - ? const CircularProgressIndicator.adaptive() - : const SizedBox(), + ? const CircularProgressIndicator.adaptive() + : const SizedBox(), Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -144,13 +139,15 @@ class CourseInvitePageController extends State mainAxisSize: MainAxisSize.min, children: [ FutureBuilder( - future: - client.getProfileFromUserId(client.userID!), + future: client.getProfileFromUserId( + client.userID!, + ), builder: (context, snapshot) { return Avatar( size: avatarSize, mxContent: snapshot.data?.avatarUrl, - name: snapshot.data?.displayName ?? + name: + snapshot.data?.displayName ?? client.userID!.localpart, userId: client.userID!, ); @@ -170,10 +167,7 @@ class CourseInvitePageController extends State ), ); }), - const Icon( - Icons.more_horiz, - size: 24.0, - ), + const Icon(Icons.more_horiz, size: 24.0), ], ); }, @@ -229,9 +223,7 @@ class CourseInvitePageController extends State child: Row( spacing: 8.0, mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).playWithAI), - ], + children: [Text(L10n.of(context).playWithAI)], ), ), ], diff --git a/lib/pangea/course_creation/course_language_filter.dart b/lib/pangea/course_creation/course_language_filter.dart index e5c45cdf5..701f77ad7 100644 --- a/lib/pangea/course_creation/course_language_filter.dart +++ b/lib/pangea/course_creation/course_language_filter.dart @@ -25,11 +25,8 @@ class CourseLanguageFilter extends StatelessWidget { displayname: (v) => v.getDisplayName(context), enableSearch: true, defaultName: L10n.of(context).allLanguages, - searchMatchFn: (item, searchValue) => LanguageModel.search( - item.value, - searchValue, - context, - ), + searchMatchFn: (item, searchValue) => + LanguageModel.search(item.value, searchValue, context), ); } } diff --git a/lib/pangea/course_creation/course_plan_filter_widget.dart b/lib/pangea/course_creation/course_plan_filter_widget.dart index 8f95e6779..6512af83e 100644 --- a/lib/pangea/course_creation/course_plan_filter_widget.dart +++ b/lib/pangea/course_creation/course_plan_filter_widget.dart @@ -49,10 +49,7 @@ class CoursePlanFilterState extends State> { borderRadius: BorderRadius.circular(40.0), color: theme.colorScheme.surfaceContainerHighest, ), - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 12.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -61,9 +58,7 @@ class CoursePlanFilterState extends State> { ? widget.displayname(widget.value as T) : widget.defaultName, ), - const Icon( - Icons.arrow_drop_down, - ), + const Icon(Icons.arrow_drop_down), ], ), ), @@ -83,9 +78,7 @@ class CoursePlanFilterState extends State> { .toList(), onChanged: widget.onChanged, buttonStyleData: ButtonStyleData( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(40), - ), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(40)), ), dropdownStyleData: DropdownStyleData( elevation: 8, @@ -94,9 +87,7 @@ class CoursePlanFilterState extends State> { color: theme.colorScheme.surfaceContainerHigh, ), ), - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.zero, - ), + menuItemStyleData: const MenuItemStyleData(padding: EdgeInsets.zero), dropdownSearchData: widget.enableSearch ? DropdownSearchData( searchController: _searchController, diff --git a/lib/pangea/course_creation/course_plan_tile_widget.dart b/lib/pangea/course_creation/course_plan_tile_widget.dart index af39160fe..22a7c40f0 100644 --- a/lib/pangea/course_creation/course_plan_tile_widget.dart +++ b/lib/pangea/course_creation/course_plan_tile_widget.dart @@ -39,8 +39,9 @@ class CoursePlanTile extends StatelessWidget { onTap: onTap, child: Container( decoration: BoxDecoration( - color: - hovered ? theme.colorScheme.onSurface.withAlpha(10) : null, + color: hovered + ? theme.colorScheme.onSurface.withAlpha(10) + : null, ), child: Row( spacing: 4.0, @@ -68,9 +69,7 @@ class CoursePlanTile extends StatelessWidget { course.title, maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: titleFontSize, - ), + style: TextStyle(fontSize: titleFontSize), ), CourseInfoChips( courseId, diff --git a/lib/pangea/course_creation/public_course_preview.dart b/lib/pangea/course_creation/public_course_preview.dart index 15b5f3424..8a56b1b32 100644 --- a/lib/pangea/course_creation/public_course_preview.dart +++ b/lib/pangea/course_creation/public_course_preview.dart @@ -19,10 +19,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class PublicCoursePreview extends StatefulWidget { final String? roomID; - const PublicCoursePreview({ - super.key, - required this.roomID, - }); + const PublicCoursePreview({super.key, required this.roomID}); @override PublicCoursePreviewController createState() => @@ -110,10 +107,7 @@ class PublicCoursePreviewController extends State return; } - final roomId = await SpaceCodeController.joinSpaceWithCode( - context, - code, - ); + final roomId = await SpaceCodeController.joinSpaceWithCode(context, code); if (roomId != null) { final room = Matrix.of(context).client.getRoomById(roomId); @@ -149,11 +143,7 @@ class PublicCoursePreviewController extends State ? await client.knockRoom(widget.roomID!) : await client.joinRoom(widget.roomID!); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'roomID': widget.roomID}, - ); + ErrorHandler.logError(e: e, s: s, data: {'roomID': widget.roomID}); rethrow; } diff --git a/lib/pangea/course_creation/public_course_preview_view.dart b/lib/pangea/course_creation/public_course_preview_view.dart index 213560f07..f9d991ee6 100644 --- a/lib/pangea/course_creation/public_course_preview_view.dart +++ b/lib/pangea/course_creation/public_course_preview_view.dart @@ -16,10 +16,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class PublicCoursePreviewView extends StatelessWidget { final PublicCoursePreviewController controller; - const PublicCoursePreviewView( - this.controller, { - super.key, - }); + const PublicCoursePreviewView(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -32,9 +29,7 @@ class PublicCoursePreviewView extends StatelessWidget { const double smallIconSize = 12.0; return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).joinWithClassCode), - ), + appBar: AppBar(title: Text(L10n.of(context).joinWithClassCode)), body: SafeArea( child: Container( alignment: Alignment.topCenter, @@ -122,10 +117,10 @@ class PublicCoursePreviewView extends StatelessWidget { ), CourseInfoChip( icon: Icons.person, - text: - L10n.of(context).countParticipants( - summary.membershipSummary.length, - ), + text: L10n.of(context) + .countParticipants( + summary.membershipSummary.length, + ), fontSize: descFontSize, iconSize: smallIconSize, ), @@ -211,10 +206,10 @@ class PublicCoursePreviewView extends StatelessWidget { ), ), Padding( - padding: const EdgeInsetsGeometry - .symmetric( - vertical: 2.0, - ), + padding: + const EdgeInsetsGeometry.symmetric( + vertical: 2.0, + ), child: Row( spacing: 8.0, mainAxisSize: MainAxisSize.min, @@ -265,13 +260,9 @@ class PublicCoursePreviewView extends StatelessWidget { Row( spacing: 8.0, children: [ - const Expanded( - child: Divider(), - ), + const Expanded(child: Divider()), Text(L10n.of(context).or), - const Expanded( - child: Divider(), - ), + const Expanded(child: Divider()), ], ), ], @@ -328,19 +319,14 @@ class _CourseAdminDisplay extends StatelessWidget { children: [ ...summary.adminUserIDs.map((adminId) { return FutureBuilder( - future: Matrix.of(context).client.getProfileFromUserId( - adminId, - ), + future: Matrix.of(context).client.getProfileFromUserId(adminId), builder: (context, snapshot) { final profile = snapshot.data; final displayName = profile?.displayName ?? adminId.localpart ?? adminId; return InkWell( onTap: profile != null - ? () => UserDialog.show( - context: context, - profile: profile, - ) + ? () => UserDialog.show(context: context, profile: profile) : null, child: Container( decoration: BoxDecoration( @@ -361,9 +347,7 @@ class _CourseAdminDisplay extends StatelessWidget { userId: adminId, ), ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 80.0, - ), + constraints: const BoxConstraints(maxWidth: 80.0), child: Text( displayName, style: TextStyle( diff --git a/lib/pangea/course_creation/selected_course_page.dart b/lib/pangea/course_creation/selected_course_page.dart index 86468890f..e6b33f165 100644 --- a/lib/pangea/course_creation/selected_course_page.dart +++ b/lib/pangea/course_creation/selected_course_page.dart @@ -25,12 +25,7 @@ class SelectedCourse extends StatefulWidget { /// In join mode, the ID of the space to join that already has this course. final String? spaceId; - const SelectedCourse( - this.courseId, - this.mode, { - super.key, - this.spaceId, - }); + const SelectedCourse(this.courseId, this.mode, {super.key, this.spaceId}); @override SelectedCourseController createState() => SelectedCourseController(); @@ -75,10 +70,7 @@ class SelectedCourseController extends State return; } - final roomId = await SpaceCodeController.joinSpaceWithCode( - context, - code, - ); + final roomId = await SpaceCodeController.joinSpaceWithCode(context, code); if (roomId != null) { final room = Matrix.of(context).client.getRoomById(roomId); @@ -97,10 +89,7 @@ class SelectedCourseController extends State } } - Future launchCourse( - String courseId, - CoursePlanModel course, - ) async { + Future launchCourse(String courseId, CoursePlanModel course) async { final client = Matrix.of(context).client; final Completer completer = Completer(); client @@ -112,9 +101,7 @@ class SelectedCourseController extends State initialState: [ sdk.StateEvent( type: PangeaEventTypes.coursePlan, - content: { - "uuid": courseId, - }, + content: {"uuid": courseId}, ), ], avatarUrl: course.imageUrl.toString(), @@ -123,10 +110,7 @@ class SelectedCourseController extends State .then((spaceId) => completer.complete(spaceId)) .catchError((error) => completer.completeError(error)); - context.go( - "/rooms/course/own/${widget.courseId}/invite", - extra: completer, - ); + context.go("/rooms/course/own/${widget.courseId}/invite", extra: completer); } Future addCourseToSpace(CoursePlanModel course) async { diff --git a/lib/pangea/course_creation/selected_course_view.dart b/lib/pangea/course_creation/selected_course_view.dart index 29d57d970..12159cf9a 100644 --- a/lib/pangea/course_creation/selected_course_view.dart +++ b/lib/pangea/course_creation/selected_course_view.dart @@ -12,10 +12,7 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; class SelectedCourseView extends StatelessWidget { final SelectedCourseController controller; - const SelectedCourseView( - this.controller, { - super.key, - }); + const SelectedCourseView(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -30,11 +27,7 @@ class SelectedCourseView extends StatelessWidget { final course = controller.course; return Scaffold( - appBar: AppBar( - title: Text( - controller.title, - ), - ), + appBar: AppBar(title: Text(controller.title)), body: SafeArea( child: Container( alignment: Alignment.topCenter, @@ -43,263 +36,253 @@ class SelectedCourseView extends StatelessWidget { child: controller.loadingCourse ? const Center(child: CircularProgressIndicator.adaptive()) : controller.courseError != null || course == null - ? Center( - child: ErrorIndicator( - message: L10n.of(context).oopsSomethingWentWrong, - ), - ) - : Column( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.only( - top: 12.0, - left: 12.0, - right: 12.0, - ), - child: ListView.builder( - itemCount: course.topicIds.length + 2, - itemBuilder: (context, index) { - final String displayname = course.title; + ? Center( + child: ErrorIndicator( + message: L10n.of(context).oopsSomethingWentWrong, + ), + ) + : Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only( + top: 12.0, + left: 12.0, + right: 12.0, + ), + child: ListView.builder( + itemCount: course.topicIds.length + 2, + itemBuilder: (context, index) { + final String displayname = course.title; - if (index == 0) { - return Column( - spacing: 8.0, - children: [ - ClipPath( - clipper: MapClipper(), - child: ImageByUrl( - imageUrl: course.imageUrl, - width: 100.0, - borderRadius: - BorderRadius.circular(0.0), - replacement: Avatar( - name: displayname, - size: 100.0, - borderRadius: - BorderRadius.circular(0.0), - ), + if (index == 0) { + return Column( + spacing: 8.0, + children: [ + ClipPath( + clipper: MapClipper(), + child: ImageByUrl( + imageUrl: course.imageUrl, + width: 100.0, + borderRadius: BorderRadius.circular( + 0.0, + ), + replacement: Avatar( + name: displayname, + size: 100.0, + borderRadius: BorderRadius.circular( + 0.0, ), ), - Text( - displayname, - style: const TextStyle( - fontSize: titleFontSize, - ), - ), - Text( - course.description, - style: const TextStyle( - fontSize: descFontSize, - ), - ), - CourseInfoChips( - controller.widget.courseId, - fontSize: descFontSize, - iconSize: smallIconSize, - ), - Padding( - padding: const EdgeInsets.only( - top: 4.0, - bottom: 8.0, - ), - child: Row( - spacing: 4.0, - children: [ - const Icon( - Icons.map, - size: largeIconSize, - ), - Text( - L10n.of(context).coursePlan, - style: const TextStyle( - fontSize: titleFontSize, - ), - ), - ], - ), - ), - ], - ); - } - - index--; - - if (index >= course.topicIds.length) { - return const SizedBox(height: 12.0); - } - - final topicId = course.topicIds[index]; - final topic = course.loadedTopics[topicId]; - - if (topic == null) { - return const SizedBox(); - } - - return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, + ), ), - child: Row( - spacing: 8.0, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - ClipPath( - clipper: PinClipper(), - child: ImageByUrl( - imageUrl: topic.imageUrl, - width: 45.0, - replacement: Container( - width: 45.0, - height: 45.0, - decoration: BoxDecoration( - color: - theme.colorScheme.secondary, - ), + Text( + displayname, + style: const TextStyle( + fontSize: titleFontSize, + ), + ), + Text( + course.description, + style: const TextStyle( + fontSize: descFontSize, + ), + ), + CourseInfoChips( + controller.widget.courseId, + fontSize: descFontSize, + iconSize: smallIconSize, + ), + Padding( + padding: const EdgeInsets.only( + top: 4.0, + bottom: 8.0, + ), + child: Row( + spacing: 4.0, + children: [ + const Icon( + Icons.map, + size: largeIconSize, + ), + Text( + L10n.of(context).coursePlan, + style: const TextStyle( + fontSize: titleFontSize, ), ), + ], + ), + ), + ], + ); + } + + index--; + + if (index >= course.topicIds.length) { + return const SizedBox(height: 12.0); + } + + final topicId = course.topicIds[index]; + final topic = course.loadedTopics[topicId]; + + if (topic == null) { + return const SizedBox(); + } + + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 4.0, + ), + child: Row( + spacing: 8.0, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipPath( + clipper: PinClipper(), + child: ImageByUrl( + imageUrl: topic.imageUrl, + width: 45.0, + replacement: Container( + width: 45.0, + height: 45.0, + decoration: BoxDecoration( + color: theme.colorScheme.secondary, + ), ), - Flexible( - child: Column( - spacing: 4.0, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - topic.title, - style: const TextStyle( - fontSize: titleFontSize, - ), - ), - Text( - topic.description, - style: const TextStyle( - fontSize: descFontSize, - ), - ), - Padding( - padding: - const EdgeInsetsGeometry - .symmetric( + ), + ), + Flexible( + child: Column( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + topic.title, + style: const TextStyle( + fontSize: titleFontSize, + ), + ), + Text( + topic.description, + style: const TextStyle( + fontSize: descFontSize, + ), + ), + Padding( + padding: + const EdgeInsetsGeometry.symmetric( vertical: 2.0, ), - child: Row( - spacing: 8.0, - mainAxisSize: - MainAxisSize.min, - children: [ - if (topic.location != null) - CourseInfoChip( - icon: Icons.location_on, - text: topic.location!, - fontSize: descFontSize, - iconSize: smallIconSize, - ), - ], - ), - ), - ], + child: Row( + spacing: 8.0, + mainAxisSize: MainAxisSize.min, + children: [ + if (topic.location != null) + CourseInfoChip( + icon: Icons.location_on, + text: topic.location!, + fontSize: descFontSize, + iconSize: smallIconSize, + ), + ], + ), ), - ), - ], + ], + ), ), - ); - }, - ), + ], + ), + ); + }, + ), + ), + ), + Container( + decoration: BoxDecoration( + color: theme.colorScheme.surface, + border: Border( + top: BorderSide( + color: theme.dividerColor, + width: 1.0, ), ), - Container( - decoration: BoxDecoration( - color: theme.colorScheme.surface, - border: Border( - top: BorderSide( - color: theme.dividerColor, - width: 1.0, - ), - ), - ), - padding: const EdgeInsets.all(12.0), - child: Column( - spacing: 8.0, - mainAxisSize: MainAxisSize.min, + ), + padding: const EdgeInsets.all(12.0), + child: Column( + spacing: 8.0, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + spacing: 12.0, children: [ - Row( - spacing: 12.0, - children: [ - const Icon( - Icons.edit, - size: mediumIconSize, + const Icon(Icons.edit, size: mediumIconSize), + Flexible( + child: Text( + L10n.of(context).editCourseLater, + style: const TextStyle( + fontSize: descFontSize, ), - Flexible( - child: Text( - L10n.of(context).editCourseLater, - style: const TextStyle( - fontSize: descFontSize, - ), - ), - ), - ], - ), - Row( - spacing: 12.0, - children: [ - const Icon( - Icons.shield, - size: mediumIconSize, - ), - Flexible( - child: Text( - L10n.of(context).newCourseAccess, - style: const TextStyle( - fontSize: descFontSize, - ), - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Column( - spacing: 8.0, - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: theme - .colorScheme.primaryContainer, - foregroundColor: theme - .colorScheme.onPrimaryContainer, - ), - onPressed: () => - showFutureLoadingDialog( - context: context, - future: () => - controller.submit(course), - ), - child: Row( - spacing: 8.0, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Icon(Icons.map_outlined), - Text( - controller.buttonText, - style: const TextStyle( - fontSize: titleFontSize, - ), - ), - ], - ), - ), - ], ), ), ], ), - ), - ], + Row( + spacing: 12.0, + children: [ + const Icon(Icons.shield, size: mediumIconSize), + Flexible( + child: Text( + L10n.of(context).newCourseAccess, + style: const TextStyle( + fontSize: descFontSize, + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Column( + spacing: 8.0, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + theme.colorScheme.primaryContainer, + foregroundColor: + theme.colorScheme.onPrimaryContainer, + ), + onPressed: () => showFutureLoadingDialog( + context: context, + future: () => controller.submit(course), + ), + child: Row( + spacing: 8.0, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + const Icon(Icons.map_outlined), + Text( + controller.buttonText, + style: const TextStyle( + fontSize: titleFontSize, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), ), + ], + ), ), ), ), diff --git a/lib/pangea/course_plans/course_activities/activity_summaries_provider.dart b/lib/pangea/course_plans/course_activities/activity_summaries_provider.dart index daf7be1a1..2780905b1 100644 --- a/lib/pangea/course_plans/course_activities/activity_summaries_provider.dart +++ b/lib/pangea/course_plans/course_activities/activity_summaries_provider.dart @@ -77,15 +77,14 @@ mixin ActivitySummariesProvider on State { Map activitySessions(String activityId) => Map.fromEntries( - roomSummaries?.entries - .where((v) => v.value.activityPlan?.activityId == activityId) ?? + roomSummaries?.entries.where( + (v) => v.value.activityPlan?.activityId == activityId, + ) ?? [], ); Map> - activitySessionStatuses( - String activityId, - ) { + activitySessionStatuses(String activityId) { final statuses = >{ ActivitySummaryStatus.notStarted: {}, ActivitySummaryStatus.inProgress: {}, @@ -145,10 +144,7 @@ mixin ActivitySummariesProvider on State { .toSet(); } - bool hasCompletedActivity( - String userID, - String activityID, - ) { + bool hasCompletedActivity(String userID, String activityID) { final completed = _completedActivities(userID); return completed.contains(activityID); } @@ -159,8 +155,9 @@ mixin ActivitySummariesProvider on State { int? activitiesToCompleteOverride, ) { final topicActivityIds = topic.activityIds.toSet(); - final completedTopicActivities = - _completedActivities(userID).intersection(topicActivityIds); + final completedTopicActivities = _completedActivities( + userID, + ).intersection(topicActivityIds); if (completedTopicActivities.length >= topicActivityIds.length) { return true; @@ -192,11 +189,7 @@ mixin ActivitySummariesProvider on State { if (topic == null) continue; if (!topic.activityListComplete) continue; - if (!_hasCompletedTopic( - userID, - topic, - activitiesToCompleteOverride, - ) && + if (!_hasCompletedTopic(userID, topic, activitiesToCompleteOverride) && topic.activityIds.isNotEmpty) { return topicId; } diff --git a/lib/pangea/course_plans/course_activities/course_activity_repo.dart b/lib/pangea/course_plans/course_activities/course_activity_repo.dart index d0e6175df..c29657a35 100644 --- a/lib/pangea/course_plans/course_activities/course_activity_repo.dart +++ b/lib/pangea/course_plans/course_activities/course_activity_repo.dart @@ -22,8 +22,9 @@ class CourseActivityRepo { await _storage.initStorage; final activities = getCached(request).plans; - final toFetch = - request.activityIds.where((id) => !activities.containsKey(id)).toList(); + final toFetch = request.activityIds + .where((id) => !activities.containsKey(id)) + .toList(); if (toFetch.isNotEmpty) { final fetchedActivities = await _fetch(request, batchId); @@ -82,9 +83,7 @@ class CourseActivityRepo { } } - static TranslateActivityResponse getCached( - TranslateActivityRequest request, - ) { + static TranslateActivityResponse getCached(TranslateActivityRequest request) { final Map activities = {}; for (final id in request.activityIds) { final cacheKey = "${id}_${request.l1}"; @@ -142,10 +141,7 @@ class CourseActivityRepo { return {}; } - static Future setSentFeedback( - String activityId, - String l1, - ) async { + static Future setSentFeedback(String activityId, String l1) async { final currentValue = sentFeedback; currentValue["${activityId}_$l1"] = DateTime.now(); await _storage.write( @@ -154,10 +150,7 @@ class CourseActivityRepo { ); } - static Future _clearSentFeedback( - String activityId, - String l1, - ) async { + static Future _clearSentFeedback(String activityId, String l1) async { final currentValue = sentFeedback; currentValue.remove("${activityId}_$l1"); await _storage.write( diff --git a/lib/pangea/course_plans/course_activities/course_activity_translation_request.dart b/lib/pangea/course_plans/course_activities/course_activity_translation_request.dart index 0f6cf2cf2..a7af6e29c 100644 --- a/lib/pangea/course_plans/course_activities/course_activity_translation_request.dart +++ b/lib/pangea/course_plans/course_activities/course_activity_translation_request.dart @@ -2,15 +2,9 @@ class TranslateActivityRequest { List activityIds; String l1; - TranslateActivityRequest({ - required this.activityIds, - required this.l1, - }); + TranslateActivityRequest({required this.activityIds, required this.l1}); - Map toJson() => { - "activity_ids": activityIds, - "l1": l1, - }; + Map toJson() => {"activity_ids": activityIds, "l1": l1}; factory TranslateActivityRequest.fromJson(Map json) { return TranslateActivityRequest( diff --git a/lib/pangea/course_plans/course_activities/course_activity_translation_response.dart b/lib/pangea/course_plans/course_activities/course_activity_translation_response.dart index ac1e995aa..43de19435 100644 --- a/lib/pangea/course_plans/course_activities/course_activity_translation_response.dart +++ b/lib/pangea/course_plans/course_activities/course_activity_translation_response.dart @@ -8,18 +8,13 @@ class TranslateActivityResponse { factory TranslateActivityResponse.fromJson(Map json) { final plansEntry = json['plans'] as Map; return TranslateActivityResponse( - plans: plansEntry.map( - (key, value) { - return MapEntry( - key, - ActivityPlanModel.fromJson(value), - ); - }, - ), + plans: plansEntry.map((key, value) { + return MapEntry(key, ActivityPlanModel.fromJson(value)); + }), ); } Map toJson() => { - "plans": plans.map((key, value) => MapEntry(key, value.toJson())), - }; + "plans": plans.map((key, value) => MapEntry(key, value.toJson())), + }; } diff --git a/lib/pangea/course_plans/course_info_batch_request.dart b/lib/pangea/course_plans/course_info_batch_request.dart index 9aecf6c4e..ecab1f9ed 100644 --- a/lib/pangea/course_plans/course_info_batch_request.dart +++ b/lib/pangea/course_plans/course_info_batch_request.dart @@ -2,8 +2,5 @@ class CourseInfoBatchRequest { final String batchId; final List uuids; - CourseInfoBatchRequest({ - required this.batchId, - required this.uuids, - }); + CourseInfoBatchRequest({required this.batchId, required this.uuids}); } diff --git a/lib/pangea/course_plans/course_locations/course_location_media_repo.dart b/lib/pangea/course_plans/course_locations/course_location_media_repo.dart index 28c78e5dd..5fc87ff3c 100644 --- a/lib/pangea/course_plans/course_locations/course_location_media_repo.dart +++ b/lib/pangea/course_plans/course_locations/course_location_media_repo.dart @@ -12,8 +12,9 @@ import 'package:fluffychat/widgets/matrix.dart'; class CourseLocationMediaRepo { static final Map> _cache = {}; - static final GetStorage _storage = - GetStorage('course_location_media_storage'); + static final GetStorage _storage = GetStorage( + 'course_location_media_storage', + ); static Future get( CourseInfoBatchRequest request, @@ -27,10 +28,7 @@ class CourseLocationMediaRepo { if (toFetch.isNotEmpty) { final fetched = await _fetch( - CourseInfoBatchRequest( - batchId: request.batchId, - uuids: toFetch, - ), + CourseInfoBatchRequest(batchId: request.batchId, uuids: toFetch), ); urls.addAll(fetched.mediaUrls); @@ -82,20 +80,13 @@ class CourseLocationMediaRepo { } } - static CourseLocationMediaResponse getCached( - CourseInfoBatchRequest request, - ) { + static CourseLocationMediaResponse getCached(CourseInfoBatchRequest request) { final List urls = []; for (final uuid in request.uuids) { try { final url = _storage.read(uuid) as String?; if (url != null) { - urls.add( - CourseMediaInfo( - uuid: uuid, - url: url, - ), - ); + urls.add(CourseMediaInfo(uuid: uuid, url: url)); } } catch (e) { // If parsing fails, remove the corrupted cache entry diff --git a/lib/pangea/course_plans/course_locations/course_location_media_response.dart b/lib/pangea/course_plans/course_locations/course_location_media_response.dart index 2eff7b9d5..a884c666d 100644 --- a/lib/pangea/course_plans/course_locations/course_location_media_response.dart +++ b/lib/pangea/course_plans/course_locations/course_location_media_response.dart @@ -5,23 +5,16 @@ import 'package:fluffychat/pangea/payload_client/paginated_response.dart'; class CourseLocationMediaResponse { final List mediaUrls; - CourseLocationMediaResponse({ - required this.mediaUrls, - }); + CourseLocationMediaResponse({required this.mediaUrls}); factory CourseLocationMediaResponse.fromCmsResponse( PayloadPaginatedResponse - cmsCoursePlanTopicLocationMediasResult, + cmsCoursePlanTopicLocationMediasResult, ) { return CourseLocationMediaResponse( mediaUrls: cmsCoursePlanTopicLocationMediasResult.docs .where((e) => e.url != null) - .map( - (e) => CourseMediaInfo( - uuid: e.id, - url: e.url!, - ), - ) + .map((e) => CourseMediaInfo(uuid: e.id, url: e.url!)) .toList(), ); } diff --git a/lib/pangea/course_plans/course_locations/course_location_model.dart b/lib/pangea/course_plans/course_locations/course_location_model.dart index ad931a6c9..1845d8ebe 100644 --- a/lib/pangea/course_plans/course_locations/course_location_model.dart +++ b/lib/pangea/course_plans/course_locations/course_location_model.dart @@ -13,7 +13,8 @@ class CourseLocationModel { return CourseLocationModel( uuid: json['uuid'] as String, name: json['name'] as String, - mediaIds: (json['media_ids'] as List?) + mediaIds: + (json['media_ids'] as List?) ?.map((e) => e as String) .toList() ?? [], @@ -21,10 +22,6 @@ class CourseLocationModel { } Map toJson() { - return { - 'uuid': uuid, - 'name': name, - 'media_ids': mediaIds, - }; + return {'uuid': uuid, 'name': name, 'media_ids': mediaIds}; } } diff --git a/lib/pangea/course_plans/course_locations/course_location_repo.dart b/lib/pangea/course_plans/course_locations/course_location_repo.dart index 6931ddff1..c19e037db 100644 --- a/lib/pangea/course_plans/course_locations/course_location_repo.dart +++ b/lib/pangea/course_plans/course_locations/course_location_repo.dart @@ -28,10 +28,7 @@ class CourseLocationRepo { if (toFetch.isNotEmpty) { final fetchedLocations = await _fetch( - CourseInfoBatchRequest( - batchId: request.batchId, - uuids: toFetch, - ), + CourseInfoBatchRequest(batchId: request.batchId, uuids: toFetch), ); locations.addAll(fetchedLocations.locations); await _setCached(fetchedLocations); @@ -83,16 +80,15 @@ class CourseLocationRepo { } } - static CourseLocationResponse getCached( - CourseInfoBatchRequest request, - ) { + static CourseLocationResponse getCached(CourseInfoBatchRequest request) { final List locations = []; for (final uuid in request.uuids) { final json = _storage.read(uuid); if (json != null) { try { - final location = - CourseLocationModel.fromJson(Map.from(json)); + final location = CourseLocationModel.fromJson( + Map.from(json), + ); locations.add(location); } catch (e) { // If parsing fails, remove the corrupted cache entry diff --git a/lib/pangea/course_plans/course_locations/course_location_response.dart b/lib/pangea/course_plans/course_locations/course_location_response.dart index ba177d8ed..51546cc45 100644 --- a/lib/pangea/course_plans/course_locations/course_location_response.dart +++ b/lib/pangea/course_plans/course_locations/course_location_response.dart @@ -5,9 +5,7 @@ import 'package:fluffychat/pangea/payload_client/paginated_response.dart'; class CourseLocationResponse { final List locations; - CourseLocationResponse({ - required this.locations, - }); + CourseLocationResponse({required this.locations}); factory CourseLocationResponse.fromCmsResponse( PayloadPaginatedResponse response, diff --git a/lib/pangea/course_plans/course_media/course_media_info.dart b/lib/pangea/course_plans/course_media/course_media_info.dart index 7556e66b4..e1e342e53 100644 --- a/lib/pangea/course_plans/course_media/course_media_info.dart +++ b/lib/pangea/course_plans/course_media/course_media_info.dart @@ -2,8 +2,5 @@ class CourseMediaInfo { final String uuid; final String url; - CourseMediaInfo({ - required this.uuid, - required this.url, - }); + CourseMediaInfo({required this.uuid, required this.url}); } diff --git a/lib/pangea/course_plans/course_media/course_media_repo.dart b/lib/pangea/course_plans/course_media/course_media_repo.dart index a2d84546f..90b59940a 100644 --- a/lib/pangea/course_plans/course_media/course_media_repo.dart +++ b/lib/pangea/course_plans/course_media/course_media_repo.dart @@ -14,9 +14,7 @@ class CourseMediaRepo { static final Map> _cache = {}; static final GetStorage _storage = GetStorage('course_media_storage'); - static Future get( - CourseInfoBatchRequest request, - ) async { + static Future get(CourseInfoBatchRequest request) async { final urls = []; await _storage.initStorage; @@ -28,10 +26,7 @@ class CourseMediaRepo { if (toFetch.isNotEmpty) { final fetchedUrls = await _fetch( - CourseInfoBatchRequest( - batchId: request.batchId, - uuids: toFetch, - ), + CourseInfoBatchRequest(batchId: request.batchId, uuids: toFetch), ); urls.addAll(fetchedUrls.mediaUrls); await _setCached(fetchedUrls); @@ -93,12 +88,7 @@ class CourseMediaRepo { for (final uuid in request.uuids) { final cached = _storage.read(uuid); if (cached != null && cached is String) { - urls.add( - CourseMediaInfo( - uuid: uuid, - url: cached, - ), - ); + urls.add(CourseMediaInfo(uuid: uuid, url: cached)); } } diff --git a/lib/pangea/course_plans/course_media/course_media_response.dart b/lib/pangea/course_plans/course_media/course_media_response.dart index ed595f389..89731a5f9 100644 --- a/lib/pangea/course_plans/course_media/course_media_response.dart +++ b/lib/pangea/course_plans/course_media/course_media_response.dart @@ -5,9 +5,7 @@ import 'package:fluffychat/pangea/payload_client/paginated_response.dart'; class CourseMediaResponse { final List mediaUrls; - CourseMediaResponse({ - required this.mediaUrls, - }); + CourseMediaResponse({required this.mediaUrls}); factory CourseMediaResponse.fromCmsResponse( PayloadPaginatedResponse response, @@ -15,12 +13,7 @@ class CourseMediaResponse { return CourseMediaResponse( mediaUrls: response.docs .where((e) => e.url != null) - .map( - (e) => CourseMediaInfo( - uuid: e.id, - url: e.url!, - ), - ) + .map((e) => CourseMediaInfo(uuid: e.id, url: e.url!)) .toList(), ); } diff --git a/lib/pangea/course_plans/course_topics/course_topic_model.dart b/lib/pangea/course_plans/course_topics/course_topic_model.dart index aa2f3434d..6920eb67e 100644 --- a/lib/pangea/course_plans/course_topics/course_topic_model.dart +++ b/lib/pangea/course_plans/course_topics/course_topic_model.dart @@ -30,17 +30,11 @@ class CourseTopicModel { locationIds.length == loadedLocations.locations.length; CourseLocationResponse get loadedLocations => CourseLocationRepo.getCached( - CourseInfoBatchRequest( - batchId: uuid, - uuids: locationIds, - ), - ); + CourseInfoBatchRequest(batchId: uuid, uuids: locationIds), + ); Future _fetchLocations() => CourseLocationRepo.get( - CourseInfoBatchRequest( - batchId: uuid, - uuids: locationIds, - ), - ); + CourseInfoBatchRequest(batchId: uuid, uuids: locationIds), + ); String? get location => loadedLocations.locations.firstOrNull?.name; @@ -53,10 +47,7 @@ class CourseTopicModel { List get loadedLocationMediaIds => loadedLocations.locations .map( (location) => CourseLocationMediaRepo.getCached( - CourseInfoBatchRequest( - batchId: uuid, - uuids: location.mediaIds, - ), + CourseInfoBatchRequest(batchId: uuid, uuids: location.mediaIds), ).mediaUrls, ) .expand((e) => e) @@ -68,10 +59,7 @@ class CourseTopicModel { final locationResp = await _fetchLocations(); for (final location in locationResp.locations) { final mediaResp = await CourseLocationMediaRepo.get( - CourseInfoBatchRequest( - batchId: uuid, - uuids: location.mediaIds, - ), + CourseInfoBatchRequest(batchId: uuid, uuids: location.mediaIds), ); allLocationMedia.addAll(mediaResp.mediaUrls.map((e) => e.url)); @@ -110,10 +98,10 @@ class CourseTopicModel { factory CourseTopicModel.fromJson(Map json) { final List? activityIdsEntry = json['activity_ids'] as List? ?? - json['activityIds'] as List?; + json['activityIds'] as List?; final List? locationIdsEntry = json['location_ids'] as List? ?? - json['locationIds'] as List?; + json['locationIds'] as List?; return CourseTopicModel( title: json['title'] as String, diff --git a/lib/pangea/course_plans/course_topics/course_topic_repo.dart b/lib/pangea/course_plans/course_topics/course_topic_repo.dart index 1b0a152bc..b7fd22289 100644 --- a/lib/pangea/course_plans/course_topics/course_topic_repo.dart +++ b/lib/pangea/course_plans/course_topics/course_topic_repo.dart @@ -22,22 +22,17 @@ class CourseTopicRepo { await _storage.initStorage; final topics = getCached(request).topics; - final toFetch = - request.topicIds.where((uuid) => !topics.containsKey(uuid)).toList(); + final toFetch = request.topicIds + .where((uuid) => !topics.containsKey(uuid)) + .toList(); if (toFetch.isNotEmpty) { final fetchedTopics = await _fetch( - TranslateTopicRequest( - topicIds: toFetch, - l1: request.l1, - ), + TranslateTopicRequest(topicIds: toFetch, l1: request.l1), batchId, ); topics.addAll(fetchedTopics.topics); - await _setCached( - fetchedTopics, - request.l1, - ); + await _setCached(fetchedTopics, request.l1); } return TranslateTopicResponse(topics: topics); @@ -91,9 +86,7 @@ class CourseTopicRepo { } } - static TranslateTopicResponse getCached( - TranslateTopicRequest request, - ) { + static TranslateTopicResponse getCached(TranslateTopicRequest request) { final Map topics = {}; for (final uuid in request.topicIds) { final cacheKey = "${uuid}_${request.l1}"; @@ -119,12 +112,7 @@ class CourseTopicRepo { ) async { final List futures = []; for (final entry in response.topics.entries) { - futures.add( - _storage.write( - "${entry.key}_$l1", - entry.value.toJson(), - ), - ); + futures.add(_storage.write("${entry.key}_$l1", entry.value.toJson())); } await Future.wait(futures); } diff --git a/lib/pangea/course_plans/course_topics/course_topic_translation_request.dart b/lib/pangea/course_plans/course_topics/course_topic_translation_request.dart index a2a7fd1ea..9061b4780 100644 --- a/lib/pangea/course_plans/course_topics/course_topic_translation_request.dart +++ b/lib/pangea/course_plans/course_topics/course_topic_translation_request.dart @@ -2,20 +2,15 @@ class TranslateTopicRequest { List topicIds; String l1; - TranslateTopicRequest({ - required this.topicIds, - required this.l1, - }); + TranslateTopicRequest({required this.topicIds, required this.l1}); - Map toJson() => { - "topic_ids": topicIds, - "l1": l1, - }; + Map toJson() => {"topic_ids": topicIds, "l1": l1}; factory TranslateTopicRequest.fromJson(Map json) { return TranslateTopicRequest( - topicIds: - json['topic_ids'] != null ? List.from(json['topic_ids']) : [], + topicIds: json['topic_ids'] != null + ? List.from(json['topic_ids']) + : [], l1: json['l1'], ); } diff --git a/lib/pangea/course_plans/course_topics/course_topic_translation_response.dart b/lib/pangea/course_plans/course_topics/course_topic_translation_response.dart index 68b7a3829..d4aa5e564 100644 --- a/lib/pangea/course_plans/course_topics/course_topic_translation_response.dart +++ b/lib/pangea/course_plans/course_topics/course_topic_translation_response.dart @@ -9,15 +9,12 @@ class TranslateTopicResponse { final topicsEntry = json['topics'] as Map; return TranslateTopicResponse( topics: topicsEntry.map( - (key, value) => MapEntry( - key, - CourseTopicModel.fromJson(value), - ), + (key, value) => MapEntry(key, CourseTopicModel.fromJson(value)), ), ); } Map toJson() => { - "topics": topics.map((key, value) => MapEntry(key, value.toJson())), - }; + "topics": topics.map((key, value) => MapEntry(key, value.toJson())), + }; } diff --git a/lib/pangea/course_plans/courses/course_filter.dart b/lib/pangea/course_plans/courses/course_filter.dart index d34a3ce85..178f212ad 100644 --- a/lib/pangea/course_plans/courses/course_filter.dart +++ b/lib/pangea/course_plans/courses/course_filter.dart @@ -33,9 +33,7 @@ class CourseFilter { } if (languageOfInstructions != null) { where["and"].add({ - "l1": { - "equals": languageOfInstructions!.langCodeShort, - }, + "l1": {"equals": languageOfInstructions!.langCodeShort}, }); } if (targetLanguage != null) { @@ -48,9 +46,7 @@ class CourseFilter { where["cefrLevel"] = {"equals": cefrLevel!.string}; } if (languageOfInstructions != null) { - where["l1"] = { - "equals": languageOfInstructions!.langCodeShort, - }; + where["l1"] = {"equals": languageOfInstructions!.langCodeShort}; } if (targetLanguage != null) { where["l2"] = {"equals": targetLanguage!.langCodeShort}; diff --git a/lib/pangea/course_plans/courses/course_plan_builder.dart b/lib/pangea/course_plans/courses/course_plan_builder.dart index 3f4310328..b08ca0ee2 100644 --- a/lib/pangea/course_plans/courses/course_plan_builder.dart +++ b/lib/pangea/course_plans/courses/course_plan_builder.dart @@ -49,11 +49,7 @@ mixin CoursePlanProvider on State { ); await course!.fetchMediaUrls(); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'courseId': courseId}, - ); + ErrorHandler.logError(e: e, s: s, data: {'courseId': courseId}); courseError = e; } finally { if (mounted) setState(() => loadingCourse = false); diff --git a/lib/pangea/course_plans/courses/course_plan_client_extension.dart b/lib/pangea/course_plans/courses/course_plan_client_extension.dart index f24514ebd..602e52f88 100644 --- a/lib/pangea/course_plans/courses/course_plan_client_extension.dart +++ b/lib/pangea/course_plans/courses/course_plan_client_extension.dart @@ -4,7 +4,6 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plan_room_extension.dart'; extension CoursePlanClientExtension on Client { - Room? getRoomByCourseId(String courseId) => rooms.firstWhereOrNull( - (room) => room.coursePlan?.uuid == courseId, - ); + Room? getRoomByCourseId(String courseId) => + rooms.firstWhereOrNull((room) => room.coursePlan?.uuid == courseId); } diff --git a/lib/pangea/course_plans/courses/course_plan_event.dart b/lib/pangea/course_plans/courses/course_plan_event.dart index 116d28c16..10c1693aa 100644 --- a/lib/pangea/course_plans/courses/course_plan_event.dart +++ b/lib/pangea/course_plans/courses/course_plan_event.dart @@ -4,14 +4,10 @@ class CoursePlanEvent { CoursePlanEvent({required this.uuid}); Map toJson() { - return { - 'uuid': uuid, - }; + return {'uuid': uuid}; } factory CoursePlanEvent.fromJson(Map json) { - return CoursePlanEvent( - uuid: json['uuid'] as String, - ); + return CoursePlanEvent(uuid: json['uuid'] as String); } } diff --git a/lib/pangea/course_plans/courses/course_plan_model.dart b/lib/pangea/course_plans/courses/course_plan_model.dart index f944739ab..ee46a6ffc 100644 --- a/lib/pangea/course_plans/courses/course_plan_model.dart +++ b/lib/pangea/course_plans/courses/course_plan_model.dart @@ -61,11 +61,13 @@ class CoursePlanModel { title: json['title'] as String, description: json['description'] as String, uuid: json['uuid'] as String, - topicIds: (json['topic_ids'] as List?) + topicIds: + (json['topic_ids'] as List?) ?.map((e) => e as String) .toList() ?? [], - mediaIds: (json['media_ids'] as List?) + mediaIds: + (json['media_ids'] as List?) ?.map((e) => e as String) .toList() ?? [], @@ -93,11 +95,11 @@ class CoursePlanModel { bool get topicListComplete => topicIds.length == loadedTopics.length; Map get loadedTopics => CourseTopicRepo.getCached( - TranslateTopicRequest( - topicIds: topicIds, - l1: MatrixState.pangeaController.userController.userL1Code!, - ), - ).topics; + TranslateTopicRequest( + topicIds: topicIds, + l1: MatrixState.pangeaController.userController.userL1Code!, + ), + ).topics; Set get activityIDs => loadedTopics.values.expand((topic) => topic.activityIds).toSet(); @@ -116,21 +118,15 @@ class CoursePlanModel { bool get mediaListComplete => mediaIds.length == loadedMediaUrls.mediaUrls.length; CourseMediaResponse get loadedMediaUrls => CourseMediaRepo.getCached( - CourseInfoBatchRequest( - batchId: uuid, - uuids: mediaIds, - ), - ); + CourseInfoBatchRequest(batchId: uuid, uuids: mediaIds), + ); Future fetchMediaUrls() => CourseMediaRepo.get( - CourseInfoBatchRequest( - batchId: uuid, - uuids: mediaIds, - ), - ); + CourseInfoBatchRequest(batchId: uuid, uuids: mediaIds), + ); Uri? get imageUrl => loadedMediaUrls.mediaUrls.isEmpty ? loadedTopics.values - .lastWhereOrNull((topic) => topic.imageUrl != null) - ?.imageUrl + .lastWhereOrNull((topic) => topic.imageUrl != null) + ?.imageUrl : Uri.tryParse( "${Environment.cmsApi}${loadedMediaUrls.mediaUrls.first.url}", ); diff --git a/lib/pangea/course_plans/courses/course_plan_room_extension.dart b/lib/pangea/course_plans/courses/course_plan_room_extension.dart index 5c7207d06..99727f1ef 100644 --- a/lib/pangea/course_plans/courses/course_plan_room_extension.dart +++ b/lib/pangea/course_plans/courses/course_plan_room_extension.dart @@ -59,14 +59,9 @@ extension CoursePlanRoomExtension on Room { if (coursePlan?.uuid == courseId) return; final future = waitForRoomInSync(); - await client.setRoomStateWithKey( - id, - PangeaEventTypes.coursePlan, - "", - { - "uuid": courseId, - }, - ); + await client.setRoomStateWithKey(id, PangeaEventTypes.coursePlan, "", { + "uuid": courseId, + }); if (coursePlan?.uuid != courseId) { await future; } @@ -104,16 +99,11 @@ extension CoursePlanRoomExtension on Room { ), }).toJson(), ), - RoomDefaults.defaultPowerLevels( - client.userID!, - ), + RoomDefaults.defaultPowerLevels(client.userID!), await client.pangeaJoinRules( 'knock_restricted', allow: [ - { - "type": "m.room_membership", - "room_id": id, - } + {"type": "m.room_membership", "room_id": id}, ], ), ], @@ -153,9 +143,7 @@ extension CoursePlanRoomExtension on Room { return CourseChatsSettingsModel.fromJson(event.content); } - Future setCourseChatsSettings( - CourseChatsSettingsModel settings, - ) async { + Future setCourseChatsSettings(CourseChatsSettingsModel settings) async { await client.setRoomStateWithKey( id, PangeaEventTypes.courseChatList, @@ -165,8 +153,8 @@ extension CoursePlanRoomExtension on Room { } bool hasDefaultChat(CourseDefaultChatsEnum type) => pangeaSpaceChildren.any( - (r) => r.canonicalAlias.localpart?.startsWith(type.alias) == true, - ); + (r) => r.canonicalAlias.localpart?.startsWith(type.alias) == true, + ); bool dismissedDefaultChat(CourseDefaultChatsEnum type) { switch (type) { @@ -183,11 +171,14 @@ extension CoursePlanRoomExtension on Room { }) async { final random = Random(); final String uploadURL = switch (type) { - CourseDefaultChatsEnum.introductions => SpaceConstants - .introChatIcons[random.nextInt(SpaceConstants.introChatIcons.length)], + CourseDefaultChatsEnum.introductions => + SpaceConstants.introChatIcons[random.nextInt( + SpaceConstants.introChatIcons.length, + )], CourseDefaultChatsEnum.announcements => - SpaceConstants.announcementChatIcons[ - random.nextInt(SpaceConstants.announcementChatIcons.length)], + SpaceConstants.announcementChatIcons[random.nextInt( + SpaceConstants.announcementChatIcons.length, + )], }; final resp = await client.createRoom( @@ -197,18 +188,12 @@ extension CoursePlanRoomExtension on Room { roomAliasName: "${type.alias}_${id.localpart}_${DateTime.now().millisecondsSinceEpoch}", initialState: [ - StateEvent( - type: EventTypes.RoomAvatar, - content: {'url': uploadURL}, - ), + StateEvent(type: EventTypes.RoomAvatar, content: {'url': uploadURL}), type.powerLevels(client.userID!), await client.pangeaJoinRules( 'knock_restricted', allow: [ - { - "type": "m.room_membership", - "room_id": id, - } + {"type": "m.room_membership", "room_id": id}, ], ), ], diff --git a/lib/pangea/course_plans/courses/course_plans_repo.dart b/lib/pangea/course_plans/courses/course_plans_repo.dart index 0106046b3..ed28e9ae5 100644 --- a/lib/pangea/course_plans/courses/course_plans_repo.dart +++ b/lib/pangea/course_plans/courses/course_plans_repo.dart @@ -38,9 +38,7 @@ class CoursePlansRepo { // TODO: Currently just getting one course plan at a time // Should take advantage of batch fetching - static Future get( - GetLocalizedCoursesRequest request, - ) async { + static Future get(GetLocalizedCoursesRequest request) async { if (request.coursePlanIds.length != 1) { throw Exception("Get only supports single course plan ID"); } @@ -108,23 +106,15 @@ class CoursePlansRepo { await _courseStorage.initStorage; final missingIds = request.coursePlanIds - .where( - (id) => _courseStorage.read("${id}_${request.l1}") == null, - ) + .where((id) => _courseStorage.read("${id}_${request.l1}") == null) .toList(); if (missingIds.isNotEmpty) { final searchResult = await _fetch( - GetLocalizedCoursesRequest( - coursePlanIds: missingIds, - l1: request.l1, - ), + GetLocalizedCoursesRequest(coursePlanIds: missingIds, l1: request.l1), ); - await _setCachedBatch( - searchResult, - request.l1, - ); + await _setCachedBatch(searchResult, request.l1); } return _getCachedBatch(request); @@ -156,9 +146,7 @@ class CoursePlansRepo { ); } - static CoursePlanModel? _getCached( - GetLocalizedCoursesRequest request, - ) { + static CoursePlanModel? _getCached(GetLocalizedCoursesRequest request) { if (lastUpdated != null && DateTime.now().difference(lastUpdated!) > cacheDuration) { clearCache(); @@ -223,10 +211,7 @@ class CoursePlansRepo { } final cacheKey = "${courseId}_$l1"; - await _courseStorage.write( - cacheKey, - course.toJson(), - ); + await _courseStorage.write(cacheKey, course.toJson()); } static Future _setCachedBatch( @@ -242,10 +227,7 @@ class CoursePlansRepo { final List futures = response.coursePlans.entries.map((entry) { final cacheKey = "${entry.key}_$l1"; - return _courseStorage.write( - cacheKey, - entry.value.toJson(), - ); + return _courseStorage.write(cacheKey, entry.value.toJson()); }).toList(); await Future.wait(futures); diff --git a/lib/pangea/course_plans/courses/get_localized_courses_request.dart b/lib/pangea/course_plans/courses/get_localized_courses_request.dart index f29b1c848..c56d43bce 100644 --- a/lib/pangea/course_plans/courses/get_localized_courses_request.dart +++ b/lib/pangea/course_plans/courses/get_localized_courses_request.dart @@ -2,15 +2,9 @@ class GetLocalizedCoursesRequest { final List coursePlanIds; final String l1; - GetLocalizedCoursesRequest({ - required this.coursePlanIds, - required this.l1, - }); + GetLocalizedCoursesRequest({required this.coursePlanIds, required this.l1}); - Map toJson() => { - "course_plan_ids": coursePlanIds, - "l1": l1, - }; + Map toJson() => {"course_plan_ids": coursePlanIds, "l1": l1}; factory GetLocalizedCoursesRequest.fromJson(Map json) { return GetLocalizedCoursesRequest( diff --git a/lib/pangea/course_plans/courses/get_localized_courses_response.dart b/lib/pangea/course_plans/courses/get_localized_courses_response.dart index 52bad4d0b..4478a1088 100644 --- a/lib/pangea/course_plans/courses/get_localized_courses_response.dart +++ b/lib/pangea/course_plans/courses/get_localized_courses_response.dart @@ -9,20 +9,14 @@ class GetLocalizedCoursesResponse { final plansEntry = json['course_plans'] as Map; return GetLocalizedCoursesResponse( coursePlans: plansEntry.map( - (key, value) => MapEntry( - key, - CoursePlanModel.fromJson(value), - ), + (key, value) => MapEntry(key, CoursePlanModel.fromJson(value)), ), ); } Map toJson() => { - "course_plans": coursePlans.map( - (key, value) => MapEntry( - key, - value.toJson(), - ), - ), - }; + "course_plans": coursePlans.map( + (key, value) => MapEntry(key, value.toJson()), + ), + }; } diff --git a/lib/pangea/course_settings/course_settings.dart b/lib/pangea/course_settings/course_settings.dart index fb081c5b5..45da03d92 100644 --- a/lib/pangea/course_settings/course_settings.dart +++ b/lib/pangea/course_settings/course_settings.dart @@ -31,10 +31,7 @@ class CourseSettings extends StatelessWidget { // final String? courseId; final ChatDetailsController controller; - const CourseSettings({ - super.key, - required this.controller, - }); + const CourseSettings({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -42,8 +39,9 @@ class CourseSettings extends StatelessWidget { return const Center(child: CircularProgressIndicator.adaptive()); } - final room = - Matrix.of(context).client.getRoomById(controller.widget.roomId); + final room = Matrix.of( + context, + ).client.getRoomById(controller.widget.roomId); if (room == null || !room.isSpace) { return Center( child: Text( @@ -78,13 +76,16 @@ class CourseSettings extends StatelessWidget { ), ElevatedButton( style: ElevatedButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.primaryContainer, - foregroundColor: - Theme.of(context).colorScheme.onPrimaryContainer, + backgroundColor: Theme.of( + context, + ).colorScheme.primaryContainer, + foregroundColor: Theme.of( + context, + ).colorScheme.onPrimaryContainer, + ), + onPressed: () => context.go( + "/rooms/spaces/${controller.roomId}/addcourse", ), - onPressed: () => context - .go("/rooms/spaces/${controller.roomId}/addcourse"), child: Row( spacing: 8.0, mainAxisSize: MainAxisSize.min, @@ -221,15 +222,16 @@ class CourseSettings extends StatelessWidget { ), if (constraints.maxWidth < 700.0) Padding( - padding: const EdgeInsetsGeometry - .symmetric( - vertical: 4.0, - ), + padding: + const EdgeInsetsGeometry.symmetric( + vertical: 4.0, + ), child: TopicParticipantList( room: room, users: usersInTopic, - avatarSize: - isColumnMode ? 50.0 : 25.0, + avatarSize: isColumnMode + ? 50.0 + : 25.0, overlap: isColumnMode ? 20.0 : 8.0, ), ), @@ -256,22 +258,21 @@ class CourseSettings extends StatelessWidget { activityCount: topic.activityIds.length, ) : activityError != null - ? ErrorIndicator( - message: - L10n.of(context).oopsSomethingWentWrong, - ) - : topic.loadedActivities.isNotEmpty - ? SizedBox( - height: isColumnMode ? 290.0 : 210.0, - child: TopicActivitiesList( - room: room, - activities: topic.loadedActivities, - loading: controller.loadingCourseSummary, - hasCompletedActivity: - controller.hasCompletedActivity, - ), - ) - : const SizedBox(), + ? ErrorIndicator( + message: L10n.of(context).oopsSomethingWentWrong, + ) + : topic.loadedActivities.isNotEmpty + ? SizedBox( + height: isColumnMode ? 290.0 : 210.0, + child: TopicActivitiesList( + room: room, + activities: topic.loadedActivities, + loading: controller.loadingCourseSummary, + hasCompletedActivity: + controller.hasCompletedActivity, + ), + ) + : const SizedBox(), ], ), ), @@ -286,10 +287,7 @@ class CourseSettings extends StatelessWidget { class ActivityCardPlaceholder extends StatelessWidget { final int activityCount; - const ActivityCardPlaceholder({ - super.key, - required this.activityCount, - }); + const ActivityCardPlaceholder({super.key, required this.activityCount}); @override Widget build(BuildContext context) { diff --git a/lib/pangea/course_settings/teacher_mode_model.dart b/lib/pangea/course_settings/teacher_mode_model.dart index dea77e843..52342833b 100644 --- a/lib/pangea/course_settings/teacher_mode_model.dart +++ b/lib/pangea/course_settings/teacher_mode_model.dart @@ -2,15 +2,9 @@ class TeacherModeModel { final bool enabled; final int? activitiesToUnlockTopic; - const TeacherModeModel({ - required this.enabled, - this.activitiesToUnlockTopic, - }); + const TeacherModeModel({required this.enabled, this.activitiesToUnlockTopic}); - TeacherModeModel copyWith({ - bool? enabled, - int? activitiesToUnlockTopic, - }) { + TeacherModeModel copyWith({bool? enabled, int? activitiesToUnlockTopic}) { return TeacherModeModel( enabled: enabled ?? this.enabled, activitiesToUnlockTopic: @@ -19,9 +13,9 @@ class TeacherModeModel { } Map toJson() => { - 'enabled': enabled, - 'activities_to_unlock_topic': activitiesToUnlockTopic, - }; + 'enabled': enabled, + 'activities_to_unlock_topic': activitiesToUnlockTopic, + }; factory TeacherModeModel.fromJson(Map json) { return TeacherModeModel( diff --git a/lib/pangea/course_settings/topic_participant_list.dart b/lib/pangea/course_settings/topic_participant_list.dart index e0bee5c2a..444027323 100644 --- a/lib/pangea/course_settings/topic_participant_list.dart +++ b/lib/pangea/course_settings/topic_participant_list.dart @@ -61,8 +61,9 @@ class TopicParticipantList extends StatelessWidget { return Stack( children: users.take(maxVisible).mapIndexed((index, user) { final level = publicProfiles[user.id]; - final LinearGradient? gradient = - level != null ? index.leaderboardGradient : null; + final LinearGradient? gradient = level != null + ? index.leaderboardGradient + : null; return Positioned( left: index * (avatarSize - overlap), child: MouseRegion( @@ -86,10 +87,7 @@ class TopicParticipantList extends StatelessWidget { ), ) else - SizedBox( - height: avatarSize, - width: avatarSize, - ), + SizedBox(height: avatarSize, width: avatarSize), Center( child: Avatar( mxContent: user.avatarUrl, @@ -111,9 +109,7 @@ class TopicParticipantList extends StatelessWidget { if (users.length > maxVisible) Text( L10n.of(context).additionalParticipants(users.length - maxVisible), - style: const TextStyle( - fontSize: 12.0, - ), + style: const TextStyle(fontSize: 12.0), ), ], ); diff --git a/lib/pangea/download/download_file_util.dart b/lib/pangea/download/download_file_util.dart index 95b3111ea..e4f213768 100644 --- a/lib/pangea/download/download_file_util.dart +++ b/lib/pangea/download/download_file_util.dart @@ -7,7 +7,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:universal_html/html.dart' as webfile; -import 'package:fluffychat/pages/chat/recording_dialog.dart'; +import 'package:fluffychat/pages/chat/recording_view_model.dart'; import 'package:fluffychat/pangea/download/download_type_enum.dart'; class DownloadUtil { @@ -19,8 +19,8 @@ class DownloadUtil { if (kIsWeb) { final blob = webfile.Blob([contents], fileType.mimetype, 'native'); webfile.AnchorElement( - href: webfile.Url.createObjectUrlFromBlob(blob).toString(), - ) + href: webfile.Url.createObjectUrlFromBlob(blob).toString(), + ) ..setAttribute("download", filename) ..click(); return; diff --git a/lib/pangea/download/download_room_extension.dart b/lib/pangea/download/download_room_extension.dart index d9eae2236..392032dc7 100644 --- a/lib/pangea/download/download_room_extension.dart +++ b/lib/pangea/download/download_room_extension.dart @@ -40,23 +40,14 @@ class _EventDownloadInfo { class EmptyChatException implements Exception {} extension DownloadExtension on Room { - Future download( - DownloadType type, - BuildContext context, - ) async { + Future download(DownloadType type, BuildContext context) async { List allPangeaMessages; final List allEvents = await getAllEvents(); final TimelineChunk chunk = TimelineChunk(events: allEvents); - final Timeline timeline = Timeline( - room: this, - chunk: chunk, - ); + final Timeline timeline = Timeline(room: this, chunk: chunk); - allPangeaMessages = getPangeaMessageEvents( - allEvents, - timeline, - ); + allPangeaMessages = getPangeaMessageEvents(allEvents, timeline); if (allPangeaMessages.isEmpty) { throw EmptyChatException(); @@ -92,8 +83,9 @@ extension DownloadExtension on Room { .replaceAll(RegExp(r'[^A-Za-z0-9\s]'), "") .replaceAll(RegExp(r'\s+'), "-"); - final String timestamp = - DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now()); + final String timestamp = DateFormat( + 'yyyy-MM-dd-hh:mm:ss', + ).format(DateTime.now()); return "$roomName-$timestamp.${type.extension}"; } @@ -105,8 +97,9 @@ extension DownloadExtension on Room { String formattedInfo = ""; final l10n = L10n.of(context); for (final _EventDownloadInfo info in eventInfo) { - final String timestamp = - DateFormat('yyyy-MM-dd hh:mm:ss').format(info.timestamp); + final String timestamp = DateFormat( + 'yyyy-MM-dd hh:mm:ss', + ).format(info.timestamp); if (!info.usageAvailable) { formattedInfo += @@ -145,11 +138,12 @@ extension DownloadExtension on Room { l10n.sentMessage, l10n.taTooltip, l10n.gaTooltip, - ] + ], ]; for (final _EventDownloadInfo info in eventInfo) { - final String timestamp = - DateFormat('yyyy-MM-dd hh:mm:ss').format(info.timestamp); + final String timestamp = DateFormat( + 'yyyy-MM-dd hh:mm:ss', + ).format(info.timestamp); if (!info.usageAvailable) { csvData.add([ @@ -185,29 +179,43 @@ extension DownloadExtension on Room { final Sheet sheetObject = excel['Sheet1']; sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 0)) - .value = TextCellValue(l10n.sender); + .value = TextCellValue( + l10n.sender, + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: 0)) - .value = TextCellValue(l10n.time); + .value = TextCellValue( + l10n.time, + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 2, rowIndex: 0)) - .value = TextCellValue(l10n.originalMessage); + .value = TextCellValue( + l10n.originalMessage, + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: 0)) - .value = TextCellValue(l10n.sentMessage); + .value = TextCellValue( + l10n.sentMessage, + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 4, rowIndex: 0)) - .value = TextCellValue(l10n.taTooltip); + .value = TextCellValue( + l10n.taTooltip, + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 5, rowIndex: 0)) - .value = TextCellValue(l10n.gaTooltip); + .value = TextCellValue( + l10n.gaTooltip, + ); for (int i = 0; i < eventInfo.length; i++) { final _EventDownloadInfo info = eventInfo[i]; sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: i + 2)) - .value = TextCellValue(info.sender); + .value = TextCellValue( + info.sender, + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 1, rowIndex: i + 2)) .value = DateTimeCellValue( @@ -221,17 +229,25 @@ extension DownloadExtension on Room { sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 2, rowIndex: i + 2)) - .value = TextCellValue(info.originalMsg); + .value = TextCellValue( + info.originalMsg, + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: i + 2)) - .value = TextCellValue(info.sentMsg); + .value = TextCellValue( + info.sentMsg, + ); if (info.usageAvailable) { sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 4, rowIndex: i + 2)) - .value = TextCellValue(info.includedIT.toString()); + .value = TextCellValue( + info.includedIT.toString(), + ); sheetObject .cell(CellIndex.indexByColumnRow(columnIndex: 5, rowIndex: i + 2)) - .value = TextCellValue(info.includedIGC.toString()); + .value = TextCellValue( + info.includedIGC.toString(), + ); } } diff --git a/lib/pangea/events/constants/pangea_event_types.dart b/lib/pangea/events/constants/pangea_event_types.dart index 80fbd1323..252c4ac6e 100644 --- a/lib/pangea/events/constants/pangea_event_types.dart +++ b/lib/pangea/events/constants/pangea_event_types.dart @@ -17,6 +17,7 @@ class PangeaEventTypes { static const choreoRecord = "pangea.record"; static const representation = "pangea.representation"; static const sttTranslation = "pangea.stt_translation"; + static const textToSpeech = "pangea.text_to_speech"; // static const vocab = "p.vocab"; static const roomInfo = "pangea.roomtopic"; diff --git a/lib/pangea/events/event_wrappers/pangea_choreo_event.dart b/lib/pangea/events/event_wrappers/pangea_choreo_event.dart index cd661c0c4..20cc0085c 100644 --- a/lib/pangea/events/event_wrappers/pangea_choreo_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_choreo_event.dart @@ -15,9 +15,7 @@ class ChoreoEvent { ChoreoEvent({required this.event}) { if (event.type != PangeaEventTypes.choreoRecord) { - throw Exception( - "${event.type} should not be used to make a ChoreoEvent", - ); + throw Exception("${event.type} should not be used to make a ChoreoEvent"); } } @@ -27,13 +25,7 @@ class ChoreoEvent { return _content; } catch (err, s) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: err, - s: s, - data: { - "event": event.toJson(), - }, - ); + ErrorHandler.logError(e: err, s: s, data: {"event": event.toJson()}); return null; } } diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 67809bb92..6f930464f 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -50,9 +50,7 @@ class PangeaMessageEvent { debugger(when: kDebugMode); ErrorHandler.logError( m: "${event.type} should not be used to make a PangeaMessageEvent", - data: { - "event": event.toJson(), - }, + data: {"event": event.toJson()}, ); } _event = event; @@ -81,50 +79,40 @@ class PangeaMessageEvent { MatrixState.pangeaController.userController.userL1?.langCode; Event? _latestEditCache; - Event get _latestEdit => _latestEditCache ??= _event - .aggregatedEvents( - timeline, - RelationshipTypes.edit, - ) + Event get _latestEdit => _latestEditCache ??= + _event + .aggregatedEvents(timeline, RelationshipTypes.edit) //sort by event.originServerTs to get the most recent first - .sorted( - (a, b) => b.originServerTs.compareTo(a.originServerTs), - ) + .sorted((a, b) => b.originServerTs.compareTo(a.originServerTs)) .firstOrNull ?? _event; // get audio events that are related to this event Set get allAudio => _latestEdit - .aggregatedEvents( - timeline, - RelationshipTypes.reply, - ) - .where((element) { + .aggregatedEvents(timeline, PangeaEventTypes.textToSpeech) + .where((element) { return element.content.tryGet>( ModelKey.transcription, ) != null; - }).toSet(); + }) + .toSet(); List get _repEvents => _latestEdit - .aggregatedEvents( - timeline, - PangeaEventTypes.representation, - ) - .map( - (e) => RepresentationEvent( - event: e, - parentMessageEvent: _latestEdit, - timeline: timeline, - ), - ) - .sorted( - (a, b) { - if (a.event == null) return -1; - if (b.event == null) return 1; - return b.event!.originServerTs.compareTo(a.event!.originServerTs); - }, - ).toList(); + .aggregatedEvents(timeline, PangeaEventTypes.representation) + .map( + (e) => RepresentationEvent( + event: e, + parentMessageEvent: _latestEdit, + timeline: timeline, + ), + ) + .sorted((a, b) { + if (a.event == null) return -1; + if (b.event == null) return 1; + return b.event!.originServerTs.compareTo(a.event!.originServerTs); + }) + .toList(); ChoreoRecordModel? get _embeddedChoreo { try { @@ -201,9 +189,7 @@ class PangeaMessageEvent { m: "error parsing originalSent", e: err, s: s, - data: { - "event": _latestEdit.toJson(), - }, + data: {"event": _latestEdit.toJson()}, ); } @@ -228,9 +214,7 @@ class PangeaMessageEvent { m: "error parsing originalWritten", e: err, s: s, - data: { - "event": _latestEdit.toJson(), - }, + data: {"event": _latestEdit.toJson()}, ); } } @@ -239,11 +223,13 @@ class PangeaMessageEvent { return _representations!; } - RepresentationEvent? get originalSent => representations - .firstWhereOrNull((element) => element.content.originalSent); + RepresentationEvent? get originalSent => representations.firstWhereOrNull( + (element) => element.content.originalSent, + ); - RepresentationEvent? get originalWritten => representations - .firstWhereOrNull((element) => element.content.originalWritten); + RepresentationEvent? get originalWritten => representations.firstWhereOrNull( + (element) => element.content.originalWritten, + ); String get originalWrittenContent { String? written = originalSent?.content.text; @@ -283,8 +269,8 @@ class PangeaMessageEvent { TextDirection get textDirection => LanguageConstants.rtlLanguageCodes.contains(messageDisplayLangCode) - ? TextDirection.rtl - : TextDirection.ltr; + ? TextDirection.rtl + : TextDirection.ltr; void updateLatestEdit() { _latestEditCache = null; @@ -294,18 +280,13 @@ class PangeaMessageEvent { RepresentationEvent? representationByLanguage( String langCode, { bool Function(RepresentationEvent)? filter, - }) => - representations.firstWhereOrNull( - (element) => - element.langCode.split("-")[0] == langCode.split("-")[0] && - (filter?.call(element) ?? true), - ); + }) => representations.firstWhereOrNull( + (element) => + element.langCode.split("-")[0] == langCode.split("-")[0] && + (filter?.call(element) ?? true), + ); - Event? getTextToSpeechLocal( - String langCode, - String text, - String? voice, - ) { + Event? getTextToSpeechLocal(String langCode, String text, String? voice) { for (final audio in allAudio) { final dataMap = audio.content.tryGetMap(ModelKey.transcription); if (dataMap == null || !dataMap.containsKey(ModelKey.tokens)) continue; @@ -325,9 +306,7 @@ class PangeaMessageEvent { ErrorHandler.logError( e: e, s: s, - data: { - "event": audio.toJson(), - }, + data: {"event": audio.toJson()}, m: "error parsing data in getTextToSpeechLocal", ); } @@ -335,17 +314,16 @@ class PangeaMessageEvent { return null; } - RepresentationEvent? _getSpeechToTextRepresentation() => - representations.firstWhereOrNull( - (element) => element.content.speechToText != null, - ); + RepresentationEvent? _getSpeechToTextRepresentation() => representations + .firstWhereOrNull((element) => element.content.speechToText != null); SpeechToTextResponseModel? getSpeechToTextLocal() { final rep = _getSpeechToTextRepresentation()?.content.speechToText; if (rep != null) return rep; - final rawBotTranscription = - event.content.tryGetMap(ModelKey.botTranscription); + final rawBotTranscription = event.content.tryGetMap( + ModelKey.botTranscription, + ); if (rawBotTranscription != null) { try { @@ -356,9 +334,7 @@ class PangeaMessageEvent { ErrorHandler.logError( e: err, s: s, - data: { - "event": _event.toJson(), - }, + data: {"event": _event.toJson()}, m: "error parsing botTranscription", ); return null; @@ -395,9 +371,7 @@ class PangeaMessageEvent { ); if (result.error != null) { - throw Exception( - "Error getting text to speech: ${result.error}", - ); + throw Exception("Error getting text to speech: ${result.error}"); } final response = result.result!; @@ -416,12 +390,8 @@ class PangeaMessageEvent { room.sendFileEvent( file, - inReplyTo: _event, extraContent: { - 'info': { - ...file.info, - ModelKey.duration: response.durationMillis, - }, + 'info': {...file.info, ModelKey.duration: response.durationMillis}, 'org.matrix.msc3245.voice': {}, 'org.matrix.msc1767.audio': { ModelKey.duration: response.durationMillis, @@ -430,15 +400,12 @@ class PangeaMessageEvent { ModelKey.transcription: response .toPangeaAudioEventData(rep?.text ?? body, langCode, voice) .toJson(), + "m.relates_to": { + "rel_type": PangeaEventTypes.textToSpeech, + "event_id": _event.eventId, + }, }, - ).then((eventId) async { - final Event? audioEvent = - eventId != null ? await room.getEventById(eventId) : null; - - if (audioEvent != null) { - allAudio.add(audioEvent); - } - }); + ); return file; } @@ -473,9 +440,7 @@ class PangeaMessageEvent { ); if (result.error != null) { - throw Exception( - "Error getting speech to text: ${result.error}", - ); + throw Exception("Error getting speech to text: ${result.error}"); } if (sendEvent) { @@ -599,7 +564,7 @@ class PangeaMessageEvent { final includedIT = (originalSent?.choreo?.endedWithIT(originalSent!.text) ?? false) && - !(originalSent?.choreo?.includedIGC ?? true); + !(originalSent?.choreo?.includedIGC ?? true); RepresentationEvent? rep; if (!includedIT) { diff --git a/lib/pangea/events/event_wrappers/pangea_representation_event.dart b/lib/pangea/events/event_wrappers/pangea_representation_event.dart index 0dac12547..e9d4380eb 100644 --- a/lib/pangea/events/event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_representation_event.dart @@ -63,25 +63,13 @@ class RepresentationEvent { List? get detections => _tokens?.detections; Set get tokenEvents => - _event?.aggregatedEvents( - timeline, - PangeaEventTypes.tokens, - ) ?? - {}; + _event?.aggregatedEvents(timeline, PangeaEventTypes.tokens) ?? {}; Set get sttEvents => - _event?.aggregatedEvents( - timeline, - PangeaEventTypes.sttTranslation, - ) ?? - {}; + _event?.aggregatedEvents(timeline, PangeaEventTypes.sttTranslation) ?? {}; Set get choreoEvents => - _event?.aggregatedEvents( - timeline, - PangeaEventTypes.choreoRecord, - ) ?? - {}; + _event?.aggregatedEvents(timeline, PangeaEventTypes.choreoRecord) ?? {}; // Note: in the case where the event is the originalSent or originalWritten event, // the content will be set on initialization by the PangeaMessageEvent @@ -105,11 +93,7 @@ class RepresentationEvent { if (_choreo != null) return _choreo; if (_event == null) { - Sentry.addBreadcrumb( - Breadcrumb( - message: "_event and _choreo both null", - ), - ); + Sentry.addBreadcrumb(Breadcrumb(message: "_event and _choreo both null")); return null; } @@ -130,9 +114,7 @@ class RepresentationEvent { if (content.speechToText == null) return []; if (_event == null) { Sentry.addBreadcrumb( - Breadcrumb( - message: "_event and _sttTranslations both null", - ), + Breadcrumb(message: "_event and _sttTranslations both null"), ); return []; } @@ -141,9 +123,7 @@ class RepresentationEvent { final List sttTranslations = []; for (final event in sttEvents) { try { - sttTranslations.add( - SttTranslationModel.fromJson(event.content), - ); + sttTranslations.add(SttTranslationModel.fromJson(event.content)); } catch (e) { Sentry.addBreadcrumb( Breadcrumb( @@ -227,10 +207,10 @@ class RepresentationEvent { langCode: langCode, senderL1: MatrixState.pangeaController.userController.userL1?.langCode ?? - LanguageKeys.unknownLanguage, + LanguageKeys.unknownLanguage, senderL2: MatrixState.pangeaController.userController.userL2?.langCode ?? - LanguageKeys.unknownLanguage, + LanguageKeys.unknownLanguage, ), ); diff --git a/lib/pangea/events/extensions/pangea_event_extension.dart b/lib/pangea/events/extensions/pangea_event_extension.dart index 6c71c7d22..463531dde 100644 --- a/lib/pangea/events/extensions/pangea_event_extension.dart +++ b/lib/pangea/events/extensions/pangea_event_extension.dart @@ -45,26 +45,28 @@ extension PangeaEvent on Event { if (type != EventTypes.Message || messageType != MessageTypes.Audio) { ErrorHandler.logError( e: "Event is not an audio message", - data: { - "event": toJson(), - }, + data: {"event": toJson()}, ); return null; } - final transcription = - content.tryGetMap(ModelKey.transcription); - final audioContent = - content.tryGetMap('org.matrix.msc1767.audio'); + final transcription = content.tryGetMap( + ModelKey.transcription, + ); + final audioContent = content.tryGetMap( + 'org.matrix.msc1767.audio', + ); final matrixFile = await downloadAndDecryptAttachment(); - final duration = audioContent?.tryGet(ModelKey.duration) ?? + final duration = + audioContent?.tryGet(ModelKey.duration) ?? content .tryGetMap('info') ?.tryGet(ModelKey.duration); - final waveform = audioContent?.tryGetList('waveform') ?? + final waveform = + audioContent?.tryGetList('waveform') ?? content .tryGetMap('org.matrix.msc1767.audio') ?.tryGetList('waveform'); diff --git a/lib/pangea/events/models/content_feedback.dart b/lib/pangea/events/models/content_feedback.dart index 7b554706f..9634b53b7 100644 --- a/lib/pangea/events/models/content_feedback.dart +++ b/lib/pangea/events/models/content_feedback.dart @@ -11,11 +11,8 @@ class ContentFeedback { ContentFeedback(this.content, this.feedback); - toJson() { - return { - 'content': content.toJson(), - 'feedback': feedback, - }; + Map toJson() { + return {'content': content.toJson(), 'feedback': feedback}; } @override diff --git a/lib/pangea/events/models/language_detection_model.dart b/lib/pangea/events/models/language_detection_model.dart index ea68d1a04..5db13c4b6 100644 --- a/lib/pangea/events/models/language_detection_model.dart +++ b/lib/pangea/events/models/language_detection_model.dart @@ -28,7 +28,7 @@ class LanguageDetectionModel { } Map toJson() => { - ModelKey.langCode: langCode, - ModelKey.confidence: confidence, - }; + ModelKey.langCode: langCode, + ModelKey.confidence: confidence, + }; } diff --git a/lib/pangea/events/models/pangea_token_model.dart b/lib/pangea/events/models/pangea_token_model.dart index 080cfaa85..29d287b42 100644 --- a/lib/pangea/events/models/pangea_token_model.dart +++ b/lib/pangea/events/models/pangea_token_model.dart @@ -65,9 +65,7 @@ class PangeaToken { // previously sent tokens have lists of lemmas if (json is Iterable) { return json - .map( - (e) => Lemma.fromJson(e as Map), - ) + .map((e) => Lemma.fromJson(e as Map)) .toList() .cast() .firstOrNull ?? @@ -82,8 +80,9 @@ class PangeaToken { } factory PangeaToken.fromJson(Map json) { - final PangeaTokenText text = - PangeaTokenText.fromJson(json[_textKey] as Map); + final PangeaTokenText text = PangeaTokenText.fromJson( + json[_textKey] as Map, + ); final morph = json['morph'] != null ? (json['morph'] as Map).map( (key, value) => MapEntry( @@ -104,12 +103,10 @@ class PangeaToken { static const String _lemmaKey = ModelKey.lemma; Map toJson() => { - _textKey: text.toJson(), - _lemmaKey: [lemma.toJson()], - 'morph': morph.map( - (key, value) => MapEntry(key.name, value), - ), - }; + _textKey: text.toJson(), + _lemmaKey: [lemma.toJson()], + 'morph': morph.map((key, value) => MapEntry(key.name, value)), + }; /// alias for the offset int get start => text.offset; @@ -179,10 +176,10 @@ class PangeaToken { } ConstructIdentifier get vocabConstructID => ConstructIdentifier( - lemma: lemma.text, - type: ConstructTypeEnum.vocab, - category: pos, - ); + lemma: lemma.text, + type: ConstructTypeEnum.vocab, + category: pos, + ); ConstructForm get vocabForm => ConstructForm(form: text.content, cId: vocabConstructID); @@ -191,8 +188,9 @@ class PangeaToken { MorphFeaturesEnum morphFeature, String morphTag, ) { - final List allTags = - MorphsRepo.cached.getDisplayTags(morphFeature.name); + final List allTags = MorphsRepo.cached.getDisplayTags( + morphFeature.name, + ); final List possibleDistractors = allTags .where( @@ -205,15 +203,18 @@ class PangeaToken { } List get morphsBasicallyEligibleForPracticeByPriority => - MorphFeaturesEnumExtension.eligibleForPractice.where((f) { - return morph.containsKey(f); - }).map((f) { - return ConstructIdentifier( - lemma: getMorphTag(f)!, - type: ConstructTypeEnum.morph, - category: f.name, - ); - }).toList(); + MorphFeaturesEnumExtension.eligibleForPractice + .where((f) { + return morph.containsKey(f); + }) + .map((f) { + return ConstructIdentifier( + lemma: getMorphTag(f)!, + type: ConstructTypeEnum.morph, + category: f.name, + ); + }) + .toList(); bool eligibleForPractice(ActivityTypeEnum activityType) { switch (activityType) { diff --git a/lib/pangea/events/models/representation_content_model.dart b/lib/pangea/events/models/representation_content_model.dart index 1f4d3d45d..e758635d4 100644 --- a/lib/pangea/events/models/representation_content_model.dart +++ b/lib/pangea/events/models/representation_content_model.dart @@ -123,15 +123,16 @@ class PangeaRepresentation { ); // for each token, record whether selected in ga, ta, or wa - List tokensToSave = - tokens.where((token) => token.lemma.saveVocab).toList(); + List tokensToSave = tokens + .where((token) => token.lemma.saveVocab) + .toList(); if (choreo != null && choreo.pastedStrings.isNotEmpty) { tokensToSave = tokensToSave .where( (token) => !choreo.pastedStrings.any( - (pasted) => pasted - .toLowerCase() - .contains(token.text.content.toLowerCase()), + (pasted) => pasted.toLowerCase().contains( + token.text.content.toLowerCase(), + ), ), ) .toList(); @@ -196,13 +197,7 @@ class PangeaRepresentation { if (tokenStep.acceptedOrIgnoredMatch != null && tokenStep.acceptedOrIgnoredMatch?.status != PangeaMatchStatusEnum.accepted) { - uses.addAll( - token.allUses( - ConstructUseTypeEnum.ga, - metadata, - 0, - ), - ); + uses.addAll(token.allUses(ConstructUseTypeEnum.ga, metadata, 0)); continue; } @@ -213,28 +208,16 @@ class PangeaRepresentation { if (selectedChoices == 0) { ErrorHandler.logError( e: "No selected choices for IT step", - data: { - "token": token.text.content, - "step": tokenStep.toJson(), - }, + data: {"token": token.text.content, "step": tokenStep.toJson()}, ); continue; } final corITPoints = ConstructUseTypeEnum.corIt.pointValue; final incITPoints = ConstructUseTypeEnum.incIt.pointValue; - final xp = max( - 0, - corITPoints + (incITPoints * (selectedChoices - 1)), - ); + final xp = max(0, corITPoints + (incITPoints * (selectedChoices - 1))); - uses.addAll( - token.allUses( - ConstructUseTypeEnum.ta, - metadata, - xp, - ), - ); + uses.addAll(token.allUses(ConstructUseTypeEnum.ta, metadata, xp)); } else if (tokenStep.acceptedOrIgnoredMatch!.match.choices != null) { final selectedChoices = tokenStep.acceptedOrIgnoredMatch!.match.choices! .where((choice) => choice.selected) @@ -242,10 +225,7 @@ class PangeaRepresentation { if (selectedChoices == 0) { ErrorHandler.logError( e: "No selected choices for IGC step", - data: { - "token": token.text.content, - "step": tokenStep.toJson(), - }, + data: {"token": token.text.content, "step": tokenStep.toJson()}, ); continue; } @@ -257,13 +237,7 @@ class PangeaRepresentation { corIGCPoints + (incIGCPoints * (selectedChoices - 1)), ); - uses.addAll( - token.allUses( - ConstructUseTypeEnum.ga, - metadata, - xp, - ), - ); + uses.addAll(token.allUses(ConstructUseTypeEnum.ga, metadata, xp)); } } diff --git a/lib/pangea/events/models/stt_translation_model.dart b/lib/pangea/events/models/stt_translation_model.dart index 06becba21..99111d995 100644 --- a/lib/pangea/events/models/stt_translation_model.dart +++ b/lib/pangea/events/models/stt_translation_model.dart @@ -6,10 +6,7 @@ class SttTranslationModel { final String translation; final String langCode; - SttTranslationModel({ - required this.translation, - required this.langCode, - }); + SttTranslationModel({required this.translation, required this.langCode}); factory SttTranslationModel.fromJson(Map json) { final content = json.tryGetMap(PangeaEventTypes.sttTranslation); @@ -24,9 +21,6 @@ class SttTranslationModel { } Map toJson() { - return { - 'translation': translation, - 'lang_code': langCode, - }; + return {'translation': translation, 'lang_code': langCode}; } } diff --git a/lib/pangea/events/models/tokens_event_content_model.dart b/lib/pangea/events/models/tokens_event_content_model.dart index 45260340b..531c4c6d7 100644 --- a/lib/pangea/events/models/tokens_event_content_model.dart +++ b/lib/pangea/events/models/tokens_event_content_model.dart @@ -11,10 +11,7 @@ class PangeaMessageTokens { List tokens; List? detections; - PangeaMessageTokens({ - required this.tokens, - this.detections, - }); + PangeaMessageTokens({required this.tokens, this.detections}); factory PangeaMessageTokens.fromJson(Map json) { // "tokens" was accidentally used as the key in the first implementation @@ -24,14 +21,14 @@ class PangeaMessageTokens { final Iterable tokensIterable = something is Iterable ? something : something is String - ? jsonDecode(json[_tokensKey]) - : null; + ? jsonDecode(json[_tokensKey]) + : null; final Iterable? detectionsIterable = json[_detectionsKey] is Iterable ? json[_detectionsKey] : json[_detectionsKey] is String - ? jsonDecode(json[_detectionsKey]) - : null; + ? jsonDecode(json[_detectionsKey]) + : null; return PangeaMessageTokens( tokens: tokensIterable .map((e) => PangeaToken.fromJson(e)) @@ -50,8 +47,9 @@ class PangeaMessageTokens { Map toJson() { final data = {}; data[_tokensKey] = jsonEncode(tokens.map((e) => e.toJson()).toList()); - data[_detectionsKey] = - jsonEncode(detections?.map((e) => e.toJson()).toList()); + data[_detectionsKey] = jsonEncode( + detections?.map((e) => e.toJson()).toList(), + ); return data; } } diff --git a/lib/pangea/events/repo/language_detection_repo.dart b/lib/pangea/events/repo/language_detection_repo.dart index c4f6a6864..79fd127be 100644 --- a/lib/pangea/events/repo/language_detection_repo.dart +++ b/lib/pangea/events/repo/language_detection_repo.dart @@ -71,11 +71,7 @@ class LanguageDetectionRepo { return Result.value(res); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } @@ -96,9 +92,8 @@ class LanguageDetectionRepo { static void _setCached( LanguageDetectionRequest request, Future response, - ) => - _cache[request.hashCode.toString()] = _LanguageDetectionCacheItem( - data: response, - timestamp: DateTime.now(), - ); + ) => _cache[request.hashCode.toString()] = _LanguageDetectionCacheItem( + data: response, + timestamp: DateTime.now(), + ); } diff --git a/lib/pangea/events/repo/language_detection_request.dart b/lib/pangea/events/repo/language_detection_request.dart index 803d5db83..cee2b7a74 100644 --- a/lib/pangea/events/repo/language_detection_request.dart +++ b/lib/pangea/events/repo/language_detection_request.dart @@ -3,18 +3,10 @@ class LanguageDetectionRequest { final String? senderl1; final String? senderl2; - LanguageDetectionRequest({ - required this.text, - this.senderl1, - this.senderl2, - }); + LanguageDetectionRequest({required this.text, this.senderl1, this.senderl2}); Map toJson() { - return { - 'full_text': text, - 'sender_l1': senderl1, - 'sender_l2': senderl2, - }; + return {'full_text': text, 'sender_l1': senderl1, 'sender_l2': senderl2}; } @override diff --git a/lib/pangea/events/repo/language_detection_response.dart b/lib/pangea/events/repo/language_detection_response.dart index a73d01c60..5000ca7f5 100644 --- a/lib/pangea/events/repo/language_detection_response.dart +++ b/lib/pangea/events/repo/language_detection_response.dart @@ -4,10 +4,7 @@ class LanguageDetectionResponse { List detections; String fullText; - LanguageDetectionResponse({ - required this.detections, - required this.fullText, - }); + LanguageDetectionResponse({required this.detections, required this.fullText}); factory LanguageDetectionResponse.fromJson(Map json) { return LanguageDetectionResponse( diff --git a/lib/pangea/events/repo/token_api_models.dart b/lib/pangea/events/repo/token_api_models.dart index 13c245723..9f14c20f7 100644 --- a/lib/pangea/events/repo/token_api_models.dart +++ b/lib/pangea/events/repo/token_api_models.dart @@ -31,11 +31,11 @@ class TokensRequestModel { }); Map toJson() => { - ModelKey.fullText: fullText, - ModelKey.userL1: senderL1, - ModelKey.userL2: senderL2, - ModelKey.langCode: langCode ?? LanguageKeys.unknownLanguage, - }; + ModelKey.fullText: fullText, + ModelKey.userL1: senderL1, + ModelKey.userL2: senderL2, + ModelKey.langCode: langCode ?? LanguageKeys.unknownLanguage, + }; // override equals and hashcode @override @@ -63,9 +63,7 @@ class TokensResponseModel { required this.detections, }); - factory TokensResponseModel.fromJson( - Map json, - ) => + factory TokensResponseModel.fromJson(Map json) => TokensResponseModel( tokens: (json[ModelKey.tokens] as Iterable) .map( diff --git a/lib/pangea/events/repo/tokens_repo.dart b/lib/pangea/events/repo/tokens_repo.dart index 0b849bc3c..e108da161 100644 --- a/lib/pangea/events/repo/tokens_repo.dart +++ b/lib/pangea/events/repo/tokens_repo.dart @@ -14,10 +14,7 @@ class _TokensCacheItem { final Future data; final DateTime timestamp; - const _TokensCacheItem({ - required this.data, - required this.timestamp, - }); + const _TokensCacheItem({required this.data, required this.timestamp}); } class TokensRepo { @@ -33,10 +30,7 @@ class TokensRepo { return _getResult(request, cached); } - final future = _fetch( - accessToken, - request: request, - ); + final future = _fetch(accessToken, request: request); _setCached(request, future); return _getResult(request, future); } @@ -60,17 +54,15 @@ class TokensRepo { ); } - final Map json = - jsonDecode(utf8.decode(res.bodyBytes).toString()); + final Map json = jsonDecode( + utf8.decode(res.bodyBytes).toString(), + ); final tokens = TokensResponseModel.fromJson(json); if (tokens.tokens.any((t) => t.pos == 'other')) { ErrorHandler.logError( e: Exception('Received token with pos "other"'), - data: { - "request": request.toJson(), - "response": json, - }, + data: {"request": request.toJson(), "response": json}, level: SentryLevel.warning, ); } @@ -86,23 +78,17 @@ class TokensRepo { return Result.value(res); } catch (e, s) { _tokensCache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } - static Future? _getCached( - TokensRequestModel request, - ) { + static Future? _getCached(TokensRequestModel request) { final cacheKeys = [..._tokensCache.keys]; for (final key in cacheKeys) { - if (_tokensCache[key]! - .timestamp - .isBefore(DateTime.now().subtract(_cacheDuration))) { + if (_tokensCache[key]!.timestamp.isBefore( + DateTime.now().subtract(_cacheDuration), + )) { _tokensCache.remove(key); } } @@ -113,9 +99,8 @@ class TokensRepo { static void _setCached( TokensRequestModel request, Future response, - ) => - _tokensCache[request.hashCode.toString()] = _TokensCacheItem( - data: response, - timestamp: DateTime.now(), - ); + ) => _tokensCache[request.hashCode.toString()] = _TokensCacheItem( + data: response, + timestamp: DateTime.now(), + ); } diff --git a/lib/pangea/events/utils/report_message.dart b/lib/pangea/events/utils/report_message.dart index 5d20ec97b..6de7a0530 100644 --- a/lib/pangea/events/utils/report_message.dart +++ b/lib/pangea/events/utils/report_message.dart @@ -13,13 +13,8 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; Future getReportsDM(User teacher, Room space) async { - final String roomId = await teacher.startDirectChat( - enableEncryption: false, - ); - space.setSpaceChild( - roomId, - suggested: false, - ); + final String roomId = await teacher.startDirectChat(enableEncryption: false); + space.setSpaceChild(roomId, suggested: false); return space.client.getRoomById(roomId)!; } @@ -34,18 +29,9 @@ void reportEvent( message: L10n.of(context).whyDoYouWantToReportThis, cancelLabel: L10n.of(context).cancel, actions: [ - AdaptiveModalAction( - value: 1, - label: L10n.of(context).offensive, - ), - AdaptiveModalAction( - value: 2, - label: L10n.of(context).translationProblem, - ), - AdaptiveModalAction( - value: 3, - label: L10n.of(context).other, - ), + AdaptiveModalAction(value: 1, label: L10n.of(context).offensive), + AdaptiveModalAction(value: 2, label: L10n.of(context).translationProblem), + AdaptiveModalAction(value: 3, label: L10n.of(context).other), ], ); if (score == null) return; @@ -103,8 +89,10 @@ Future reportOffensiveMessage( final resp = await showFutureLoadingDialog>( context: context, future: () async { - final List teachers = - await getReportTeachers(context, reportedInRoom); + final List teachers = await getReportTeachers( + context, + reportedInRoom, + ); if (teachers.isEmpty) { throw L10n.of(context).noTeachersFound; } @@ -141,15 +129,12 @@ Future reportOffensiveMessage( final String reportingUserId = Matrix.of(context).client.userID ?? ""; final String roomName = reportedInRoom.getLocalizedDisplayname(); - final String messageTitle = L10n.of(context).reportMessageTitle( - reportingUserId, - reportedUserId, - roomName, - ); - final String messageBody = L10n.of(context).reportMessageBody( - reportedMessage, - reason ?? L10n.of(context).none, - ); + final String messageTitle = L10n.of( + context, + ).reportMessageTitle(reportingUserId, reportedUserId, roomName); + final String messageBody = L10n.of( + context, + ).reportMessageBody(reportedMessage, reason ?? L10n.of(context).none); final String message = "$messageTitle\n\n$messageBody"; for (final Room reportDM in reportDMs) { final event = { @@ -190,9 +175,7 @@ Future> getReportTeachers( } } - final List otherSpaces = Matrix.of(context) - .client - .rooms + final List otherSpaces = Matrix.of(context).client.rooms .where((room) => room.isSpace && !reportRoomParentSpaces.contains(room)) .toList(); diff --git a/lib/pangea/extensions/join_rule_extension.dart b/lib/pangea/extensions/join_rule_extension.dart index ede8eef50..aac92c69b 100644 --- a/lib/pangea/extensions/join_rule_extension.dart +++ b/lib/pangea/extensions/join_rule_extension.dart @@ -13,23 +13,20 @@ extension JoinRuleExtension on Client { try { joinCode = await requestSpaceCode(); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'joinRule': joinRule, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'joinRule': joinRule}); } - return StateEvent( - type: EventTypes.RoomJoinRules, - content: { - ModelKey.joinRule: joinRule, - if (joinCode != null) ModelKey.accessCode: joinCode, - if (allow != null) 'allow': allow, - }, - ); + final Map content = {ModelKey.joinRule: joinRule}; + + if (joinCode != null) { + content[ModelKey.accessCode] = joinCode; + } + + if (allow != null) { + content['allow'] = allow; + } + + return StateEvent(type: EventTypes.RoomJoinRules, content: content); } } @@ -59,10 +56,7 @@ extension JoinRuleExtensionOnRoom on Room { String joinRule, { List>? allow, }) async { - final currentJoinRule = getState( - EventTypes.RoomJoinRules, - )?.content ?? - {}; + final currentJoinRule = getState(EventTypes.RoomJoinRules)?.content ?? {}; if (currentJoinRule[ModelKey.joinRule] == joinRule && (currentJoinRule['allow'] == allow)) { diff --git a/lib/pangea/extensions/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension.dart index 85c869ba3..a9c99c5ad 100644 --- a/lib/pangea/extensions/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension.dart @@ -42,5 +42,5 @@ part "room_space_settings_extension.dart"; part "room_user_permissions_extension.dart"; extension PangeaRoom on Room { -// analytics + // analytics } diff --git a/lib/pangea/extensions/pangea_rooms_chunk_extension.dart b/lib/pangea/extensions/pangea_rooms_chunk_extension.dart index 8906094e8..eeca86dab 100644 --- a/lib/pangea/extensions/pangea_rooms_chunk_extension.dart +++ b/lib/pangea/extensions/pangea_rooms_chunk_extension.dart @@ -4,13 +4,13 @@ import 'package:matrix/matrix_api_lite/generated/model.dart'; import 'package:fluffychat/pangea/spaces/space_constants.dart'; -extension PangeaRoomsChunk on PublicRoomsChunk { +extension PangeaRoomsChunk on PublishedRoomsChunk { /// Use Random with a seed to get the default /// avatar associated with this space String defaultAvatar() { final int seed = roomId.hashCode; - return SpaceConstants.publicSpaceIcons[Random(seed).nextInt( - SpaceConstants.publicSpaceIcons.length, - )]; + return SpaceConstants.publicSpaceIcons[Random( + seed, + ).nextInt(SpaceConstants.publicSpaceIcons.length)]; } } diff --git a/lib/pangea/extensions/room_capacity_extension.dart b/lib/pangea/extensions/room_capacity_extension.dart index 5b38b4aab..95c9a2a04 100644 --- a/lib/pangea/extensions/room_capacity_extension.dart +++ b/lib/pangea/extensions/room_capacity_extension.dart @@ -2,12 +2,9 @@ part of "pangea_room_extension.dart"; extension RoomCapacityExtension on Room { Future updateRoomCapacity(int newCapacity) => - client.setRoomStateWithKey( - id, - PangeaEventTypes.capacity, - '', - {'capacity': newCapacity}, - ); + client.setRoomStateWithKey(id, PangeaEventTypes.capacity, '', { + 'capacity': newCapacity, + }); int? get capacity { final t = getState(PangeaEventTypes.capacity)?.content['capacity']; diff --git a/lib/pangea/extensions/room_children_and_parents_extension.dart b/lib/pangea/extensions/room_children_and_parents_extension.dart index b79828301..7e1b8d6f7 100644 --- a/lib/pangea/extensions/room_children_and_parents_extension.dart +++ b/lib/pangea/extensions/room_children_and_parents_extension.dart @@ -11,31 +11,18 @@ extension ChildrenAndParentsRoomExtension on Room { } List get pangeaSpaceParents => client.rooms - .where( - (r) => r.isSpace, - ) - .where( - (space) => space.spaceChildren.any( - (room) => room.roomId == id, - ), - ) + .where((r) => r.isSpace) + .where((space) => space.spaceChildren.any((room) => room.roomId == id)) .toList(); List get pangeaSpaceChildren => client.rooms - .where( - (r) => spaceChildren.any( - (child) => r.id == child.roomId, - ), - ) + .where((r) => spaceChildren.any((child) => r.id == child.roomId)) .toList(); /// Wrapper around call to setSpaceChild with added functionality /// to prevent adding one room to multiple spaces, and resets the /// subspace's JoinRules and Visibility to defaults. - Future addToSpace( - String roomId, { - bool? suggested, - }) async { + Future addToSpace(String roomId, {bool? suggested}) async { final Room? child = client.getRoomById(roomId); if (child == null) return; @@ -46,18 +33,12 @@ extension ChildrenAndParentsRoomExtension on Room { ErrorHandler.logError( e: e, m: 'Failed to remove child from parent', - data: { - "roomID": roomId, - "parentID": parent.id, - }, + data: {"roomID": roomId, "parentID": parent.id}, ); } } - await _trySetSpaceChild( - roomId, - suggested: suggested, - ); + await _trySetSpaceChild(roomId, suggested: suggested); } Future _trySetSpaceChild( @@ -136,10 +117,7 @@ extension ChildrenAndParentsRoomExtension on Room { await client.pangeaJoinRules( 'knock_restricted', allow: [ - { - "type": "m.room_membership", - "room_id": id, - } + {"type": "m.room_membership", "room_id": id}, ], ), ], diff --git a/lib/pangea/extensions/room_events_extension.dart b/lib/pangea/extensions/room_events_extension.dart index 0f10dcbe6..fb56d313c 100644 --- a/lib/pangea/extensions/room_events_extension.dart +++ b/lib/pangea/extensions/room_events_extension.dart @@ -14,26 +14,14 @@ extension EventsRoomExtension on Room { try { await room.leave(); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomID': room.id, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'roomID': room.id}); } } try { await leave(); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomID': id, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'roomID': id}); } } @@ -111,19 +99,21 @@ extension EventsRoomExtension on Room { replyText = replyText.split('\n').map((line) => '> $line').join('\n'); content['format'] = 'org.matrix.custom.html'; // be sure that we strip any previous reply fallbacks - final replyHtml = (inReplyTo.formattedText.isNotEmpty - ? inReplyTo.formattedText - : htmlEscape.convert(inReplyTo.body).replaceAll('\n', '
')) - .replaceAll( - RegExp( - r'.*', - caseSensitive: false, - multiLine: false, - dotAll: true, - ), - '', - ); - final repliedHtml = content.tryGet('formatted_body') ?? + final replyHtml = + (inReplyTo.formattedText.isNotEmpty + ? inReplyTo.formattedText + : htmlEscape.convert(inReplyTo.body).replaceAll('\n', '
')) + .replaceAll( + RegExp( + r'.*', + caseSensitive: false, + multiLine: false, + dotAll: true, + ), + '', + ); + final repliedHtml = + content.tryGet('formatted_body') ?? htmlEscape .convert(content.tryGet('body') ?? '') .replaceAll('\n', '
'); @@ -134,9 +124,7 @@ extension EventsRoomExtension on Room { content['body'] = '${replyText.replaceAll('@room', '@\u200broom')}\n\n${content.tryGet('body') ?? ''}'; content['m.relates_to'] = { - 'm.in_reply_to': { - 'event_id': inReplyTo.eventId, - }, + 'm.in_reply_to': {'event_id': inReplyTo.eventId}, }; } @@ -190,13 +178,7 @@ extension EventsRoomExtension on Room { nextBatch: '', rooms: RoomsUpdate( join: { - id: JoinedRoomUpdate( - timeline: TimelineUpdate( - events: [ - event, - ], - ), - ), + id: JoinedRoomUpdate(timeline: TimelineUpdate(events: [event])), }, ), ); @@ -234,10 +216,7 @@ extension EventsRoomExtension on Room { // ); // } - final event = { - 'msgtype': msgtype, - 'body': message, - }; + final event = {'msgtype': msgtype, 'body': message}; if (choreo != null) { event[ModelKey.choreoRecord] = choreo.toJson(); } @@ -305,9 +284,7 @@ extension EventsRoomExtension on Room { await timeline.requestFuture( historyCount: 100, filter: StateFilter( - types: [ - PangeaEventTypes.construct, - ], + types: [PangeaEventTypes.construct], senders: [userID], ), ); @@ -322,9 +299,7 @@ extension EventsRoomExtension on Room { await timeline.requestHistory( historyCount: 100, filter: StateFilter( - types: [ - PangeaEventTypes.construct, - ], + types: [PangeaEventTypes.construct], senders: [userID], ), ); @@ -349,8 +324,10 @@ extension EventsRoomExtension on Room { } Future> getAllEvents({String? since}) async { - final GetRoomEventsResponse initalResp = - await client.getRoomEvents(id, Direction.b); + final GetRoomEventsResponse initalResp = await client.getRoomEvents( + id, + Direction.b, + ); if (initalResp.end == null) return []; String? nextStartToken = initalResp.end; diff --git a/lib/pangea/extensions/room_information_extension.dart b/lib/pangea/extensions/room_information_extension.dart index cb2fbbb86..b5253e840 100644 --- a/lib/pangea/extensions/room_information_extension.dart +++ b/lib/pangea/extensions/room_information_extension.dart @@ -10,9 +10,7 @@ extension RoomInformationRoomExtension on Room { Future get botIsInRoom async { final List participants = await requestParticipants(); - return participants.any( - (User user) => user.id == BotName.byEnvironment, - ); + return participants.any((User user) => user.id == BotName.byEnvironment); } String? get roomType => diff --git a/lib/pangea/extensions/room_space_settings_extension.dart b/lib/pangea/extensions/room_space_settings_extension.dart index 47dea0d74..92c21a69a 100644 --- a/lib/pangea/extensions/room_space_settings_extension.dart +++ b/lib/pangea/extensions/room_space_settings_extension.dart @@ -26,12 +26,12 @@ extension SpaceRoomExtension on Room { final List participants = await requestParticipants(); return isSpace ? participants - .where( - (e) => - e.powerLevel == SpaceConstants.powerLevelOfAdmin && - e.id != BotName.byEnvironment, - ) - .toList() + .where( + (e) => + e.powerLevel == SpaceConstants.powerLevelOfAdmin && + e.id != BotName.byEnvironment, + ) + .toList() : participants; } } diff --git a/lib/pangea/instructions/instruction_settings.dart b/lib/pangea/instructions/instruction_settings.dart index 20933ff5e..6b4550ff2 100644 --- a/lib/pangea/instructions/instruction_settings.dart +++ b/lib/pangea/instructions/instruction_settings.dart @@ -40,7 +40,7 @@ class InstructionSettings { for (final key in InstructionsEnum.values) { instructions[key.toString()] = (accountData[key.toString()]?.content[key.toString()] as bool?) ?? - false; + false; } return InstructionSettings(instructions); } @@ -83,8 +83,6 @@ class InstructionSettings { final entries = _instructions.entries.toList() ..sort((a, b) => a.key.hashCode.compareTo(b.key.hashCode)); - return Object.hashAll( - entries.map((e) => Object.hash(e.key, e.value)), - ); + return Object.hashAll(entries.map((e) => Object.hash(e.key, e.value))); } } diff --git a/lib/pangea/instructions/instructions_enum.dart b/lib/pangea/instructions/instructions_enum.dart index a3cecfcdc..7103186bf 100644 --- a/lib/pangea/instructions/instructions_enum.dart +++ b/lib/pangea/instructions/instructions_enum.dart @@ -35,6 +35,7 @@ enum InstructionsEnum { dismissSupportChat, shimmerNewToken, shimmerTranslation, + showedActivityMenu, } extension InstructionsEnumExtension on InstructionsEnum { @@ -69,12 +70,11 @@ extension InstructionsEnumExtension on InstructionsEnum { case InstructionsEnum.dismissSupportChat: case InstructionsEnum.shimmerNewToken: case InstructionsEnum.shimmerTranslation: + case InstructionsEnum.showedActivityMenu: ErrorHandler.logError( e: Exception("No title for this instruction"), m: 'InstructionsEnumExtension.title', - data: { - 'this': this, - }, + data: {'this': this}, ); debugger(when: kDebugMode); return ""; @@ -133,6 +133,7 @@ extension InstructionsEnumExtension on InstructionsEnum { case InstructionsEnum.dismissSupportChat: case InstructionsEnum.shimmerNewToken: case InstructionsEnum.shimmerTranslation: + case InstructionsEnum.showedActivityMenu: return ""; case InstructionsEnum.disableLanguageTools: return l10n.disableLanguageToolsDesc; @@ -141,9 +142,12 @@ extension InstructionsEnumExtension on InstructionsEnum { } } - bool get isToggledOff => - MatrixState.pangeaController.userController.profile.instructionSettings - .getStatus(this); + bool get isToggledOff => MatrixState + .pangeaController + .userController + .profile + .instructionSettings + .getStatus(this); void setToggledOff(bool value) { final userController = MatrixState.pangeaController.userController; diff --git a/lib/pangea/instructions/instructions_inline_tooltip.dart b/lib/pangea/instructions/instructions_inline_tooltip.dart index 5839d5657..0d81adda0 100644 --- a/lib/pangea/instructions/instructions_inline_tooltip.dart +++ b/lib/pangea/instructions/instructions_inline_tooltip.dart @@ -146,7 +146,8 @@ class InlineTooltipState extends State child: Center( child: Text( widget.message, - style: widget.textStyle ?? + style: + widget.textStyle ?? (FluffyThemes.isColumnMode(context) ? Theme.of(context).textTheme.titleSmall : Theme.of(context).textTheme.bodyMedium), diff --git a/lib/pangea/instructions/instructions_toggle.dart b/lib/pangea/instructions/instructions_toggle.dart index b2b653e44..62516dee3 100644 --- a/lib/pangea/instructions/instructions_toggle.dart +++ b/lib/pangea/instructions/instructions_toggle.dart @@ -9,10 +9,7 @@ import 'package:fluffychat/widgets/matrix.dart'; /// User can toggle on to prevent Instruction Card /// from appearing in future sessions class InstructionsToggle extends StatefulWidget { - const InstructionsToggle({ - super.key, - required this.instructionsKey, - }); + const InstructionsToggle({super.key, required this.instructionsKey}); final InstructionsEnum instructionsKey; diff --git a/lib/pangea/instructions/reset_instructions_list_tile.dart b/lib/pangea/instructions/reset_instructions_list_tile.dart index 4154498ee..7293271e9 100644 --- a/lib/pangea/instructions/reset_instructions_list_tile.dart +++ b/lib/pangea/instructions/reset_instructions_list_tile.dart @@ -5,10 +5,7 @@ import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; class ResetInstructionsListTile extends StatelessWidget { - const ResetInstructionsListTile({ - super.key, - required this.controller, - }); + const ResetInstructionsListTile({super.key, required this.controller}); final SettingsLearningController controller; @@ -17,12 +14,8 @@ class ResetInstructionsListTile extends StatelessWidget { //TODO: add to L10n return ListTile( leading: const Icon(Icons.lightbulb), - title: Text( - L10n.of(context).resetInstructionTooltipsTitle, - ), - subtitle: Text( - L10n.of(context).resetInstructionTooltipsDesc, - ), + title: Text(L10n.of(context).resetInstructionTooltipsTitle), + subtitle: Text(L10n.of(context).resetInstructionTooltipsDesc), onTap: () async { final resp = await showOkCancelAlertDialog( context: context, diff --git a/lib/pangea/join_codes/knock_space_extension.dart b/lib/pangea/join_codes/knock_space_extension.dart index a958299df..984db1b1f 100644 --- a/lib/pangea/join_codes/knock_space_extension.dart +++ b/lib/pangea/join_codes/knock_space_extension.dart @@ -6,17 +6,11 @@ import 'package:matrix/matrix_api_lite/generated/api.dart'; extension on Api { Future knockSpace(String code) async { - final requestUri = Uri( - path: '_synapse/client/pangea/v1/knock_with_code', - ); + final requestUri = Uri(path: '_synapse/client/pangea/v1/knock_with_code'); final request = Request('POST', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; request.headers['authorization'] = 'Bearer ${bearerToken!}'; - request.bodyBytes = utf8.encode( - jsonEncode({ - 'access_code': code, - }), - ); + request.bodyBytes = utf8.encode(jsonEncode({'access_code': code})); final response = await httpClient.send(request); if (response.statusCode != 200) { if (response.statusCode == 429) { diff --git a/lib/pangea/join_codes/space_code_controller.dart b/lib/pangea/join_codes/space_code_controller.dart index e1590a130..844213d84 100644 --- a/lib/pangea/join_codes/space_code_controller.dart +++ b/lib/pangea/join_codes/space_code_controller.dart @@ -23,15 +23,13 @@ class SpaceCodeController extends BaseController { static Future joinCachedSpaceCode(BuildContext context) async { final String? spaceCode = SpaceCodeRepo.spaceCode; if (spaceCode == null) return null; - final spaceId = await joinSpaceWithCode( - context, - spaceCode, - ); + final spaceId = await joinSpaceWithCode(context, spaceCode); await SpaceCodeRepo.clearSpaceCode(); if (spaceId != null) { - final room = - MatrixState.pangeaController.matrixState.client.getRoomById(spaceId); + final room = MatrixState.pangeaController.matrixState.client.getRoomById( + spaceId, + ); room?.isSpace ?? true ? context.go('/rooms/spaces/$spaceId/details') : context.go('/rooms/${room?.id}'); @@ -51,8 +49,9 @@ class SpaceCodeController extends BaseController { final resp = await showFutureLoadingDialog( context: context, future: () async { - final KnockSpaceResponse knockResult = - await client.knockWithCode(spaceCode); + final KnockSpaceResponse knockResult = await client.knockWithCode( + spaceCode, + ); if (knockResult.roomIds.isEmpty && knockResult.alreadyJoined.isEmpty && @@ -117,10 +116,7 @@ class SpaceCodeController extends BaseController { Room? room = client.getRoomById(spaceId); if (room == null) { - await client.waitForRoomInSync( - spaceId, - join: true, - ); + await client.waitForRoomInSync(spaceId, join: true); room = client.getRoomById(spaceId); if (room == null) { throw Exception("Failed to join space with id $spaceId"); diff --git a/lib/pangea/join_codes/space_code_repo.dart b/lib/pangea/join_codes/space_code_repo.dart index af6ab3fd8..1c5270104 100644 --- a/lib/pangea/join_codes/space_code_repo.dart +++ b/lib/pangea/join_codes/space_code_repo.dart @@ -13,17 +13,11 @@ class SpaceCodeRepo { static Future setSpaceCode(String code) async { if (code.isEmpty) return; - await _spaceStorage.write( - PLocalKey.cachedSpaceCodeToJoin, - code, - ); + await _spaceStorage.write(PLocalKey.cachedSpaceCodeToJoin, code); } static Future setRecentCode(String code) async { - await _spaceStorage.write( - PLocalKey.justInputtedCode, - code, - ); + await _spaceStorage.write(PLocalKey.justInputtedCode, code); } static Future clearSpaceCode() async { diff --git a/lib/pangea/join_codes/too_many_requests_dialog.dart b/lib/pangea/join_codes/too_many_requests_dialog.dart index 778bfb6ce..e18a3f780 100644 --- a/lib/pangea/join_codes/too_many_requests_dialog.dart +++ b/lib/pangea/join_codes/too_many_requests_dialog.dart @@ -9,18 +9,13 @@ class TooManyRequestsDialog extends StatelessWidget { @override Widget build(BuildContext context) { return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ - const BotFace( - width: 100, - expression: BotExpression.idle, - ), + const BotFace(width: 100, expression: BotExpression.idle), const SizedBox(height: 16), Text( L10n.of(context).areYouLikeMe, diff --git a/lib/pangea/languages/language_arc_model.dart b/lib/pangea/languages/language_arc_model.dart index c55d8a66b..832b9000e 100644 --- a/lib/pangea/languages/language_arc_model.dart +++ b/lib/pangea/languages/language_arc_model.dart @@ -4,10 +4,7 @@ class LanguageArc { final LanguageModel l1; final LanguageModel l2; - LanguageArc({ - required this.l1, - required this.l2, - }); + LanguageArc({required this.l1, required this.l2}); factory LanguageArc.fromJson(Map json) { return LanguageArc( @@ -17,10 +14,7 @@ class LanguageArc { } Map toJson() { - return { - 'l1': l1.toJson(), - 'l2': l2.toJson(), - }; + return {'l1': l1.toJson(), 'l2': l2.toJson()}; } @override diff --git a/lib/pangea/languages/language_constants.dart b/lib/pangea/languages/language_constants.dart index 89a005ce5..1c0b3dc3a 100644 --- a/lib/pangea/languages/language_constants.dart +++ b/lib/pangea/languages/language_constants.dart @@ -20,1135 +20,427 @@ class LanguageConstants { ]; static List> get languageList => [ - { - "language_code": "ab", - "language_name": "Abkhazian", - "l2_support": "na", - }, - { - "language_code": "ace", - "language_name": "Achinese", - "l2_support": "na", - }, - { - "language_code": "ach", - "language_name": "Acoli", - "l2_support": "na", - }, - { - "language_code": "af", - "language_name": "Afrikaans", - "l2_support": "na", - }, - { - "language_code": "ak", - "language_name": "Akan", - "l2_support": "na", - }, - { - "language_code": "alz", - "language_name": "Alur", - "l2_support": "na", - }, - { - "language_code": "am", - "language_name": "Amharic", - "l2_support": "beta", - }, - { - "language_code": "ar", - "language_name": "Arabic", - "l2_support": "beta", - }, - { - "language_code": "as", - "language_name": "Assamese", - "l2_support": "na", - }, - { - "language_code": "awa", - "language_name": "Awadhi", - "l2_support": "na", - }, - { - "language_code": "ay", - "language_name": "Aymara", - "l2_support": "na", - }, - { - "language_code": "az", - "language_name": "Azerbaijani", - "l2_support": "na", - }, - { - "language_code": "ba", - "language_name": "Bashkir", - "l2_support": "na", - }, - { - "language_code": "ban", - "language_name": "Balinese", - "l2_support": "na", - }, - { - "language_code": "bbc", - "language_name": "Batak Toba", - "l2_support": "na", - }, - { - "language_code": "be", - "language_name": "Belarusian", - "l2_support": "na", - }, - { - "language_code": "bem", - "language_name": "Bemba", - "l2_support": "na", - }, - { - "language_code": "bew", - "language_name": "Betawi", - "l2_support": "na", - }, - { - "language_code": "bg", - "language_name": "Bulgarian", - "l2_support": "beta", - }, - { - "language_code": "bho", - "language_name": "Bhojpuri", - "l2_support": "na", - }, - { - "language_code": "bik", - "language_name": "Bikol", - "l2_support": "na", - }, - { - "language_code": "bm", - "language_name": "Bambara", - "l2_support": "na", - }, - { - "language_code": "bn", - "language_name": "Bangla", - "l2_support": "beta", - }, - { - "language_code": "bn-BD", - "language_name": "Bengali (Bangladesh)", - "l2_support": "beta", - }, - { - "language_code": "bn-IN", - "language_name": "Bengali (India)", - "l2_support": "beta", - }, - { - "language_code": "br", - "language_name": "Breton", - "l2_support": "na", - }, - { - "language_code": "bs", - "language_name": "Bosnian", - "l2_support": "na", - }, - { - "language_code": "bts", - "language_name": "Batak Simalungun", - "l2_support": "na", - }, - { - "language_code": "btx", - "language_name": "Batak Karo", - "l2_support": "na", - }, - { - "language_code": "bua", - "language_name": "Buriat", - "l2_support": "na", - }, - { - "language_code": "ca", - "language_name": "Catalan", - "l2_support": "full", - }, - { - "language_code": "ceb", - "language_name": "Cebuano", - "l2_support": "na", - }, - { - "language_code": "cgg", - "language_name": "Chiga", - "l2_support": "na", - }, - { - "language_code": "chm", - "language_name": "Mari", - "l2_support": "na", - }, - { - "language_code": "ckb", - "language_name": "Central Kurdish", - "l2_support": "na", - }, - { - "language_code": "cnh", - "language_name": "Hakha Chin", - "l2_support": "na", - }, - { - "language_code": "co", - "language_name": "Corsican", - "l2_support": "na", - }, - { - "language_code": "crh", - "language_name": "Crimean Turkish", - "l2_support": "na", - }, - { - "language_code": "crs", - "language_name": "Seselwa Creole French", - "l2_support": "na", - }, - { - "language_code": "cs", - "language_name": "Czech", - "l2_support": "beta", - }, - { - "language_code": "cv", - "language_name": "Chuvash", - "l2_support": "na", - }, - { - "language_code": "cy", - "language_name": "Welsh", - "l2_support": "na", - }, - { - "language_code": "da", - "language_name": "Danish", - "l2_support": "beta", - }, - { - "language_code": "de", - "language_name": "German", - "l2_support": "full", - }, - { - "language_code": "din", - "language_name": "Dinka", - "l2_support": "na", - }, - { - "language_code": "doi", - "language_name": "Dogri", - "l2_support": "na", - }, - { - "language_code": "dov", - "language_name": "Dombe", - "l2_support": "na", - }, - { - "language_code": "dv", - "language_name": "Divehi", - "l2_support": "na", - }, - { - "language_code": "dz", - "language_name": "Dzongkha", - "l2_support": "na", - }, - { - "language_code": "ee", - "language_name": "Ewe", - "l2_support": "na", - }, - { - "language_code": "el", - "language_name": "Greek", - "l2_support": "beta", - }, - { - "language_code": "en", - "language_name": "English", - "l2_support": "full", - }, - { - "language_code": "en-AU", - "language_name": "English (Australia)", - "l2_support": "full", - }, - { - "language_code": "en-GB", - "language_name": "English (UK)", - "l2_support": "full", - }, - { - "language_code": "en-IN", - "language_name": "English (India)", - "l2_support": "full", - }, - { - "language_code": "en-US", - "language_name": "English (US)", - "l2_support": "full", - }, - { - "language_code": "eo", - "language_name": "Esperanto", - "l2_support": "na", - }, - { - "language_code": "es", - "language_name": "Spanish", - "l2_support": "full", - }, - { - "language_code": "es-ES", - "language_name": "Spanish (Spain)", - "l2_support": "full", - }, - { - "language_code": "es-MX", - "language_name": "Spanish (Mexico)", - "l2_support": "full", - }, - { - "language_code": "et", - "language_name": "Estonian", - "l2_support": "beta", - }, - { - "language_code": "eu", - "language_name": "Basque", - "l2_support": "beta", - }, - { - "language_code": "fa", - "language_name": "Persian", - "l2_support": "na", - }, - { - "language_code": "ff", - "language_name": "Fulah", - "l2_support": "na", - }, - { - "language_code": "fi", - "language_name": "Finnish", - "l2_support": "beta", - }, - { - "language_code": "fil", - "language_name": "Filipino", - "l2_support": "na", - }, - { - "language_code": "fj", - "language_name": "Fijian", - "l2_support": "na", - }, - { - "language_code": "fo", - "language_name": "Faroese", - "l2_support": "na", - }, - { - "language_code": "fr", - "language_name": "French", - "l2_support": "full", - }, - { - "language_code": "fr-CA", - "language_name": "French (Canada)", - "l2_support": "full", - }, - { - "language_code": "fr-FR", - "language_name": "French (France)", - "l2_support": "full", - }, - { - "language_code": "fy", - "language_name": "Western Frisian", - "l2_support": "na", - }, - { - "language_code": "ga", - "language_name": "Irish", - "l2_support": "na", - }, - { - "language_code": "gaa", - "language_name": "Ga", - "l2_support": "na", - }, - { - "language_code": "gd", - "language_name": "Scottish Gaelic", - "l2_support": "na", - }, - { - "language_code": "gl", - "language_name": "Galician", - "l2_support": "beta", - }, - { - "language_code": "gn", - "language_name": "Guarani", - "l2_support": "na", - }, - { - "language_code": "gom", - "language_name": "Goan Konkani", - "l2_support": "na", - }, - { - "language_code": "gu", - "language_name": "Gujarati", - "l2_support": "beta", - }, - { - "language_code": "ha", - "language_name": "Hausa", - "l2_support": "na", - }, - { - "language_code": "haw", - "language_name": "Hawaiian", - "l2_support": "na", - }, - { - "language_code": "he", - "language_name": "Hebrew", - "l2_support": "na", - }, - { - "language_code": "hi", - "language_name": "Hindi", - "l2_support": "beta", - }, - { - "language_code": "hil", - "language_name": "Hiligaynon", - "l2_support": "na", - }, - { - "language_code": "hmn", - "language_name": "Hmong", - "l2_support": "na", - }, - { - "language_code": "hne", - "language_name": "Chhattisgarhi", - "l2_support": "na", - }, - { - "language_code": "hr", - "language_name": "Croatian", - "l2_support": "na", - }, - { - "language_code": "hrx", - "language_name": "Hunsrik", - "l2_support": "na", - }, - { - "language_code": "ht", - "language_name": "Haitian Creole", - "l2_support": "na", - }, - { - "language_code": "hu", - "language_name": "Hungarian", - "l2_support": "beta", - }, - { - "language_code": "hy", - "language_name": "Armenian", - "l2_support": "na", - }, - { - "language_code": "id", - "language_name": "Indonesian", - "l2_support": "beta", - }, - { - "language_code": "ig", - "language_name": "Igbo", - "l2_support": "na", - }, - { - "language_code": "ilo", - "language_name": "Iloko", - "l2_support": "na", - }, - { - "language_code": "is", - "language_name": "Icelandic", - "l2_support": "na", - }, - { - "language_code": "it", - "language_name": "Italian", - "l2_support": "full", - }, - { - "language_code": "iw", - "language_name": "Hebrew", - "l2_support": "na", - }, - { - "language_code": "ja", - "language_name": "Japanese", - "l2_support": "full", - }, - { - "language_code": "jv", - "language_name": "Javanese", - "l2_support": "na", - }, - { - "language_code": "ka", - "language_name": "Georgian", - "l2_support": "na", - }, - { - "language_code": "kk", - "language_name": "Kazakh", - "l2_support": "na", - }, - { - "language_code": "km", - "language_name": "Khmer", - "l2_support": "na", - }, - { - "language_code": "kn", - "language_name": "Kannada", - "l2_support": "beta", - }, - { - "language_code": "ko", - "language_name": "Korean", - "l2_support": "full", - }, - { - "language_code": "kok", - "language_name": "Konkani", - "l2_support": "na", - }, - { - "language_code": "kri", - "language_name": "Krio", - "l2_support": "na", - }, - { - "language_code": "ks", - "language_name": "Kashmiri", - "l2_support": "na", - }, - { - "language_code": "ktu", - "language_name": "Kituba (Democratic Republic of Congo)", - "l2_support": "na", - }, - { - "language_code": "ku", - "language_name": "Kurdish", - "l2_support": "na", - }, - { - "language_code": "ky", - "language_name": "Kyrgyz", - "l2_support": "na", - }, - { - "language_code": "la", - "language_name": "Latin", - "l2_support": "na", - }, - { - "language_code": "lb", - "language_name": "Luxembourgish", - "l2_support": "na", - }, - { - "language_code": "lg", - "language_name": "Ganda", - "l2_support": "na", - }, - { - "language_code": "li", - "language_name": "Limburgish", - "l2_support": "na", - }, - { - "language_code": "lij", - "language_name": "Ligurian", - "l2_support": "na", - }, - { - "language_code": "lmo", - "language_name": "Lombard", - "l2_support": "na", - }, - { - "language_code": "ln", - "language_name": "Lingala", - "l2_support": "na", - }, - { - "language_code": "lo", - "language_name": "Lao", - "l2_support": "na", - }, - { - "language_code": "lt", - "language_name": "Lithuanian", - "l2_support": "beta", - }, - { - "language_code": "ltg", - "language_name": "Latgalian", - "l2_support": "na", - }, - { - "language_code": "luo", - "language_name": "Luo (Kenya and Tanzania)", - "l2_support": "na", - }, - { - "language_code": "lus", - "language_name": "Mizo", - "l2_support": "na", - }, - { - "language_code": "lv", - "language_name": "Latvian", - "l2_support": "beta", - }, - { - "language_code": "mai", - "language_name": "Maithili", - "l2_support": "na", - }, - { - "language_code": "mak", - "language_name": "Makasar", - "l2_support": "na", - }, - { - "language_code": "mg", - "language_name": "Malagasy", - "l2_support": "na", - }, - { - "language_code": "mi", - "language_name": "Māori", - "l2_support": "na", - }, - { - "language_code": "min", - "language_name": "Minangkabau", - "l2_support": "na", - }, - { - "language_code": "mk", - "language_name": "Macedonian", - "l2_support": "na", - }, - { - "language_code": "ml", - "language_name": "Malayalam", - "l2_support": "na", - }, - { - "language_code": "mn", - "language_name": "Mongolian", - "l2_support": "beta", - }, - { - "language_code": "mni", - "language_name": "Manipuri", - "l2_support": "na", - }, - { - "language_code": "mr", - "language_name": "Marathi", - "l2_support": "beta", - }, - { - "language_code": "ms", - "language_name": "Malay", - "l2_support": "beta", - }, - { - "language_code": "ms-Arab", - "language_name": "Malay (Arabic)", - "l2_support": "beta", - }, - { - "language_code": "ms-MY", - "language_name": "Malay (Malaysia)", - "l2_support": "beta", - }, - { - "language_code": "mt", - "language_name": "Maltese", - "l2_support": "na", - }, - { - "language_code": "mwr", - "language_name": "Marwari", - "l2_support": "na", - }, - { - "language_code": "my", - "language_name": "Burmese", - "l2_support": "na", - }, - { - "language_code": "nan", - "language_name": "Min Nan", - "l2_support": "na", - }, - { - "language_code": "nb", - "language_name": "Norwegian (Bokmål)", - "l2_support": "na", - }, - { - "language_code": "ne", - "language_name": "Nepali", - "l2_support": "na", - }, - { - "language_code": "new", - "language_name": "Newari", - "l2_support": "na", - }, - { - "language_code": "nl", - "language_name": "Dutch", - "l2_support": "beta", - }, - { - "language_code": "nl-BE", - "language_name": "Flemish", - "l2_support": "beta", - }, - { - "language_code": "no", - "language_name": "Norwegian", - "l2_support": "na", - }, - { - "language_code": "nr", - "language_name": "South Ndebele", - "l2_support": "na", - }, - { - "language_code": "nso", - "language_name": "Northern Sotho", - "l2_support": "na", - }, - { - "language_code": "nus", - "language_name": "Nuer", - "l2_support": "na", - }, - { - "language_code": "ny", - "language_name": "Nyanja", - "l2_support": "na", - }, - { - "language_code": "oc", - "language_name": "Occitan", - "l2_support": "na", - }, - { - "language_code": "om", - "language_name": "Oromo", - "l2_support": "na", - }, - { - "language_code": "or", - "language_name": "Odia", - "l2_support": "na", - }, - { - "language_code": "pa", - "language_name": "Punjabi", - "l2_support": "beta", - }, - { - "language_code": "pa-Arab", - "language_name": "Punjabi (Shahmukhi)", - "l2_support": "beta", - }, - { - "language_code": "pa-IN", - "language_name": "Punjabi (Gurmukhi)", - "l2_support": "beta", - }, - { - "language_code": "pag", - "language_name": "Pangasinan", - "l2_support": "na", - }, - { - "language_code": "pam", - "language_name": "Pampanga", - "l2_support": "na", - }, - { - "language_code": "pap", - "language_name": "Papiamento", - "l2_support": "na", - }, - { - "language_code": "pl", - "language_name": "Polish", - "l2_support": "beta", - }, - { - "language_code": "ps", - "language_name": "Pashto", - "l2_support": "na", - }, - { - "language_code": "pt", - "language_name": "Portuguese", - "l2_support": "full", - }, - { - "language_code": "pt-BR", - "language_name": "Portuguese (Brazil)", - "l2_support": "full", - }, - { - "language_code": "pt-PT", - "language_name": "Portuguese (Portugal)", - "l2_support": "full", - }, - { - "language_code": "qu", - "language_name": "Quechua", - "l2_support": "na", - }, - { - "language_code": "raj", - "language_name": "Rajasthani", - "l2_support": "na", - }, - { - "language_code": "rn", - "language_name": "Rundi", - "l2_support": "na", - }, - { - "language_code": "ro", - "language_name": "Romanian", - "l2_support": "beta", - }, - { - "language_code": "ro-MD", - "language_name": "Moldovan", - "l2_support": "beta", - }, - { - "language_code": "ro-RO", - "language_name": "Romanian (Romania)", - "l2_support": "beta", - }, - { - "language_code": "rom", - "language_name": "Romany", - "l2_support": "na", - }, - { - "language_code": "ru", - "language_name": "Russian", - "l2_support": "full", - }, - { - "language_code": "rw", - "language_name": "Kinyarwanda", - "l2_support": "na", - }, - { - "language_code": "sa", - "language_name": "Sanskrit", - "l2_support": "na", - }, - { - "language_code": "sat", - "language_name": "Santali", - "l2_support": "na", - }, - { - "language_code": "scn", - "language_name": "Sicilian", - "l2_support": "na", - }, - { - "language_code": "sd", - "language_name": "Sindhi", - "l2_support": "na", - }, - { - "language_code": "sg", - "language_name": "Sango", - "l2_support": "na", - }, - { - "language_code": "shn", - "language_name": "Shan", - "l2_support": "na", - }, - { - "language_code": "si", - "language_name": "Sinhala", - "l2_support": "na", - }, - { - "language_code": "sk", - "language_name": "Slovak", - "l2_support": "beta", - }, - { - "language_code": "sl", - "language_name": "Slovenian", - "l2_support": "na", - }, - { - "language_code": "sm", - "language_name": "Samoan", - "l2_support": "na", - }, - { - "language_code": "sn", - "language_name": "Shona", - "l2_support": "na", - }, - { - "language_code": "so", - "language_name": "Somali", - "l2_support": "na", - }, - { - "language_code": "sq", - "language_name": "Albanian", - "l2_support": "na", - }, - { - "language_code": "sr", - "language_name": "Serbian", - "l2_support": "beta", - }, - { - "language_code": "sr-ME", - "language_name": "Montenegrin", - "l2_support": "beta", - }, - { - "language_code": "sr-RS", - "language_name": "Serbian", - "l2_support": "beta", - }, - { - "language_code": "ss", - "language_name": "Swati", - "l2_support": "na", - }, - { - "language_code": "st", - "language_name": "Southern Sotho", - "l2_support": "na", - }, - { - "language_code": "su", - "language_name": "Sundanese", - "l2_support": "na", - }, - { - "language_code": "sv", - "language_name": "Swedish", - "l2_support": "na", - }, - { - "language_code": "sw", - "language_name": "Swahili", - "l2_support": "na", - }, - { - "language_code": "szl", - "language_name": "Silesian", - "l2_support": "na", - }, - { - "language_code": "ta", - "language_name": "Tamil", - "l2_support": "na", - }, - { - "language_code": "te", - "language_name": "Telugu", - "l2_support": "na", - }, - { - "language_code": "tet", - "language_name": "Tetum", - "l2_support": "na", - }, - { - "language_code": "tg", - "language_name": "Tajik", - "l2_support": "na", - }, - { - "language_code": "th", - "language_name": "Thai", - "l2_support": "na", - }, - { - "language_code": "ti", - "language_name": "Tigrinya", - "l2_support": "na", - }, - { - "language_code": "tk", - "language_name": "Turkmen", - "l2_support": "na", - }, - { - "language_code": "tl", - "language_name": "Tagalog", - "l2_support": "na", - }, - { - "language_code": "tn", - "language_name": "Tswana", - "l2_support": "na", - }, - { - "language_code": "tr", - "language_name": "Turkish", - "l2_support": "na", - }, - { - "language_code": "ts", - "language_name": "Tsonga", - "l2_support": "na", - }, - { - "language_code": "tt", - "language_name": "Tatar", - "l2_support": "na", - }, - { - "language_code": "ug", - "language_name": "Uyghur", - "l2_support": "na", - }, - { - "language_code": "uk", - "language_name": "Ukrainian", - "l2_support": "beta", - }, - { - "language_code": "ur", - "language_name": "Urdu", - "l2_support": "beta", - }, - { - "language_code": "ur-IN", - "language_name": "Urdu (India)", - "l2_support": "beta", - }, - { - "language_code": "ur-PK", - "language_name": "Urdu (Pakistan)", - "l2_support": "beta", - }, - { - "language_code": "uz", - "language_name": "Uzbek", - "l2_support": "na", - }, - { - "language_code": "vi", - "language_name": "Vietnamese", - "l2_support": "full", - }, - { - "language_code": "wuu", - "language_name": "Wu", - "l2_support": "na", - }, - { - "language_code": "xh", - "language_name": "Xhosa", - "l2_support": "na", - }, - { - "language_code": "yi", - "language_name": "Yiddish", - "l2_support": "na", - }, - { - "language_code": "yo", - "language_name": "Yoruba", - "l2_support": "na", - }, - { - "language_code": "yua", - "language_name": "Yucateco", - "l2_support": "na", - }, - { - "language_code": "yue", - "language_name": "Cantonese", - "l2_support": "beta", - }, - { - "language_code": "yue-CN", - "language_name": "Cantonese (China)", - "l2_support": "beta", - }, - { - "language_code": "yue-HK", - "language_name": "Cantonese (Hong Kong)", - "l2_support": "beta", - }, - { - "language_code": "zh", - "language_name": "Chinese", - "l2_support": "full", - }, - { - "language_code": "zh-CN", - "language_name": "Chinese (Simplified)", - "l2_support": "full", - }, - { - "language_code": "zh-TW", - "language_name": "Chinese (Traditional)", - "l2_support": "full", - }, - { - "language_code": "zu", - "language_name": "Zulu", - "l2_support": "na", - } - ]; + {"language_code": "ab", "language_name": "Abkhazian", "l2_support": "na"}, + {"language_code": "ace", "language_name": "Achinese", "l2_support": "na"}, + {"language_code": "ach", "language_name": "Acoli", "l2_support": "na"}, + {"language_code": "af", "language_name": "Afrikaans", "l2_support": "na"}, + {"language_code": "ak", "language_name": "Akan", "l2_support": "na"}, + {"language_code": "alz", "language_name": "Alur", "l2_support": "na"}, + {"language_code": "am", "language_name": "Amharic", "l2_support": "beta"}, + {"language_code": "ar", "language_name": "Arabic", "l2_support": "beta"}, + {"language_code": "as", "language_name": "Assamese", "l2_support": "na"}, + {"language_code": "awa", "language_name": "Awadhi", "l2_support": "na"}, + {"language_code": "ay", "language_name": "Aymara", "l2_support": "na"}, + {"language_code": "az", "language_name": "Azerbaijani", "l2_support": "na"}, + {"language_code": "ba", "language_name": "Bashkir", "l2_support": "na"}, + {"language_code": "ban", "language_name": "Balinese", "l2_support": "na"}, + {"language_code": "bbc", "language_name": "Batak Toba", "l2_support": "na"}, + {"language_code": "be", "language_name": "Belarusian", "l2_support": "na"}, + {"language_code": "bem", "language_name": "Bemba", "l2_support": "na"}, + {"language_code": "bew", "language_name": "Betawi", "l2_support": "na"}, + {"language_code": "bg", "language_name": "Bulgarian", "l2_support": "beta"}, + {"language_code": "bho", "language_name": "Bhojpuri", "l2_support": "na"}, + {"language_code": "bik", "language_name": "Bikol", "l2_support": "na"}, + {"language_code": "bm", "language_name": "Bambara", "l2_support": "na"}, + {"language_code": "bn", "language_name": "Bangla", "l2_support": "beta"}, + { + "language_code": "bn-BD", + "language_name": "Bengali (Bangladesh)", + "l2_support": "beta", + }, + { + "language_code": "bn-IN", + "language_name": "Bengali (India)", + "l2_support": "beta", + }, + {"language_code": "br", "language_name": "Breton", "l2_support": "na"}, + {"language_code": "bs", "language_name": "Bosnian", "l2_support": "na"}, + { + "language_code": "bts", + "language_name": "Batak Simalungun", + "l2_support": "na", + }, + {"language_code": "btx", "language_name": "Batak Karo", "l2_support": "na"}, + {"language_code": "bua", "language_name": "Buriat", "l2_support": "na"}, + {"language_code": "ca", "language_name": "Catalan", "l2_support": "full"}, + {"language_code": "ceb", "language_name": "Cebuano", "l2_support": "na"}, + {"language_code": "cgg", "language_name": "Chiga", "l2_support": "na"}, + {"language_code": "chm", "language_name": "Mari", "l2_support": "na"}, + { + "language_code": "ckb", + "language_name": "Central Kurdish", + "l2_support": "na", + }, + {"language_code": "cnh", "language_name": "Hakha Chin", "l2_support": "na"}, + {"language_code": "co", "language_name": "Corsican", "l2_support": "na"}, + { + "language_code": "crh", + "language_name": "Crimean Turkish", + "l2_support": "na", + }, + { + "language_code": "crs", + "language_name": "Seselwa Creole French", + "l2_support": "na", + }, + {"language_code": "cs", "language_name": "Czech", "l2_support": "beta"}, + {"language_code": "cv", "language_name": "Chuvash", "l2_support": "na"}, + {"language_code": "cy", "language_name": "Welsh", "l2_support": "na"}, + {"language_code": "da", "language_name": "Danish", "l2_support": "beta"}, + {"language_code": "de", "language_name": "German", "l2_support": "full"}, + {"language_code": "din", "language_name": "Dinka", "l2_support": "na"}, + {"language_code": "doi", "language_name": "Dogri", "l2_support": "na"}, + {"language_code": "dov", "language_name": "Dombe", "l2_support": "na"}, + {"language_code": "dv", "language_name": "Divehi", "l2_support": "na"}, + {"language_code": "dz", "language_name": "Dzongkha", "l2_support": "na"}, + {"language_code": "ee", "language_name": "Ewe", "l2_support": "na"}, + {"language_code": "el", "language_name": "Greek", "l2_support": "beta"}, + {"language_code": "en", "language_name": "English", "l2_support": "full"}, + { + "language_code": "en-AU", + "language_name": "English (Australia)", + "l2_support": "full", + }, + { + "language_code": "en-GB", + "language_name": "English (UK)", + "l2_support": "full", + }, + { + "language_code": "en-IN", + "language_name": "English (India)", + "l2_support": "full", + }, + { + "language_code": "en-US", + "language_name": "English (US)", + "l2_support": "full", + }, + {"language_code": "eo", "language_name": "Esperanto", "l2_support": "na"}, + {"language_code": "es", "language_name": "Spanish", "l2_support": "full"}, + { + "language_code": "es-ES", + "language_name": "Spanish (Spain)", + "l2_support": "full", + }, + { + "language_code": "es-MX", + "language_name": "Spanish (Mexico)", + "l2_support": "full", + }, + {"language_code": "et", "language_name": "Estonian", "l2_support": "beta"}, + {"language_code": "eu", "language_name": "Basque", "l2_support": "beta"}, + {"language_code": "fa", "language_name": "Persian", "l2_support": "na"}, + {"language_code": "ff", "language_name": "Fulah", "l2_support": "na"}, + {"language_code": "fi", "language_name": "Finnish", "l2_support": "beta"}, + {"language_code": "fil", "language_name": "Filipino", "l2_support": "na"}, + {"language_code": "fj", "language_name": "Fijian", "l2_support": "na"}, + {"language_code": "fo", "language_name": "Faroese", "l2_support": "na"}, + {"language_code": "fr", "language_name": "French", "l2_support": "full"}, + { + "language_code": "fr-CA", + "language_name": "French (Canada)", + "l2_support": "full", + }, + { + "language_code": "fr-FR", + "language_name": "French (France)", + "l2_support": "full", + }, + { + "language_code": "fy", + "language_name": "Western Frisian", + "l2_support": "na", + }, + {"language_code": "ga", "language_name": "Irish", "l2_support": "na"}, + {"language_code": "gaa", "language_name": "Ga", "l2_support": "na"}, + { + "language_code": "gd", + "language_name": "Scottish Gaelic", + "l2_support": "na", + }, + {"language_code": "gl", "language_name": "Galician", "l2_support": "beta"}, + {"language_code": "gn", "language_name": "Guarani", "l2_support": "na"}, + { + "language_code": "gom", + "language_name": "Goan Konkani", + "l2_support": "na", + }, + {"language_code": "gu", "language_name": "Gujarati", "l2_support": "beta"}, + {"language_code": "ha", "language_name": "Hausa", "l2_support": "na"}, + {"language_code": "haw", "language_name": "Hawaiian", "l2_support": "na"}, + {"language_code": "he", "language_name": "Hebrew", "l2_support": "na"}, + {"language_code": "hi", "language_name": "Hindi", "l2_support": "beta"}, + {"language_code": "hil", "language_name": "Hiligaynon", "l2_support": "na"}, + {"language_code": "hmn", "language_name": "Hmong", "l2_support": "na"}, + { + "language_code": "hne", + "language_name": "Chhattisgarhi", + "l2_support": "na", + }, + {"language_code": "hr", "language_name": "Croatian", "l2_support": "na"}, + {"language_code": "hrx", "language_name": "Hunsrik", "l2_support": "na"}, + { + "language_code": "ht", + "language_name": "Haitian Creole", + "l2_support": "na", + }, + {"language_code": "hu", "language_name": "Hungarian", "l2_support": "beta"}, + {"language_code": "hy", "language_name": "Armenian", "l2_support": "na"}, + { + "language_code": "id", + "language_name": "Indonesian", + "l2_support": "beta", + }, + {"language_code": "ig", "language_name": "Igbo", "l2_support": "na"}, + {"language_code": "ilo", "language_name": "Iloko", "l2_support": "na"}, + {"language_code": "is", "language_name": "Icelandic", "l2_support": "na"}, + {"language_code": "it", "language_name": "Italian", "l2_support": "full"}, + {"language_code": "iw", "language_name": "Hebrew", "l2_support": "na"}, + {"language_code": "ja", "language_name": "Japanese", "l2_support": "full"}, + {"language_code": "jv", "language_name": "Javanese", "l2_support": "na"}, + {"language_code": "ka", "language_name": "Georgian", "l2_support": "na"}, + {"language_code": "kk", "language_name": "Kazakh", "l2_support": "na"}, + {"language_code": "km", "language_name": "Khmer", "l2_support": "na"}, + {"language_code": "kn", "language_name": "Kannada", "l2_support": "beta"}, + {"language_code": "ko", "language_name": "Korean", "l2_support": "full"}, + {"language_code": "kok", "language_name": "Konkani", "l2_support": "na"}, + {"language_code": "kri", "language_name": "Krio", "l2_support": "na"}, + {"language_code": "ks", "language_name": "Kashmiri", "l2_support": "na"}, + { + "language_code": "ktu", + "language_name": "Kituba (Democratic Republic of Congo)", + "l2_support": "na", + }, + {"language_code": "ku", "language_name": "Kurdish", "l2_support": "na"}, + {"language_code": "ky", "language_name": "Kyrgyz", "l2_support": "na"}, + {"language_code": "la", "language_name": "Latin", "l2_support": "na"}, + { + "language_code": "lb", + "language_name": "Luxembourgish", + "l2_support": "na", + }, + {"language_code": "lg", "language_name": "Ganda", "l2_support": "na"}, + {"language_code": "li", "language_name": "Limburgish", "l2_support": "na"}, + {"language_code": "lij", "language_name": "Ligurian", "l2_support": "na"}, + {"language_code": "lmo", "language_name": "Lombard", "l2_support": "na"}, + {"language_code": "ln", "language_name": "Lingala", "l2_support": "na"}, + {"language_code": "lo", "language_name": "Lao", "l2_support": "na"}, + { + "language_code": "lt", + "language_name": "Lithuanian", + "l2_support": "beta", + }, + {"language_code": "ltg", "language_name": "Latgalian", "l2_support": "na"}, + { + "language_code": "luo", + "language_name": "Luo (Kenya and Tanzania)", + "l2_support": "na", + }, + {"language_code": "lus", "language_name": "Mizo", "l2_support": "na"}, + {"language_code": "lv", "language_name": "Latvian", "l2_support": "beta"}, + {"language_code": "mai", "language_name": "Maithili", "l2_support": "na"}, + {"language_code": "mak", "language_name": "Makasar", "l2_support": "na"}, + {"language_code": "mg", "language_name": "Malagasy", "l2_support": "na"}, + {"language_code": "mi", "language_name": "Māori", "l2_support": "na"}, + { + "language_code": "min", + "language_name": "Minangkabau", + "l2_support": "na", + }, + {"language_code": "mk", "language_name": "Macedonian", "l2_support": "na"}, + {"language_code": "ml", "language_name": "Malayalam", "l2_support": "na"}, + {"language_code": "mn", "language_name": "Mongolian", "l2_support": "beta"}, + {"language_code": "mni", "language_name": "Manipuri", "l2_support": "na"}, + {"language_code": "mr", "language_name": "Marathi", "l2_support": "beta"}, + {"language_code": "ms", "language_name": "Malay", "l2_support": "beta"}, + { + "language_code": "ms-Arab", + "language_name": "Malay (Arabic)", + "l2_support": "beta", + }, + { + "language_code": "ms-MY", + "language_name": "Malay (Malaysia)", + "l2_support": "beta", + }, + {"language_code": "mt", "language_name": "Maltese", "l2_support": "na"}, + {"language_code": "mwr", "language_name": "Marwari", "l2_support": "na"}, + {"language_code": "my", "language_name": "Burmese", "l2_support": "na"}, + {"language_code": "nan", "language_name": "Min Nan", "l2_support": "na"}, + { + "language_code": "nb", + "language_name": "Norwegian (Bokmål)", + "l2_support": "na", + }, + {"language_code": "ne", "language_name": "Nepali", "l2_support": "na"}, + {"language_code": "new", "language_name": "Newari", "l2_support": "na"}, + {"language_code": "nl", "language_name": "Dutch", "l2_support": "beta"}, + { + "language_code": "nl-BE", + "language_name": "Flemish", + "l2_support": "beta", + }, + {"language_code": "no", "language_name": "Norwegian", "l2_support": "na"}, + { + "language_code": "nr", + "language_name": "South Ndebele", + "l2_support": "na", + }, + { + "language_code": "nso", + "language_name": "Northern Sotho", + "l2_support": "na", + }, + {"language_code": "nus", "language_name": "Nuer", "l2_support": "na"}, + {"language_code": "ny", "language_name": "Nyanja", "l2_support": "na"}, + {"language_code": "oc", "language_name": "Occitan", "l2_support": "na"}, + {"language_code": "om", "language_name": "Oromo", "l2_support": "na"}, + {"language_code": "or", "language_name": "Odia", "l2_support": "na"}, + {"language_code": "pa", "language_name": "Punjabi", "l2_support": "beta"}, + { + "language_code": "pa-Arab", + "language_name": "Punjabi (Shahmukhi)", + "l2_support": "beta", + }, + { + "language_code": "pa-IN", + "language_name": "Punjabi (Gurmukhi)", + "l2_support": "beta", + }, + {"language_code": "pag", "language_name": "Pangasinan", "l2_support": "na"}, + {"language_code": "pam", "language_name": "Pampanga", "l2_support": "na"}, + {"language_code": "pap", "language_name": "Papiamento", "l2_support": "na"}, + {"language_code": "pl", "language_name": "Polish", "l2_support": "beta"}, + {"language_code": "ps", "language_name": "Pashto", "l2_support": "na"}, + { + "language_code": "pt", + "language_name": "Portuguese", + "l2_support": "full", + }, + { + "language_code": "pt-BR", + "language_name": "Portuguese (Brazil)", + "l2_support": "full", + }, + { + "language_code": "pt-PT", + "language_name": "Portuguese (Portugal)", + "l2_support": "full", + }, + {"language_code": "qu", "language_name": "Quechua", "l2_support": "na"}, + {"language_code": "raj", "language_name": "Rajasthani", "l2_support": "na"}, + {"language_code": "rn", "language_name": "Rundi", "l2_support": "na"}, + {"language_code": "ro", "language_name": "Romanian", "l2_support": "beta"}, + { + "language_code": "ro-MD", + "language_name": "Moldovan", + "l2_support": "beta", + }, + { + "language_code": "ro-RO", + "language_name": "Romanian (Romania)", + "l2_support": "beta", + }, + {"language_code": "rom", "language_name": "Romany", "l2_support": "na"}, + {"language_code": "ru", "language_name": "Russian", "l2_support": "full"}, + {"language_code": "rw", "language_name": "Kinyarwanda", "l2_support": "na"}, + {"language_code": "sa", "language_name": "Sanskrit", "l2_support": "na"}, + {"language_code": "sat", "language_name": "Santali", "l2_support": "na"}, + {"language_code": "scn", "language_name": "Sicilian", "l2_support": "na"}, + {"language_code": "sd", "language_name": "Sindhi", "l2_support": "na"}, + {"language_code": "sg", "language_name": "Sango", "l2_support": "na"}, + {"language_code": "shn", "language_name": "Shan", "l2_support": "na"}, + {"language_code": "si", "language_name": "Sinhala", "l2_support": "na"}, + {"language_code": "sk", "language_name": "Slovak", "l2_support": "beta"}, + {"language_code": "sl", "language_name": "Slovenian", "l2_support": "na"}, + {"language_code": "sm", "language_name": "Samoan", "l2_support": "na"}, + {"language_code": "sn", "language_name": "Shona", "l2_support": "na"}, + {"language_code": "so", "language_name": "Somali", "l2_support": "na"}, + {"language_code": "sq", "language_name": "Albanian", "l2_support": "na"}, + {"language_code": "sr", "language_name": "Serbian", "l2_support": "beta"}, + { + "language_code": "sr-ME", + "language_name": "Montenegrin", + "l2_support": "beta", + }, + { + "language_code": "sr-RS", + "language_name": "Serbian", + "l2_support": "beta", + }, + {"language_code": "ss", "language_name": "Swati", "l2_support": "na"}, + { + "language_code": "st", + "language_name": "Southern Sotho", + "l2_support": "na", + }, + {"language_code": "su", "language_name": "Sundanese", "l2_support": "na"}, + {"language_code": "sv", "language_name": "Swedish", "l2_support": "na"}, + {"language_code": "sw", "language_name": "Swahili", "l2_support": "na"}, + {"language_code": "szl", "language_name": "Silesian", "l2_support": "na"}, + {"language_code": "ta", "language_name": "Tamil", "l2_support": "na"}, + {"language_code": "te", "language_name": "Telugu", "l2_support": "na"}, + {"language_code": "tet", "language_name": "Tetum", "l2_support": "na"}, + {"language_code": "tg", "language_name": "Tajik", "l2_support": "na"}, + {"language_code": "th", "language_name": "Thai", "l2_support": "na"}, + {"language_code": "ti", "language_name": "Tigrinya", "l2_support": "na"}, + {"language_code": "tk", "language_name": "Turkmen", "l2_support": "na"}, + {"language_code": "tl", "language_name": "Tagalog", "l2_support": "na"}, + {"language_code": "tn", "language_name": "Tswana", "l2_support": "na"}, + {"language_code": "tr", "language_name": "Turkish", "l2_support": "na"}, + {"language_code": "ts", "language_name": "Tsonga", "l2_support": "na"}, + {"language_code": "tt", "language_name": "Tatar", "l2_support": "na"}, + {"language_code": "ug", "language_name": "Uyghur", "l2_support": "na"}, + {"language_code": "uk", "language_name": "Ukrainian", "l2_support": "beta"}, + {"language_code": "ur", "language_name": "Urdu", "l2_support": "beta"}, + { + "language_code": "ur-IN", + "language_name": "Urdu (India)", + "l2_support": "beta", + }, + { + "language_code": "ur-PK", + "language_name": "Urdu (Pakistan)", + "l2_support": "beta", + }, + {"language_code": "uz", "language_name": "Uzbek", "l2_support": "na"}, + { + "language_code": "vi", + "language_name": "Vietnamese", + "l2_support": "full", + }, + {"language_code": "wuu", "language_name": "Wu", "l2_support": "na"}, + {"language_code": "xh", "language_name": "Xhosa", "l2_support": "na"}, + {"language_code": "yi", "language_name": "Yiddish", "l2_support": "na"}, + {"language_code": "yo", "language_name": "Yoruba", "l2_support": "na"}, + {"language_code": "yua", "language_name": "Yucateco", "l2_support": "na"}, + { + "language_code": "yue", + "language_name": "Cantonese", + "l2_support": "beta", + }, + { + "language_code": "yue-CN", + "language_name": "Cantonese (China)", + "l2_support": "beta", + }, + { + "language_code": "yue-HK", + "language_name": "Cantonese (Hong Kong)", + "l2_support": "beta", + }, + {"language_code": "zh", "language_name": "Chinese", "l2_support": "full"}, + { + "language_code": "zh-CN", + "language_name": "Chinese (Simplified)", + "l2_support": "full", + }, + { + "language_code": "zh-TW", + "language_name": "Chinese (Traditional)", + "l2_support": "full", + }, + {"language_code": "zu", "language_name": "Zulu", "l2_support": "na"}, + ]; } diff --git a/lib/pangea/languages/language_model.dart b/lib/pangea/languages/language_model.dart index 4eda72f43..6470e6e76 100644 --- a/lib/pangea/languages/language_model.dart +++ b/lib/pangea/languages/language_model.dart @@ -25,12 +25,10 @@ class LanguageModel { TextDirection? textDirection, }) : _textDirection = textDirection; - factory LanguageModel.fromJson(json) { - final String code = json['language_code'] ?? - codeFromNameOrCode( - json['language_name'], - json['language_flag'], - ); + factory LanguageModel.fromJson(Map json) { + final String code = + json['language_code'] ?? + codeFromNameOrCode(json['language_name'], json['language_flag']); return LanguageModel( langCode: code, @@ -50,14 +48,14 @@ class LanguageModel { } Map toJson() => { - 'language_code': langCode, - 'language_name': displayName, - 'script': script, - 'l2_support': l2Support.storageString, - 'text_direction': textDirection.name, - 'locale_emoji': localeEmoji, - 'voices': voices, - }; + 'language_code': langCode, + 'language_name': displayName, + 'script': script, + 'l2_support': l2Support.storageString, + 'text_direction': textDirection.name, + 'locale_emoji': localeEmoji, + 'voices': voices, + }; bool get l2 => l2Support != L2SupportEnum.na; @@ -74,9 +72,9 @@ class LanguageModel { //PTODO - add flag for unknown static LanguageModel get unknown => LanguageModel( - langCode: LanguageKeys.unknownLanguage, - displayName: "Unknown", - ); + langCode: LanguageKeys.unknownLanguage, + displayName: "Unknown", + ); String getDisplayName(BuildContext context) { final langKey = "${langCode.replaceAll("-", "")}DisplayName"; diff --git a/lib/pangea/languages/language_repo.dart b/lib/pangea/languages/language_repo.dart index 9ad7b6329..6f0f26731 100644 --- a/lib/pangea/languages/language_repo.dart +++ b/lib/pangea/languages/language_repo.dart @@ -23,13 +23,9 @@ class LanguageRepo { } static Future> _fetch() async { - final Requests req = Requests( - choreoApiKey: Environment.choreoApiKey, - ); + final Requests req = Requests(choreoApiKey: Environment.choreoApiKey); - final Response res = await req.get( - url: PApiUrls.getLanguages, - ); + final Response res = await req.get(url: PApiUrls.getLanguages); if (res.statusCode != 200) { throw Exception( diff --git a/lib/pangea/languages/p_language_store.dart b/lib/pangea/languages/p_language_store.dart index 60327881b..6cfe4b042 100644 --- a/lib/pangea/languages/p_language_store.dart +++ b/lib/pangea/languages/p_language_store.dart @@ -34,10 +34,11 @@ class PLanguageStore { ) .toList(); - static Future initialize({forceRefresh = false}) async { + static Future initialize({bool forceRefresh = false}) async { _langList = await _getCachedLanguages(); final isOutdated = await _shouldFetch; - final shouldFetch = forceRefresh || + final shouldFetch = + forceRefresh || isOutdated || _langList.isEmpty || _langList.every((lang) => !lang.l2); @@ -47,8 +48,8 @@ class PLanguageStore { _langList = result.isValue ? result.asValue!.value : LanguageConstants.languageList - .map((e) => LanguageModel.fromJson(e)) - .toList(); + .map((e) => LanguageModel.fromJson(e)) + .toList(); await _MyShared.saveJson(PrefKey.languagesKey, { PrefKey.languagesKey: _langList.map((e) => e.toJson()).toList(), @@ -105,13 +106,11 @@ class PLanguageStore { } static LanguageModel? byLangCode(String langCode) => - _langList.firstWhereOrNull( - (element) => element.langCode == langCode, - ); + _langList.firstWhereOrNull((element) => element.langCode == langCode); } class _MyShared { - static saveString(String key, String value) async { + static Future saveString(String key, String value) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString(key, value); } @@ -122,7 +121,7 @@ class _MyShared { return source; } - static saveJson(String key, Map value) async { + static Future saveJson(String key, Map value) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString(key, json.encode(value)); } diff --git a/lib/pangea/learning_settings/country_picker_tile.dart b/lib/pangea/learning_settings/country_picker_tile.dart index 5391d4164..7eb08ce7e 100644 --- a/lib/pangea/learning_settings/country_picker_tile.dart +++ b/lib/pangea/learning_settings/country_picker_tile.dart @@ -33,7 +33,8 @@ class CountryPickerDropdownState extends State { Widget build(BuildContext context) { final countries = CountryService().getAll(); return DropdownButtonFormField2( - customButton: widget.learningController.country != null && + customButton: + widget.learningController.country != null && countries.any( (country) => country.name == widget.learningController.country!.name, @@ -43,9 +44,7 @@ class CountryPickerDropdownState extends State { isDropdown: true, ) : null, - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.zero, - ), + menuItemStyleData: const MenuItemStyleData(padding: EdgeInsets.zero), isExpanded: true, decoration: InputDecoration( labelText: L10n.of(context).countryInformation, @@ -65,10 +64,7 @@ class CountryPickerDropdownState extends State { color: widget.learningController.country == country ? Theme.of(context).colorScheme.primary.withAlpha(20) : Colors.transparent, - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 12, - ), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: CountryPickerTile(country), ), ), @@ -84,9 +80,7 @@ class CountryPickerDropdownState extends State { child: TextField( autofocus: true, controller: _searchController, - decoration: const InputDecoration( - prefixIcon: Icon(Icons.search), - ), + decoration: const InputDecoration(prefixIcon: Icon(Icons.search)), ), ), searchMatchFn: (item, searchValue) { @@ -108,11 +102,7 @@ class CountryPickerTile extends StatelessWidget { final Country country; final bool isDropdown; - const CountryPickerTile( - this.country, { - super.key, - this.isDropdown = false, - }); + const CountryPickerTile(this.country, {super.key, this.isDropdown = false}); @override Widget build(BuildContext context) { @@ -127,10 +117,7 @@ class CountryPickerTile extends StatelessWidget { ), const SizedBox(width: 10), Text( - CountryDisplayUtil.countryDisplayName( - country.name, - context, - ) ?? + CountryDisplayUtil.countryDisplayName(country.name, context) ?? '', style: const TextStyle().copyWith( color: Theme.of(context).textTheme.bodyLarge!.color, diff --git a/lib/pangea/learning_settings/disable_language_tools_popup.dart b/lib/pangea/learning_settings/disable_language_tools_popup.dart index 120161211..5f37c189d 100644 --- a/lib/pangea/learning_settings/disable_language_tools_popup.dart +++ b/lib/pangea/learning_settings/disable_language_tools_popup.dart @@ -9,19 +9,13 @@ import 'package:fluffychat/widgets/matrix.dart'; class DisableLanguageToolsPopup extends StatelessWidget { final String overlayId; - const DisableLanguageToolsPopup({ - super.key, - required this.overlayId, - }); + const DisableLanguageToolsPopup({super.key, required this.overlayId}); Future _disableLanguageTools() async { - await MatrixState.pangeaController.userController.updateProfile( - (profile) { - profile.toolSettings.autoIGC = false; - return profile; - }, - waitForDataInSync: true, - ); + await MatrixState.pangeaController.userController.updateProfile((profile) { + profile.toolSettings.autoIGC = false; + return profile; + }, waitForDataInSync: true); } @override @@ -53,8 +47,9 @@ class DisableLanguageToolsPopup extends StatelessWidget { MatrixState.pAnyState.closeOverlay(overlayId); }, style: TextButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.primary.withAlpha(25), + backgroundColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(25), ), child: Text(L10n.of(context).confirm), ), diff --git a/lib/pangea/learning_settings/gender_dropdown.dart b/lib/pangea/learning_settings/gender_dropdown.dart index 352bf6956..e97ad6fa3 100644 --- a/lib/pangea/learning_settings/gender_dropdown.dart +++ b/lib/pangea/learning_settings/gender_dropdown.dart @@ -28,14 +28,11 @@ class GenderDropdown extends StatelessWidget { final l10n = L10n.of(context); return DropdownButtonFormField2( - customButton: - CustomDropdownTextButton(text: initialGender.title(context)), - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.zero, - ), - decoration: InputDecoration( - labelText: l10n.gender, + customButton: CustomDropdownTextButton( + text: initialGender.title(context), ), + menuItemStyleData: const MenuItemStyleData(padding: EdgeInsets.zero), + decoration: InputDecoration(labelText: l10n.gender), isExpanded: true, dropdownStyleData: DropdownStyleData( maxHeight: kIsWeb ? 500 : null, @@ -50,10 +47,7 @@ class GenderDropdown extends StatelessWidget { value: genderOption, child: Container( color: Colors.transparent, - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 12, - ), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: Text( genderOption.title(context), style: const TextStyle().copyWith( diff --git a/lib/pangea/learning_settings/gender_enum.dart b/lib/pangea/learning_settings/gender_enum.dart index 47f7bc82c..57e7323c2 100644 --- a/lib/pangea/learning_settings/gender_enum.dart +++ b/lib/pangea/learning_settings/gender_enum.dart @@ -2,12 +2,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; -enum GenderEnum { - unselected, - woman, - man, - other, -} +enum GenderEnum { unselected, woman, man, other } extension GenderEnumExtension on GenderEnum { String get string { diff --git a/lib/pangea/learning_settings/language_mismatch_popup.dart b/lib/pangea/learning_settings/language_mismatch_popup.dart index 34f0dcb6c..c0e963fe9 100644 --- a/lib/pangea/learning_settings/language_mismatch_popup.dart +++ b/lib/pangea/learning_settings/language_mismatch_popup.dart @@ -21,13 +21,10 @@ class LanguageMismatchPopup extends StatelessWidget { }); Future _updateLanguage() async { - await MatrixState.pangeaController.userController.updateProfile( - (profile) { - profile.userSettings.targetLanguage = targetLanguage; - return profile; - }, - waitForDataInSync: true, - ); + await MatrixState.pangeaController.userController.updateProfile((profile) { + profile.userSettings.targetLanguage = targetLanguage; + return profile; + }, waitForDataInSync: true); onConfirm(); } @@ -60,8 +57,9 @@ class LanguageMismatchPopup extends StatelessWidget { MatrixState.pAnyState.closeOverlay(overlayId); }, style: TextButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.primary.withAlpha(25), + backgroundColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(25), ), child: Text(L10n.of(context).confirm), ), diff --git a/lib/pangea/learning_settings/language_mismatch_repo.dart b/lib/pangea/learning_settings/language_mismatch_repo.dart index 430f3a586..d31e30d05 100644 --- a/lib/pangea/learning_settings/language_mismatch_repo.dart +++ b/lib/pangea/learning_settings/language_mismatch_repo.dart @@ -11,10 +11,7 @@ class LanguageMismatchRepo { static Future setRoom(String roomId) async => _set(_roomKey(roomId)); static Future _set(String key) async { - await _storage.write( - key, - DateTime.now().toIso8601String(), - ); + await _storage.write(key, DateTime.now().toIso8601String()); } static bool _get(String key) { diff --git a/lib/pangea/learning_settings/p_language_dialog.dart b/lib/pangea/learning_settings/p_language_dialog.dart index 92a79d816..ae0e61573 100644 --- a/lib/pangea/learning_settings/p_language_dialog.dart +++ b/lib/pangea/learning_settings/p_language_dialog.dart @@ -97,24 +97,17 @@ Future pLanguageDialog( future: () async { try { await pangeaController.userController - .updateProfile( - (profile) { - profile.userSettings.sourceLanguage = - selectedSourceLanguage?.langCode; - profile.userSettings.targetLanguage = - selectedTargetLanguage.langCode; - return profile; - }, - waitForDataInSync: true, - ); + .updateProfile((profile) { + profile.userSettings.sourceLanguage = + selectedSourceLanguage?.langCode; + profile.userSettings.targetLanguage = + selectedTargetLanguage.langCode; + return profile; + }, waitForDataInSync: true); Navigator.pop(context); } catch (err, s) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: err, - s: s, - data: {}, - ); + ErrorHandler.logError(e: err, s: s, data: {}); rethrow; } finally { callback(); @@ -126,8 +119,9 @@ Future pLanguageDialog( content: Text( L10n.of(context).noIdenticalLanguages, ), - backgroundColor: - Theme.of(context).colorScheme.primary, + backgroundColor: Theme.of( + context, + ).colorScheme.primary, ), ); }, diff --git a/lib/pangea/learning_settings/p_language_dropdown.dart b/lib/pangea/learning_settings/p_language_dropdown.dart index 5957e003a..146f4164e 100644 --- a/lib/pangea/learning_settings/p_language_dropdown.dart +++ b/lib/pangea/learning_settings/p_language_dropdown.dart @@ -54,8 +54,9 @@ class PLanguageDropdownState extends State { // if there is no initial language, the system language should be the first in the list // otherwise, display in alphabetical order - final List languagePriority = - widget.initialLanguage == null ? [systemLang] : []; + final List languagePriority = widget.initialLanguage == null + ? [systemLang] + : []; int sortLanguages(LanguageModel a, LanguageModel b) { final String aLang = a.langCode; @@ -85,7 +86,8 @@ class PLanguageDropdownState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ DropdownButtonFormField2( - customButton: widget.initialLanguage != null && + customButton: + widget.initialLanguage != null && sortedLanguages.contains(widget.initialLanguage) ? LanguageDropDownEntry( languageModel: widget.initialLanguage!, @@ -94,9 +96,7 @@ class PLanguageDropdownState extends State { enabled: widget.enabled, ) : null, - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.zero, - ), + menuItemStyleData: const MenuItemStyleData(padding: EdgeInsets.zero), decoration: InputDecoration( labelText: widget.decorationText, enabledBorder: hasError @@ -122,7 +122,8 @@ class PLanguageDropdownState extends State { maxHeight: kIsWeb ? 500 : null, decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), - color: widget.backgroundColor ?? + color: + widget.backgroundColor ?? Theme.of(context).colorScheme.surfaceContainerHigh, ), ), @@ -161,11 +162,8 @@ class PLanguageDropdownState extends State { ), ), ), - searchMatchFn: (item, searchValue) => LanguageModel.search( - item.value, - searchValue, - context, - ), + searchMatchFn: (item, searchValue) => + LanguageModel.search(item.value, searchValue, context), ), onMenuStateChange: (isOpen) { if (!isOpen) _searchController.clear(); @@ -177,9 +175,7 @@ class PLanguageDropdownState extends State { child: widget.error == null ? const SizedBox.shrink() : Padding( - padding: const EdgeInsets.symmetric( - vertical: 5, - ), + padding: const EdgeInsets.symmetric(vertical: 5), child: Text( widget.error!, style: TextStyle( @@ -214,10 +210,7 @@ class LanguageDropDownEntry extends StatelessWidget { children: [ Opacity( opacity: enabled ? 1 : 0.5, - child: Avatar( - name: languageModel.langCode, - size: 30, - ), + child: Avatar(name: languageModel.langCode, size: 30), ), const SizedBox(width: 10), Expanded( diff --git a/lib/pangea/learning_settings/p_question_container.dart b/lib/pangea/learning_settings/p_question_container.dart index 95a9b7c20..ff095b7ca 100644 --- a/lib/pangea/learning_settings/p_question_container.dart +++ b/lib/pangea/learning_settings/p_question_container.dart @@ -5,7 +5,7 @@ class PQuestionContainer extends StatelessWidget { const PQuestionContainer({super.key, required this.title}); @override Widget build(BuildContext context) { - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); return Container( constraints: const BoxConstraints(minWidth: 100, maxWidth: 650), padding: EdgeInsets.all(size.height * 0.01), diff --git a/lib/pangea/learning_settings/p_settings_switch_list_tile.dart b/lib/pangea/learning_settings/p_settings_switch_list_tile.dart index a0e928def..edb55378d 100644 --- a/lib/pangea/learning_settings/p_settings_switch_list_tile.dart +++ b/lib/pangea/learning_settings/p_settings_switch_list_tile.dart @@ -58,10 +58,7 @@ class PSettingsSwitchListTileState e: err, m: "Failed to updates user setting", s: s, - data: { - "newValue": newValue, - "currentValue": currentValue, - }, + data: {"newValue": newValue, "currentValue": currentValue}, ); } } diff --git a/lib/pangea/learning_settings/settings_learning.dart b/lib/pangea/learning_settings/settings_learning.dart index a874c3629..34521a6a2 100644 --- a/lib/pangea/learning_settings/settings_learning.dart +++ b/lib/pangea/learning_settings/settings_learning.dart @@ -29,10 +29,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class SettingsLearning extends StatefulWidget { final bool isDialog; - const SettingsLearning({ - this.isDialog = true, - super.key, - }); + const SettingsLearning({this.isDialog = true, super.key}); @override SettingsLearningController createState() => SettingsLearningController(); @@ -118,10 +115,7 @@ class SettingsLearningController extends State { await showFutureLoadingDialog( context: context, future: () async => pangeaController.userController - .updateProfile( - (_) => _profile, - waitForDataInSync: true, - ) + .updateProfile((_) => _profile, waitForDataInSync: true) .timeout(const Duration(seconds: 15)), ); Navigator.of(context).pop(); @@ -131,13 +125,11 @@ class SettingsLearningController extends State { _profile.instructionSettings = InstructionSettings(); await showFutureLoadingDialog( context: context, - future: () async => pangeaController.userController.updateProfile( - (profile) { - profile.instructionSettings = InstructionSettings(); - return profile; - }, - waitForDataInSync: true, - ), + future: () async => + pangeaController.userController.updateProfile((profile) { + profile.instructionSettings = InstructionSettings(); + return profile; + }, waitForDataInSync: true), onError: (e, s) { debugger(when: kDebugMode); ErrorHandler.logError( @@ -273,11 +265,7 @@ class SettingsLearningController extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text(title), - if (steps != null) - Text( - steps, - textAlign: TextAlign.start, - ), + if (steps != null) Text(steps, textAlign: TextAlign.start), if (description != null) Text(description), ], ), @@ -289,10 +277,7 @@ class SettingsLearningController extends State { Navigator.of(context).pop(); }, ), - TextButton( - onPressed: buttonAction, - child: Text(buttonText), - ), + TextButton(onPressed: buttonAction, child: Text(buttonText)), ], ); }, @@ -301,8 +286,8 @@ class SettingsLearningController extends State { LanguageModel? get _targetLanguage => _profile.userSettings.targetLanguage != null - ? PLanguageStore.byLangCode(_profile.userSettings.targetLanguage!) - : null; + ? PLanguageStore.byLangCode(_profile.userSettings.targetLanguage!) + : null; GenderEnum get gender => _profile.userSettings.gender; @@ -344,13 +329,13 @@ class SettingsLearningController extends State { LanguageModel? get _selectedBaseLanguage => _profile.userSettings.sourceLanguage != null - ? PLanguageStore.byLangCode(_profile.userSettings.sourceLanguage!) - : null; + ? PLanguageStore.byLangCode(_profile.userSettings.sourceLanguage!) + : null; LanguageModel? get _selectedTargetLanguage => _profile.userSettings.targetLanguage != null - ? PLanguageStore.byLangCode(_profile.userSettings.targetLanguage!) - : null; + ? PLanguageStore.byLangCode(_profile.userSettings.targetLanguage!) + : null; LanguageModel? get userL1 => pangeaController.userController.userL1; LanguageModel? get userL2 => pangeaController.userController.userL2; diff --git a/lib/pangea/learning_settings/settings_learning_view.dart b/lib/pangea/learning_settings/settings_learning_view.dart index fd12bd93a..9a1f949e4 100644 --- a/lib/pangea/learning_settings/settings_learning_view.dart +++ b/lib/pangea/learning_settings/settings_learning_view.dart @@ -35,9 +35,7 @@ class SettingsLearningView extends StatelessWidget { appBar: AppBar( automaticallyImplyLeading: !controller.widget.isDialog, centerTitle: true, - title: Text( - L10n.of(context).learningSettings, - ), + title: Text(L10n.of(context).learningSettings), leading: controller.widget.isDialog ? IconButton( icon: const Icon(Icons.close), @@ -60,46 +58,53 @@ class SettingsLearningView extends StatelessWidget { spacing: 16.0, children: [ Padding( - padding: - const EdgeInsets.symmetric(horizontal: 16.0), + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), child: Column( spacing: 16.0, children: [ PLanguageDropdown( onChange: (lang) => controller.setSelectedLanguage( - sourceLanguage: lang, - ), + sourceLanguage: lang, + ), initialLanguage: controller.selectedSourceLanguage ?? - LanguageModel.unknown, - languages: MatrixState.pangeaController - .pLanguageStore.baseOptions, + LanguageModel.unknown, + languages: MatrixState + .pangeaController + .pLanguageStore + .baseOptions, isL2List: false, - decorationText: - L10n.of(context).myBaseLanguage, + decorationText: L10n.of( + context, + ).myBaseLanguage, hasError: controller.languageMatchError != null, - backgroundColor: Theme.of(context) - .colorScheme - .surfaceContainerHigh, + backgroundColor: Theme.of( + context, + ).colorScheme.surfaceContainerHigh, ), PLanguageDropdown( onChange: (lang) => controller.setSelectedLanguage( - targetLanguage: lang, - ), + targetLanguage: lang, + ), initialLanguage: controller.selectedTargetLanguage, - languages: MatrixState.pangeaController - .pLanguageStore.targetOptions, + languages: MatrixState + .pangeaController + .pLanguageStore + .targetOptions, isL2List: true, - decorationText: - L10n.of(context).iWantToLearn, + decorationText: L10n.of( + context, + ).iWantToLearn, error: controller.languageMatchError, - backgroundColor: Theme.of(context) - .colorScheme - .surfaceContainerHigh, + backgroundColor: Theme.of( + context, + ).colorScheme.surfaceContainerHigh, ), if (controller.userL1?.langCodeShort == controller.userL2?.langCodeShort) @@ -112,18 +117,19 @@ class SettingsLearningView extends StatelessWidget { children: [ Icon( Icons.info_outlined, - color: Theme.of(context) - .colorScheme - .error, + color: Theme.of( + context, + ).colorScheme.error, ), Flexible( child: Text( - L10n.of(context) - .noIdenticalLanguages, + L10n.of( + context, + ).noIdenticalLanguages, style: TextStyle( - color: Theme.of(context) - .colorScheme - .error, + color: Theme.of( + context, + ).colorScheme.error, ), ), ), @@ -158,13 +164,12 @@ class SettingsLearningView extends StatelessWidget { ), ), ...ToolSetting.values - .where( - (tool) => tool.isAvailableSetting, - ) + .where((tool) => tool.isAvailableSetting) .map( (toolSetting) => _ProfileSwitchTile( - value: - controller.getToolSetting(toolSetting), + value: controller.getToolSetting( + toolSetting, + ), setting: toolSetting, onChanged: (v) { controller.updateToolSetting( @@ -182,17 +187,13 @@ class SettingsLearningView extends StatelessWidget { SwitchListTile.adaptive( value: controller.publicProfile, onChanged: controller.setPublicProfile, - title: Text( - L10n.of(context).publicProfileTitle, - ), + title: Text(L10n.of(context).publicProfileTitle), subtitle: Text( L10n.of(context).publicProfileDesc, ), activeThumbColor: AppConfig.activeToggleColor, ), - ResetInstructionsListTile( - controller: controller, - ), + ResetInstructionsListTile(controller: controller), ], ), ), diff --git a/lib/pangea/learning_settings/voice_dropdown.dart b/lib/pangea/learning_settings/voice_dropdown.dart index ba105c60d..6362481ef 100644 --- a/lib/pangea/learning_settings/voice_dropdown.dart +++ b/lib/pangea/learning_settings/voice_dropdown.dart @@ -23,17 +23,16 @@ class VoiceDropdown extends StatelessWidget { @override Widget build(BuildContext context) { final voices = (language?.voices ?? []); - final value = - this.value != null && voices.contains(this.value) ? this.value : null; + final value = this.value != null && voices.contains(this.value) + ? this.value + : null; return DropdownButtonFormField2( - customButton: - value != null ? CustomDropdownTextButton(text: value) : null, + customButton: value != null + ? CustomDropdownTextButton(text: value) + : null, menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), + padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), ), decoration: InputDecoration( labelText: L10n.of(context).voiceDropdownTitle, @@ -47,10 +46,7 @@ class VoiceDropdown extends StatelessWidget { ), ), items: voices.map((voice) { - return DropdownMenuItem( - value: voice, - child: Text(voice), - ); + return DropdownMenuItem(value: voice, child: Text(voice)); }).toList(), onChanged: enabled ? onChanged : null, value: voices.contains(value) ? value : null, diff --git a/lib/pangea/lemmas/lemma.dart b/lib/pangea/lemmas/lemma.dart index 26a551620..243d101f6 100644 --- a/lib/pangea/lemmas/lemma.dart +++ b/lib/pangea/lemmas/lemma.dart @@ -11,11 +11,7 @@ class Lemma { /// server handles this determination final bool saveVocab; - Lemma({ - required this.text, - required this.saveVocab, - required this.form, - }); + Lemma({required this.text, required this.saveVocab, required this.form}); factory Lemma.fromJson(Map json) { return Lemma( @@ -26,9 +22,7 @@ class Lemma { } Map toJson() { - final Map data = { - 'text': text, - }; + final Map data = {'text': text}; if (saveVocab) { data['save_vocab'] = saveVocab; diff --git a/lib/pangea/lemmas/lemma_highlight_emoji_row.dart b/lib/pangea/lemmas/lemma_highlight_emoji_row.dart index 241b7f85d..5378bf935 100644 --- a/lib/pangea/lemmas/lemma_highlight_emoji_row.dart +++ b/lib/pangea/lemmas/lemma_highlight_emoji_row.dart @@ -51,56 +51,56 @@ class LemmaHighlightEmojiRowState extends State return switch (controller.state) { AsyncError() => const SizedBox.shrink(), AsyncLoaded(value: final lemmaInfo) => SizedBox( - height: 70.0, - child: Row( - spacing: 4.0, - mainAxisSize: MainAxisSize.min, - children: [ - ...lemmaInfo.emoji.map( - (emoji) { - final targetId = "${widget.targetId}-$emoji"; - return EmojiChoiceItem( - cId: widget.cId, - emoji: emoji, - onSelectEmoji: () => - widget.onEmojiSelected(emoji, targetId), - selected: widget.emoji == emoji, - transformTargetId: targetId, - badge: widget.emoji == emoji - ? widget.selectedEmojiBadge - : null, - showShimmer: widget.emoji == null, - enabled: widget.enabled, - ); - }, - ), - ], - ), + height: 70.0, + child: Row( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + children: [ + ...lemmaInfo.emoji.map((emoji) { + final targetId = "${widget.targetId}-$emoji"; + return EmojiChoiceItem( + cId: widget.cId, + emoji: emoji, + onSelectEmoji: () => + widget.onEmojiSelected(emoji, targetId), + selected: widget.emoji == emoji, + transformTargetId: targetId, + badge: widget.emoji == emoji + ? widget.selectedEmojiBadge + : null, + showShimmer: widget.emoji == null, + enabled: widget.enabled, + ); + }), + ], ), + ), _ => SizedBox( - height: 70.0, - child: Row( - spacing: 4.0, - mainAxisSize: MainAxisSize.min, - children: List.generate( - 3, - (_) => Shimmer.fromColors( - baseColor: Colors.transparent, - highlightColor: - Theme.of(context).colorScheme.primary.withAlpha(70), - child: Container( - height: 55.0, - width: 55.0, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + height: 70.0, + child: Row( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + children: List.generate( + 3, + (_) => Shimmer.fromColors( + baseColor: Colors.transparent, + highlightColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(70), + child: Container( + height: 55.0, + width: 55.0, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, ), ), ), ), ), ), + ), }; }, ); @@ -160,18 +160,15 @@ class EmojiChoiceItemState extends State { padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: widget.enabled && (hovered || widget.selected) - ? Theme.of(context) - .colorScheme - .secondary - .withAlpha(30) + ? Theme.of( + context, + ).colorScheme.secondary.withAlpha(30) : Colors.transparent, - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), border: widget.selected - ? Border.all( - color: Colors.transparent, - width: 4, - ) + ? Border.all(color: Colors.transparent, width: 4) : null, ), child: Text( @@ -182,11 +179,7 @@ class EmojiChoiceItemState extends State { ), ), if (widget.badge != null) - Positioned( - right: 6, - bottom: 6, - child: widget.badge!, - ), + Positioned(right: 6, bottom: 6, child: widget.badge!), ], ), ), diff --git a/lib/pangea/lemmas/lemma_info_repo.dart b/lib/pangea/lemmas/lemma_info_repo.dart index 0ed7b408b..9ff293002 100644 --- a/lib/pangea/lemmas/lemma_info_repo.dart +++ b/lib/pangea/lemmas/lemma_info_repo.dart @@ -75,11 +75,7 @@ class LemmaInfoRepo { await _storage.write(key, resultFuture.toJson()); _cache.remove(key); // Invalidate in-memory cache } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'lemma': request.lemma}, - ); + ErrorHandler.logError(e: e, s: s, data: {'lemma': request.lemma}); } } @@ -123,9 +119,7 @@ class LemmaInfoRepo { ); } - return LemmaInfoResponse.fromJson( - jsonDecode(utf8.decode(res.bodyBytes)), - ); + return LemmaInfoResponse.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); } static Future>? _getCached( @@ -153,9 +147,7 @@ class LemmaInfoRepo { await set(request, result.asValue!.value); } - static LemmaInfoResponse? _getStored( - LemmaInfoRequest request, - ) { + static LemmaInfoResponse? _getStored(LemmaInfoRequest request) { final key = request.hashCode.toString(); try { final entry = _storage.read(key); @@ -163,11 +155,7 @@ class LemmaInfoRepo { return LemmaInfoResponse.fromJson(entry); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'lemma': request.lemma}, - ); + ErrorHandler.logError(e: e, s: s, data: {'lemma': request.lemma}); _storage.remove(key); return null; } diff --git a/lib/pangea/lemmas/lemma_info_request.dart b/lib/pangea/lemmas/lemma_info_request.dart index 75ca7dd8a..baebb72a3 100644 --- a/lib/pangea/lemmas/lemma_info_request.dart +++ b/lib/pangea/lemmas/lemma_info_request.dart @@ -20,8 +20,8 @@ class LemmaInfoRequest { required this.lemma, required this.messageInfo, this.feedback = const [], - }) : partOfSpeech = partOfSpeech.toLowerCase(), - lemmaLang = lemmaLang.toLowerCase(); + }) : partOfSpeech = partOfSpeech.toLowerCase(), + lemmaLang = lemmaLang.toLowerCase(); Map toJson() { return { @@ -56,8 +56,8 @@ class LemmaInfoRequest { } ConstructIdentifier get cId => ConstructIdentifier( - lemma: lemma, - type: ConstructTypeEnum.vocab, - category: partOfSpeech, - ); + lemma: lemma, + type: ConstructTypeEnum.vocab, + category: partOfSpeech, + ); } diff --git a/lib/pangea/lemmas/lemma_info_response.dart b/lib/pangea/lemmas/lemma_info_response.dart index 1035cd6e7..517e3bbf0 100644 --- a/lib/pangea/lemmas/lemma_info_response.dart +++ b/lib/pangea/lemmas/lemma_info_response.dart @@ -6,10 +6,7 @@ class LemmaInfoResponse implements JsonSerializable { final List emoji; final String meaning; - LemmaInfoResponse({ - required this.emoji, - required this.meaning, - }); + LemmaInfoResponse({required this.emoji, required this.meaning}); factory LemmaInfoResponse.fromJson(Map json) { return LemmaInfoResponse( @@ -19,17 +16,12 @@ class LemmaInfoResponse implements JsonSerializable { ); } - static LemmaInfoResponse get error => LemmaInfoResponse( - emoji: [], - meaning: 'ERROR', - ); + static LemmaInfoResponse get error => + LemmaInfoResponse(emoji: [], meaning: 'ERROR'); @override Map toJson() { - return { - 'emoji': emoji, - 'meaning': meaning, - }; + return {'emoji': emoji, 'meaning': meaning}; } @override diff --git a/lib/pangea/lemmas/lemma_meaning_builder.dart b/lib/pangea/lemmas/lemma_meaning_builder.dart index c7c99ae36..7ea5f5fb2 100644 --- a/lib/pangea/lemmas/lemma_meaning_builder.dart +++ b/lib/pangea/lemmas/lemma_meaning_builder.dart @@ -17,7 +17,8 @@ class LemmaMeaningBuilder extends StatefulWidget { final Widget Function( BuildContext context, LemmaMeaningBuilderState controller, - ) builder; + ) + builder; const LemmaMeaningBuilder({ super.key, @@ -33,8 +34,9 @@ class LemmaMeaningBuilder extends StatefulWidget { } class LemmaMeaningBuilderState extends State { - final ValueNotifier> _loader = - ValueNotifier(const AsyncState.idle()); + final ValueNotifier> _loader = ValueNotifier( + const AsyncState.idle(), + ); int _loadVersion = 0; @@ -68,13 +70,14 @@ class LemmaMeaningBuilderState extends State { isLoaded ? (_loader.value as AsyncLoaded).value : null; LemmaInfoRequest get _request => LemmaInfoRequest( - lemma: widget.constructId.lemma, - partOfSpeech: widget.constructId.category, - lemmaLang: widget.langCode, - userL1: MatrixState.pangeaController.userController.userL1?.langCode ?? - LanguageKeys.defaultLanguage, - messageInfo: widget.messageInfo, - ); + lemma: widget.constructId.lemma, + partOfSpeech: widget.constructId.category, + lemmaLang: widget.langCode, + userL1: + MatrixState.pangeaController.userController.userL1?.langCode ?? + LanguageKeys.defaultLanguage, + messageInfo: widget.messageInfo, + ); Future _load() async { final int version = ++_loadVersion; @@ -97,10 +100,7 @@ class LemmaMeaningBuilderState extends State { Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: _loader, - builder: (context, _, __) => widget.builder( - context, - this, - ), + builder: (context, _, _) => widget.builder(context, this), ); } } diff --git a/lib/pangea/lemmas/user_set_lemma_info.dart b/lib/pangea/lemmas/user_set_lemma_info.dart index 33d1ed6ca..7d95a217c 100644 --- a/lib/pangea/lemmas/user_set_lemma_info.dart +++ b/lib/pangea/lemmas/user_set_lemma_info.dart @@ -4,10 +4,7 @@ class UserSetLemmaInfo { final String? meaning; final List? emojis; - UserSetLemmaInfo({ - this.emojis, - this.meaning, - }); + UserSetLemmaInfo({this.emojis, this.meaning}); factory UserSetLemmaInfo.fromJson(Map json) { return UserSetLemmaInfo( @@ -17,16 +14,10 @@ class UserSetLemmaInfo { } Map toJson() { - return { - 'emojis': emojis, - 'meaning': meaning, - }; + return {'emojis': emojis, 'meaning': meaning}; } - UserSetLemmaInfo copyWith({ - List? emojis, - String? meaning, - }) { + UserSetLemmaInfo copyWith({List? emojis, String? meaning}) { return UserSetLemmaInfo( emojis: emojis ?? this.emojis, meaning: meaning ?? this.meaning, diff --git a/lib/pangea/login/pages/course_code_page.dart b/lib/pangea/login/pages/course_code_page.dart index fd2e36cb3..9df8c8aa8 100644 --- a/lib/pangea/login/pages/course_code_page.dart +++ b/lib/pangea/login/pages/course_code_page.dart @@ -10,9 +10,7 @@ import 'package:fluffychat/pangea/spaces/space_constants.dart'; import 'package:fluffychat/widgets/matrix.dart'; class CourseCodePage extends StatefulWidget { - const CourseCodePage({ - super.key, - }); + const CourseCodePage({super.key}); @override State createState() => CourseCodePageState(); @@ -40,10 +38,7 @@ class CourseCodePageState extends State { return; } - final roomId = await SpaceCodeController.joinSpaceWithCode( - context, - _code, - ); + final roomId = await SpaceCodeController.joinSpaceWithCode(context, _code); if (roomId != null) { final room = Matrix.of(context).client.getRoomById(roomId); @@ -57,17 +52,12 @@ class CourseCodePageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).joinWithCode), - ), + appBar: AppBar(title: Text(L10n.of(context).joinWithCode)), body: SafeArea( child: Center( child: Container( padding: const EdgeInsets.all(20.0), - constraints: const BoxConstraints( - maxWidth: 350, - maxHeight: 600, - ), + constraints: const BoxConstraints(maxWidth: 350, maxHeight: 600), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -104,9 +94,7 @@ class CourseCodePageState extends State { ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).submit), - ], + children: [Text(L10n.of(context).submit)], ), ), ], diff --git a/lib/pangea/login/pages/create_pangea_account_page.dart b/lib/pangea/login/pages/create_pangea_account_page.dart index 69347f823..0953b7aaa 100644 --- a/lib/pangea/login/pages/create_pangea_account_page.dart +++ b/lib/pangea/login/pages/create_pangea_account_page.dart @@ -21,9 +21,7 @@ import 'package:fluffychat/pangea/login/utils/lang_code_repo.dart'; import 'package:fluffychat/widgets/matrix.dart'; class CreatePangeaAccountPage extends StatefulWidget { - const CreatePangeaAccountPage({ - super.key, - }); + const CreatePangeaAccountPage({super.key}); @override CreatePangeaAccountPageState createState() => CreatePangeaAccountPageState(); @@ -116,15 +114,14 @@ class CreatePangeaAccountPageState extends State { try { final random = Random(); final selectedAvatarPath = avatarPath(random.nextInt(4) + 1); - final avatarUrl = - Uri.parse("${AppConfig.assetsBaseURL}/$selectedAvatarPath"); - await client.setAvatarUrl(client.userID!, avatarUrl); - } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: {}, + final avatarUrl = Uri.parse( + "${AppConfig.assetsBaseURL}/$selectedAvatarPath", ); + await client.setProfileField(client.userID!, 'avatar_url', { + 'avatar_url': avatarUrl, + }); + } catch (err, s) { + ErrorHandler.logError(e: err, s: s, data: {}); } } @@ -135,16 +132,13 @@ class CreatePangeaAccountPageState extends State { return; } - await MatrixState.pangeaController.userController.updateProfile( - (profile) { - profile.userSettings.targetLanguage = target; - if (base != null) { - profile.userSettings.sourceLanguage = base; - } - return profile; - }, - waitForDataInSync: true, - ); + await MatrixState.pangeaController.userController.updateProfile((profile) { + profile.userSettings.targetLanguage = target; + if (base != null) { + profile.userSettings.sourceLanguage = base; + } + return profile; + }, waitForDataInSync: true); } Future _createUserInPangea() async { @@ -174,17 +168,14 @@ class CreatePangeaAccountPageState extends State { final updateFuture = [ _setAvatar(), - MatrixState.pangeaController.userController.updateProfile( - (profile) { - profile.userSettings.targetLanguage = targetLangCode; - if (baseLangCode != null) { - profile.userSettings.sourceLanguage = baseLangCode; - } - profile.userSettings.createdAt = DateTime.now(); - return profile; - }, - waitForDataInSync: true, - ), + MatrixState.pangeaController.userController.updateProfile((profile) { + profile.userSettings.targetLanguage = targetLangCode; + if (baseLangCode != null) { + profile.userSettings.sourceLanguage = baseLangCode; + } + profile.userSettings.createdAt = DateTime.now(); + return profile; + }, waitForDataInSync: true), if (targetLangCode != null) MatrixState.pangeaController.userController.updateAnalyticsProfile( targetLanguage: PLanguageStore.byLangCode(targetLangCode), @@ -228,30 +219,30 @@ class CreatePangeaAccountPageState extends State { child: _loading ? const CircularProgressIndicator.adaptive() : _profileError != null || _courseError != null - ? Column( + ? Column( + spacing: 8.0, + mainAxisSize: MainAxisSize.min, + children: [ + ErrorIndicator( + message: L10n.of(context).oopsSomethingWentWrong, + ), + Row( spacing: 8.0, mainAxisSize: MainAxisSize.min, children: [ - ErrorIndicator( - message: L10n.of(context).oopsSomethingWentWrong, + TextButton( + onPressed: _createUserInPangea, + child: Text(L10n.of(context).tryAgain), ), - Row( - spacing: 8.0, - mainAxisSize: MainAxisSize.min, - children: [ - TextButton( - onPressed: _createUserInPangea, - child: Text(L10n.of(context).tryAgain), - ), - TextButton( - onPressed: Navigator.of(context).pop, - child: Text(L10n.of(context).cancel), - ), - ], + TextButton( + onPressed: Navigator.of(context).pop, + child: Text(L10n.of(context).cancel), ), ], - ) - : null, + ), + ], + ) + : null, ), ), ); diff --git a/lib/pangea/login/pages/find_course_page.dart b/lib/pangea/login/pages/find_course_page.dart index 4f0eda0a4..fdb9a686e 100644 --- a/lib/pangea/login/pages/find_course_page.dart +++ b/lib/pangea/login/pages/find_course_page.dart @@ -83,36 +83,30 @@ class FindCoursePageState extends State { .where( (c) => !Matrix.of(context).client.rooms.any( - (r) => - r.id == c.room.roomId && - r.membership == Membership.join, - ) && + (r) => r.id == c.room.roomId && r.membership == Membership.join, + ) && coursePlans.containsKey(c.courseId), ) .toList(); if (targetLanguageFilter != null) { - filtered = filtered.where( - (chunk) { - final course = coursePlans[chunk.courseId]; - if (course == null) return false; - return course.targetLanguage.split('-').first == - targetLanguageFilter!.langCodeShort; - }, - ).toList(); + filtered = filtered.where((chunk) { + final course = coursePlans[chunk.courseId]; + if (course == null) return false; + return course.targetLanguage.split('-').first == + targetLanguageFilter!.langCodeShort; + }).toList(); } final searchText = searchController.text.trim().toLowerCase(); if (searchText.isNotEmpty) { - filtered = filtered.where( - (chunk) { - final course = coursePlans[chunk.courseId]; - if (course == null) return false; - final name = chunk.room.name?.toLowerCase() ?? ''; - final description = course.description.toLowerCase(); - return name.contains(searchText) || description.contains(searchText); - }, - ).toList(); + filtered = filtered.where((chunk) { + final course = coursePlans[chunk.courseId]; + if (course == null) return false; + final name = chunk.room.name?.toLowerCase() ?? ''; + final description = course.description.toLowerCase(); + return name.contains(searchText) || description.contains(searchText); + }).toList(); } // sort by join rule, with knock rooms at the end @@ -129,9 +123,9 @@ class FindCoursePageState extends State { Future _loadPublicSpaces() async { try { - final resp = await Matrix.of(context).client.requestPublicCourses( - since: nextBatch, - ); + final resp = await Matrix.of( + context, + ).client.requestPublicCourses(since: nextBatch); for (final room in resp.courses) { if (!discoveredCourses.any((e) => e.room.roomId == room.room.roomId)) { @@ -142,13 +136,7 @@ class FindCoursePageState extends State { nextBatch = resp.nextBatch; } catch (e, s) { error = e; - ErrorHandler.logError( - e: e, - s: s, - data: { - 'nextBatch': nextBatch, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'nextBatch': nextBatch}); } } @@ -177,8 +165,10 @@ class FindCoursePageState extends State { try { final resp = await CoursePlansRepo.search( GetLocalizedCoursesRequest( - coursePlanIds: - discoveredCourses.map((c) => c.courseId).toSet().toList(), + coursePlanIds: discoveredCourses + .map((c) => c.courseId) + .toSet() + .toList(), l1: MatrixState.pangeaController.userController.userL1Code!, ), ); @@ -193,8 +183,9 @@ class FindCoursePageState extends State { e: e, s: s, data: { - 'discoveredCourses': - discoveredCourses.map((c) => c.courseId).toList(), + 'discoveredCourses': discoveredCourses + .map((c) => c.courseId) + .toList(), }, ); } finally { @@ -222,10 +213,7 @@ class FindCoursePageState extends State { class FindCoursePageView extends StatelessWidget { final FindCoursePageState controller; - const FindCoursePageView({ - super.key, - required this.controller, - }); + const FindCoursePageView({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -253,16 +241,12 @@ class FindCoursePageView extends StatelessWidget { ? null : theme.colorScheme.secondaryContainer, border: OutlineInputBorder( - borderSide: - isColumnMode ? const BorderSide() : BorderSide.none, + borderSide: isColumnMode + ? const BorderSide() + : BorderSide.none, borderRadius: BorderRadius.circular(100), ), - contentPadding: const EdgeInsets.fromLTRB( - 0, - 0, - 20.0, - 0, - ), + contentPadding: const EdgeInsets.fromLTRB(0, 0, 20.0, 0), hintText: L10n.of(context).findCourse, hintStyle: TextStyle( color: theme.colorScheme.onPrimaryContainer, @@ -343,7 +327,7 @@ class FindCoursePageView extends StatelessWidget { ), ValueListenableBuilder( valueListenable: controller.searchController, - builder: (context, _, __) { + builder: (context, _, _) { if (controller.error != null) { return ErrorIndicator( message: L10n.of(context).oopsSomethingWentWrong, @@ -355,9 +339,7 @@ class FindCoursePageView extends StatelessWidget { } if (controller.filteredCourses.isEmpty) { - return Text( - L10n.of(context).nothingFound, - ); + return Text(L10n.of(context).nothingFound); } return Expanded( @@ -386,17 +368,10 @@ class _PublicCourseTile extends StatelessWidget { final PublicCoursesChunk chunk; final CoursePlanModel? course; - const _PublicCourseTile({ - required this.chunk, - this.course, - }); + const _PublicCourseTile({required this.chunk, this.course}); - void _navigateToCoursePage( - BuildContext context, - ) { - context.go( - '/rooms/course/${Uri.encodeComponent(chunk.room.roomId)}', - ); + void _navigateToCoursePage(BuildContext context) { + context.go('/rooms/course/${Uri.encodeComponent(chunk.room.roomId)}'); } @override @@ -410,12 +385,8 @@ class _PublicCourseTile extends StatelessWidget { return Padding( padding: isColumnMode - ? const EdgeInsets.only( - bottom: 32.0, - ) - : const EdgeInsets.only( - bottom: 16.0, - ), + ? const EdgeInsets.only(bottom: 32.0) + : const EdgeInsets.only(bottom: 16.0), child: Material( type: MaterialType.transparency, child: InkWell( @@ -425,9 +396,7 @@ class _PublicCourseTile extends StatelessWidget { padding: const EdgeInsets.all(12.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12.0), - border: Border.all( - color: theme.colorScheme.primary, - ), + border: Border.all(color: theme.colorScheme.primary), ), child: Column( spacing: 4.0, @@ -443,9 +412,7 @@ class _PublicCourseTile extends StatelessWidget { borderRadius: BorderRadius.circular(10.0), replacement: Avatar( name: displayname, - borderRadius: BorderRadius.circular( - 10.0, - ), + borderRadius: BorderRadius.circular(10.0), size: 58.0, ), ), @@ -464,14 +431,11 @@ class _PublicCourseTile extends StatelessWidget { spacing: 4.0, mainAxisSize: MainAxisSize.min, children: [ - const Icon( - Icons.group, - size: 16.0, - ), + const Icon(Icons.group, size: 16.0), Text( - L10n.of(context).countParticipants( - space.numJoinedMembers, - ), + L10n.of( + context, + ).countParticipants(space.numJoinedMembers), style: theme.textTheme.bodyMedium, ), ], @@ -482,30 +446,19 @@ class _PublicCourseTile extends StatelessWidget { ], ), if (course != null) ...[ - CourseInfoChips( - courseId, - iconSize: 12.0, - fontSize: 12.0, - ), - Text( - course!.description, - style: theme.textTheme.bodyMedium, - ), + CourseInfoChips(courseId, iconSize: 12.0, fontSize: 12.0), + Text(course!.description, style: theme.textTheme.bodyMedium), ], const SizedBox(height: 12.0), HoverBuilder( builder: (context, hovered) => ElevatedButton( onPressed: () => _navigateToCoursePage(context), style: ElevatedButton.styleFrom( - backgroundColor: - theme.colorScheme.primaryContainer.withAlpha( - hovered ? 255 : 200, - ), + backgroundColor: theme.colorScheme.primaryContainer + .withAlpha(hovered ? 255 : 200), foregroundColor: theme.colorScheme.onPrimaryContainer, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 12.0, - ), + borderRadius: BorderRadius.circular(12.0), ), ), child: Row( @@ -513,12 +466,8 @@ class _PublicCourseTile extends StatelessWidget { children: [ Text( space.joinRule == JoinRules.knock.name - ? L10n.of( - context, - ).knock - : L10n.of( - context, - ).join, + ? L10n.of(context).knock + : L10n.of(context).join, ), ], ), diff --git a/lib/pangea/login/pages/language_selection_page.dart b/lib/pangea/login/pages/language_selection_page.dart index f1f3f22e7..8972a050c 100644 --- a/lib/pangea/login/pages/language_selection_page.dart +++ b/lib/pangea/login/pages/language_selection_page.dart @@ -48,8 +48,9 @@ class LanguageSelectionPageState extends State { void _setFromCache() { LangCodeRepo.get().then((langSettings) { if (langSettings == null) return; - final cachedTargetLang = - PLanguageStore.byLangCode(langSettings.targetLangCode); + final cachedTargetLang = PLanguageStore.byLangCode( + langSettings.targetLangCode, + ); final cachedBaseLang = langSettings.baseLangCode != null ? PLanguageStore.byLangCode(langSettings.baseLangCode!) : null; @@ -105,16 +106,12 @@ class LanguageSelectionPageState extends State { return Scaffold( appBar: AppBar( title: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 500, - ), + constraints: const BoxConstraints(maxWidth: 500), child: Row( spacing: 12.0, children: [ Navigator.of(context).canPop() - ? BackButton( - onPressed: Navigator.of(context).maybePop, - ) + ? BackButton(onPressed: Navigator.of(context).maybePop) : const SizedBox(width: 40.0), Expanded( child: LayoutBuilder( @@ -127,9 +124,7 @@ class LanguageSelectionPageState extends State { }, ), ), - const SizedBox( - width: 40.0, - ), + const SizedBox(width: 40.0), ], ), ), @@ -139,9 +134,7 @@ class LanguageSelectionPageState extends State { child: Center( child: Container( padding: const EdgeInsets.all(20.0), - constraints: const BoxConstraints( - maxWidth: 500, - ), + constraints: const BoxConstraints(maxWidth: 500), child: Column( spacing: 24.0, children: [ @@ -162,7 +155,7 @@ class LanguageSelectionPageState extends State { alignment: Alignment.topCenter, child: ValueListenableBuilder( valueListenable: _searchController, - builder: (context, val, __) { + builder: (context, val, _) { return SingleChildScrollView( child: Padding( padding: const EdgeInsets.only( @@ -202,8 +195,8 @@ class LanguageSelectionPageState extends State { ), backgroundColor: _selectedLanguage == l - ? theme.colorScheme.primary - : theme.colorScheme.surface, + ? theme.colorScheme.primary + : theme.colorScheme.surface, padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 4.0, @@ -252,7 +245,8 @@ class LanguageSelectionPageState extends State { ), AnimatedSize( duration: FluffyThemes.animationDuration, - child: _selectedLanguage != null && + child: + _selectedLanguage != null && _selectedLanguage?.langCodeShort == _baseLanguage?.langCodeShort ? PLanguageDropdown( @@ -280,9 +274,7 @@ class LanguageSelectionPageState extends State { ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).letsGo), - ], + children: [Text(L10n.of(context).letsGo)], ), ), ), diff --git a/lib/pangea/login/pages/login_options_view.dart b/lib/pangea/login/pages/login_options_view.dart index c6a73b9e4..2f2c6564e 100644 --- a/lib/pangea/login/pages/login_options_view.dart +++ b/lib/pangea/login/pages/login_options_view.dart @@ -22,19 +22,13 @@ class LoginOptionsView extends StatelessWidget { return Scaffold( appBar: AppBar( title: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 450, - ), + constraints: const BoxConstraints(maxWidth: 450), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BackButton( - onPressed: Navigator.of(context).pop, - ), + BackButton(onPressed: Navigator.of(context).pop), Text(L10n.of(context).login), - const SizedBox( - width: 40.0, - ), + const SizedBox(width: 40.0), ], ), ), @@ -43,10 +37,7 @@ class LoginOptionsView extends StatelessWidget { body: SafeArea( child: Center( child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 300, - maxHeight: 600, - ), + constraints: const BoxConstraints(maxWidth: 300, maxHeight: 600), child: Column( spacing: 16.0, mainAxisAlignment: MainAxisAlignment.end, @@ -78,8 +69,9 @@ class LoginOptionsView extends StatelessWidget { children: [ PangeaLogoSvg( width: 20, - forceColor: - Theme.of(context).colorScheme.onPrimaryContainer, + forceColor: Theme.of( + context, + ).colorScheme.onPrimaryContainer, ), Text(L10n.of(context).email), ], @@ -104,8 +96,9 @@ class LoginOptionsView extends StatelessWidget { }, ), TextSpan( - text: - L10n.of(context).andCertifyIAmAtLeast13YearsOfAge, + text: L10n.of( + context, + ).andCertifyIAmAtLeast13YearsOfAge, ), ], style: TextStyle( diff --git a/lib/pangea/login/pages/login_or_signup_view.dart b/lib/pangea/login/pages/login_or_signup_view.dart index b0b8086c4..916689809 100644 --- a/lib/pangea/login/pages/login_or_signup_view.dart +++ b/lib/pangea/login/pages/login_or_signup_view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/widgets/pangea_logo_svg.dart'; @@ -63,9 +63,7 @@ class LoginOrSignupViewState extends State { body: SafeArea( child: Center( child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 300, - ), + constraints: const BoxConstraints(maxWidth: 300), child: Column( spacing: 50.0, mainAxisSize: MainAxisSize.min, @@ -78,9 +76,10 @@ class LoginOrSignupViewState extends State { forceColor: theme.colorScheme.onSurface, ), Text( - AppConfig.applicationName, - style: theme.textTheme.headlineSmall - ?.copyWith(fontWeight: FontWeight.bold), + AppSettings.applicationName.value, + style: theme.textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + ), ), ], ), @@ -109,9 +108,7 @@ class LoginOrSignupViewState extends State { ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).start), - ], + children: [Text(L10n.of(context).start)], ), ), ElevatedButton( @@ -122,9 +119,7 @@ class LoginOrSignupViewState extends State { ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).loginToAccount), - ], + children: [Text(L10n.of(context).loginToAccount)], ), ), ], diff --git a/lib/pangea/login/pages/new_course_page.dart b/lib/pangea/login/pages/new_course_page.dart index fd15070a6..3daf6dbb1 100644 --- a/lib/pangea/login/pages/new_course_page.dart +++ b/lib/pangea/login/pages/new_course_page.dart @@ -46,16 +46,18 @@ class NewCoursePageState extends State { final ValueNotifier?> _courses = ValueNotifier(null); - final ValueNotifier _targetLanguageFilter = - ValueNotifier(null); + final ValueNotifier _targetLanguageFilter = ValueNotifier( + null, + ); @override void initState() { super.initState(); if (widget.initialLanguageCode != null) { - _targetLanguageFilter.value = - PLanguageStore.byLangCode(widget.initialLanguageCode!); + _targetLanguageFilter.value = PLanguageStore.byLangCode( + widget.initialLanguageCode!, + ); } if (_targetLanguageFilter.value == null) { @@ -74,9 +76,7 @@ class NewCoursePageState extends State { } CourseFilter get _filter { - return CourseFilter( - targetLanguage: _targetLanguageFilter.value, - ); + return CourseFilter(targetLanguage: _targetLanguageFilter.value); } void _setTargetLanguageFilter(LanguageModel? language) { @@ -96,26 +96,19 @@ class NewCoursePageState extends State { if (resp.coursePlans.isEmpty) { ErrorHandler.logError( e: "No courses found", - data: { - 'filter': _filter.toJson(), - }, + data: {'filter': _filter.toJson()}, ); } } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'filter': _filter.toJson(), - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'filter': _filter.toJson()}); _courses.value = Result.error(e); } } Future _onSelect(CoursePlanModel course) async { - final existingRoom = - Matrix.of(context).client.getRoomByCourseId(course.uuid); + final existingRoom = Matrix.of( + context, + ).client.getRoomByCourseId(course.uuid); if (existingRoom == null || widget.spaceId != null) { context.go( @@ -132,12 +125,7 @@ class NewCoursePageState extends State { builder: (context) => AlertDialog.adaptive( title: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 256), - child: Center( - child: Text( - course.title, - textAlign: TextAlign.center, - ), - ), + child: Center(child: Text(course.title, textAlign: TextAlign.center)), ), content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256), @@ -155,16 +143,12 @@ class NewCoursePageState extends State { AdaptiveDialogAction( onPressed: () => Navigator.of(context).pop(1), bigButtons: true, - child: Text( - L10n.of(context).goToExistingCourse, - ), + child: Text(L10n.of(context).goToExistingCourse), ), AdaptiveDialogAction( onPressed: () => Navigator.of(context).pop(null), bigButtons: true, - child: Text( - L10n.of(context).cancel, - ), + child: Text(L10n.of(context).cancel), ), ], ), @@ -182,10 +166,7 @@ class NewCoursePageState extends State { } else { ErrorHandler.logError( e: "Existing course room is not a space", - data: { - 'roomId': existingRoom.id, - 'courseId': course.uuid, - }, + data: {'roomId': existingRoom.id, 'courseId': course.uuid}, ); context.go('/rooms/${existingRoom.id}'); } @@ -208,9 +189,7 @@ class NewCoursePageState extends State { child: Center( child: Container( padding: const EdgeInsets.all(20.0), - constraints: const BoxConstraints( - maxWidth: 450, - ), + constraints: const BoxConstraints(maxWidth: 450), child: Column( children: [ if (widget.showFilters) ...[ @@ -224,7 +203,7 @@ class NewCoursePageState extends State { children: [ ValueListenableBuilder( valueListenable: _targetLanguageFilter, - builder: (context, value, __) { + builder: (context, value, _) { return CourseLanguageFilter( value: _targetLanguageFilter.value, onChanged: _setTargetLanguageFilter, @@ -240,7 +219,7 @@ class NewCoursePageState extends State { ], ValueListenableBuilder( valueListenable: _courses, - builder: (context, value, __) { + builder: (context, value, _) { final loading = value == null; if (loading || value.isError || @@ -264,22 +243,20 @@ class NewCoursePageState extends State { style: theme.textTheme.bodyLarge, ), ElevatedButton( - onPressed: () => context.go( - '/rooms', - ), + onPressed: () => context.go('/rooms'), style: ElevatedButton.styleFrom( backgroundColor: theme - .colorScheme.primaryContainer, + .colorScheme + .primaryContainer, foregroundColor: theme - .colorScheme.onPrimaryContainer, + .colorScheme + .onPrimaryContainer, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - L10n.of(context).continueText, - ), + Text(L10n.of(context).continueText), ], ), ), @@ -330,8 +307,9 @@ class NewCoursePageState extends State { ImageByUrl( imageUrl: course.imageUrl, width: 58.0, - borderRadius: - BorderRadius.circular(10.0), + borderRadius: BorderRadius.circular( + 10.0, + ), replacement: Avatar( name: course.title, borderRadius: BorderRadius.circular( diff --git a/lib/pangea/login/pages/pangea_login_scaffold.dart b/lib/pangea/login/pages/pangea_login_scaffold.dart index af1fc603a..2de62a449 100644 --- a/lib/pangea/login/pages/pangea_login_scaffold.dart +++ b/lib/pangea/login/pages/pangea_login_scaffold.dart @@ -2,7 +2,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; @@ -31,23 +31,17 @@ class PangeaLoginScaffold extends StatelessWidget { final isColumnMode = FluffyThemes.isColumnMode(context); return SafeArea( child: Scaffold( - appBar: customAppBar ?? - AppBar( - toolbarHeight: isColumnMode ? null : 40.0, - actions: actions, - ), + appBar: + customAppBar ?? + AppBar(toolbarHeight: isColumnMode ? null : 40.0, actions: actions), body: LayoutBuilder( builder: (context, constraints) { return SingleChildScrollView( child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, - ), + constraints: BoxConstraints(minHeight: constraints.maxHeight), child: Center( child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 300, - ), + constraints: const BoxConstraints(maxWidth: 300), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -65,26 +59,23 @@ class PangeaLoginScaffold extends StatelessWidget { fit: BoxFit.cover, ) : mainAssetUrl != null - ? mainAssetUrl!.toString().startsWith("mxc") - ? MxcImage( - uri: mainAssetUrl, - fit: BoxFit.cover, - width: isColumnMode ? 175 : 125, - height: isColumnMode ? 175 : 125, - ) - : Image.network( - mainAssetUrl.toString(), - fit: BoxFit.cover, - ) - : Image.asset( - mainAssetPath, - fit: BoxFit.cover, - ), + ? mainAssetUrl!.toString().startsWith("mxc") + ? MxcImage( + uri: mainAssetUrl, + fit: BoxFit.cover, + width: isColumnMode ? 175 : 125, + height: isColumnMode ? 175 : 125, + ) + : Image.network( + mainAssetUrl.toString(), + fit: BoxFit.cover, + ) + : Image.asset(mainAssetPath, fit: BoxFit.cover), ), ), if (showAppName) Text( - AppConfig.applicationName, + AppSettings.applicationName.value, style: Theme.of(context).textTheme.displaySmall, ), const SizedBox(height: 12), diff --git a/lib/pangea/login/pages/pangea_login_view.dart b/lib/pangea/login/pages/pangea_login_view.dart index 8690136aa..eafa08513 100644 --- a/lib/pangea/login/pages/pangea_login_view.dart +++ b/lib/pangea/login/pages/pangea_login_view.dart @@ -16,19 +16,13 @@ class PasswordLoginView extends StatelessWidget { child: Scaffold( appBar: AppBar( title: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 450, - ), + constraints: const BoxConstraints(maxWidth: 450), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BackButton( - onPressed: Navigator.of(context).pop, - ), + BackButton(onPressed: Navigator.of(context).pop), Text(L10n.of(context).loginWithEmail), - const SizedBox( - width: 40.0, - ), + const SizedBox(width: 40.0), ], ), ), @@ -37,10 +31,7 @@ class PasswordLoginView extends StatelessWidget { body: SafeArea( child: Center( child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 300, - maxHeight: 600, - ), + constraints: const BoxConstraints(maxWidth: 300, maxHeight: 600), child: Column( spacing: 16.0, mainAxisAlignment: MainAxisAlignment.end, @@ -80,8 +71,9 @@ class PasswordLoginView extends StatelessWidget { }, validator: (value) { if (value == null || value.isEmpty) { - return L10n.of(context) - .pleaseEnterYourPassword; + return L10n.of( + context, + ).pleaseEnterYourPassword; } return null; }, @@ -101,7 +93,8 @@ class PasswordLoginView extends StatelessWidget { FocusManager.instance.primaryFocus?.unfocus(), ), TextButton( - onPressed: controller.loadingSignIn || + onPressed: + controller.loadingSignIn || controller.client == null ? () {} : controller.passwordForgotten, @@ -120,17 +113,16 @@ class PasswordLoginView extends StatelessWidget { ), ), ElevatedButton( - onPressed: - controller.enabledSignIn ? controller.login : null, + onPressed: controller.enabledSignIn + ? controller.login + : null, style: ElevatedButton.styleFrom( backgroundColor: theme.colorScheme.primaryContainer, foregroundColor: theme.colorScheme.onPrimaryContainer, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).login), - ], + children: [Text(L10n.of(context).login)], ), ), ], diff --git a/lib/pangea/login/pages/signup.dart b/lib/pangea/login/pages/signup.dart index a30527363..ab6510684 100644 --- a/lib/pangea/login/pages/signup.dart +++ b/lib/pangea/login/pages/signup.dart @@ -14,10 +14,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class SignupPage extends StatefulWidget { final bool withEmail; - const SignupPage({ - this.withEmail = false, - super.key, - }); + const SignupPage({this.withEmail = false, super.key}); @override SignupPageController createState() => SignupPageController(); @@ -107,8 +104,9 @@ class SignupPageController extends State { return L10n.of(context).chooseAStrongPassword; } if (value.length < minPassLength) { - return L10n.of(context) - .pleaseChooseAtLeastChars(minPassLength.toString()); + return L10n.of( + context, + ).pleaseChooseAtLeastChars(minPassLength.toString()); } return null; } @@ -135,7 +133,7 @@ class SignupPageController extends State { String? signupError; - void signup([_]) async { + void signup([dynamic _]) async { setState(() => signupError = null); final valid = formKey.currentState!.validate(); if (!valid) return; @@ -166,14 +164,15 @@ class SignupPageController extends State { final client = await Matrix.of(context).getLoginClient(); final email = emailController.text; if (email.isNotEmpty) { - Matrix.of(context).currentClientSecret = - DateTime.now().millisecondsSinceEpoch.toString(); - Matrix.of(context).currentThreepidCreds = - await client.requestTokenToRegisterEmail( - Matrix.of(context).currentClientSecret, - email, - 0, - ); + Matrix.of(context).currentClientSecret = DateTime.now() + .millisecondsSinceEpoch + .toString(); + Matrix.of(context).currentThreepidCreds = await client + .requestTokenToRegisterEmail( + Matrix.of(context).currentClientSecret, + email, + 0, + ); } final displayname = usernameController.text; @@ -195,10 +194,9 @@ class SignupPageController extends State { GoogleAnalytics.login("pangea", registerRes?.userId); if (displayname != localPart && client.userID != null) { - await client.setDisplayName( - client.userID!, - displayname, - ); + await client.setProfileField(client.userID!, 'displayname', { + 'displayname': displayname, + }); } } diff --git a/lib/pangea/login/pages/signup_view.dart b/lib/pangea/login/pages/signup_view.dart index 6b79ee241..cd2db3e87 100644 --- a/lib/pangea/login/pages/signup_view.dart +++ b/lib/pangea/login/pages/signup_view.dart @@ -33,13 +33,9 @@ class SignupPageView extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BackButton( - onPressed: Navigator.of(context).pop, - ), + BackButton(onPressed: Navigator.of(context).pop), Text(L10n.of(context).signUp), - const SizedBox( - width: 40.0, - ), + const SizedBox(width: 40.0), ], ), ), @@ -48,10 +44,7 @@ class SignupPageView extends StatelessWidget { body: SafeArea( child: Center( child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 300, - maxHeight: 600, - ), + constraints: const BoxConstraints(maxWidth: 300, maxHeight: 600), child: Column( spacing: 16.0, mainAxisAlignment: MainAxisAlignment.end, @@ -66,9 +59,7 @@ class SignupPageView extends StatelessWidget { const PangeaSsoButton(provider: SSOProvider.apple), const PangeaSsoButton(provider: SSOProvider.google), ElevatedButton( - onPressed: () => context.go( - '/home/language/signup/email', - ), + onPressed: () => context.go('/home/language/signup/email'), style: ElevatedButton.styleFrom( backgroundColor: theme.colorScheme.primaryContainer, foregroundColor: theme.colorScheme.onPrimaryContainer, @@ -79,8 +70,9 @@ class SignupPageView extends StatelessWidget { children: [ PangeaLogoSvg( width: 20, - forceColor: - Theme.of(context).colorScheme.onPrimaryContainer, + forceColor: Theme.of( + context, + ).colorScheme.onPrimaryContainer, ), Text(L10n.of(context).withEmail), ], @@ -105,8 +97,9 @@ class SignupPageView extends StatelessWidget { }, ), TextSpan( - text: L10n.of(context) - .andCertifyIAmAtLeast13YearsOfAge, + text: L10n.of( + context, + ).andCertifyIAmAtLeast13YearsOfAge, ), ], style: TextStyle( diff --git a/lib/pangea/login/pages/signup_with_email_view.dart b/lib/pangea/login/pages/signup_with_email_view.dart index 840e43812..7d33de127 100644 --- a/lib/pangea/login/pages/signup_with_email_view.dart +++ b/lib/pangea/login/pages/signup_with_email_view.dart @@ -17,18 +17,12 @@ class SignupWithEmailView extends StatelessWidget { child: Scaffold( appBar: AppBar( title: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 450, - ), + constraints: const BoxConstraints(maxWidth: 450), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BackButton( - onPressed: Navigator.of(context).pop, - ), - const SizedBox( - width: 40.0, - ), + BackButton(onPressed: Navigator.of(context).pop), + const SizedBox(width: 40.0), ], ), ), @@ -37,10 +31,7 @@ class SignupWithEmailView extends StatelessWidget { body: SafeArea( child: Center( child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 300, - maxHeight: 600, - ), + constraints: const BoxConstraints(maxWidth: 300, maxHeight: 600), child: Column( spacing: 24.0, mainAxisAlignment: MainAxisAlignment.end, @@ -76,8 +67,9 @@ class SignupWithEmailView extends StatelessWidget { obscureText: !controller.showPassword, validator: controller.password1TextFieldValidator, controller: controller.passwordController, - onFieldSubmitted: - controller.enableSignUp ? controller.signup : null, + onFieldSubmitted: controller.enableSignUp + ? controller.signup + : null, decoration: InputDecoration( hintText: L10n.of(context).password, suffixIcon: IconButton( @@ -94,17 +86,16 @@ class SignupWithEmailView extends StatelessWidget { FocusManager.instance.primaryFocus?.unfocus(), ), ElevatedButton( - onPressed: - controller.enableSignUp ? controller.signup : null, + onPressed: controller.enableSignUp + ? controller.signup + : null, style: ElevatedButton.styleFrom( backgroundColor: theme.colorScheme.primaryContainer, foregroundColor: theme.colorScheme.onPrimaryContainer, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(L10n.of(context).createAccount), - ], + children: [Text(L10n.of(context).createAccount)], ), ), ], diff --git a/lib/pangea/login/utils/lang_code_repo.dart b/lib/pangea/login/utils/lang_code_repo.dart index 8e1572ae8..4e76718e5 100644 --- a/lib/pangea/login/utils/lang_code_repo.dart +++ b/lib/pangea/login/utils/lang_code_repo.dart @@ -4,15 +4,12 @@ class LanguageSettings { final String targetLangCode; final String? baseLangCode; - LanguageSettings({ - required this.targetLangCode, - this.baseLangCode, - }); + LanguageSettings({required this.targetLangCode, this.baseLangCode}); Map toJson() => { - 'targetLangCode': targetLangCode, - 'baseLangCode': baseLangCode, - }; + 'targetLangCode': targetLangCode, + 'baseLangCode': baseLangCode, + }; factory LanguageSettings.fromJson(Map json) => LanguageSettings( diff --git a/lib/pangea/login/utils/sso_login_action.dart b/lib/pangea/login/utils/sso_login_action.dart index fa2aac089..4a005e2fb 100644 --- a/lib/pangea/login/utils/sso_login_action.dart +++ b/lib/pangea/login/utils/sso_login_action.dart @@ -21,14 +21,12 @@ Future pangeaSSOLoginAction( final bool isDefaultPlatform = (PlatformInfos.isMobile || PlatformInfos.isWeb || PlatformInfos.isMacOS); final redirectUrl = kIsWeb - ? Uri.parse(html.window.location.href) - .resolveUri( - Uri(pathSegments: ['auth.html']), - ) - .toString() + ? Uri.parse( + html.window.location.href, + ).resolveUri(Uri(pathSegments: ['auth.html'])).toString() : isDefaultPlatform - ? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login' - : 'http://localhost:3001//login'; + ? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login' + : 'http://localhost:3001//login'; final client = await Matrix.of(context).getLoginClient(); final url = client.homeserver!.replace( path: '/_matrix/client/v3/login/sso/redirect/${provider.id ?? ''}', @@ -57,15 +55,14 @@ Future pangeaSSOLoginAction( final redirect = client.onLoginStateChanged.stream .where((state) => state == LoginState.loggedIn) .first - .then( - (_) { - final route = FluffyChatApp.router.state.fullPath; - if (route == null || - (!route.contains("/rooms") && !route.contains('registration'))) { - context.go('/rooms'); - } - }, - ).timeout(const Duration(seconds: 30)); + .then((_) { + final route = FluffyChatApp.router.state.fullPath; + if (route == null || + (!route.contains("/rooms") && !route.contains('registration'))) { + context.go('/rooms'); + } + }) + .timeout(const Duration(seconds: 30)); final loginRes = await client.login( LoginType.mLoginToken, diff --git a/lib/pangea/login/widgets/app_config_dialog.dart b/lib/pangea/login/widgets/app_config_dialog.dart index 454fdad1f..950bb6e1e 100644 --- a/lib/pangea/login/widgets/app_config_dialog.dart +++ b/lib/pangea/login/widgets/app_config_dialog.dart @@ -6,10 +6,7 @@ import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart' class AppConfigDialog extends StatefulWidget { final List overrides; - const AppConfigDialog({ - super.key, - required this.overrides, - }); + const AppConfigDialog({super.key, required this.overrides}); @override State createState() => AppConfigDialogState(); @@ -38,9 +35,7 @@ class AppConfigDialogState extends State { type: MaterialType.transparency, child: Container( padding: const EdgeInsets.all(16.0), - constraints: const BoxConstraints( - maxWidth: 256, - ), + constraints: const BoxConstraints(maxWidth: 256), child: SingleChildScrollView( child: RadioGroup( groupValue: selectedOverride, @@ -59,14 +54,13 @@ class AppConfigDialogState extends State { ), value: override, ); - }).toList() - ..insert( - 0, - RadioListTile.adaptive( - title: Text(L10n.of(context).defaultOption), - value: null, - ), + }).toList()..insert( + 0, + RadioListTile.adaptive( + title: Text(L10n.of(context).defaultOption), + value: null, ), + ), ], ), ), diff --git a/lib/pangea/login/widgets/full_width_button.dart b/lib/pangea/login/widgets/full_width_button.dart index fe30be714..abbb30fde 100644 --- a/lib/pangea/login/widgets/full_width_button.dart +++ b/lib/pangea/login/widgets/full_width_button.dart @@ -50,8 +50,8 @@ class FullWidthButtonState extends State { decoration: BoxDecoration( color: widget.enabled ? depressed - ? shadowColor - : Theme.of(context).colorScheme.primary + ? shadowColor + : Theme.of(context).colorScheme.primary : Theme.of(context).disabledColor, borderRadius: BorderRadius.circular(36), ), @@ -74,8 +74,9 @@ class FullWidthButtonState extends State { Text( widget.title, style: TextStyle( - color: - Theme.of(context).colorScheme.onPrimary, + color: Theme.of( + context, + ).colorScheme.onPrimary, fontSize: 16, ), ), @@ -154,9 +155,7 @@ class FullWidthTextField extends StatelessWidget { decoration: InputDecoration( labelText: labelText, hintText: hintText, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(36.0), - ), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(36.0)), contentPadding: const EdgeInsets.symmetric( horizontal: 30, vertical: 8.0, diff --git a/lib/pangea/login/widgets/p_sso_button.dart b/lib/pangea/login/widgets/p_sso_button.dart index a02219333..79ad16ce9 100644 --- a/lib/pangea/login/widgets/p_sso_button.dart +++ b/lib/pangea/login/widgets/p_sso_button.dart @@ -21,18 +21,12 @@ class PangeaSsoButton extends StatelessWidget { final SSOProvider provider; final String? title; - const PangeaSsoButton({ - required this.provider, - this.title, - super.key, - }); + const PangeaSsoButton({required this.provider, this.title, super.key}); Future _runSSOLogin(BuildContext context) async { final token = await showAdaptiveDialog( context: context, - builder: (context) => SSODialog( - future: () => _getSSOToken(context), - ), + builder: (context) => SSODialog(future: () => _getSSOToken(context)), ); if (token == null || token.isEmpty) { @@ -46,18 +40,17 @@ class PangeaSsoButton extends StatelessWidget { } Future _getSSOToken(BuildContext context) async { - final bool isDefaultPlatform = (PlatformInfos.isMobile || + final bool isDefaultPlatform = + (PlatformInfos.isMobile || PlatformInfos.isWeb || PlatformInfos.isMacOS); final redirectUrl = kIsWeb - ? Uri.parse(html.window.location.href) - .resolveUri( - Uri(pathSegments: ['auth.html']), - ) - .toString() + ? Uri.parse( + html.window.location.href, + ).resolveUri(Uri(pathSegments: ['auth.html'])).toString() : isDefaultPlatform - ? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login' - : 'http://localhost:3001//login'; + ? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login' + : 'http://localhost:3001//login'; final client = await Matrix.of(context).getLoginClient(); final url = client.homeserver!.replace( path: '/_matrix/client/v3/login/sso/redirect/${provider.id}', @@ -85,23 +78,19 @@ class PangeaSsoButton extends StatelessWidget { return token; } - Future _ssoAction( - String token, - BuildContext context, - ) async { + Future _ssoAction(String token, BuildContext context) async { final client = Matrix.of(context).client; final redirect = client.onLoginStateChanged.stream .where((state) => state == LoginState.loggedIn) .first - .then( - (_) { - final route = FluffyChatApp.router.state.fullPath; - if (route == null || - (!route.contains("/rooms") && !route.contains('registration'))) { - context.go('/rooms'); - } - }, - ).timeout(const Duration(seconds: 30)); + .then((_) { + final route = FluffyChatApp.router.state.fullPath; + if (route == null || + (!route.contains("/rooms") && !route.contains('registration'))) { + context.go('/rooms'); + } + }) + .timeout(const Duration(seconds: 30)); final loginRes = await client.login( LoginType.mLoginToken, diff --git a/lib/pangea/login/widgets/p_sso_dialog.dart b/lib/pangea/login/widgets/p_sso_dialog.dart index 2620c3a11..94b6bc37b 100644 --- a/lib/pangea/login/widgets/p_sso_dialog.dart +++ b/lib/pangea/login/widgets/p_sso_dialog.dart @@ -9,10 +9,7 @@ import 'package:fluffychat/utils/url_launcher.dart'; class SSODialog extends StatefulWidget { final Future Function() future; - const SSODialog({ - super.key, - required this.future, - }); + const SSODialog({super.key, required this.future}); @override SSODialogState createState() => SSODialogState(); @@ -51,9 +48,7 @@ class SSODialogState extends State { @override Widget build(BuildContext context) { return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), backgroundColor: Theme.of(context).colorScheme.surface, child: Container( padding: const EdgeInsets.all(12.0), diff --git a/lib/pangea/morphs/default_morph_mapping.dart b/lib/pangea/morphs/default_morph_mapping.dart index 4721d4389..fb6003db5 100644 --- a/lib/pangea/morphs/default_morph_mapping.dart +++ b/lib/pangea/morphs/default_morph_mapping.dart @@ -228,7 +228,7 @@ final MorphFeaturesAndTags defaultMorphMapping = MorphFeaturesAndTags.fromJson({ { "feature": "x", "tag": ["X"], - } + }, ], }); diff --git a/lib/pangea/morphs/get_grammar_copy.dart b/lib/pangea/morphs/get_grammar_copy.dart index ce811ba63..00aa0fe9d 100644 --- a/lib/pangea/morphs/get_grammar_copy.dart +++ b/lib/pangea/morphs/get_grammar_copy.dart @@ -506,19 +506,14 @@ String? getGrammarCopy({ ErrorHandler.logError( e: Exception('Empty tag'), m: 'Empty tag in getGrammarCopy', - data: { - 'context': context, - }, + data: {'context': context}, ); return L10n.of(context).grammarCopyUnknown; default: // debugger(when: kDebugMode); ErrorHandler.logError( e: 'Need to add copy to intl_en.arb', - data: { - 'tag': key, - 'context': context, - }, + data: {'tag': key, 'context': context}, level: SentryLevel.warning, ); return lemma; // Fallback to the lemma itself if no match is found diff --git a/lib/pangea/morphs/morph_feature_display.dart b/lib/pangea/morphs/morph_feature_display.dart index a3dbcc23d..db3dab703 100644 --- a/lib/pangea/morphs/morph_feature_display.dart +++ b/lib/pangea/morphs/morph_feature_display.dart @@ -4,10 +4,7 @@ import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/morphs/morph_icon.dart'; class MorphFeatureDisplay extends StatelessWidget { - const MorphFeatureDisplay({ - super.key, - required this.morphFeature, - }); + const MorphFeatureDisplay({super.key, required this.morphFeature}); final MorphFeaturesEnum morphFeature; @@ -19,10 +16,7 @@ class MorphFeatureDisplay extends StatelessWidget { SizedBox( width: 24.0, height: 24.0, - child: MorphIcon( - morphFeature: morphFeature, - morphTag: null, - ), + child: MorphIcon(morphFeature: morphFeature, morphTag: null), ), const SizedBox(width: 10.0), Text( diff --git a/lib/pangea/morphs/morph_features_enum.dart b/lib/pangea/morphs/morph_features_enum.dart index 5c8fb7567..20084549c 100644 --- a/lib/pangea/morphs/morph_features_enum.dart +++ b/lib/pangea/morphs/morph_features_enum.dart @@ -153,33 +153,33 @@ extension MorphFeaturesEnumExtension on MorphFeaturesEnum { /// the subset of morphological categories that are important to practice for learning the language /// by order of importance static List get eligibleForPractice => [ - MorphFeaturesEnum.Pos, - MorphFeaturesEnum.Tense, - MorphFeaturesEnum.VerbForm, - MorphFeaturesEnum.VerbType, - MorphFeaturesEnum.Voice, - MorphFeaturesEnum.AdvType, - MorphFeaturesEnum.Aspect, - MorphFeaturesEnum.Case, - MorphFeaturesEnum.ConjType, - MorphFeaturesEnum.Definite, - MorphFeaturesEnum.Degree, - MorphFeaturesEnum.Evident, - MorphFeaturesEnum.Gender, - MorphFeaturesEnum.Mood, - MorphFeaturesEnum.NounType, - MorphFeaturesEnum.NumForm, - MorphFeaturesEnum.NumType, - MorphFeaturesEnum.Number, - MorphFeaturesEnum.NumberPsor, - MorphFeaturesEnum.Person, - MorphFeaturesEnum.Polarity, - MorphFeaturesEnum.Polite, - MorphFeaturesEnum.Poss, - MorphFeaturesEnum.PrepCase, - MorphFeaturesEnum.PronType, - MorphFeaturesEnum.Reflex, - ]; + MorphFeaturesEnum.Pos, + MorphFeaturesEnum.Tense, + MorphFeaturesEnum.VerbForm, + MorphFeaturesEnum.VerbType, + MorphFeaturesEnum.Voice, + MorphFeaturesEnum.AdvType, + MorphFeaturesEnum.Aspect, + MorphFeaturesEnum.Case, + MorphFeaturesEnum.ConjType, + MorphFeaturesEnum.Definite, + MorphFeaturesEnum.Degree, + MorphFeaturesEnum.Evident, + MorphFeaturesEnum.Gender, + MorphFeaturesEnum.Mood, + MorphFeaturesEnum.NounType, + MorphFeaturesEnum.NumForm, + MorphFeaturesEnum.NumType, + MorphFeaturesEnum.Number, + MorphFeaturesEnum.NumberPsor, + MorphFeaturesEnum.Person, + MorphFeaturesEnum.Polarity, + MorphFeaturesEnum.Polite, + MorphFeaturesEnum.Poss, + MorphFeaturesEnum.PrepCase, + MorphFeaturesEnum.PronType, + MorphFeaturesEnum.Reflex, + ]; bool get isEligibleForPractice { return eligibleForPractice.contains(this); diff --git a/lib/pangea/morphs/morph_icon.dart b/lib/pangea/morphs/morph_icon.dart index 745de3588..ce9735f75 100644 --- a/lib/pangea/morphs/morph_icon.dart +++ b/lib/pangea/morphs/morph_icon.dart @@ -40,10 +40,7 @@ class MorphIcon extends StatelessWidget { context: context, ), colorReplacements: theme.brightness == Brightness.dark - ? { - "white": theme.cardColor.hexValue.toString(), - "black": "white", - } + ? {"white": theme.cardColor.hexValue.toString(), "black": "white"} : {}, errorIcon: Icon(morphFeature.fallbackIcon), width: size?.width, diff --git a/lib/pangea/morphs/morph_meaning/morph_info_repo.dart b/lib/pangea/morphs/morph_meaning/morph_info_repo.dart index 9a418b203..204b6a8b1 100644 --- a/lib/pangea/morphs/morph_meaning/morph_info_repo.dart +++ b/lib/pangea/morphs/morph_meaning/morph_info_repo.dart @@ -29,7 +29,7 @@ class MorphInfoRepo { static final Map _cache = {}; static const Duration _cacheDuration = Duration(minutes: 10); -// Persistent storage + // Persistent storage static final GetStorage _storage = GetStorage('morph_info_storage'); static Future> get( @@ -72,11 +72,7 @@ class MorphInfoRepo { await _storage.write(key, resultFuture.toJson()); _cache.remove(key); // Invalidate in-memory cache } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'request': request.toJson()}, - ); + ErrorHandler.logError(e: e, s: s, data: {'request': request.toJson()}); } } @@ -88,7 +84,8 @@ class MorphInfoRepo { }) async { try { final cachedJson = await _getCached(request); - final resp = cachedJson?.result ?? + final resp = + cachedJson?.result ?? MorphInfoResponse( userL1: request.userL1, userL2: request.userL2, @@ -98,11 +95,7 @@ class MorphInfoRepo { resp.setMorphDefinition(feature.name, tag, definition); await set(request, resp); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'request': request.toJson()}, - ); + ErrorHandler.logError(e: e, s: s, data: {'request': request.toJson()}); } } @@ -140,9 +133,7 @@ class MorphInfoRepo { ); } - return MorphInfoResponse.fromJson( - jsonDecode(utf8.decode(res.bodyBytes)), - ); + return MorphInfoResponse.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); } static Future>? _getCached( @@ -170,9 +161,7 @@ class MorphInfoRepo { await set(request, result.asValue!.value); } - static MorphInfoResponse? _getStored( - MorphInfoRequest request, - ) { + static MorphInfoResponse? _getStored(MorphInfoRequest request) { final key = request.storageKey; try { final entry = _storage.read(key); @@ -180,11 +169,7 @@ class MorphInfoRepo { return MorphInfoResponse.fromJson(entry); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'request': request.toJson()}, - ); + ErrorHandler.logError(e: e, s: s, data: {'request': request.toJson()}); _storage.remove(key); return null; } diff --git a/lib/pangea/morphs/morph_meaning/morph_info_request.dart b/lib/pangea/morphs/morph_meaning/morph_info_request.dart index 1d3eb2ea7..f3f916706 100644 --- a/lib/pangea/morphs/morph_meaning/morph_info_request.dart +++ b/lib/pangea/morphs/morph_meaning/morph_info_request.dart @@ -2,16 +2,10 @@ class MorphInfoRequest { final String userL1; final String userL2; - MorphInfoRequest({ - required this.userL1, - required this.userL2, - }); + MorphInfoRequest({required this.userL1, required this.userL2}); Map toJson() { - return { - 'user_l1': userL1, - 'user_l2': userL2, - }; + return {'user_l1': userL1, 'user_l2': userL2}; } @override diff --git a/lib/pangea/morphs/morph_meaning/morph_info_response.dart b/lib/pangea/morphs/morph_meaning/morph_info_response.dart index a7e044651..ab19180ee 100644 --- a/lib/pangea/morphs/morph_meaning/morph_info_response.dart +++ b/lib/pangea/morphs/morph_meaning/morph_info_response.dart @@ -20,11 +20,7 @@ class MorphologicalTag { } Map toJson() { - return { - 'code': code, - 'l1_title': l1Title, - 'l1_description': l1Description, - }; + return {'code': code, 'l1_title': l1Title, 'l1_description': l1Description}; } } @@ -41,8 +37,9 @@ class MorphologicalFeature { factory MorphologicalFeature.fromJson(Map json) { final tagsFromJson = json['tags'] as List; - final List tagsList = - tagsFromJson.map((tag) => MorphologicalTag.fromJson(tag)).toList(); + final List tagsList = tagsFromJson + .map((tag) => MorphologicalTag.fromJson(tag)) + .toList(); return MorphologicalFeature( code: json['code'], @@ -131,17 +128,17 @@ class MorphInfoResponse { } final tagIndex = features[featureIndex].tags.indexWhere( - (tag) => tag.code.toLowerCase() == morphTag.toLowerCase(), - ); + (tag) => tag.code.toLowerCase() == morphTag.toLowerCase(), + ); if (tagIndex == -1) { features[featureIndex].tags.add( - MorphologicalTag( - code: morphTag, - l1Title: morphTag, - l1Description: defintion, - ), - ); + MorphologicalTag( + code: morphTag, + l1Title: morphTag, + l1Description: defintion, + ), + ); return; } diff --git a/lib/pangea/morphs/morph_models.dart b/lib/pangea/morphs/morph_models.dart index c10fb1f0c..cff569dc1 100644 --- a/lib/pangea/morphs/morph_models.dart +++ b/lib/pangea/morphs/morph_models.dart @@ -27,10 +27,7 @@ class MorphFeature { .toList(); Map toJson() { - return { - 'feature': feature, - 'tag': tags, - }; + return {'feature': feature, 'tag': tags}; } } @@ -67,9 +64,7 @@ class MorphFeaturesAndTags { debugger(when: kDebugMode); ErrorHandler.logError( m: "Morph construct category $feature not found in morph categories and labels", - data: { - "feature": feature, - }, + data: {"feature": feature}, ); return []; } @@ -86,11 +81,8 @@ class MorphFeaturesAndTags { ?.displayTags ?? []; - List get displayFeatures => features - .where( - (f) => f.feature.toLowerCase() != "foreign", - ) - .toList(); + List get displayFeatures => + features.where((f) => f.feature.toLowerCase() != "foreign").toList(); List get categories => features.map((e) => e.feature).toList(); @@ -105,9 +97,7 @@ class MorphFeaturesAndTags { } ErrorHandler.logError( m: "Morph construct lemma $morphLemma not found in morph categories and labels", - data: { - "morphLemma": morphLemma, - }, + data: {"morphLemma": morphLemma}, ); return "Other"; } diff --git a/lib/pangea/morphs/morph_repo.dart b/lib/pangea/morphs/morph_repo.dart index 40f5d0a37..473582956 100644 --- a/lib/pangea/morphs/morph_repo.dart +++ b/lib/pangea/morphs/morph_repo.dart @@ -32,10 +32,7 @@ class MorphsRepo { static const int _cacheDurationMinutes = 1; static void set(String languageCode, MorphFeaturesAndTags response) { - _morphsStorage.write( - languageCode, - response.toJson(), - ); + _morphsStorage.write(languageCode, response.toJson()); } static MorphFeaturesAndTags fromJson(Map json) { @@ -61,13 +58,7 @@ class MorphsRepo { return response; } catch (e, s) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: e, - s: s, - data: { - "languageCode": languageCode, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {"languageCode": languageCode}); return defaultMorphMapping; } } diff --git a/lib/pangea/morphs/morph_tag_display.dart b/lib/pangea/morphs/morph_tag_display.dart index 814579020..801488ae2 100644 --- a/lib/pangea/morphs/morph_tag_display.dart +++ b/lib/pangea/morphs/morph_tag_display.dart @@ -10,8 +10,8 @@ class MorphTagDisplay extends StatelessWidget { required MorphFeaturesEnum morphFeature, required String morphTag, required this.textColor, - }) : _morphFeature = morphFeature, - _morphTag = morphTag; + }) : _morphFeature = morphFeature, + _morphTag = morphTag; final MorphFeaturesEnum _morphFeature; final String _morphTag; @@ -36,9 +36,9 @@ class MorphTagDisplay extends StatelessWidget { context: context, ) ?? _morphTag, - style: Theme.of(context).textTheme.titleLarge?.copyWith( - color: textColor, - ), + style: Theme.of( + context, + ).textTheme.titleLarge?.copyWith(color: textColor), ), ], ); diff --git a/lib/pangea/morphs/parts_of_speech_enum.dart b/lib/pangea/morphs/parts_of_speech_enum.dart index b8f919be9..9c88b43ef 100644 --- a/lib/pangea/morphs/parts_of_speech_enum.dart +++ b/lib/pangea/morphs/parts_of_speech_enum.dart @@ -45,12 +45,12 @@ enum PartOfSpeechEnum { } bool get isContentWord => [ - PartOfSpeechEnum.noun, - PartOfSpeechEnum.verb, - PartOfSpeechEnum.adj, - PartOfSpeechEnum.adv, - PartOfSpeechEnum.idiom, - PartOfSpeechEnum.phrasalv, - PartOfSpeechEnum.compn, - ].contains(this); + PartOfSpeechEnum.noun, + PartOfSpeechEnum.verb, + PartOfSpeechEnum.adj, + PartOfSpeechEnum.adv, + PartOfSpeechEnum.idiom, + PartOfSpeechEnum.phrasalv, + PartOfSpeechEnum.compn, + ].contains(this); } diff --git a/lib/pangea/payload_client/image_sizes.dart b/lib/pangea/payload_client/image_sizes.dart index e8d2c42a2..06d2b032b 100644 --- a/lib/pangea/payload_client/image_sizes.dart +++ b/lib/pangea/payload_client/image_sizes.dart @@ -3,19 +3,16 @@ class ImageSizes { final ImageSize? medium; final ImageSize? large; - const ImageSizes({ - this.thumbnail, - this.medium, - this.large, - }); + const ImageSizes({this.thumbnail, this.medium, this.large}); factory ImageSizes.fromJson(Map json) { return ImageSizes( thumbnail: json['thumbnail'] != null ? ImageSize.fromJson(json['thumbnail']) : null, - medium: - json['medium'] != null ? ImageSize.fromJson(json['medium']) : null, + medium: json['medium'] != null + ? ImageSize.fromJson(json['medium']) + : null, large: json['large'] != null ? ImageSize.fromJson(json['large']) : null, ); } diff --git a/lib/pangea/payload_client/join_field.dart b/lib/pangea/payload_client/join_field.dart index 133482281..96e871b30 100644 --- a/lib/pangea/payload_client/join_field.dart +++ b/lib/pangea/payload_client/join_field.dart @@ -3,15 +3,9 @@ class JoinField { final bool? hasNextPage; final int? totalDocs; - const JoinField({ - this.docs, - this.hasNextPage, - this.totalDocs, - }); + const JoinField({this.docs, this.hasNextPage, this.totalDocs}); - factory JoinField.fromJson( - Map json, - ) { + factory JoinField.fromJson(Map json) { final raw = json['docs']; final list = (raw is List) ? raw.map((e) => e as String).toList() : null; @@ -23,10 +17,6 @@ class JoinField { } Map toJson() { - return { - 'docs': docs, - 'hasNextPage': hasNextPage, - 'totalDocs': totalDocs, - }; + return {'docs': docs, 'hasNextPage': hasNextPage, 'totalDocs': totalDocs}; } } diff --git a/lib/pangea/payload_client/models/course_plan/cms_course_plan_topic_location_media.dart b/lib/pangea/payload_client/models/course_plan/cms_course_plan_topic_location_media.dart index aae5ff103..9c15af5cf 100644 --- a/lib/pangea/payload_client/models/course_plan/cms_course_plan_topic_location_media.dart +++ b/lib/pangea/payload_client/models/course_plan/cms_course_plan_topic_location_media.dart @@ -48,8 +48,9 @@ class CmsCoursePlanTopicLocationMedia { return CmsCoursePlanTopicLocationMedia( id: json['id'], alt: json['alt'], - coursePlanTopicLocations: - List.from(json['coursePlanTopicLocations'] as List), + coursePlanTopicLocations: List.from( + json['coursePlanTopicLocations'] as List, + ), createdBy: json['createdBy'] != null ? PolymorphicRelationship.fromJson(json['createdBy']) : null, diff --git a/lib/pangea/payload_client/payload_client.dart b/lib/pangea/payload_client/payload_client.dart index 786164ff6..a3a64af5c 100644 --- a/lib/pangea/payload_client/payload_client.dart +++ b/lib/pangea/payload_client/payload_client.dart @@ -10,10 +10,7 @@ class PayloadClient { final String accessToken; final String basePath = "/cms/api"; - PayloadClient({ - required this.baseUrl, - required this.accessToken, - }); + PayloadClient({required this.baseUrl, required this.accessToken}); Map get _headers { final headers = { @@ -188,8 +185,9 @@ class PayloadClient { build('$prefix[$i]', value[i]); } } else { - final String encodedKey = - encode ? Uri.encodeQueryComponent(prefix) : prefix; + final String encodedKey = encode + ? Uri.encodeQueryComponent(prefix) + : prefix; final String encodedVal = encode ? Uri.encodeQueryComponent(value.toString()) : value.toString(); diff --git a/lib/pangea/payload_client/polymorphic_relationship.dart b/lib/pangea/payload_client/polymorphic_relationship.dart index 5f786ec0f..b5242fb95 100644 --- a/lib/pangea/payload_client/polymorphic_relationship.dart +++ b/lib/pangea/payload_client/polymorphic_relationship.dart @@ -2,10 +2,7 @@ class PolymorphicRelationship { final String relationTo; final String value; - PolymorphicRelationship({ - required this.relationTo, - required this.value, - }); + PolymorphicRelationship({required this.relationTo, required this.value}); factory PolymorphicRelationship.fromJson(Map json) { return PolymorphicRelationship( @@ -15,9 +12,6 @@ class PolymorphicRelationship { } Map toJson() { - return { - 'relationTo': relationTo, - 'value': value, - }; + return {'relationTo': relationTo, 'value': value}; } } diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart index 3627f78a3..e15079d09 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_builder.dart @@ -16,7 +16,8 @@ class PhoneticTranscriptionBuilder extends StatefulWidget { final Widget Function( BuildContext context, PhoneticTranscriptionBuilderState controller, - ) builder; + ) + builder; const PhoneticTranscriptionBuilder({ super.key, @@ -33,8 +34,9 @@ class PhoneticTranscriptionBuilder extends StatefulWidget { class PhoneticTranscriptionBuilderState extends State { - final ValueNotifier> _loader = - ValueNotifier(const AsyncState.idle()); + final ValueNotifier> _loader = ValueNotifier( + const AsyncState.idle(), + ); @override void initState() { @@ -66,12 +68,12 @@ class PhoneticTranscriptionBuilderState isLoaded ? (_loader.value as AsyncLoaded).value : null; PhoneticTranscriptionRequest get _request => PhoneticTranscriptionRequest( - arc: LanguageArc( - l1: MatrixState.pangeaController.userController.userL1!, - l2: widget.textLanguage, - ), - content: PangeaTokenText.fromString(widget.text), - ); + arc: LanguageArc( + l1: MatrixState.pangeaController.userController.userL1!, + l2: widget.textLanguage, + ), + content: PangeaTokenText.fromString(widget.text), + ); Future _load() async { _loader.value = const AsyncState.loading(); @@ -84,8 +86,14 @@ class PhoneticTranscriptionBuilderState resp.isError ? _loader.value = AsyncState.error(resp.asError!.error) : _loader.value = AsyncState.loaded( - resp.asValue!.value.phoneticTranscriptionResult - .phoneticTranscription.first.phoneticL1Transcription.content, + resp + .asValue! + .value + .phoneticTranscriptionResult + .phoneticTranscription + .first + .phoneticL1Transcription + .content, ); } @@ -93,10 +101,7 @@ class PhoneticTranscriptionBuilderState Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: _loader, - builder: (context, _, __) => widget.builder( - context, - this, - ), + builder: (context, _, _) => widget.builder(context, this), ); } } diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart index d7df0b778..76b3a3e66 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_repo.dart @@ -54,9 +54,10 @@ class PhoneticTranscriptionRepo { static const Duration _cacheDuration = Duration(minutes: 10); static const Duration _storageDuration = Duration(days: 7); -// Persistent storage - static final GetStorage _storage = - GetStorage('phonetic_transcription_storage'); + // Persistent storage + static final GetStorage _storage = GetStorage( + 'phonetic_transcription_storage', + ); static Future> get( String accessToken, @@ -105,11 +106,7 @@ class PhoneticTranscriptionRepo { await _storage.write(key, item.toJson()); _cache.remove(key); // Invalidate in-memory cache } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'request': request.toJson()}, - ); + ErrorHandler.logError(e: e, s: s, data: {'request': request.toJson()}); } } @@ -192,11 +189,7 @@ class PhoneticTranscriptionRepo { } return item.response; } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {'request': request.toJson()}, - ); + ErrorHandler.logError(e: e, s: s, data: {'request': request.toJson()}); _storage.remove(key); return null; } diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart index 464193b01..f9e164f6f 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart @@ -15,8 +15,9 @@ class PhoneticTranscriptionRequest { factory PhoneticTranscriptionRequest.fromJson(Map json) { return PhoneticTranscriptionRequest( arc: LanguageArc.fromJson(json['arc'] as Map), - content: - PangeaTokenText.fromJson(json['content'] as Map), + content: PangeaTokenText.fromJson( + json['content'] as Map, + ), requiresTokenization: json['requires_tokenization'] ?? true, ); } diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart index 612ef42c1..7512215b1 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart @@ -39,8 +39,9 @@ class PhoneticTranscriptionToken { factory PhoneticTranscriptionToken.fromJson(Map json) { return PhoneticTranscriptionToken( arc: LanguageArc.fromJson(json['arc'] as Map), - tokenL2: - PangeaTokenText.fromJson(json['token_l2'] as Map), + tokenL2: PangeaTokenText.fromJson( + json['token_l2'] as Map, + ), phoneticL1Transcription: PangeaTokenText.fromJson( json['phonetic_l1_transcription'] as Map, ), @@ -48,10 +49,10 @@ class PhoneticTranscriptionToken { } Map toJson() => { - 'arc': arc.toJson(), - 'token_l2': tokenL2.toJson(), - 'phonetic_l1_transcription': phoneticL1Transcription.toJson(), - }; + 'arc': arc.toJson(), + 'token_l2': tokenL2.toJson(), + 'phonetic_l1_transcription': phoneticL1Transcription.toJson(), + }; } class PhoneticTranscription { @@ -88,19 +89,20 @@ class PhoneticTranscription { } Map toJson() => { - 'arc': arc.toJson(), - 'transcription_l2': transcriptionL2.toJson(), - 'phonetic_transcription': - phoneticTranscription.map((e) => e.toJson()).toList(), - 'delim': delim.value, - }; + 'arc': arc.toJson(), + 'transcription_l2': transcriptionL2.toJson(), + 'phonetic_transcription': phoneticTranscription + .map((e) => e.toJson()) + .toList(), + 'delim': delim.value, + }; } class PhoneticTranscriptionResponse { final LanguageArc arc; final PangeaTokenText content; final Map - tokenization; // You can define a typesafe model if needed + tokenization; // You can define a typesafe model if needed final PhoneticTranscription phoneticTranscriptionResult; PhoneticTranscriptionResponse({ @@ -113,8 +115,9 @@ class PhoneticTranscriptionResponse { factory PhoneticTranscriptionResponse.fromJson(Map json) { return PhoneticTranscriptionResponse( arc: LanguageArc.fromJson(json['arc'] as Map), - content: - PangeaTokenText.fromJson(json['content'] as Map), + content: PangeaTokenText.fromJson( + json['content'] as Map, + ), tokenization: Map.from(json['tokenization'] as Map), phoneticTranscriptionResult: PhoneticTranscription.fromJson( json['phonetic_transcription_result'] as Map, diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart index 8f5737125..ddbe72f29 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart @@ -70,8 +70,9 @@ class _PhoneticTranscriptionWidgetState return HoverBuilder( builder: (context, hovering) { return Tooltip( - message: - _isPlaying ? L10n.of(context).stop : L10n.of(context).playAudio, + message: _isPlaying + ? L10n.of(context).stop + : L10n.of(context).playAudio, child: GestureDetector( onTap: () => _handleAudioTap(targetId), child: AnimatedContainer( @@ -95,46 +96,46 @@ class _PhoneticTranscriptionWidgetState AsyncError(error: final error) => error is UnsubscribedException ? ErrorIndicator( - message: L10n.of(context) - .subscribeToUnlockTranscriptions, + message: L10n.of( + context, + ).subscribeToUnlockTranscriptions, onTap: () { MatrixState - .pangeaController.subscriptionController + .pangeaController + .subscriptionController .showPaywall(context); }, ) : ErrorIndicator( - message: - L10n.of(context).failedToFetchTranscription, + message: L10n.of( + context, + ).failedToFetchTranscription, ), AsyncLoaded(value: final transcription) => Row( - spacing: 8.0, - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: Text( - transcription, - textScaler: TextScaler.noScaling, - style: widget.style ?? - Theme.of(context).textTheme.bodyMedium, - maxLines: widget.maxLines, - overflow: TextOverflow.ellipsis, - ), + spacing: 8.0, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + transcription, + textScaler: TextScaler.noScaling, + style: + widget.style ?? + Theme.of(context).textTheme.bodyMedium, + maxLines: widget.maxLines, + overflow: TextOverflow.ellipsis, ), - Icon( - _isPlaying - ? Icons.pause_outlined - : Icons.volume_up, - size: widget.iconSize ?? 24, - color: widget.iconColor ?? - Theme.of(context).iconTheme.color, - ), - ], - ), - _ => const TextLoadingShimmer( - width: 125.0, - height: 20.0, - ), + ), + Icon( + _isPlaying ? Icons.pause_outlined : Icons.volume_up, + size: widget.iconSize ?? 24, + color: + widget.iconColor ?? + Theme.of(context).iconTheme.color, + ), + ], + ), + _ => const TextLoadingShimmer(width: 125.0, height: 20.0), }; }, ), diff --git a/lib/pangea/practice_activities/activity_type_enum.dart b/lib/pangea/practice_activities/activity_type_enum.dart index 50b91c348..38724d88d 100644 --- a/lib/pangea/practice_activities/activity_type_enum.dart +++ b/lib/pangea/practice_activities/activity_type_enum.dart @@ -119,25 +119,13 @@ enum ActivityTypeEnum { ConstructUseTypeEnum.ignMM, ]; case ActivityTypeEnum.lemmaAudio: - return [ - ConstructUseTypeEnum.corLA, - ConstructUseTypeEnum.incLA, - ]; + return [ConstructUseTypeEnum.corLA, ConstructUseTypeEnum.incLA]; case ActivityTypeEnum.lemmaMeaning: - return [ - ConstructUseTypeEnum.corLM, - ConstructUseTypeEnum.incLM, - ]; + return [ConstructUseTypeEnum.corLM, ConstructUseTypeEnum.incLM]; case ActivityTypeEnum.grammarCategory: - return [ - ConstructUseTypeEnum.corGC, - ConstructUseTypeEnum.incGC, - ]; + return [ConstructUseTypeEnum.corGC, ConstructUseTypeEnum.incGC]; case ActivityTypeEnum.grammarError: - return [ - ConstructUseTypeEnum.corGE, - ConstructUseTypeEnum.incGE, - ]; + return [ConstructUseTypeEnum.corGE, ConstructUseTypeEnum.incGE]; } } @@ -238,21 +226,21 @@ enum ActivityTypeEnum { } static List get practiceTypes => [ - ActivityTypeEnum.emoji, - ActivityTypeEnum.wordMeaning, - ActivityTypeEnum.wordFocusListening, - ActivityTypeEnum.morphId, - ]; + ActivityTypeEnum.emoji, + ActivityTypeEnum.wordMeaning, + ActivityTypeEnum.wordFocusListening, + ActivityTypeEnum.morphId, + ]; static List get _vocabPracticeTypes => [ - ActivityTypeEnum.lemmaMeaning, - ActivityTypeEnum.lemmaAudio, - ]; + ActivityTypeEnum.lemmaMeaning, + ActivityTypeEnum.lemmaAudio, + ]; static List get _grammarPracticeTypes => [ - ActivityTypeEnum.grammarCategory, - ActivityTypeEnum.grammarError, - ]; + ActivityTypeEnum.grammarCategory, + ActivityTypeEnum.grammarError, + ]; static List analyticsPracticeTypes( ConstructTypeEnum constructType, diff --git a/lib/pangea/practice_activities/emoji_activity_generator.dart b/lib/pangea/practice_activities/emoji_activity_generator.dart index 5b8881a5a..29ec38d7c 100644 --- a/lib/pangea/practice_activities/emoji_activity_generator.dart +++ b/lib/pangea/practice_activities/emoji_activity_generator.dart @@ -42,8 +42,9 @@ class EmojiActivityGenerator { .map((token) => token.vocabConstructID.getLemmaInfo(messageInfo)) .toList(); - final List> lemmaInfos = - await Future.wait(lemmaInfoFutures); + final List> lemmaInfos = await Future.wait( + lemmaInfoFutures, + ); for (int i = 0; i < missingEmojis.length; i++) { if (lemmaInfos[i].isError) { @@ -51,11 +52,10 @@ class EmojiActivityGenerator { } final e = lemmaInfos[i].asValue!.value.emoji.firstWhere( - (e) => !usedEmojis.contains(e), - orElse: () => throw Exception( - "Not enough unique emojis for tokens in message", - ), - ); + (e) => !usedEmojis.contains(e), + orElse: () => + throw Exception("Not enough unique emojis for tokens in message"), + ); final token = missingEmojis[i]; matchInfo[token.vocabForm] ??= []; @@ -67,9 +67,7 @@ class EmojiActivityGenerator { activity: EmojiPracticeActivityModel( tokens: req.target.tokens, langCode: req.userL2, - matchContent: PracticeMatchActivity( - matchInfo: matchInfo, - ), + matchContent: PracticeMatchActivity(matchInfo: matchInfo), ), ); } diff --git a/lib/pangea/practice_activities/lemma_activity_generator.dart b/lib/pangea/practice_activities/lemma_activity_generator.dart index 79d9d2e2f..291462ea5 100644 --- a/lib/pangea/practice_activities/lemma_activity_generator.dart +++ b/lib/pangea/practice_activities/lemma_activity_generator.dart @@ -11,9 +11,7 @@ import 'package:fluffychat/pangea/practice_activities/practice_activity_model.da import 'package:fluffychat/widgets/matrix.dart'; class LemmaActivityGenerator { - static Future get( - MessageActivityRequest req, - ) async { + static Future get(MessageActivityRequest req) async { debugger(when: kDebugMode && req.target.tokens.length != 1); final token = req.target.tokens.first; @@ -37,16 +35,17 @@ class LemmaActivityGenerator { int? maxChoices = 4, }) async { final constructs = await MatrixState - .pangeaController.matrixState.analyticsDataService + .pangeaController + .matrixState + .analyticsDataService .getAggregatedConstructs(ConstructTypeEnum.vocab); final List constructIds = constructs.keys.toList(); // Offload computation to an isolate - final Map distances = - await compute(_computeDistancesInIsolate, { - 'lemmas': constructIds, - 'target': token.lemma.text, - }); + final Map distances = await compute( + _computeDistancesInIsolate, + {'lemmas': constructIds, 'target': token.lemma.text}, + ); // Sort lemmas by distance final sortedLemmas = distances.keys.toList() @@ -70,8 +69,9 @@ class LemmaActivityGenerator { } // Ensure the target lemma (token.vocabConstructID) is included while keeping unique lemma texts - final int existingIndex = uniqueByLemma - .indexWhere((c) => c.lemma == token.vocabConstructID.lemma); + final int existingIndex = uniqueByLemma.indexWhere( + (c) => c.lemma == token.vocabConstructID.lemma, + ); if (existingIndex >= 0) { uniqueByLemma[existingIndex] = token.vocabConstructID; } else { @@ -120,9 +120,13 @@ class LemmaActivityGenerator { } else if (s[i - 1] == t[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; } else { - dp[i][j] = 1 + - [dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]] - .reduce((a, b) => a < b ? a : b); + dp[i][j] = + 1 + + [ + dp[i - 1][j], + dp[i][j - 1], + dp[i - 1][j - 1], + ].reduce((a, b) => a < b ? a : b); } } } diff --git a/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart b/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart index 3e32611b0..c5e30baa8 100644 --- a/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart +++ b/lib/pangea/practice_activities/lemma_meaning_activity_generator.dart @@ -15,12 +15,14 @@ class LemmaMeaningActivityGenerator { required Map messageInfo, }) async { final List>> lemmaInfoFutures = req - .target.tokens + .target + .tokens .map((token) => token.vocabConstructID.getLemmaInfo(messageInfo)) .toList(); - final List> lemmaInfos = - await Future.wait(lemmaInfoFutures); + final List> lemmaInfos = await Future.wait( + lemmaInfoFutures, + ); if (lemmaInfos.any((result) => result.isError)) { throw lemmaInfos.firstWhere((result) => result.isError).error!; @@ -35,9 +37,7 @@ class LemmaMeaningActivityGenerator { activity: LemmaMeaningPracticeActivityModel( tokens: req.target.tokens, langCode: req.userL2, - matchContent: PracticeMatchActivity( - matchInfo: matchInfo, - ), + matchContent: PracticeMatchActivity(matchInfo: matchInfo), ), ); } diff --git a/lib/pangea/practice_activities/message_activity_request.dart b/lib/pangea/practice_activities/message_activity_request.dart index d0bdc5e1c..0fe576a90 100644 --- a/lib/pangea/practice_activities/message_activity_request.dart +++ b/lib/pangea/practice_activities/message_activity_request.dart @@ -149,9 +149,7 @@ class MessageActivityRequest { class MessageActivityResponse { final PracticeActivityModel activity; - MessageActivityResponse({ - required this.activity, - }); + MessageActivityResponse({required this.activity}); factory MessageActivityResponse.fromJson(Map json) { if (!json.containsKey('activity')) { diff --git a/lib/pangea/practice_activities/morph_activity_generator.dart b/lib/pangea/practice_activities/morph_activity_generator.dart index 0c2e2bf5b..2e5ed1690 100644 --- a/lib/pangea/practice_activities/morph_activity_generator.dart +++ b/lib/pangea/practice_activities/morph_activity_generator.dart @@ -14,9 +14,7 @@ typedef POSActivitySequence = List; class MorphActivityGenerator { /// Generate a morphological activity for a given token and morphological feature - static MessageActivityResponse get( - MessageActivityRequest req, - ) { + static MessageActivityResponse get(MessageActivityRequest req) { debugger(when: kDebugMode && req.target.tokens.length != 1); debugger(when: kDebugMode && req.target.morphFeature == null); diff --git a/lib/pangea/practice_activities/multiple_choice_activity_model.dart b/lib/pangea/practice_activities/multiple_choice_activity_model.dart index 8ab9119b2..f715185a8 100644 --- a/lib/pangea/practice_activities/multiple_choice_activity_model.dart +++ b/lib/pangea/practice_activities/multiple_choice_activity_model.dart @@ -9,10 +9,7 @@ class MultipleChoiceActivity { final Set choices; final Set answers; - MultipleChoiceActivity({ - required this.choices, - required this.answers, - }); + MultipleChoiceActivity({required this.choices, required this.answers}); Color choiceColor(String value) => answers.contains(value) ? AppConfig.success : AppConfig.warning; @@ -35,10 +32,7 @@ class MultipleChoiceActivity { } Map toJson() { - return { - 'choices': List.from(choices), - 'answer': List.from(answers), - }; + return {'choices': List.from(choices), 'answer': List.from(answers)}; } // ovveride operator == and hashCode diff --git a/lib/pangea/practice_activities/practice_activity_model.dart b/lib/pangea/practice_activities/practice_activity_model.dart index 24f9caa55..29dc56791 100644 --- a/lib/pangea/practice_activities/practice_activity_model.dart +++ b/lib/pangea/practice_activities/practice_activity_model.dart @@ -15,21 +15,18 @@ sealed class PracticeActivityModel { final List tokens; final String langCode; - const PracticeActivityModel({ - required this.tokens, - required this.langCode, - }); + const PracticeActivityModel({required this.tokens, required this.langCode}); String get storageKey => '${activityType.name}-${tokens.map((e) => e.text.content).join("-")}'; PracticeTarget get practiceTarget => PracticeTarget( - activityType: activityType, - tokens: tokens, - morphFeature: this is MorphPracticeActivityModel - ? (this as MorphPracticeActivityModel).morphFeature - : null, - ); + activityType: activityType, + tokens: tokens, + morphFeature: this is MorphPracticeActivityModel + ? (this as MorphPracticeActivityModel).morphFeature + : null, + ); ActivityTypeEnum get activityType { switch (this) { @@ -56,27 +53,21 @@ sealed class PracticeActivityModel { factory PracticeActivityModel.fromJson(Map json) { if (json['lang_code'] is! String) { - Sentry.addBreadcrumb( - Breadcrumb(data: {"json": json}), - ); + Sentry.addBreadcrumb(Breadcrumb(data: {"json": json})); throw ("lang_code is not a string in PracticeActivityModel.fromJson"); } final targetConstructsEntry = json['tgt_constructs'] ?? json['target_constructs']; if (targetConstructsEntry is! List) { - Sentry.addBreadcrumb( - Breadcrumb(data: {"json": json}), - ); + Sentry.addBreadcrumb(Breadcrumb(data: {"json": json})); throw ("tgt_constructs is not a list in PracticeActivityModel.fromJson"); } final type = ActivityTypeEnum.fromString(json['activity_type']); final morph = json['morph_feature'] != null - ? MorphFeaturesEnumExtension.fromString( - json['morph_feature'] as String, - ) + ? MorphFeaturesEnumExtension.fromString(json['morph_feature'] as String) : null; final tokens = (json['target_tokens'] as List) @@ -238,17 +229,15 @@ sealed class MultipleChoicePracticeActivityModel extends PracticeActivityModel { OneConstructUse constructUse(String choiceContent) { final correct = multipleChoiceContent.isCorrect(choiceContent); - final useType = - correct ? activityType.correctUse : activityType.incorrectUse; + final useType = correct + ? activityType.correctUse + : activityType.incorrectUse; final token = tokens.first; return OneConstructUse( useType: useType, constructType: ConstructTypeEnum.vocab, - metadata: ConstructUseMetaData( - roomId: null, - timeStamp: DateTime.now(), - ), + metadata: ConstructUseMetaData(roomId: null, timeStamp: DateTime.now()), category: token.pos, lemma: token.lemma.text, form: token.lemma.text, @@ -273,10 +262,7 @@ sealed class MatchPracticeActivityModel extends PracticeActivityModel { required this.matchContent, }); - bool isCorrect( - PangeaToken token, - String choice, - ) => + bool isCorrect(PangeaToken token, String choice) => matchContent.matchInfo[token.vocabForm]!.contains(choice); @override @@ -324,17 +310,15 @@ class MorphCategoryPracticeActivityModel extends MorphPracticeActivityModel { OneConstructUse constructUse(String choiceContent) { final correct = multipleChoiceContent.isCorrect(choiceContent); final token = tokens.first; - final useType = - correct ? activityType.correctUse : activityType.incorrectUse; + final useType = correct + ? activityType.correctUse + : activityType.incorrectUse; final tag = token.getMorphTag(morphFeature)!; return OneConstructUse( useType: useType, constructType: ConstructTypeEnum.morph, - metadata: ConstructUseMetaData( - roomId: null, - timeStamp: DateTime.now(), - ), + metadata: ConstructUseMetaData(roomId: null, timeStamp: DateTime.now()), category: morphFeature.name, lemma: tag, form: token.lemma.form, diff --git a/lib/pangea/practice_activities/practice_choice.dart b/lib/pangea/practice_activities/practice_choice.dart index b5b9a4c9d..64806d392 100644 --- a/lib/pangea/practice_activities/practice_choice.dart +++ b/lib/pangea/practice_activities/practice_choice.dart @@ -7,10 +7,7 @@ class PracticeChoice { /// Form of the associated token final ConstructForm form; - PracticeChoice({ - required this.choiceContent, - required this.form, - }); + PracticeChoice({required this.choiceContent, required this.form}); @override bool operator ==(Object other) { diff --git a/lib/pangea/practice_activities/practice_generation_repo.dart b/lib/pangea/practice_activities/practice_generation_repo.dart index 35a961940..4fd9a1148 100644 --- a/lib/pangea/practice_activities/practice_generation_repo.dart +++ b/lib/pangea/practice_activities/practice_generation_repo.dart @@ -32,26 +32,24 @@ class _RequestCacheItem { final PracticeActivityModel practiceActivity; final DateTime timestamp; - _RequestCacheItem({ - required this.practiceActivity, - required this.timestamp, - }); + _RequestCacheItem({required this.practiceActivity, required this.timestamp}); bool get isExpired => DateTime.now().difference(timestamp) > PracticeRepo._cacheDuration; factory _RequestCacheItem.fromJson(Map json) { return _RequestCacheItem( - practiceActivity: - PracticeActivityModel.fromJson(json['practiceActivity']), + practiceActivity: PracticeActivityModel.fromJson( + json['practiceActivity'], + ), timestamp: DateTime.parse(json['timestamp']), ); } Map toJson() => { - 'practiceActivity': practiceActivity.toJson(), - 'timestamp': timestamp.toIso8601String(), - }; + 'practiceActivity': practiceActivity.toJson(), + 'timestamp': timestamp.toIso8601String(), + }; } /// Controller for handling activity completions. @@ -144,16 +142,11 @@ class PracticeRepo { case ActivityTypeEnum.wordFocusListening: return WordFocusListeningGenerator.get(req); case ActivityTypeEnum.hiddenWordListening: - return _fetch( - accessToken: accessToken, - requestModel: req, - ); + return _fetch(accessToken: accessToken, requestModel: req); } } - static PracticeActivityModel? _getCached( - MessageActivityRequest req, - ) { + static PracticeActivityModel? _getCached(MessageActivityRequest req) { final keys = List.from(_storage.getKeys()); for (final k in keys) { try { @@ -180,12 +173,11 @@ class PracticeRepo { static Future _setCached( MessageActivityRequest req, MessageActivityResponse res, - ) => - _storage.write( - req.hashCode.toString(), - _RequestCacheItem( - practiceActivity: res.activity, - timestamp: DateTime.now(), - ).toJson(), - ); + ) => _storage.write( + req.hashCode.toString(), + _RequestCacheItem( + practiceActivity: res.activity, + timestamp: DateTime.now(), + ).toJson(), + ); } diff --git a/lib/pangea/practice_activities/practice_match.dart b/lib/pangea/practice_activities/practice_match.dart index e621f6978..84ae13be5 100644 --- a/lib/pangea/practice_activities/practice_match.dart +++ b/lib/pangea/practice_activities/practice_match.dart @@ -12,21 +12,15 @@ class PracticeMatchActivity { List choices = List.empty(growable: true); - PracticeMatchActivity({ - required this.matchInfo, - }) { + PracticeMatchActivity({required this.matchInfo}) { for (final ith in matchInfo.entries) { - debugPrint( - 'Construct: ${ith.key.form}, Forms: ${ith.value}', - ); + debugPrint('Construct: ${ith.key.form}, Forms: ${ith.value}'); } final List usedForms = []; for (final matchEntry in matchInfo.entries) { if (matchEntry.value.isEmpty) { - throw Exception( - "No forms available for construct ${matchEntry.key}", - ); + throw Exception("No forms available for construct ${matchEntry.key}"); } final String choiceContent = matchEntry.value.firstWhere( @@ -37,10 +31,7 @@ class PracticeMatchActivity { ); choices.add( - PracticeChoice( - choiceContent: choiceContent, - form: matchEntry.key, - ), + PracticeChoice(choiceContent: choiceContent, form: matchEntry.key), ); usedForms.add(choiceContent); @@ -58,28 +49,22 @@ class PracticeMatchActivity { final Map> matchInfo = {}; for (final constructJson in json['match_info']) { final ConstructForm cId = ConstructForm.fromJson(constructJson['cId']); - final List surfaceForms = - List.from(constructJson['forms']); + final List surfaceForms = List.from( + constructJson['forms'], + ); matchInfo[cId] = surfaceForms; } - return PracticeMatchActivity( - matchInfo: matchInfo, - ); + return PracticeMatchActivity(matchInfo: matchInfo); } Map toJson() { final List> matchInfo = []; for (final cId in this.matchInfo.keys) { - matchInfo.add({ - 'cId': cId.toJson(), - 'forms': this.matchInfo[cId], - }); + matchInfo.add({'cId': cId.toJson(), 'forms': this.matchInfo[cId]}); } - return { - 'match_info': matchInfo, - }; + return {'match_info': matchInfo}; } @override diff --git a/lib/pangea/practice_activities/practice_record.dart b/lib/pangea/practice_activities/practice_record.dart index 61e8e2d55..9e2ba338a 100644 --- a/lib/pangea/practice_activities/practice_record.dart +++ b/lib/pangea/practice_activities/practice_record.dart @@ -27,9 +27,7 @@ class PracticeRecord { } } - factory PracticeRecord.fromJson( - Map json, - ) { + factory PracticeRecord.fromJson(Map json) { return PracticeRecord( responses: (json['responses'] as List) .map( @@ -43,9 +41,7 @@ class PracticeRecord { } Map toJson() { - return { - 'responses': responses.map((e) => e.toJson()).toList(), - }; + return {'responses': responses.map((e) => e.toJson()).toList()}; } int get completeResponses => @@ -61,13 +57,8 @@ class PracticeRecord { return responses[responses.length - 1]; } - bool alreadyHasMatchResponse( - ConstructIdentifier cId, - String text, - ) => - responses.any( - (element) => element.cId == cId && element.text == text, - ); + bool alreadyHasMatchResponse(ConstructIdentifier cId, String text) => + responses.any((element) => element.cId == cId && element.text == text); /// [target] needed for saving the record, little funky /// [cId] identifies the construct in the case of match activities which have multiple diff --git a/lib/pangea/practice_activities/practice_record_repo.dart b/lib/pangea/practice_activities/practice_record_repo.dart index 431ceda4f..9aecd0f8d 100644 --- a/lib/pangea/practice_activities/practice_record_repo.dart +++ b/lib/pangea/practice_activities/practice_record_repo.dart @@ -5,10 +5,7 @@ class _PracticeRecordCacheEntry { final PracticeRecord record; final DateTime timestamp; - _PracticeRecordCacheEntry({ - required this.record, - required this.timestamp, - }); + _PracticeRecordCacheEntry({required this.record, required this.timestamp}); bool get isExpired => DateTime.now().difference(timestamp).inMinutes > 15; } @@ -17,9 +14,7 @@ class _PracticeRecordCacheEntry { class PracticeRecordRepo { static final Map _cache = {}; - static PracticeRecord get( - PracticeTarget target, - ) { + static PracticeRecord get(PracticeTarget target) { final cached = _getCached(target); if (cached != null) return cached; @@ -29,15 +24,10 @@ class PracticeRecordRepo { return entry; } - static void set( - PracticeTarget selection, - PracticeRecord entry, - ) => + static void set(PracticeTarget selection, PracticeRecord entry) => _setCached(selection, entry); - static PracticeRecord? _getCached( - PracticeTarget target, - ) { + static PracticeRecord? _getCached(PracticeTarget target) { final keys = List.from(_cache.keys); for (final k in keys) { final item = _cache[k]!; @@ -49,10 +39,7 @@ class PracticeRecordRepo { return _cache[target.storageKey]?.record; } - static void _setCached( - PracticeTarget target, - PracticeRecord entry, - ) { + static void _setCached(PracticeTarget target, PracticeRecord entry) { _cache[target.storageKey] = _PracticeRecordCacheEntry( record: entry, timestamp: DateTime.now(), diff --git a/lib/pangea/practice_activities/practice_selection.dart b/lib/pangea/practice_activities/practice_selection.dart index 39a28c782..b39c6872b 100644 --- a/lib/pangea/practice_activities/practice_selection.dart +++ b/lib/pangea/practice_activities/practice_selection.dart @@ -17,22 +17,17 @@ class PracticeSelection { PracticeTarget? getTarget(ActivityTypeEnum type) => activities(type).firstOrNull; - PracticeTarget? getMorphTarget( - PangeaToken t, - MorphFeaturesEnum morph, - ) => + PracticeTarget? getMorphTarget(PangeaToken t, MorphFeaturesEnum morph) => activities(ActivityTypeEnum.morphId).firstWhereOrNull( (entry) => entry.tokens.contains(t) && entry.morphFeature == morph, ); Map toJson() => { - 'activityQueue': _activityQueue.map( - (key, value) => MapEntry( - key.toString(), - value.map((e) => e.toJson()).toList(), - ), - ), - }; + 'activityQueue': _activityQueue.map( + (key, value) => + MapEntry(key.toString(), value.map((e) => e.toJson()).toList()), + ), + }; static PracticeSelection fromJson(Map json) { return PracticeSelection( diff --git a/lib/pangea/practice_activities/practice_selection_repo.dart b/lib/pangea/practice_activities/practice_selection_repo.dart index 31afe6a76..c677cfe8b 100644 --- a/lib/pangea/practice_activities/practice_selection_repo.dart +++ b/lib/pangea/practice_activities/practice_selection_repo.dart @@ -19,9 +19,9 @@ class _PracticeSelectionCacheEntry { bool get isExpired => DateTime.now().difference(timestamp).inDays > 1; Map toJson() => { - 'selection': selection.toJson(), - 'timestamp': timestamp.toIso8601String(), - }; + 'selection': selection.toJson(), + 'timestamp': timestamp.toIso8601String(), + }; factory _PracticeSelectionCacheEntry.fromJson(Map json) { return _PracticeSelectionCacheEntry( @@ -47,10 +47,7 @@ class PracticeSelectionRepo { final cached = _getCached(eventId); if (cached != null) return cached; - final newEntry = await _fetch( - tokens: tokens, - langCode: messageLanguage, - ); + final newEntry = await _fetch(tokens: tokens, langCode: messageLanguage); _setCached(eventId, newEntry); return newEntry; @@ -74,9 +71,7 @@ class PracticeSelectionRepo { return selection; } - static PracticeSelection? _getCached( - String eventId, - ) { + static PracticeSelection? _getCached(String eventId) { try { final keys = List.from(_storage.getKeys()); for (final String key in keys) { @@ -105,10 +100,7 @@ class PracticeSelectionRepo { } } - static void _setCached( - String eventId, - PracticeSelection entry, - ) { + static void _setCached(String eventId, PracticeSelection entry) { final cachedEntry = _PracticeSelectionCacheEntry( selection: entry, timestamp: DateTime.now(), @@ -131,16 +123,14 @@ class PracticeSelectionRepo { PangeaToken b, int aScore, int bScore, - ) => - bScore.compareTo(aScore); + ) => bScore.compareTo(aScore); static int _sortMorphTargets( PracticeTarget a, PracticeTarget b, int aScore, int bScore, - ) => - bScore.compareTo(aScore); + ) => bScore.compareTo(aScore); static List _tokenToMorphTargets(PangeaToken t) { return t.morphsBasicallyEligibleForPracticeByPriority @@ -176,10 +166,7 @@ class PracticeSelectionRepo { return []; } - final scores = await _fetchPriorityScores( - practiceTokens, - activityType, - ); + final scores = await _fetchPriorityScores(practiceTokens, activityType); practiceTokens.sort((a, b) => _sortTokens(a, b, scores[a]!, scores[b]!)); practiceTokens = practiceTokens.take(8).toList(); @@ -231,21 +218,23 @@ class PracticeSelectionRepo { } final ids = tokens.map((t) => t.vocabConstructID).toList(); - final idMap = { - for (final token in tokens) token: token.vocabConstructID, - }; + final idMap = {for (final token in tokens) token: token.vocabConstructID}; final constructs = await MatrixState - .pangeaController.matrixState.analyticsDataService + .pangeaController + .matrixState + .analyticsDataService .getConstructUses(ids); for (final token in tokens) { final construct = constructs[idMap[token]]; - final lastUsed = - construct?.lastUseByTypes(activityType.associatedUseTypes); + final lastUsed = construct?.lastUseByTypes( + activityType.associatedUseTypes, + ); - final daysSinceLastUsed = - lastUsed == null ? 20 : DateTime.now().difference(lastUsed).inDays; + final daysSinceLastUsed = lastUsed == null + ? 20 + : DateTime.now().difference(lastUsed).inDays; scores[token] = daysSinceLastUsed * (token.vocabConstructID.isContentWord ? 10 : 9); diff --git a/lib/pangea/practice_activities/practice_target.dart b/lib/pangea/practice_activities/practice_target.dart index 87c1ce7e7..4b4bc8de5 100644 --- a/lib/pangea/practice_activities/practice_target.dart +++ b/lib/pangea/practice_activities/practice_target.dart @@ -59,8 +59,9 @@ class PracticeTarget { } return PracticeTarget( - tokens: - (json['tokens'] as List).map((e) => PangeaToken.fromJson(e)).toList(), + tokens: (json['tokens'] as List) + .map((e) => PangeaToken.fromJson(e)) + .toList(), activityType: type, morphFeature: json['morphFeature'] == null ? null diff --git a/lib/pangea/practice_activities/word_focus_listening_generator.dart b/lib/pangea/practice_activities/word_focus_listening_generator.dart index 0511b6fdb..4946de532 100644 --- a/lib/pangea/practice_activities/word_focus_listening_generator.dart +++ b/lib/pangea/practice_activities/word_focus_listening_generator.dart @@ -4,9 +4,7 @@ import 'package:fluffychat/pangea/practice_activities/practice_activity_model.da import 'package:fluffychat/pangea/practice_activities/practice_match.dart'; class WordFocusListeningGenerator { - static MessageActivityResponse get( - MessageActivityRequest req, - ) { + static MessageActivityResponse get(MessageActivityRequest req) { if (req.target.tokens.length <= 1) { throw Exception( "Word focus listening activity requires at least 2 tokens", diff --git a/lib/pangea/space_analytics/analytics_request_indicator.dart b/lib/pangea/space_analytics/analytics_request_indicator.dart index 6b9d854a8..b57f5fce1 100644 --- a/lib/pangea/space_analytics/analytics_request_indicator.dart +++ b/lib/pangea/space_analytics/analytics_request_indicator.dart @@ -14,10 +14,7 @@ import 'package:fluffychat/widgets/future_loading_dialog.dart'; class AnalyticsRequestIndicator extends StatefulWidget { final Room room; - const AnalyticsRequestIndicator({ - super.key, - required this.room, - }); + const AnalyticsRequestIndicator({super.key, required this.room}); @override AnalyticsRequestIndicatorState createState() => @@ -62,8 +59,9 @@ class AnalyticsRequestIndicatorState extends State { final analyticsRoomIds = analyticsRooms.map((r) => r.id).toSet(); _analyticsRoomSub?.cancel(); _analyticsRoomSub = widget.room.client.onSync.stream.listen((update) async { - final joined = update.rooms?.join?.entries - .where((e) => analyticsRoomIds.contains(e.key)); + final joined = update.rooms?.join?.entries.where( + (e) => analyticsRoomIds.contains(e.key), + ); if (joined == null || joined.isEmpty) return; final Set updatedRoomIds = {}; @@ -155,14 +153,9 @@ class AnalyticsRequestIndicatorState extends State { child: _knockingAdmins.isEmpty ? const SizedBox() : Padding( - padding: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 1, - ), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), child: Material( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), clipBehavior: Clip.hardEdge, child: ListTile( minVerticalPadding: 0, diff --git a/lib/pangea/space_analytics/analytics_requests_repo.dart b/lib/pangea/space_analytics/analytics_requests_repo.dart index 2c3b39ffd..01a4816a5 100644 --- a/lib/pangea/space_analytics/analytics_requests_repo.dart +++ b/lib/pangea/space_analytics/analytics_requests_repo.dart @@ -7,22 +7,16 @@ class _AnalyticsRequestEntry { final RequestStatus status; final DateTime timestamp; - _AnalyticsRequestEntry({ - required this.status, - required this.timestamp, - }); + _AnalyticsRequestEntry({required this.status, required this.timestamp}); Map toJson() { - return { - 'status': status.name, - 'timestamp': timestamp.toIso8601String(), - }; + return {'status': status.name, 'timestamp': timestamp.toIso8601String()}; } _AnalyticsRequestEntry.fromJson(Map json) - : status = RequestStatus.fromString(json['status']) ?? - RequestStatus.unrequested, - timestamp = DateTime.parse(json['timestamp']); + : status = + RequestStatus.fromString(json['status']) ?? RequestStatus.unrequested, + timestamp = DateTime.parse(json['timestamp']); bool get isExpired { final now = DateTime.now(); @@ -32,8 +26,9 @@ class _AnalyticsRequestEntry { } class AnalyticsRequestsRepo { - static final GetStorage _requestStorage = - GetStorage('analytics_request_storage'); + static final GetStorage _requestStorage = GetStorage( + 'analytics_request_storage', + ); static String _storageKey(String userId, LanguageModel language) { return 'analytics_request_${userId}_${language.langCodeShort}'; @@ -87,10 +82,7 @@ class AnalyticsRequestsRepo { await _requestStorage.write(key, entry.toJson()); } - static Future remove( - String userId, - LanguageModel language, - ) async { + static Future remove(String userId, LanguageModel language) async { final key = _storageKey(userId, language); await _requestStorage.remove(key); } diff --git a/lib/pangea/space_analytics/download_space_analytics_dialog.dart b/lib/pangea/space_analytics/download_space_analytics_dialog.dart index 5a783a918..b709ba019 100644 --- a/lib/pangea/space_analytics/download_space_analytics_dialog.dart +++ b/lib/pangea/space_analytics/download_space_analytics_dialog.dart @@ -6,6 +6,7 @@ import 'package:excel/excel.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_downloads/space_analytics_summary_enum.dart'; @@ -123,11 +124,7 @@ class DownloadAnalyticsDialogState extends State { }); } } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {}, - ); + ErrorHandler.logError(e: e, s: s, data: {}); _clean(); _error = e; @@ -145,11 +142,7 @@ class DownloadAnalyticsDialogState extends State { final fileName = "analytics_${widget.space.name}_${DateTime.now().toIso8601String()}.${_downloadType == DownloadType.xlsx ? 'xlsx' : 'csv'}"; - await DownloadUtil.downloadFile( - content, - fileName, - DownloadType.csv, - ); + await DownloadUtil.downloadFile(content, fileName, DownloadType.csv); } Future _getAnalyticsModel( @@ -179,22 +172,14 @@ class DownloadAnalyticsDialogState extends State { ); if (mounted) setState(() => _downloadStatuses[userID] = 2); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - "userID": userID, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {"userID": userID}); if (mounted) setState(() => _downloadStatuses[userID] = -2); } return summary; } - List _formatExcelRow( - SpaceAnalyticsSummaryModel summary, - ) { + List _formatExcelRow(SpaceAnalyticsSummaryModel summary) { final List row = []; for (int i = 0; i < SpaceAnalyticsSummaryEnum.values.length; i++) { final key = SpaceAnalyticsSummaryEnum.values[i]; @@ -210,21 +195,16 @@ class DownloadAnalyticsDialogState extends State { return row; } - List _getExcelFileContent( - List summaries, - ) { + List _getExcelFileContent(List summaries) { final excel = Excel.createExcel(); final sheet = excel['Sheet1']; for (final key in SpaceAnalyticsSummaryEnum.values) { sheet - .cell( - CellIndex.indexByColumnRow( - rowIndex: 0, - columnIndex: key.index, - ), - ) - .value = TextCellValue(key.header(L10n.of(context))); + .cell(CellIndex.indexByColumnRow(rowIndex: 0, columnIndex: key.index)) + .value = TextCellValue( + key.header(L10n.of(context)), + ); } final rows = summaries.map((summary) => _formatExcelRow(summary)).toList(); @@ -234,16 +214,17 @@ class DownloadAnalyticsDialogState extends State { for (int j = 0; j < row.length; j++) { final cell = row[j]; sheet - .cell(CellIndex.indexByColumnRow(rowIndex: i + 2, columnIndex: j)) - .value = cell; + .cell( + CellIndex.indexByColumnRow(rowIndex: i + 2, columnIndex: j), + ) + .value = + cell; } } return excel.encode() ?? []; } - String _getCSVFileContent( - List summaries, - ) { + String _getCSVFileContent(List summaries) { final List> rows = []; final headerRow = []; for (final key in SpaceAnalyticsSummaryEnum.values) { @@ -279,9 +260,7 @@ class DownloadAnalyticsDialogState extends State { Widget build(BuildContext context) { return Dialog( child: Container( - constraints: const BoxConstraints( - maxWidth: 400, - ), + constraints: const BoxConstraints(maxWidth: 400), padding: const EdgeInsets.symmetric(vertical: 20), child: Column( mainAxisSize: MainAxisSize.min, @@ -289,7 +268,9 @@ class DownloadAnalyticsDialogState extends State { Text( L10n.of(context).fileType, style: TextStyle( - fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, + fontSize: + AppSettings.fontSizeFactor.value * + AppConfig.messageFontSize, ), ), Padding( @@ -312,10 +293,7 @@ class DownloadAnalyticsDialogState extends State { Padding( padding: const EdgeInsets.all(8.0), child: ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 300, - minHeight: 0, - ), + constraints: const BoxConstraints(maxHeight: 300, minHeight: 0), child: ListView.builder( shrinkWrap: true, itemCount: widget.analyticsRooms.length, @@ -340,10 +318,7 @@ class DownloadAnalyticsDialogState extends State { width: 40, height: 30, child: (_downloadStatuses[userId] ?? 0) < 0 - ? const Icon( - Icons.error_outline, - size: 16, - ) + ? const Icon(Icons.error_outline, size: 16) : Center( child: AnimatedContainer( duration: @@ -352,8 +327,9 @@ class DownloadAnalyticsDialogState extends State { width: 12, decoration: BoxDecoration( color: _downloadStatusColor(userId!), - borderRadius: - BorderRadius.circular(100), + borderRadius: BorderRadius.circular( + 100, + ), ), ), ), diff --git a/lib/pangea/space_analytics/space_analytics.dart b/lib/pangea/space_analytics/space_analytics.dart index 5db3b2248..6aa19d514 100644 --- a/lib/pangea/space_analytics/space_analytics.dart +++ b/lib/pangea/space_analytics/space_analytics.dart @@ -160,8 +160,8 @@ class SpaceAnalyticsState extends State { selectedLanguage = availableLanguages.contains(_userL2) || availableLanguages.isEmpty - ? _userL2 - : availableLanguages.firstOrNull; + ? _userL2 + : availableLanguages.firstOrNull; await refresh(); if (mounted) { @@ -193,38 +193,34 @@ class SpaceAnalyticsState extends State { setState(() { downloads = Map.fromEntries( - _availableUsers.map( - (user) { - final room = _analyticsRoomOfUser(user); - final hasLangData = _availableUsersForLang.contains(user); + _availableUsers.map((user) { + final room = _analyticsRoomOfUser(user); + final hasLangData = _availableUsersForLang.contains(user); - RequestStatus? requestStatus; - if (room != null) { - requestStatus = RequestStatus.available; - } else if (!hasLangData) { - requestStatus = RequestStatus.unavailable; - } else { - requestStatus = AnalyticsRequestsRepo.get( - user.id, - selectedLanguage!, - ) ?? - RequestStatus.unrequested; - } + RequestStatus? requestStatus; + if (room != null) { + requestStatus = RequestStatus.available; + } else if (!hasLangData) { + requestStatus = RequestStatus.unavailable; + } else { + requestStatus = + AnalyticsRequestsRepo.get(user.id, selectedLanguage!) ?? + RequestStatus.unrequested; + } - final DownloadStatus downloadStatus = - requestStatus == RequestStatus.available - ? DownloadStatus.loading - : DownloadStatus.unavailable; + final DownloadStatus downloadStatus = + requestStatus == RequestStatus.available + ? DownloadStatus.loading + : DownloadStatus.unavailable; - return MapEntry( - user, - AnalyticsDownload( - requestStatus: requestStatus, - downloadStatus: downloadStatus, - ), - ); - }, - ), + return MapEntry( + user, + AnalyticsDownload( + requestStatus: requestStatus, + downloadStatus: downloadStatus, + ), + ); + }), ); }); @@ -243,12 +239,11 @@ class SpaceAnalyticsState extends State { } } - Future _setAnalyticsModel( - Room analyticsRoom, - ) async { + Future _setAnalyticsModel(Room analyticsRoom) async { final String? userID = analyticsRoom.creatorId; - final user = - room?.getParticipants().firstWhereOrNull((p) => p.id == userID); + final user = room?.getParticipants().firstWhereOrNull( + (p) => p.id == userID, + ); if (user == null) return; SpaceAnalyticsSummaryModel? summary; @@ -291,14 +286,12 @@ class SpaceAnalyticsState extends State { final roomId = _analyticsRoomIdOfUser(user); if (roomId == null) return; await Matrix.of(context).client.knockRoom( - roomId, - via: room?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == roomId, - ) - ?.via, - reason: widget.roomId, - ); + roomId, + via: room?.spaceChildren + .firstWhereOrNull((child) => child.roomId == roomId) + ?.via, + reason: widget.roomId, + ); status = RequestStatus.requested; } catch (e) { status = RequestStatus.unavailable; @@ -314,11 +307,7 @@ class SpaceAnalyticsState extends State { } } finally { if (status != null) { - await AnalyticsRequestsRepo.set( - user.id, - selectedLanguage!, - status, - ); + await AnalyticsRequestsRepo.set(user.id, selectedLanguage!, status); downloads[user]?.requestStatus = status; } @@ -369,10 +358,10 @@ class SpaceAnalyticsState extends State { Room? _analyticsRoomOfUser(User user) { return Matrix.of(context).client.rooms.firstWhereOrNull( - (r) => - r.isAnalyticsRoomOfUser(user.id) && - r.madeForLang == selectedLanguage?.langCodeShort, - ); + (r) => + r.isAnalyticsRoomOfUser(user.id) && + r.madeForLang == selectedLanguage?.langCodeShort, + ); } void setSelectedLanguage(LanguageModel? lang) { diff --git a/lib/pangea/space_analytics/space_analytics_download_enum.dart b/lib/pangea/space_analytics/space_analytics_download_enum.dart index b928d7734..169314492 100644 --- a/lib/pangea/space_analytics/space_analytics_download_enum.dart +++ b/lib/pangea/space_analytics/space_analytics_download_enum.dart @@ -88,5 +88,5 @@ enum DownloadStatus { loading, /// The data has been downloaded successfully. - complete; + complete, } diff --git a/lib/pangea/space_analytics/space_analytics_inactive_dialog.dart b/lib/pangea/space_analytics/space_analytics_inactive_dialog.dart index 4de0ad719..30bc98a09 100644 --- a/lib/pangea/space_analytics/space_analytics_inactive_dialog.dart +++ b/lib/pangea/space_analytics/space_analytics_inactive_dialog.dart @@ -4,9 +4,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart'; class SpaceAnalyticsInactiveDialog extends StatelessWidget { - const SpaceAnalyticsInactiveDialog({ - super.key, - }); + const SpaceAnalyticsInactiveDialog({super.key}); @override Widget build(BuildContext context) { @@ -15,10 +13,7 @@ class SpaceAnalyticsInactiveDialog extends StatelessWidget { maxWidth: 450.0, dialogContent: SingleChildScrollView( child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24.0, - vertical: 40.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 40.0), child: Column( spacing: 12.0, mainAxisSize: MainAxisSize.min, diff --git a/lib/pangea/space_analytics/space_analytics_request_dialog.dart b/lib/pangea/space_analytics/space_analytics_request_dialog.dart index f47a59217..2ae255878 100644 --- a/lib/pangea/space_analytics/space_analytics_request_dialog.dart +++ b/lib/pangea/space_analytics/space_analytics_request_dialog.dart @@ -10,9 +10,7 @@ import 'package:fluffychat/pangea/analytics_page/analytics_page_constants.dart'; import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart'; class SpaceAnalyticsRequestDialog extends StatelessWidget { - const SpaceAnalyticsRequestDialog({ - super.key, - }); + const SpaceAnalyticsRequestDialog({super.key}); @override Widget build(BuildContext context) { @@ -42,7 +40,7 @@ class SpaceAnalyticsRequestDialog extends StatelessWidget { imageUrl: "${AppConfig.assetsBaseURL}/${AnalyticsPageConstants.dinoBotFileName}", errorWidget: (context, e, s) => const SizedBox.shrink(), - progressIndicatorBuilder: (context, _, __) => + progressIndicatorBuilder: (context, _, _) => const SizedBox.shrink(), width: 150.0, ), diff --git a/lib/pangea/space_analytics/space_analytics_requested_dialog.dart b/lib/pangea/space_analytics/space_analytics_requested_dialog.dart index 76fbe3dc9..62d6c84ab 100644 --- a/lib/pangea/space_analytics/space_analytics_requested_dialog.dart +++ b/lib/pangea/space_analytics/space_analytics_requested_dialog.dart @@ -52,7 +52,7 @@ class SpaceAnalyticsRequestedDialog extends StatelessWidget { imageUrl: "${AppConfig.assetsBaseURL}/${AnalyticsPageConstants.dinoBotFileName}", errorWidget: (context, e, s) => const SizedBox.shrink(), - progressIndicatorBuilder: (context, _, __) => + progressIndicatorBuilder: (context, _, _) => const SizedBox.shrink(), width: 150.0, ), @@ -120,10 +120,9 @@ class SpaceAnalyticsRequestedDialog extends StatelessWidget { left: 4.0, child: IconButton.filled( style: IconButton.styleFrom( - backgroundColor: Theme.of(context) - .colorScheme - .surfaceContainerHigh - .withAlpha(170), + backgroundColor: Theme.of( + context, + ).colorScheme.surfaceContainerHigh.withAlpha(170), ), icon: Icon( Icons.close_outlined, diff --git a/lib/pangea/space_analytics/space_analytics_view.dart b/lib/pangea/space_analytics/space_analytics_view.dart index 686ecdfe2..e32f29886 100644 --- a/lib/pangea/space_analytics/space_analytics_view.dart +++ b/lib/pangea/space_analytics/space_analytics_view.dart @@ -16,10 +16,7 @@ import 'package:fluffychat/widgets/layouts/max_width_body.dart'; class SpaceAnalyticsView extends StatelessWidget { final SpaceAnalyticsState controller; - const SpaceAnalyticsView({ - super.key, - required this.controller, - }); + const SpaceAnalyticsView({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -73,9 +70,9 @@ class SpaceAnalyticsView extends StatelessWidget { children: [ if (controller.lastUpdatedString != null) Text( - L10n.of(context).lastUpdated( - controller.lastUpdatedString!, - ), + L10n.of( + context, + ).lastUpdated(controller.lastUpdatedString!), textAlign: TextAlign.end, style: TextStyle( fontSize: !mini ? 12.0 : 8.0, @@ -107,9 +104,9 @@ class SpaceAnalyticsView extends StatelessWidget { Text( mini ? controller.selectedLanguage!.langCode - .toUpperCase() + .toUpperCase() : controller.selectedLanguage! - .getDisplayName(context), + .getDisplayName(context), style: TextStyle( color: theme.colorScheme.onPrimaryContainer, @@ -194,98 +191,95 @@ class SpaceAnalyticsView extends StatelessWidget { ), ], ), - ...controller.sortedDownloads.mapIndexed( - (index, entry) { - final download = entry.value; - return TableRow( - children: [ - TableCell( - child: Opacity( - opacity: download.requestStatus.opacity, - child: Padding( - padding: EdgeInsets.symmetric( - vertical: !mini ? 12.0 : 4.0, - ), - child: Row( - spacing: !mini ? 16.0 : 8.0, - children: [ - Avatar( - size: !mini ? 64.0 : 40.0, - mxContent: entry.key.avatarUrl, - name: entry.key.calcDisplayname(), - userId: entry.key.id, - presenceUserId: entry.key.id, + ...controller.sortedDownloads.mapIndexed(( + index, + entry, + ) { + final download = entry.value; + return TableRow( + children: [ + TableCell( + child: Opacity( + opacity: download.requestStatus.opacity, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: !mini ? 12.0 : 4.0, + ), + child: Row( + spacing: !mini ? 16.0 : 8.0, + children: [ + Avatar( + size: !mini ? 64.0 : 40.0, + mxContent: entry.key.avatarUrl, + name: entry.key.calcDisplayname(), + userId: entry.key.id, + presenceUserId: entry.key.id, + ), + Flexible( + child: Column( + spacing: 4.0, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + height: index == 0 ? 8.0 : 0.0, + ), + Text( + entry.key.id, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: !mini ? 16.0 : 12.0, + fontWeight: FontWeight.w500, + ), + ), + _RequestButton( + status: download.requestStatus, + onPressed: () => + controller.requestAnalytics( + entry.key, + ), + mini: mini, + ), + const SizedBox(height: 8.0), + ], ), - Flexible( - child: Column( - spacing: 4.0, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - SizedBox( - height: - index == 0 ? 8.0 : 0.0, - ), - Text( - entry.key.id, - maxLines: 1, - overflow: - TextOverflow.ellipsis, - style: TextStyle( - fontSize: - !mini ? 16.0 : 12.0, - fontWeight: FontWeight.w500, - ), - ), - _RequestButton( - status: - download.requestStatus, - onPressed: () => controller - .requestAnalytics( - entry.key, - ), - mini: mini, - ), - const SizedBox(height: 8.0), - ], - ), - ), - ], - ), + ), + ], ), ), ), - _TableContentCell( - text: download.summary?.level?.toString(), - downloadStatus: download.downloadStatus, - requestStatus: download.requestStatus, - mini: mini, - ), - _TableContentCell( - text: download.summary?.numLemmas.toString(), - downloadStatus: download.downloadStatus, - requestStatus: download.requestStatus, - mini: mini, - ), - _TableContentCell( - text: download.summary?.numMorphConstructs - .toString(), - downloadStatus: download.downloadStatus, - requestStatus: download.requestStatus, - mini: mini, - ), - _TableContentCell( - text: download.summary?.numCompletedActivities - .toString(), - downloadStatus: download.downloadStatus, - requestStatus: download.requestStatus, - mini: mini, - ), - ], - ); - }, - ), + ), + _TableContentCell( + text: download.summary?.level?.toString(), + downloadStatus: download.downloadStatus, + requestStatus: download.requestStatus, + mini: mini, + ), + _TableContentCell( + text: download.summary?.numLemmas.toString(), + downloadStatus: download.downloadStatus, + requestStatus: download.requestStatus, + mini: mini, + ), + _TableContentCell( + text: download.summary?.numMorphConstructs + .toString(), + downloadStatus: download.downloadStatus, + requestStatus: download.requestStatus, + mini: mini, + ), + _TableContentCell( + text: download.summary?.numCompletedActivities + .toString(), + downloadStatus: download.downloadStatus, + requestStatus: download.requestStatus, + mini: mini, + ), + ], + ); + }), ], ) : const CircularProgressIndicator.adaptive(), @@ -392,22 +386,14 @@ class _TableHeaderCell extends StatelessWidget { child: Tooltip( message: text, child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 6.0, - horizontal: 8.0, - ), + padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 8.0), child: Column( spacing: 10.0, children: [ Icon(icon, size: 22.0), mini ? const SizedBox.shrink() - : Text( - text, - style: const TextStyle( - fontSize: 12.0, - ), - ), + : Text(text, style: const TextStyle(fontSize: 12.0)), ], ), ), @@ -432,10 +418,7 @@ class _TableContentCell extends StatelessWidget { @override Widget build(BuildContext context) { if (downloadStatus != DownloadStatus.complete) { - return _MissingContentCell( - downloadStatus, - requestStatus, - ); + return _MissingContentCell(downloadStatus, requestStatus); } return TableCell( @@ -461,10 +444,7 @@ class _MissingContentCell extends StatelessWidget { final DownloadStatus status; final RequestStatus requestStatus; - const _MissingContentCell( - this.status, - this.requestStatus, - ); + const _MissingContentCell(this.status, this.requestStatus); @override Widget build(BuildContext context) { @@ -530,10 +510,7 @@ class _RequestButton extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ if (status.icon != null) - Icon( - status.icon, - size: !mini ? 12.0 : 8.0, - ), + Icon(status.icon, size: !mini ? 12.0 : 8.0), Text( status.label(context), style: TextStyle(fontSize: !mini ? 12.0 : 8.0), diff --git a/lib/pangea/spaces/client_spaces_extension.dart b/lib/pangea/spaces/client_spaces_extension.dart index d72c85a58..64f4dd844 100644 --- a/lib/pangea/spaces/client_spaces_extension.dart +++ b/lib/pangea/spaces/client_spaces_extension.dart @@ -20,18 +20,12 @@ extension SpacesClientExtension on Client { topic: topic?.trim(), powerLevelContentOverride: {'events_default': 100}, initialState: [ - RoomDefaults.defaultSpacePowerLevels( - userID!, - spaceChild: spaceChild, - ), + RoomDefaults.defaultSpacePowerLevels(userID!, spaceChild: spaceChild), await pangeaJoinRules( joinRules.toString().replaceAll('JoinRules.', ''), ), if (avatarUrl != null) - StateEvent( - type: EventTypes.RoomAvatar, - content: {'url': avatarUrl}, - ), + StateEvent(type: EventTypes.RoomAvatar, content: {'url': avatarUrl}), if (initialState != null) ...initialState, ], ); diff --git a/lib/pangea/spaces/knocking_users_indicator.dart b/lib/pangea/spaces/knocking_users_indicator.dart index 9816f3637..fc9d2e666 100644 --- a/lib/pangea/spaces/knocking_users_indicator.dart +++ b/lib/pangea/spaces/knocking_users_indicator.dart @@ -13,10 +13,7 @@ import 'package:fluffychat/utils/stream_extension.dart'; class KnockingUsersIndicator extends StatefulWidget { final Room room; - const KnockingUsersIndicator({ - super.key, - required this.room, - }); + const KnockingUsersIndicator({super.key, required this.room}); @override KnockingUsersIndicatorState createState() => KnockingUsersIndicatorState(); @@ -36,11 +33,13 @@ class KnockingUsersIndicatorState extends State { .rateLimit(const Duration(seconds: 1)) .listen((_) => _setKnockingUsers()); - widget.room.requestParticipants( - [Membership.join, Membership.invite, Membership.knock], - false, - true, - ).then((_) => _setKnockingUsers()); + widget.room + .requestParticipants( + [Membership.join, Membership.invite, Membership.knock], + false, + true, + ) + .then((_) => _setKnockingUsers()); } bool _isMemberUpdate(({String roomId, StrippedStateEvent state}) event) => @@ -68,14 +67,9 @@ class KnockingUsersIndicatorState extends State { child: _knockingUsers.isEmpty || !widget.room.isRoomAdmin ? const SizedBox() : Padding( - padding: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 1, - ), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), child: Material( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), clipBehavior: Clip.hardEdge, child: ListTile( minVerticalPadding: 0, @@ -94,8 +88,9 @@ class KnockingUsersIndicatorState extends State { child: Text( _knockingUsers.length == 1 ? L10n.of(context).aUserIsKnocking - : L10n.of(context) - .usersAreKnocking(_knockingUsers.length), + : L10n.of( + context, + ).usersAreKnocking(_knockingUsers.length), style: Theme.of(context).textTheme.bodyMedium, ), ), diff --git a/lib/pangea/spaces/load_participants_builder.dart b/lib/pangea/spaces/load_participants_builder.dart index 1ba9ee35a..55fcee0fa 100644 --- a/lib/pangea/spaces/load_participants_builder.dart +++ b/lib/pangea/spaces/load_participants_builder.dart @@ -11,10 +11,8 @@ import 'package:fluffychat/widgets/matrix.dart'; class LoadParticipantsBuilder extends StatefulWidget { final Room? room; final bool loadProfiles; - final Widget Function( - BuildContext context, - LoadParticipantsBuilderState, - ) builder; + final Widget Function(BuildContext context, LoadParticipantsBuilderState) + builder; const LoadParticipantsBuilder({ required this.room, @@ -70,13 +68,7 @@ class LoadParticipantsBuilderState extends State { if (widget.loadProfiles) await _cacheLevels(); } catch (err, s) { error = err.toString(); - ErrorHandler.logError( - e: err, - s: s, - data: { - 'roomId': widget.room?.id, - }, - ); + ErrorHandler.logError(e: err, s: s, data: {'roomId': widget.room?.id}); } finally { if (mounted) { setState(() => loading = false); @@ -114,7 +106,8 @@ class LoadParticipantsBuilderState extends State { for (final user in participants) { if (_levelsCache[user.id] == null && user.membership == Membership.join) { _levelsCache[user.id] = await MatrixState - .pangeaController.userController + .pangeaController + .userController .getPublicAnalyticsProfile(user.id); } } @@ -135,19 +128,15 @@ extension LeaderboardGradient on int { final Color? color = this == 0 ? AppConfig.gold : this == 1 - ? Colors.grey[400]! - : this == 2 - ? Colors.brown[400]! - : null; + ? Colors.grey[400]! + : this == 2 + ? Colors.brown[400]! + : null; if (color == null) return null; return LinearGradient( - colors: [ - color, - Colors.white, - color, - ], + colors: [color, Colors.white, color], begin: Alignment.topLeft, end: Alignment.bottomRight, ); diff --git a/lib/pangea/spaces/public_course_extension.dart b/lib/pangea/spaces/public_course_extension.dart index 8d3722c35..5c1cd917a 100644 --- a/lib/pangea/spaces/public_course_extension.dart +++ b/lib/pangea/spaces/public_course_extension.dart @@ -11,10 +11,7 @@ extension PublicCourseExtension on Api { }) async { final requestUri = Uri( path: '/_synapse/client/unstable/org.pangea/public_courses', - queryParameters: { - 'limit': limit.toString(), - 'since': since, - }, + queryParameters: {'limit': limit.toString(), 'since': since}, ); final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['content-type'] = 'application/json'; @@ -36,11 +33,7 @@ extension PublicCoursesRequest on Client { Future requestPublicCourses({ int limit = 10, String? since, - }) => - getPublicCourses( - limit: limit, - since: since, - ); + }) => getPublicCourses(limit: limit, since: since); } class PublicCoursesResponse extends GetPublicRoomsResponse { @@ -56,32 +49,26 @@ class PublicCoursesResponse extends GetPublicRoomsResponse { @override PublicCoursesResponse.fromJson(super.json) - : courses = (json['chunk'] as List) - .map((e) => PublicCoursesChunk.fromJson(e)) - .toList(), - super.fromJson(); + : courses = (json['chunk'] as List) + .map((e) => PublicCoursesChunk.fromJson(e)) + .toList(), + super.fromJson(); } class PublicCoursesChunk { - final PublicRoomsChunk room; + final PublishedRoomsChunk room; final String courseId; - PublicCoursesChunk({ - required this.room, - required this.courseId, - }); + PublicCoursesChunk({required this.room, required this.courseId}); factory PublicCoursesChunk.fromJson(Map json) { return PublicCoursesChunk( - room: PublicRoomsChunk.fromJson(json), + room: PublishedRoomsChunk.fromJson(json), courseId: json['course_id'] as String, ); } Map toJson() { - return { - 'room': room.toJson(), - 'course_id': courseId, - }; + return {'room': room.toJson(), 'course_id': courseId}; } } diff --git a/lib/pangea/spaces/space_navigation_column.dart b/lib/pangea/spaces/space_navigation_column.dart index b6d630870..3dd39e6bd 100644 --- a/lib/pangea/spaces/space_navigation_column.dart +++ b/lib/pangea/spaces/space_navigation_column.dart @@ -96,8 +96,9 @@ class SpaceNavigationColumnState extends State { : FluffyThemes.navRailWidth - 8.0; final double navRailExtraWidth = widget.showNavRail ? 250.0 : 0.0; - final double columnWidth = - isColumnMode ? FluffyThemes.columnWidth + 1.0 : 0; + final double columnWidth = isColumnMode + ? FluffyThemes.columnWidth + 1.0 + : 0; final double railWidth = widget.showNavRail ? navRailWidth + 1.0 : 0; final double baseWidth = columnWidth + railWidth; @@ -124,10 +125,7 @@ class SpaceNavigationColumnState extends State { width: FluffyThemes.columnWidth, child: _MainView(state: widget.state), ), - Container( - width: 1.0, - color: theme.dividerColor, - ), + Container(width: 1.0, color: theme.dividerColor), ], ), ), @@ -156,10 +154,7 @@ class SpaceNavigationColumnState extends State { profile: _profile, onProfileUpdate: _updateProfile, ), - Container( - width: 1, - color: Theme.of(context).dividerColor, - ), + Container(width: 1, color: Theme.of(context).dividerColor), ], ); }, @@ -173,9 +168,7 @@ class SpaceNavigationColumnState extends State { class _MainView extends StatelessWidget { final GoRouterState state; - const _MainView({ - required this.state, - }); + const _MainView({required this.state}); @override Widget build(BuildContext context) { @@ -183,7 +176,7 @@ class _MainView extends StatelessWidget { if (path == null) { return ChatList( activeChat: state.pathParameters['roomid'], - activeSpaceId: state.pathParameters['spaceid'], + activeSpace: state.pathParameters['spaceid'], ); } @@ -210,9 +203,8 @@ class _MainView extends StatelessWidget { imageUrl: "${AppConfig.assetsBaseURL}/${SpaceConstants.sideBearFileName}", errorWidget: (context, url, error) => const SizedBox(), - placeholder: (context, url) => const Center( - child: CircularProgressIndicator.adaptive(), - ), + placeholder: (context, url) => + const Center(child: CircularProgressIndicator.adaptive()), ), ), ); @@ -220,7 +212,7 @@ class _MainView extends StatelessWidget { return ChatList( activeChat: state.pathParameters['roomid'], - activeSpaceId: state.pathParameters['spaceid'], + activeSpace: state.pathParameters['spaceid'], ); } } diff --git a/lib/pangea/speech_to_text/speech_to_text_repo.dart b/lib/pangea/speech_to_text/speech_to_text_repo.dart index 730db58b9..6c698612d 100644 --- a/lib/pangea/speech_to_text/speech_to_text_repo.dart +++ b/lib/pangea/speech_to_text/speech_to_text_repo.dart @@ -14,10 +14,7 @@ class _SpeechToTextCacheItem { final Future data; final DateTime timestamp; - const _SpeechToTextCacheItem({ - required this.data, - required this.timestamp, - }); + const _SpeechToTextCacheItem({required this.data, required this.timestamp}); } class SpeechToTextRepo { @@ -72,11 +69,7 @@ class SpeechToTextRepo { return Result.value(res); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } @@ -97,9 +90,8 @@ class SpeechToTextRepo { static void _setCached( SpeechToTextRequestModel request, Future response, - ) => - _cache[request.hashCode.toString()] = _SpeechToTextCacheItem( - data: response, - timestamp: DateTime.now(), - ); + ) => _cache[request.hashCode.toString()] = _SpeechToTextCacheItem( + data: response, + timestamp: DateTime.now(), + ); } diff --git a/lib/pangea/speech_to_text/speech_to_text_request_model.dart b/lib/pangea/speech_to_text/speech_to_text_request_model.dart index a0a736ff6..f655fac05 100644 --- a/lib/pangea/speech_to_text/speech_to_text_request_model.dart +++ b/lib/pangea/speech_to_text/speech_to_text_request_model.dart @@ -18,9 +18,9 @@ class SpeechToTextRequestModel { }); Map toJson() => { - "audio_content": base64Encode(audioContent), - "config": config.toJson(), - }; + "audio_content": base64Encode(audioContent), + "config": config.toJson(), + }; @override bool operator ==(Object other) { @@ -33,12 +33,10 @@ class SpeechToTextRequestModel { @override int get hashCode { - final bytesSample = - audioContent.length > 10 ? audioContent.sublist(0, 10) : audioContent; - return Object.hashAll([ - Object.hashAll(bytesSample), - config.hashCode, - ]); + final bytesSample = audioContent.length > 10 + ? audioContent.sublist(0, 10) + : audioContent; + return Object.hashAll([Object.hashAll(bytesSample), config.hashCode]); } } @@ -60,11 +58,11 @@ class SpeechToTextAudioConfigModel { }); Map toJson() => { - "encoding": encoding.value, - "sample_rate_hertz": sampleRateHertz, - "user_l1": userL1, - "user_l2": userL2, - "enable_word_confidence": enableWordConfidence, - "enable_automatic_punctuation": enableAutomaticPunctuation, - }; + "encoding": encoding.value, + "sample_rate_hertz": sampleRateHertz, + "user_l1": userL1, + "user_l2": userL2, + "enable_word_confidence": enableWordConfidence, + "enable_automatic_punctuation": enableAutomaticPunctuation, + }; } diff --git a/lib/pangea/speech_to_text/speech_to_text_response_model.dart b/lib/pangea/speech_to_text/speech_to_text_response_model.dart index e042be077..79658cc91 100644 --- a/lib/pangea/speech_to_text/speech_to_text_response_model.dart +++ b/lib/pangea/speech_to_text/speech_to_text_response_model.dart @@ -8,9 +8,7 @@ import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; class SpeechToTextResponseModel { final List results; - SpeechToTextResponseModel({ - required this.results, - }); + SpeechToTextResponseModel({required this.results}); Transcript get transcript => results.first.transcripts.first; @@ -29,13 +27,10 @@ class SpeechToTextResponseModel { } Map toJson() => { - "results": results.map((e) => e.toJson()).toList(), - }; + "results": results.map((e) => e.toJson()).toList(), + }; - List constructs( - String roomId, - String eventId, - ) { + List constructs(String roomId, String eventId) { final List constructs = []; final metadata = ConstructUseMetaData( roomId: roomId, @@ -70,8 +65,8 @@ class SpeechToTextResult { ); Map toJson() => { - "transcripts": transcripts.map((e) => e.toJson()).toList(), - }; + "transcripts": transcripts.map((e) => e.toJson()).toList(), + }; } class Transcript { @@ -93,24 +88,24 @@ class Transcript { double? get wordsPerMinute => wordsPerHr != null ? wordsPerHr! / 60 : null; factory Transcript.fromJson(Map json) => Transcript( - text: json['transcript'], - confidence: json['confidence'] <= 100 - ? json['confidence'] - : json['confidence'] / 100, - sttTokens: (json['stt_tokens'] as List) - .map((e) => STTToken.fromJson(e)) - .toList(), - langCode: json['lang_code'], - wordsPerHr: json['words_per_hr'], - ); + text: json['transcript'], + confidence: json['confidence'] <= 100 + ? json['confidence'] + : json['confidence'] / 100, + sttTokens: (json['stt_tokens'] as List) + .map((e) => STTToken.fromJson(e)) + .toList(), + langCode: json['lang_code'], + wordsPerHr: json['words_per_hr'], + ); Map toJson() => { - "transcript": text, - "confidence": confidence, - "stt_tokens": sttTokens.map((e) => e.toJson()).toList(), - "lang_code": langCode, - "words_per_hr": wordsPerHr, - }; + "transcript": text, + "confidence": confidence, + "stt_tokens": sttTokens.map((e) => e.toJson()).toList(), + "lang_code": langCode, + "words_per_hr": wordsPerHr, + }; Color get color => confidence > 80 ? AppConfig.success : AppConfig.warning; } @@ -151,11 +146,11 @@ class STTToken { } Map toJson() => { - "token": token.toJson(), - "start_time": startTime?.inMilliseconds, - "end_time": endTime?.inMilliseconds, - "confidence": confidence, - }; + "token": token.toJson(), + "start_time": startTime?.inMilliseconds, + "end_time": endTime?.inMilliseconds, + "confidence": confidence, + }; @override bool operator ==(Object other) { diff --git a/lib/pangea/subscription/controllers/subscription_controller.dart b/lib/pangea/subscription/controllers/subscription_controller.dart index 69d9c5276..444d773e9 100644 --- a/lib/pangea/subscription/controllers/subscription_controller.dart +++ b/lib/pangea/subscription/controllers/subscription_controller.dart @@ -115,15 +115,13 @@ class SubscriptionController with ChangeNotifier { } if (!kIsWeb) { - Purchases.addCustomerInfoUpdateListener( - (CustomerInfo info) async { - final bool? wasSubscribed = isSubscribed; - await updateCustomerInfo(); - if (wasSubscribed == false && isSubscribed == true) { - subscriptionNotifier.value = true; - } - }, - ); + Purchases.addCustomerInfoUpdateListener((CustomerInfo info) async { + final bool? wasSubscribed = isSubscribed; + await updateCustomerInfo(); + if (wasSubscribed == false && isSubscribed == true) { + subscriptionNotifier.value = true; + } + }); } else { if (SubscriptionManagementRepo.getBeganWebPayment()) { await SubscriptionManagementRepo.removeBeganWebPayment(); @@ -184,9 +182,7 @@ class SubscriptionController with ChangeNotifier { ErrorHandler.logError( m: "Tried to subscribe to web SubscriptionDetails with Null duration", s: StackTrace.current, - data: { - "selectedSubscription": selectedSubscription.toJson(), - }, + data: {"selectedSubscription": selectedSubscription.toJson()}, ); return; } @@ -195,10 +191,7 @@ class SubscriptionController with ChangeNotifier { isPromo: isPromo, ); await SubscriptionManagementRepo.setBeganWebPayment(); - launchUrlString( - paymentLink, - webOnlyWindowName: "_self", - ); + launchUrlString(paymentLink, webOnlyWindowName: "_self"); return; } if (selectedSubscription.package == null) { @@ -214,10 +207,7 @@ class SubscriptionController with ChangeNotifier { return; } - GoogleAnalytics.beginPurchaseSubscription( - selectedSubscription, - context, - ); + GoogleAnalytics.beginPurchaseSubscription(selectedSubscription, context); await Purchases.purchasePackage(selectedSubscription.package!); GoogleAnalytics.updateUserSubscriptionStatus(true); } @@ -244,8 +234,8 @@ class SubscriptionController with ChangeNotifier { return isSubscribed! ? SubscriptionStatus.subscribed : shouldShowPaywall - ? SubscriptionStatus.shouldShowPaywall - : SubscriptionStatus.dimissedPaywall; + ? SubscriptionStatus.shouldShowPaywall + : SubscriptionStatus.dimissedPaywall; } /// whether or not the paywall should be shown @@ -273,13 +263,11 @@ class SubscriptionController with ChangeNotifier { context: context, constraints: BoxConstraints( maxHeight: PlatformInfos.isMobile - ? MediaQuery.of(context).size.height - 50 + ? MediaQuery.heightOf(context) - 50 : 600, ), builder: (_) { - return SubscriptionPaywall( - pangeaController: _pangeaController, - ); + return SubscriptionPaywall(pangeaController: _pangeaController); }, ); await SubscriptionManagementRepo.setDismissedPaywall(); @@ -318,15 +306,12 @@ class SubscriptionController with ChangeNotifier { return paymentLink; } - String? get defaultManagementURL => - currentSubscriptionInfo?.currentSubscription - ?.defaultManagementURL(availableSubscriptionInfo?.appIds); + String? get defaultManagementURL => currentSubscriptionInfo + ?.currentSubscription + ?.defaultManagementURL(availableSubscriptionInfo?.appIds); } -enum SubscriptionDuration { - month, - year, -} +enum SubscriptionDuration { month, year } extension SubscriptionDurationExtension on SubscriptionDuration { String get value => this == SubscriptionDuration.month ? "month" : "year"; @@ -372,8 +357,8 @@ class SubscriptionDetails { return appId == appIds?.androidId ? AppConfig.googlePlayMangementUrl : appId == appIds?.appleId - ? AppConfig.appleMangementUrl - : Environment.stripeManagementUrl; + ? AppConfig.appleMangementUrl + : Environment.stripeManagementUrl; } Map toJson() { diff --git a/lib/pangea/subscription/models/base_subscription_info.dart b/lib/pangea/subscription/models/base_subscription_info.dart index 61f6cae0f..8abd042aa 100644 --- a/lib/pangea/subscription/models/base_subscription_info.dart +++ b/lib/pangea/subscription/models/base_subscription_info.dart @@ -44,8 +44,9 @@ class CurrentSubscriptionInfo { String? get purchasePlatformDisplayName { if (currentSubscription?.appId == null) return null; - return availableSubscriptionInfo.appIds - ?.appDisplayName(currentSubscription!.appId!); + return availableSubscriptionInfo.appIds?.appDisplayName( + currentSubscription!.appId!, + ); } bool get purchasedOnWeb => @@ -74,11 +75,7 @@ class AvailableSubscriptionsInfo { List? allProducts; DateTime? lastUpdated; - AvailableSubscriptionsInfo({ - this.appIds, - this.allProducts, - this.lastUpdated, - }); + AvailableSubscriptionsInfo({this.appIds, this.allProducts, this.lastUpdated}); Future setAvailableSubscriptions() async { final cachedInfo = @@ -125,15 +122,16 @@ class AvailableSubscriptionsInfo { ); } - Map toJson({validate = true}) { + Map toJson({bool validate = true}) { if (validate && (appIds == null || allProducts == null)) { throw "appIds or allProducts is null in AvailableSubscriptionsInfo"; } final data = {}; data['app_ids'] = appIds?.toJson(); - data['all_products'] = - allProducts?.map((product) => product.toJson()).toList(); + data['all_products'] = allProducts + ?.map((product) => product.toJson()) + .toList(); data['last_updated'] = (lastUpdated ?? DateTime.now()).toIso8601String(); return data; } diff --git a/lib/pangea/subscription/models/mobile_subscriptions.dart b/lib/pangea/subscription/models/mobile_subscriptions.dart index 23a73de64..6677d0a5a 100644 --- a/lib/pangea/subscription/models/mobile_subscriptions.dart +++ b/lib/pangea/subscription/models/mobile_subscriptions.dart @@ -23,9 +23,7 @@ class MobileSubscriptionInfo extends CurrentSubscriptionInfo { ? PurchasesConfiguration(Environment.rcGoogleKey) : PurchasesConfiguration(Environment.rcIosKey); try { - await Purchases.configure( - configuration..appUserID = userID, - ); + await Purchases.configure(configuration..appUserID = userID); await super.configure(); await setMobilePackages(); } catch (err) { @@ -77,22 +75,23 @@ class MobileSubscriptionInfo extends CurrentSubscriptionInfo { return; } - final List activeEntitlements = - info.entitlements.all.entries - .where( - (MapEntry entry) => - entry.value.isActive && - (entry.value.expirationDate == null || - DateTime.parse(entry.value.expirationDate!) - .isAfter(DateTime.now())), - ) - .map((MapEntry entry) => entry.value) - .toList(); + final List activeEntitlements = info + .entitlements + .all + .entries + .where( + (MapEntry entry) => + entry.value.isActive && + (entry.value.expirationDate == null || + DateTime.parse( + entry.value.expirationDate!, + ).isAfter(DateTime.now())), + ) + .map((MapEntry entry) => entry.value) + .toList(); if (activeEntitlements.length > 1) { - debugPrint( - "User has more than one active entitlement.", - ); + debugPrint("User has more than one active entitlement."); } else if (activeEntitlements.isEmpty) { debugPrint("User has no active entitlements"); resetSubscription(); diff --git a/lib/pangea/subscription/models/web_subscriptions.dart b/lib/pangea/subscription/models/web_subscriptions.dart index 2ceb36d11..040601a53 100644 --- a/lib/pangea/subscription/models/web_subscriptions.dart +++ b/lib/pangea/subscription/models/web_subscriptions.dart @@ -28,8 +28,8 @@ class WebSubscriptionInfo extends CurrentSubscriptionInfo { expirationDate = DateTime.tryParse(currentSubscription.expiresDate); unsubscribeDetectedAt = currentSubscription.unsubscribeDetectedAt != null - ? DateTime.parse(currentSubscription.unsubscribeDetectedAt!) - : null; + ? DateTime.parse(currentSubscription.unsubscribeDetectedAt!) + : null; } } catch (err) { currentSubscriptionId = AppConfig.errorSubscriptionId; diff --git a/lib/pangea/subscription/pages/change_subscription.dart b/lib/pangea/subscription/pages/change_subscription.dart index d7a291c95..670992d2c 100644 --- a/lib/pangea/subscription/pages/change_subscription.dart +++ b/lib/pangea/subscription/pages/change_subscription.dart @@ -12,22 +12,22 @@ import 'package:fluffychat/widgets/matrix.dart'; class ChangeSubscription extends StatelessWidget { final SubscriptionManagementController controller; - ChangeSubscription({ - required this.controller, - super.key, - }); + ChangeSubscription({required this.controller, super.key}); final PangeaController pangeaController = MatrixState.pangeaController; List get subscriptions => - pangeaController.subscriptionController.availableSubscriptionInfo + pangeaController + .subscriptionController + .availableSubscriptionInfo ?.availableSubscriptions ?? []; bool get inTrialWindow => pangeaController.userController.inTrialWindow(); - String get trialEnds => DateFormat.yMMMd() - .format(DateTime.now().add(const Duration(days: kIsWeb ? 0 : 7))); + String get trialEnds => DateFormat.yMMMd().format( + DateTime.now().add(const Duration(days: kIsWeb ? 0 : 7)), + ); @override Widget build(BuildContext context) { @@ -35,9 +35,7 @@ class ChangeSubscription extends StatelessWidget { return const Center( child: Padding( padding: EdgeInsets.all(16.0), - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), + child: CircularProgressIndicator.adaptive(strokeWidth: 2), ), ); } @@ -64,22 +62,21 @@ class ChangeSubscription extends StatelessWidget { child: Column( children: [ ListTile( - title: Text( - subscription.displayName(context), - ), + title: Text(subscription.displayName(context)), trailing: Icon( controller.selectedSubscription?.id != subscription.id ? Icons.keyboard_arrow_right_outlined : Icons.keyboard_arrow_down_outlined, ), - enabled: (!subscription.isTrial || inTrialWindow) && + enabled: + (!subscription.isTrial || inTrialWindow) && !controller.isCurrentSubscription(subscription), onTap: () => controller.selectSubscription(subscription), ), AnimatedSize( duration: FluffyThemes.animationDuration, - child: controller.selectedSubscription?.id != - subscription.id + child: + controller.selectedSubscription?.id != subscription.id ? const SizedBox() : Column( children: [ @@ -101,14 +98,18 @@ class ChangeSubscription extends StatelessWidget { if (!kIsWeb) Container( decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .onPrimary, + color: Theme.of( + context, + ).colorScheme.onPrimary, borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16.0), - topRight: Radius.circular(16.0), - ), + topLeft: Radius.circular( + 16.0, + ), + topRight: Radius.circular( + 16.0, + ), + ), ), padding: const EdgeInsets.all(16.0), child: Row( @@ -119,8 +120,9 @@ class ChangeSubscription extends StatelessWidget { L10n.of(context).startingToday, ), Text( - L10n.of(context) - .oneWeekFreeTrial, + L10n.of( + context, + ).oneWeekFreeTrial, ), ], ), @@ -132,8 +134,9 @@ class ChangeSubscription extends StatelessWidget { MainAxisAlignment.spaceBetween, children: [ Text( - L10n.of(context) - .paidSubscriptionStarts( + L10n.of( + context, + ).paidSubscriptionStarts( trialEnds, ), ), @@ -159,13 +162,15 @@ class ChangeSubscription extends StatelessWidget { CrossAxisAlignment.start, children: [ Text( - L10n.of(context) - .cancelInSubscriptionSettings, + L10n.of( + context, + ).cancelInSubscriptionSettings, ), if (!kIsWeb) Text( - L10n.of(context) - .cancelToAvoidCharges(trialEnds), + L10n.of( + context, + ).cancelToAvoidCharges(trialEnds), ), const SizedBox(height: 20.0), ElevatedButton( @@ -179,8 +184,9 @@ class ChangeSubscription extends StatelessWidget { children: [ Text( subscription.isTrial - ? L10n.of(context) - .activateTrial + ? L10n.of( + context, + ).activateTrial : L10n.of(context).pay, ), ], @@ -205,11 +211,7 @@ class ChangeSubscription extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.info_outlined), - Flexible( - child: Text( - L10n.of(context).promoCodeInfo, - ), - ), + Flexible(child: Text(L10n.of(context).promoCodeInfo)), ], ), ), diff --git a/lib/pangea/subscription/pages/settings_subscription.dart b/lib/pangea/subscription/pages/settings_subscription.dart index 37145333a..a342fa83c 100644 --- a/lib/pangea/subscription/pages/settings_subscription.dart +++ b/lib/pangea/subscription/pages/settings_subscription.dart @@ -64,7 +64,9 @@ class SubscriptionManagementController extends State bool get subscriptionsAvailable => subscriptionController - .availableSubscriptionInfo?.availableSubscriptions.isNotEmpty ?? + .availableSubscriptionInfo + ?.availableSubscriptions + .isNotEmpty ?? false; bool get currentSubscriptionAvailable => @@ -76,15 +78,19 @@ class SubscriptionManagementController extends State bool get currentSubscriptionIsTrial => currentSubscriptionAvailable && (subscriptionController - .currentSubscriptionInfo?.currentSubscription?.isTrial ?? + .currentSubscriptionInfo + ?.currentSubscription + ?.isTrial ?? false); String? get purchasePlatformDisplayName => subscriptionController - .currentSubscriptionInfo?.purchasePlatformDisplayName; + .currentSubscriptionInfo + ?.purchasePlatformDisplayName; bool get currentSubscriptionIsPromotional => subscriptionController - .currentSubscriptionInfo?.currentSubscriptionIsPromotional ?? + .currentSubscriptionInfo + ?.currentSubscriptionIsPromotional ?? false; String get currentSubscriptionTitle => @@ -105,7 +111,8 @@ class SubscriptionManagementController extends State return true; } return subscriptionController - .currentSubscriptionInfo!.currentPlatformMatchesPurchasePlatform; + .currentSubscriptionInfo! + .currentPlatformMatchesPurchasePlatform; } DateTime? get expirationDate => @@ -159,10 +166,7 @@ class SubscriptionManagementController extends State ErrorHandler.logError( e: e, s: s, - data: { - "subscription_id": subscription.id, - "is_promo": isPromo, - }, + data: {"subscription_id": subscription.id, "is_promo": isPromo}, ); } finally { if (mounted) setState(() => loading = false); @@ -186,7 +190,9 @@ class SubscriptionManagementController extends State managementUrl += "?prefilled_email=${Uri.encodeComponent(email)}"; } final String? purchaseAppId = subscriptionController - .currentSubscriptionInfo?.currentSubscription?.appId; + .currentSubscriptionInfo + ?.currentSubscription + ?.appId; if (purchaseAppId == null) return; final SubscriptionAppIds? appIds = @@ -241,8 +247,4 @@ class SubscriptionManagementController extends State Widget build(BuildContext context) => SettingsSubscriptionView(this); } -enum ManagementOption { - cancel, - paymentMethod, - history, -} +enum ManagementOption { cancel, paymentMethod, history } diff --git a/lib/pangea/subscription/pages/settings_subscription_view.dart b/lib/pangea/subscription/pages/settings_subscription_view.dart index b48ab2fcf..76c7d1f82 100644 --- a/lib/pangea/subscription/pages/settings_subscription_view.dart +++ b/lib/pangea/subscription/pages/settings_subscription_view.dart @@ -45,17 +45,15 @@ class SettingsSubscriptionView extends StatelessWidget { ListTile( title: Text(L10n.of(context).paymentMethod), trailing: const Icon(Icons.credit_card), - onTap: () => controller.launchMangementUrl( - ManagementOption.paymentMethod, - ), + onTap: () => + controller.launchMangementUrl(ManagementOption.paymentMethod), enabled: controller.showManagementOptions, ), ListTile( title: Text(L10n.of(context).paymentHistory), trailing: const Icon(Icons.keyboard_arrow_right_outlined), - onTap: () => controller.launchMangementUrl( - ManagementOption.history, - ), + onTap: () => + controller.launchMangementUrl(ManagementOption.history), enabled: controller.showManagementOptions, ), if (controller.expirationDate != null) ...[ @@ -80,10 +78,7 @@ class SettingsSubscriptionView extends StatelessWidget { spacing: 8.0, mainAxisSize: MainAxisSize.min, children: [ - const Icon( - Icons.info_outline, - size: 20, - ), + const Icon(Icons.info_outline, size: 20), Flexible( child: Text( L10n.of(context).waitForSubscriptionChanges, @@ -104,9 +99,7 @@ class SettingsSubscriptionView extends StatelessWidget { return Scaffold( appBar: AppBar( centerTitle: true, - title: Text( - L10n.of(context).subscriptionManagement, - ), + title: Text(L10n.of(context).subscriptionManagement), ), body: ListTileTheme( iconColor: Theme.of(context).textTheme.bodyLarge!.color, @@ -116,9 +109,7 @@ class SettingsSubscriptionView extends StatelessWidget { if (isSubscribed == null) const Center(child: CircularProgressIndicator.adaptive()) else if (isSubscribed && !controller.showManagementOptions) - ManagementNotAvailableWarning( - controller: controller, - ) + ManagementNotAvailableWarning(controller: controller) else if (isSubscribed && controller.showManagementOptions) ...managementButtons else @@ -134,10 +125,7 @@ class SettingsSubscriptionView extends StatelessWidget { class ManagementNotAvailableWarning extends StatelessWidget { final SubscriptionManagementController controller; - const ManagementNotAvailableWarning({ - required this.controller, - super.key, - }); + const ManagementNotAvailableWarning({required this.controller, super.key}); @override Widget build(BuildContext context) { @@ -169,10 +157,7 @@ class ManagementNotAvailableWarning extends StatelessWidget { return Center( child: Padding( padding: const EdgeInsets.all(20), - child: Text( - getWarningText(), - textAlign: TextAlign.center, - ), + child: Text(getWarningText(), textAlign: TextAlign.center), ), ); } diff --git a/lib/pangea/subscription/repo/subscription_management_repo.dart b/lib/pangea/subscription/repo/subscription_management_repo.dart index 673a1103b..4ed0d2ab7 100644 --- a/lib/pangea/subscription/repo/subscription_management_repo.dart +++ b/lib/pangea/subscription/repo/subscription_management_repo.dart @@ -20,10 +20,7 @@ class SubscriptionManagementRepo { static Future setAvailableSubscriptionsInfo( AvailableSubscriptionsInfo info, ) async { - await _cache.write( - PLocalKey.availableSubscriptionInfo, - info.toJson(), - ); + await _cache.write(PLocalKey.availableSubscriptionInfo, info.toJson()); } static bool getBeganWebPayment() { @@ -88,10 +85,7 @@ class SubscriptionManagementRepo { } static Future setSubscriptionEndDate(DateTime? date) async { - await _cache.write( - PLocalKey.subscriptionEndDate, - date?.toIso8601String(), - ); + await _cache.write(PLocalKey.subscriptionEndDate, date?.toIso8601String()); } static DateTime? getSubscriptionEndDate() { diff --git a/lib/pangea/subscription/repo/subscription_repo.dart b/lib/pangea/subscription/repo/subscription_repo.dart index f844a20b5..637bd3365 100644 --- a/lib/pangea/subscription/repo/subscription_repo.dart +++ b/lib/pangea/subscription/repo/subscription_repo.dart @@ -21,13 +21,9 @@ class SubscriptionRepo { choreoApiKey: Environment.choreoApiKey, accessToken: MatrixState.pangeaController.userController.accessToken, ); - final http.Response res = await req.get( - url: PApiUrls.rcAppsChoreo, - ); + final http.Response res = await req.get(url: PApiUrls.rcAppsChoreo); - return SubscriptionAppIds.fromJson( - jsonDecode(res.body), - ); + return SubscriptionAppIds.fromJson(jsonDecode(res.body)); } catch (err) { ErrorHandler.logError( m: "Failed to fetch app information for revenuecat API", @@ -44,19 +40,14 @@ class SubscriptionRepo { choreoApiKey: Environment.choreoApiKey, accessToken: MatrixState.pangeaController.userController.accessToken, ); - final http.Response res = await req.get( - url: PApiUrls.rcProductsChoreo, - ); + final http.Response res = await req.get(url: PApiUrls.rcProductsChoreo); final Map json = jsonDecode(res.body); - final RCProductsResponseModel resp = - RCProductsResponseModel.fromJson(json); + final RCProductsResponseModel resp = RCProductsResponseModel.fromJson( + json, + ); return resp.allProducts; } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: {}, - ); + ErrorHandler.logError(e: err, s: s, data: {}); return null; } } @@ -67,25 +58,16 @@ class SubscriptionRepo { choreoApiKey: Environment.choreoApiKey, accessToken: MatrixState.pangeaController.userController.accessToken, ); - final http.Response res = await req.get( - url: PApiUrls.rcProductsTrial, - ); + final http.Response res = await req.get(url: PApiUrls.rcProductsTrial); if (res.statusCode != 201) { - ErrorHandler.logError( - e: res.body, - data: {}, - ); + ErrorHandler.logError(e: res.body, data: {}); return false; } else { return true; } } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: {}, - ); + ErrorHandler.logError(e: err, s: s, data: {}); return false; } } @@ -101,10 +83,7 @@ class SubscriptionRepo { final http.Response res = await req.get(url: PApiUrls.rcSubscription); final Map json = jsonDecode(res.body); final RCSubscriptionResponseModel resp = - RCSubscriptionResponseModel.fromJson( - json, - allProducts, - ); + RCSubscriptionResponseModel.fromJson(json, allProducts); return resp; } } @@ -112,13 +91,9 @@ class SubscriptionRepo { class RCProductsResponseModel { List allProducts; - RCProductsResponseModel({ - required this.allProducts, - }); + RCProductsResponseModel({required this.allProducts}); - factory RCProductsResponseModel.fromJson( - Map json, - ) { + factory RCProductsResponseModel.fromJson(Map json) { final List offerings = json["items"] as List; final res = offerings .map((offering) => SubscriptionDetails.fromJson(offering)) @@ -180,18 +155,16 @@ class RCSubscriptionResponseModel { if (activeEntitlements.isEmpty) { debugPrint("User has no active entitlements"); - return RCSubscriptionResponseModel( - allSubscriptions: history, - ); + return RCSubscriptionResponseModel(allSubscriptions: history); } final String currentSubscriptionId = activeEntitlements[0]; - final SubscriptionDetails? currentSubscription = - allProducts?.firstWhereOrNull( - (SubscriptionDetails sub) => - sub.id.contains(currentSubscriptionId) || - currentSubscriptionId.contains(sub.id), - ); + final SubscriptionDetails? currentSubscription = allProducts + ?.firstWhereOrNull( + (SubscriptionDetails sub) => + sub.id.contains(currentSubscriptionId) || + currentSubscriptionId.contains(sub.id), + ); return RCSubscriptionResponseModel( currentSubscription: currentSubscription, @@ -202,8 +175,7 @@ class RCSubscriptionResponseModel { } static List getActiveEntitlements(Map json) { - return json['entitlements'] - .entries + return json['entitlements'].entries .where( (MapEntry entitlement) => DateTime.parse( entitlement.value['expires_date'], @@ -218,8 +190,7 @@ class RCSubscriptionResponseModel { } static List getAllEntitlements(Map json) { - return json['entitlements'] - .entries + return json['entitlements'].entries .map( (MapEntry entitlement) => entitlement.value['product_identifier'], diff --git a/lib/pangea/subscription/utils/subscription_app_id.dart b/lib/pangea/subscription/utils/subscription_app_id.dart index 4ba71ee7a..bb86263cc 100644 --- a/lib/pangea/subscription/utils/subscription_app_id.dart +++ b/lib/pangea/subscription/utils/subscription_app_id.dart @@ -12,8 +12,8 @@ class SubscriptionAppIds { String? get currentAppId => kIsWeb ? stripeId : Platform.isAndroid - ? androidId - : appleId; + ? androidId + : appleId; String? appDisplayName(String appId) { if (appId == stripeId) return "web"; @@ -38,24 +38,20 @@ class SubscriptionAppIds { } } -enum RCPlatform { - stripe, - android, - apple, -} +enum RCPlatform { stripe, android, apple } extension RCPlatformExtension on RCPlatform { RCPlatform get currentPlatform => kIsWeb ? RCPlatform.stripe : Platform.isAndroid - ? RCPlatform.android - : RCPlatform.apple; + ? RCPlatform.android + : RCPlatform.apple; String get string { return currentPlatform == RCPlatform.stripe ? 'stripe' : currentPlatform == RCPlatform.android - ? 'play_store' - : 'app_store'; + ? 'play_store' + : 'app_store'; } } diff --git a/lib/pangea/subscription/widgets/paywall_card.dart b/lib/pangea/subscription/widgets/paywall_card.dart index 6263dd162..faaf743f3 100644 --- a/lib/pangea/subscription/widgets/paywall_card.dart +++ b/lib/pangea/subscription/widgets/paywall_card.dart @@ -10,12 +10,11 @@ import 'package:fluffychat/widgets/matrix.dart'; class PaywallCard extends StatelessWidget { const PaywallCard({super.key}); - static Future show( - BuildContext context, - String targetId, - ) async { + static Future show(BuildContext context, String targetId) async { if (!MatrixState - .pangeaController.subscriptionController.shouldShowPaywall) { + .pangeaController + .subscriptionController + .shouldShowPaywall) { return; } @@ -51,8 +50,9 @@ class PaywallCard extends StatelessWidget { .showPaywall(context); }, style: TextButton.styleFrom( - backgroundColor: - Theme.of(context).colorScheme.primary.withAlpha(25), + backgroundColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(25), ), child: Text(L10n.of(context).getAccess), ), diff --git a/lib/pangea/subscription/widgets/subscription_options.dart b/lib/pangea/subscription/widgets/subscription_options.dart index 95a8e633f..f9e64e7aa 100644 --- a/lib/pangea/subscription/widgets/subscription_options.dart +++ b/lib/pangea/subscription/widgets/subscription_options.dart @@ -9,10 +9,7 @@ import 'package:fluffychat/pangea/subscription/controllers/subscription_controll class SubscriptionOptions extends StatelessWidget { final PangeaController pangeaController; - const SubscriptionOptions({ - super.key, - required this.pangeaController, - }); + const SubscriptionOptions({super.key, required this.pangeaController}); @override Widget build(BuildContext context) { @@ -30,26 +27,25 @@ class SubscriptionOptions extends StatelessWidget { buttonText: L10n.of(context).activateTrial, ), ] - : pangeaController.subscriptionController.availableSubscriptionInfo! - .availableSubscriptions - .map( - (subscription) => SubscriptionCard( - subscription: subscription, - onTap: () { - pangeaController.subscriptionController - .submitSubscriptionChange( - subscription, - context, - ); - }, - title: subscription.displayName(context), - enabled: !subscription.isTrial, - description: subscription.isTrial - ? L10n.of(context).trialPeriodExpired - : null, - ), - ) - .toList(), + : pangeaController + .subscriptionController + .availableSubscriptionInfo! + .availableSubscriptions + .map( + (subscription) => SubscriptionCard( + subscription: subscription, + onTap: () { + pangeaController.subscriptionController + .submitSubscriptionChange(subscription, context); + }, + title: subscription.displayName(context), + enabled: !subscription.isTrial, + description: subscription.isTrial + ? L10n.of(context).trialPeriodExpired + : null, + ), + ) + .toList(), ); } } @@ -102,16 +98,18 @@ class SubscriptionCard extends StatelessWidget { textAlign: TextAlign.center, style: TextStyle( fontSize: 20, - color: - enabled ? null : const Color.fromARGB(255, 174, 174, 174), + color: enabled + ? null + : const Color.fromARGB(255, 174, 174, 174), ), ), Text( description ?? subscription?.displayPrice(context) ?? '', textAlign: TextAlign.center, style: TextStyle( - color: - enabled ? null : const Color.fromARGB(255, 174, 174, 174), + color: enabled + ? null + : const Color.fromARGB(255, 174, 174, 174), ), ), OutlinedButton( @@ -122,9 +120,7 @@ class SubscriptionCard extends StatelessWidget { } : null, style: buttonStyle, - child: Text( - buttonText ?? L10n.of(context).subscribe, - ), + child: Text(buttonText ?? L10n.of(context).subscribe), ), ], ), diff --git a/lib/pangea/subscription/widgets/subscription_paywall.dart b/lib/pangea/subscription/widgets/subscription_paywall.dart index ee4e0b344..4ad6dd899 100644 --- a/lib/pangea/subscription/widgets/subscription_paywall.dart +++ b/lib/pangea/subscription/widgets/subscription_paywall.dart @@ -9,10 +9,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class SubscriptionPaywall extends StatelessWidget { final PangeaController pangeaController; - const SubscriptionPaywall({ - super.key, - required this.pangeaController, - }); + const SubscriptionPaywall({super.key, required this.pangeaController}); @override Widget build(BuildContext context) { @@ -46,9 +43,7 @@ class SubscriptionPaywall extends StatelessWidget { ), const SizedBox(height: 40), Center( - child: SubscriptionOptions( - pangeaController: pangeaController, - ), + child: SubscriptionOptions(pangeaController: pangeaController), ), ], ), diff --git a/lib/pangea/subscription/widgets/subscription_snackbar.dart b/lib/pangea/subscription/widgets/subscription_snackbar.dart index 95cd94b22..b2c5430c0 100644 --- a/lib/pangea/subscription/widgets/subscription_snackbar.dart +++ b/lib/pangea/subscription/widgets/subscription_snackbar.dart @@ -27,9 +27,5 @@ void showSubscribedSnackbar(BuildContext context) { ], ), ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: text, - ), - ); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: text)); } diff --git a/lib/pangea/text_to_speech/text_to_speech_repo.dart b/lib/pangea/text_to_speech/text_to_speech_repo.dart index 3b68486e6..d75afd4c9 100644 --- a/lib/pangea/text_to_speech/text_to_speech_repo.dart +++ b/lib/pangea/text_to_speech/text_to_speech_repo.dart @@ -14,10 +14,7 @@ class _TextToSpeechCacheItem { final Future data; final DateTime timestamp; - const _TextToSpeechCacheItem({ - required this.data, - required this.timestamp, - }); + const _TextToSpeechCacheItem({required this.data, required this.timestamp}); } class TextToSpeechRepo { @@ -72,11 +69,7 @@ class TextToSpeechRepo { return Result.value(res); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } @@ -97,9 +90,8 @@ class TextToSpeechRepo { static void _setCached( TextToSpeechRequestModel request, Future response, - ) => - _cache[request.hashCode.toString()] = _TextToSpeechCacheItem( - data: response, - timestamp: DateTime.now(), - ); + ) => _cache[request.hashCode.toString()] = _TextToSpeechCacheItem( + data: response, + timestamp: DateTime.now(), + ); } diff --git a/lib/pangea/text_to_speech/text_to_speech_request_model.dart b/lib/pangea/text_to_speech/text_to_speech_request_model.dart index c8a13a0fe..a03b57b9e 100644 --- a/lib/pangea/text_to_speech/text_to_speech_request_model.dart +++ b/lib/pangea/text_to_speech/text_to_speech_request_model.dart @@ -19,13 +19,13 @@ class TextToSpeechRequestModel { }); Map toJson() => { - ModelKey.text: text, - ModelKey.langCode: langCode, - ModelKey.userL1: userL1, - ModelKey.userL2: userL2, - ModelKey.tokens: tokens.map((token) => token.toJson()).toList(), - 'voice': voice, - }; + ModelKey.text: text, + ModelKey.langCode: langCode, + ModelKey.userL1: userL1, + ModelKey.userL2: userL2, + ModelKey.tokens: tokens.map((token) => token.toJson()).toList(), + 'voice': voice, + }; @override bool operator ==(Object other) { diff --git a/lib/pangea/text_to_speech/text_to_speech_response_model.dart b/lib/pangea/text_to_speech/text_to_speech_response_model.dart index 645005a3d..6148ade57 100644 --- a/lib/pangea/text_to_speech/text_to_speech_response_model.dart +++ b/lib/pangea/text_to_speech/text_to_speech_response_model.dart @@ -18,9 +18,7 @@ class TextToSpeechResponseModel { required this.ttsTokens, }); - factory TextToSpeechResponseModel.fromJson( - Map json, - ) => + factory TextToSpeechResponseModel.fromJson(Map json) => TextToSpeechResponseModel( audioContent: json["audio_content"], mimeType: json["mime_type"], @@ -33,13 +31,13 @@ class TextToSpeechResponseModel { ); Map toJson() => { - "audio_content": audioContent, - "mime_type": mimeType, - "duration_millis": durationMillis, - "wave_form": List.from(waveform.map((x) => x)), - "file_extension": fileExtension, - "tts_tokens": List.from(ttsTokens.map((x) => x.toJson())), - }; + "audio_content": audioContent, + "mime_type": mimeType, + "duration_millis": durationMillis, + "wave_form": List.from(waveform.map((x) => x)), + "file_extension": fileExtension, + "tts_tokens": List.from(ttsTokens.map((x) => x.toJson())), + }; PangeaAudioEventData toPangeaAudioEventData( String text, @@ -60,23 +58,19 @@ class TTSToken { final int endMS; final PangeaTokenText text; - TTSToken({ - required this.startMS, - required this.endMS, - required this.text, - }); + TTSToken({required this.startMS, required this.endMS, required this.text}); factory TTSToken.fromJson(Map json) => TTSToken( - startMS: json["start_ms"], - endMS: json["end_ms"], - text: PangeaTokenText.fromJson(json["text"]), - ); + startMS: json["start_ms"], + endMS: json["end_ms"], + text: PangeaTokenText.fromJson(json["text"]), + ); Map toJson() => { - "start_ms": startMS, - "end_ms": endMS, - "text": text.toJson(), - }; + "start_ms": startMS, + "end_ms": endMS, + "text": text.toJson(), + }; @override bool operator ==(Object other) { @@ -106,21 +100,22 @@ class PangeaAudioEventData { }); factory PangeaAudioEventData.fromJson(dynamic json) => PangeaAudioEventData( - text: json[ModelKey.text] as String, - langCode: json[ModelKey.langCode] as String, - tokens: List.from( - (json[ModelKey.tokens] as Iterable) - .map((x) => TTSToken.fromJson(x)) - .toList(), - ), - voice: json[ModelKey.voice] as String?, - ); + text: json[ModelKey.text] as String, + langCode: json[ModelKey.langCode] as String, + tokens: List.from( + (json[ModelKey.tokens] as Iterable) + .map((x) => TTSToken.fromJson(x)) + .toList(), + ), + voice: json[ModelKey.voice] as String?, + ); Map toJson() => { - ModelKey.text: text, - ModelKey.langCode: langCode, - ModelKey.tokens: - List>.from(tokens.map((x) => x.toJson())), - if (voice != null) ModelKey.voice: voice, - }; + ModelKey.text: text, + ModelKey.langCode: langCode, + ModelKey.tokens: List>.from( + tokens.map((x) => x.toJson()), + ), + if (voice != null) ModelKey.voice: voice, + }; } diff --git a/lib/pangea/text_to_speech/tts_controller.dart b/lib/pangea/text_to_speech/tts_controller.dart index a67865316..3bc949fcd 100644 --- a/lib/pangea/text_to_speech/tts_controller.dart +++ b/lib/pangea/text_to_speech/tts_controller.dart @@ -42,9 +42,7 @@ class TtsController { if (message != 'canceled' && message != 'interrupted') { error_handler.ErrorHandler.logError( e: 'TTS error', - data: { - 'message': message, - }, + data: {'message': message}, ); } } @@ -55,11 +53,7 @@ class TtsController { await _setAvailableBaseLanguages(); } catch (e, s) { debugger(when: kDebugMode); - error_handler.ErrorHandler.logError( - e: e, - s: s, - data: {}, - ); + error_handler.ErrorHandler.logError(e: e, s: s, data: {}); } } @@ -96,9 +90,7 @@ class TtsController { 'availableLangCodes': _availableLangCodes, }; debugPrint("TTS: Language not supported: $jsonData"); - Sentry.addBreadcrumb( - Breadcrumb.fromJson(jsonData), - ); + Sentry.addBreadcrumb(Breadcrumb.fromJson(jsonData)); } } @@ -112,18 +104,12 @@ class TtsController { if (result != 1) { error_handler.ErrorHandler.logError( m: 'Unexpected result from tts.stop', - data: { - 'result': result, - }, + data: {'result': result}, ); } } catch (e, s) { debugger(when: kDebugMode); - error_handler.ErrorHandler.logError( - e: e, - s: s, - data: {}, - ); + error_handler.ErrorHandler.logError(e: e, s: s, data: {}); } } @@ -182,7 +168,11 @@ class TtsController { await _setSpeakingLanguage(langCode); final enableTTS = MatrixState - .pangeaController.userController.profile.toolSettings.enableTTS; + .pangeaController + .userController + .profile + .toolSettings + .enableTTS; if (enableTTS) { final token = PangeaTokenText( @@ -193,16 +183,8 @@ class TtsController { onStart?.call(); await (_isLangFullySupported(langCode) - ? _speak( - text, - langCode, - [token], - ) - : _speakFromChoreo( - text, - langCode, - [token], - )); + ? _speak(text, langCode, [token]) + : _speakFromChoreo(text, langCode, [token])); } else if (targetID != null && context != null) { await _showTTSDisabledPopup(context, targetID); } @@ -248,13 +230,7 @@ class TtsController { // } } catch (e, s) { debugger(when: kDebugMode); - error_handler.ErrorHandler.logError( - e: e, - s: s, - data: { - 'text': text, - }, - ); + error_handler.ErrorHandler.logError(e: e, s: s, data: {'text': text}); await _speakFromChoreo(text, langCode, tokens); } finally { stop(); @@ -275,9 +251,11 @@ class TtsController { text: text, langCode: langCode, tokens: tokens, - userL1: MatrixState.pangeaController.userController.userL1Code ?? + userL1: + MatrixState.pangeaController.userController.userL1Code ?? LanguageKeys.unknownLanguage, - userL2: MatrixState.pangeaController.userController.userL2Code ?? + userL2: + MatrixState.pangeaController.userController.userL2Code ?? LanguageKeys.unknownLanguage, ), ); @@ -291,20 +269,14 @@ class TtsController { audioPlayer?.dispose(); audioPlayer = AudioPlayer(); await audioPlayer!.setAudioSource( - BytesAudioSource( - audioContent, - ttsRes.mimeType, - ), + BytesAudioSource(audioContent, ttsRes.mimeType), ); await audioPlayer!.play(); } catch (e, s) { error_handler.ErrorHandler.logError( e: 'Error playing audio', s: s, - data: { - 'error': e.toString(), - 'text': text, - }, + data: {'error': e.toString(), 'text': text}, ); } finally { audioPlayer?.dispose(); @@ -328,28 +300,27 @@ class TtsController { static Future _showTTSDisabledPopup( BuildContext context, String targetID, - ) async => - OverlayUtil.showPositionedCard( - context: context, - backDropToDismiss: false, - cardToShow: Column( - spacing: 12.0, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CardHeader(InstructionsEnum.ttsDisabled.title(L10n.of(context))), - Padding( - padding: const EdgeInsets.all(6.0), - child: Text( - InstructionsEnum.ttsDisabled.body(L10n.of(context)), - style: BotStyle.text(context), - ), - ), - ], + ) async => OverlayUtil.showPositionedCard( + context: context, + backDropToDismiss: false, + cardToShow: Column( + spacing: 12.0, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CardHeader(InstructionsEnum.ttsDisabled.title(L10n.of(context))), + Padding( + padding: const EdgeInsets.all(6.0), + child: Text( + InstructionsEnum.ttsDisabled.body(L10n.of(context)), + style: BotStyle.text(context), + ), ), - maxHeight: 300, - maxWidth: 300, - transformTargetId: targetID, - closePrevOverlay: false, - overlayKey: InstructionsEnum.ttsDisabled.toString(), - ); + ], + ), + maxHeight: 300, + maxWidth: 300, + transformTargetId: targetID, + closePrevOverlay: false, + overlayKey: InstructionsEnum.ttsDisabled.toString(), + ); } diff --git a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart index 856d89495..1fadd9102 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart @@ -48,24 +48,20 @@ class TokenInfoFeedbackDialog extends StatelessWidget { // first, update lemma info if changed if (response.updatedLemmaInfo != null) { - await _updateLemmaInfo( - token, - response.updatedLemmaInfo!, - ); + await _updateLemmaInfo(token, response.updatedLemmaInfo!); } // second, update the phonetic info if changed if (response.updatedPhonetics != null) { - await _updatePhoneticTranscription( - response.updatedPhonetics!, - ); + await _updatePhoneticTranscription(response.updatedPhonetics!); } final originalSent = event?.originalSent; // if no other changes, just return the message final hasTokenUpdate = response.updatedToken != null; - final hasLangUpdate = originalSent != null && + final hasLangUpdate = + originalSent != null && response.updatedLanguage != null && response.updatedLanguage != originalSent.langCode; @@ -86,10 +82,7 @@ class TokenInfoFeedbackDialog extends StatelessWidget { tokens: tokens, detections: [ if (updatedLanguage != null) - LanguageDetectionModel( - langCode: updatedLanguage, - confidence: 1, - ), + LanguageDetectionModel(langCode: updatedLanguage, confidence: 1), ], ); @@ -127,22 +120,21 @@ class TokenInfoFeedbackDialog extends StatelessWidget { Future _updateLemmaInfo( PangeaToken token, LemmaInfoResponse response, - ) => - LemmaInfoRepo.set( - token.vocabConstructID.lemmaInfoRequest( - event?.event.content ?? {}, - ), - response, - ); + ) => LemmaInfoRepo.set( + token.vocabConstructID.lemmaInfoRequest(event?.event.content ?? {}), + response, + ); Future _updatePhoneticTranscription( PhoneticTranscriptionResponse response, ) async { final req = PhoneticTranscriptionRequest( arc: LanguageArc( - l1: PLanguageStore.byLangCode(requestData.wordCardL1) ?? + l1: + PLanguageStore.byLangCode(requestData.wordCardL1) ?? MatrixState.pangeaController.userController.userL1!, - l2: PLanguageStore.byLangCode(langCode) ?? + l2: + PLanguageStore.byLangCode(langCode) ?? MatrixState.pangeaController.userController.userL2!, ), content: response.content, diff --git a/lib/pangea/token_info_feedback/token_info_feedback_notification.dart b/lib/pangea/token_info_feedback/token_info_feedback_notification.dart index cc52fbf12..0143f7e7b 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_notification.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_notification.dart @@ -8,10 +8,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class TokenFeedbackNotification extends StatefulWidget { final String message; - const TokenFeedbackNotification({ - super.key, - required this.message, - }); + const TokenFeedbackNotification({super.key, required this.message}); @override State createState() => @@ -35,12 +32,7 @@ class _TokenFeedbackNotificationState extends State _slideAnimation = Tween( begin: const Offset(0, -1), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOut, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOut)); _slideController.forward(); } @@ -84,10 +76,7 @@ class _TokenFeedbackNotificationState extends State ), child: Row( children: [ - const BotFace( - width: 30, - expression: BotExpression.idle, - ), + const BotFace(width: 30, expression: BotExpression.idle), const SizedBox(width: 12), Expanded( child: Text( diff --git a/lib/pangea/token_info_feedback/token_info_feedback_request.dart b/lib/pangea/token_info_feedback/token_info_feedback_request.dart index c7f7636a8..e7e6f4520 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_request.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_request.dart @@ -54,10 +54,7 @@ class TokenInfoFeedbackRequest { final TokenInfoFeedbackRequestData data; final String userFeedback; - TokenInfoFeedbackRequest({ - required this.data, - required this.userFeedback, - }); + TokenInfoFeedbackRequest({required this.data, required this.userFeedback}); Map toJson() { return { diff --git a/lib/pangea/toolbar/layout/measure_render_box.dart b/lib/pangea/toolbar/layout/measure_render_box.dart index 85bbed893..ee749a538 100644 --- a/lib/pangea/toolbar/layout/measure_render_box.dart +++ b/lib/pangea/toolbar/layout/measure_render_box.dart @@ -43,9 +43,7 @@ class MeasureRenderBoxState extends State { WidgetsBinding.instance.addPostFrameCallback((_) => _updateOffset()); return true; }, - child: SizeChangedLayoutNotifier( - child: widget.child, - ), + child: SizeChangedLayoutNotifier(child: widget.child), ); } } diff --git a/lib/pangea/toolbar/layout/message_selection_positioner.dart b/lib/pangea/toolbar/layout/message_selection_positioner.dart index c4bf54b8c..c7d3c9c00 100644 --- a/lib/pangea/toolbar/layout/message_selection_positioner.dart +++ b/lib/pangea/toolbar/layout/message_selection_positioner.dart @@ -103,27 +103,22 @@ class MessageSelectionPositionerState extends State super.dispose(); } - T _runWithLogging( - Function runner, - String errorMessage, - T defaultValue, - ) { + T _runWithLogging(Function runner, String errorMessage, T defaultValue) { try { return runner(); } catch (e, s) { ErrorHandler.logError( e: "$errorMessage: $e", s: s, - data: { - "eventID": widget.event.eventId, - }, + data: {"eventID": widget.event.eventId}, ); return defaultValue; } } - final Duration transitionAnimationDuration = - const Duration(milliseconds: 300); + final Duration transitionAnimationDuration = const Duration( + milliseconds: 300, + ); double get _horizontalPadding => FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; @@ -154,15 +149,21 @@ class MessageSelectionPositionerState extends State widget.event.senderId == widget.event.room.client.userID; bool get showDetails => - AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store) && + AppSettings.displayChatDetailsColumn.value && FluffyThemes.isThreeColumnMode(context) && widget.chatController.room.membership == Membership.join; - MediaQueryData? get mediaQuery => _runWithLogging( - () => MediaQuery.of(context), - "Error getting media query", - null, - ); + Size? get screenSize => _runWithLogging( + () => MediaQuery.sizeOf(context), + "Error getting media query size", + null, + ); + + EdgeInsets? get screenPadding => _runWithLogging( + () => MediaQuery.paddingOf(context), + "Error getting media query padding", + null, + ); double get columnWidth => FluffyThemes.isColumnMode(context) ? (FluffyThemes.columnWidth + FluffyThemes.navRailWidth + 2.0) @@ -172,13 +173,14 @@ class MessageSelectionPositionerState extends State const double messageMargin = 16.0; // widget.event.isActivityMessage ? 0 : Avatar.defaultSize + 16 + 8; final bool showingDetails = widget.chatController.displayChatDetailsColumn; - final double totalMaxWidth = FluffyThemes.maxTimelineWidth - + final double totalMaxWidth = + FluffyThemes.maxTimelineWidth - (showingDetails ? FluffyThemes.columnWidth : 0) - messageMargin; double? maxWidth; - if (mediaQuery != null) { - final chatViewWidth = mediaQuery!.size.width - columnWidth; + if (screenSize != null) { + final chatViewWidth = screenSize!.width - columnWidth; maxWidth = chatViewWidth - (2 * _horizontalPadding) - messageMargin; } @@ -192,20 +194,20 @@ class MessageSelectionPositionerState extends State Size get _defaultMessageSize => const Size(FluffyThemes.columnWidth / 2, 100); RenderBox? get _overlayMessageRenderBox => _runWithLogging( - () => MatrixState.pAnyState.getRenderBox( - 'overlay_message_${widget.event.eventId}', - ), - "Error getting overlay message render box", - null, - ); + () => MatrixState.pAnyState.getRenderBox( + 'overlay_message_${widget.event.eventId}', + ), + "Error getting overlay message render box", + null, + ); RenderBox? get _reactionsRenderBox => _runWithLogging( - () => MatrixState.pAnyState.getRenderBox( - 'message_reactions_${widget.event.eventId}', - ), - "Error getting reactions render box", - null, - ); + () => MatrixState.pAnyState.getRenderBox( + 'message_reactions_${widget.event.eventId}', + ), + "Error getting reactions render box", + null, + ); Size? get _overlayMessageSize => _overlayMessageRenderBox?.size; @@ -222,12 +224,10 @@ class MessageSelectionPositionerState extends State } RenderBox? get _messageRenderBox => _runWithLogging( - () => MatrixState.pAnyState.getRenderBox( - widget.event.eventId, - ), - "Error getting message render box", - null, - ); + () => MatrixState.pAnyState.getRenderBox(widget.event.eventId), + "Error getting message render box", + null, + ); Offset? get _originalMessageOffset { if (_messageRenderBox == null || !_messageRenderBox!.hasSize) { @@ -254,9 +254,10 @@ class MessageSelectionPositionerState extends State } bool get isRtl { - final locale = Provider.of(context, listen: false) - .locale - ?.languageCode; + final locale = Provider.of( + context, + listen: false, + ).locale?.languageCode; return locale != null && LanguageConstants.rtlLanguageCodes.contains(locale); } @@ -278,7 +279,7 @@ class MessageSelectionPositionerState extends State } double? get messageRightOffset { - if (mediaQuery == null || !ownMessage) return null; + if (screenSize == null || !ownMessage) return null; final offset = _originalMessageOffset; if (offset == null) { @@ -286,13 +287,13 @@ class MessageSelectionPositionerState extends State } if (isRtl) { - return mediaQuery!.size.width - + return screenSize!.width - columnWidth - offset.dx - originalMessageSize.width; } - return mediaQuery!.size.width - + return screenSize!.width - offset.dx - originalMessageSize.width - (showDetails ? FluffyThemes.columnWidth : 0); @@ -325,9 +326,9 @@ class MessageSelectionPositionerState extends State double? get _wordCardLeftOffset { if (ownMessage) return null; if (widget.overlayController.selectedToken != null && - mediaQuery != null && - (mediaQuery!.size.width < _toolbarMaxWidth + messageLeftOffset!)) { - return mediaQuery!.size.width - _toolbarMaxWidth - 8.0; + screenSize != null && + (screenSize!.width < _toolbarMaxWidth + messageLeftOffset!)) { + return screenSize!.width - _toolbarMaxWidth - 8.0; } return messageLeftOffset; } @@ -337,10 +338,8 @@ class MessageSelectionPositionerState extends State } double? get _screenHeight { - if (mediaQuery == null) return null; - return mediaQuery!.size.height - - mediaQuery!.padding.bottom - - mediaQuery!.padding.top; + if (screenSize == null || screenPadding == null) return null; + return screenSize!.height - screenPadding!.bottom - screenPadding!.top; } bool get shouldScroll { @@ -353,7 +352,8 @@ class MessageSelectionPositionerState extends State final offset = _originalMessageOffset; if (offset == null) return false; - final bottomOffset = offset.dy + + final bottomOffset = + offset.dy + originalMessageSize.height + _reactionsHeight + AppConfig.toolbarMenuHeight + @@ -373,16 +373,15 @@ class MessageSelectionPositionerState extends State final originalContentHeight = messageHeight + _reactionsHeight + AppConfig.toolbarMenuHeight + 8.0; - final screenHeight = mediaQuery!.size.height - mediaQuery!.padding.bottom; + final screenHeight = screenSize!.height - screenPadding!.bottom; double boxHeight = screenHeight - offset.dy - originalContentHeight; final neededSpace = - boxHeight + _fullContentHeight + mediaQuery!.padding.top + 4.0; + boxHeight + _fullContentHeight + screenPadding!.top + 4.0; if (neededSpace > screenHeight) { - boxHeight = - screenHeight - _fullContentHeight - mediaQuery!.padding.top - 4.0; + boxHeight = screenHeight - _fullContentHeight - screenPadding!.top - 4.0; } return boxHeight; @@ -409,7 +408,7 @@ class MessageSelectionPositionerState extends State @override Widget build(BuildContext context) { - if (_messageRenderBox == null || mediaQuery == null) { + if (_messageRenderBox == null || screenSize == null) { return const SizedBox.shrink(); } @@ -420,17 +419,19 @@ class MessageSelectionPositionerState extends State Column( children: [ SizedBox( - width: mediaQuery!.size.width - + width: + screenSize!.width - columnWidth - (showDetails ? FluffyThemes.columnWidth : 0), height: _screenHeight!, child: Stack( - alignment: - ownMessage ? Alignment.centerRight : Alignment.centerLeft, + alignment: ownMessage + ? Alignment.centerRight + : Alignment.centerLeft, children: [ ValueListenableBuilder( valueListenable: _startedTransition, - builder: (context, started, __) { + builder: (context, started, _) { return !started ? OverMessageOverlay(controller: this) : const SizedBox(); @@ -438,7 +439,7 @@ class MessageSelectionPositionerState extends State ), ValueListenableBuilder( valueListenable: _startedTransition, - builder: (context, started, __) { + builder: (context, started, _) { return !started && shouldScroll ? Positioned( top: 0, @@ -451,9 +452,7 @@ class MessageSelectionPositionerState extends State ), if (readingAssistanceMode == ReadingAssistanceMode.practiceMode) ...[ - CenteredMessage( - controller: this, - ), + CenteredMessage(controller: this), PracticeModeTransitionAnimation( targetId: "overlay_center_message_${widget.event.eventId}", @@ -491,7 +490,8 @@ class MessageSelectionPositionerState extends State final type = practice.practiceMode.associatedActivityType; - final complete = type != null && + final complete = + type != null && practice.isPracticeSessionDone(type); if (instruction != null && !complete) { @@ -516,10 +516,7 @@ class MessageSelectionPositionerState extends State ), ], ), - if (showDetails) - const SizedBox( - width: FluffyThemes.columnWidth, - ), + if (showDetails) const SizedBox(width: FluffyThemes.columnWidth), ], ), ); @@ -528,10 +525,7 @@ class MessageSelectionPositionerState extends State class MessageReactionPicker extends StatelessWidget { final ChatController chatController; - const MessageReactionPicker({ - super.key, - required this.chatController, - }); + const MessageReactionPicker({super.key, required this.chatController}); @override Widget build(BuildContext context) { @@ -561,9 +555,7 @@ class MessageReactionPicker extends StatelessWidget { return Material( elevation: 4, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), shadowColor: theme.colorScheme.surface.withAlpha(128), child: SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -577,26 +569,17 @@ class MessageReactionPicker extends StatelessWidget { padding: EdgeInsets.zero, icon: Center( child: Opacity( - opacity: sentReactions.contains( - emoji, - ) - ? 0.33 - : 1, + opacity: sentReactions.contains(emoji) ? 0.33 : 1, child: Text( emoji, - style: const TextStyle( - fontSize: 20, - ), + style: const TextStyle(fontSize: 20), textAlign: TextAlign.center, ), ), ), onPressed: sentReactions.contains(emoji) ? null - : () => event?.room.sendReaction( - event.eventId, - emoji, - ), + : () => event?.room.sendReaction(event.eventId, emoji), ), ), // IconButton( diff --git a/lib/pangea/toolbar/layout/over_message_overlay.dart b/lib/pangea/toolbar/layout/over_message_overlay.dart index fd378b002..e217ab1ae 100644 --- a/lib/pangea/toolbar/layout/over_message_overlay.dart +++ b/lib/pangea/toolbar/layout/over_message_overlay.dart @@ -48,14 +48,18 @@ class OverMessageOverlay extends StatelessWidget { child: ValueListenableBuilder( valueListenable: controller.widget.overlayController.selectedMode, - builder: (context, mode, __) { + builder: (context, mode, _) { return OverlayCenterContent( event: controller.widget.event, messageHeight: mode != SelectMode.emoji ? controller.originalMessageSize.height : null, - messageWidth: controller.widget.overlayController - .selectModeController.isShowingExtraContent + messageWidth: + controller + .widget + .overlayController + .selectModeController + .isShowingExtraContent ? max(controller.originalMessageSize.width, 150) : controller.originalMessageSize.width, overlayController: controller.widget.overlayController, @@ -83,7 +87,8 @@ class OverMessageOverlay extends StatelessWidget { AnimatedContainer( duration: FluffyThemes.animationDuration, height: max(0, controller.spaceBelowContent), - width: controller.mediaQuery!.size.width - + width: + controller.screenSize!.width - controller.columnWidth - (controller.showDetails ? FluffyThemes.columnWidth : 0), ), diff --git a/lib/pangea/toolbar/layout/overlay_center_content.dart b/lib/pangea/toolbar/layout/overlay_center_content.dart index d2af9e89a..b4810bbd6 100644 --- a/lib/pangea/toolbar/layout/overlay_center_content.dart +++ b/lib/pangea/toolbar/layout/overlay_center_content.dart @@ -54,7 +54,8 @@ class OverlayCenterContent extends StatelessWidget { Widget build(BuildContext context) { final ownMessage = event.senderId == event.room.client.userID; return IgnorePointer( - ignoring: !isTransitionAnimation && + ignoring: + !isTransitionAnimation && readingAssistanceMode != ReadingAssistanceMode.practiceMode, child: Container( constraints: const BoxConstraints( @@ -87,8 +88,8 @@ class OverlayCenterContent extends StatelessWidget { readingAssistanceMode: readingAssistanceMode, canRefresh: (event.eventId == chatController.refreshEventID) && - (readingAssistanceMode != - ReadingAssistanceMode.practiceMode), + (readingAssistanceMode != + ReadingAssistanceMode.practiceMode), ), ), Padding( @@ -98,7 +99,7 @@ class OverlayCenterContent extends StatelessWidget { ), child: ValueListenableBuilder( valueListenable: reactionsWidth, - builder: (context, width, __) => PangeaMessageReactions( + builder: (context, width, _) => PangeaMessageReactions( event, chatController.timeline!, chatController, diff --git a/lib/pangea/toolbar/layout/overlay_message.dart b/lib/pangea/toolbar/layout/overlay_message.dart index 13008833f..ec9018ace 100644 --- a/lib/pangea/toolbar/layout/overlay_message.dart +++ b/lib/pangea/toolbar/layout/overlay_message.dart @@ -63,11 +63,13 @@ class OverlayMessage extends StatelessWidget { final theme = Theme.of(context); final bool ownMessage = event.senderId == Matrix.of(context).client.userID; - final displayTime = event.type == EventTypes.RoomCreate || + final displayTime = + event.type == EventTypes.RoomCreate || nextEvent == null || !event.originServerTs.sameEnvironment(nextEvent!.originServerTs); - final nextEventSameSender = nextEvent != null && + final nextEventSameSender = + nextEvent != null && { EventTypes.Message, EventTypes.Sticker, @@ -76,7 +78,8 @@ class OverlayMessage extends StatelessWidget { nextEvent!.senderId == event.senderId && !displayTime; - final previousEventSameSender = previousEvent != null && + final previousEventSameSender = + previousEvent != null && { EventTypes.Message, EventTypes.Sticker, @@ -88,14 +91,14 @@ class OverlayMessage extends StatelessWidget { final textColor = event.isActivityMessage ? ThemeData.light().colorScheme.onPrimary : ownMessage - ? ThemeData.dark().colorScheme.onPrimary - : theme.colorScheme.onSurface; + ? ThemeData.dark().colorScheme.onPrimary + : theme.colorScheme.onSurface; final linkColor = theme.brightness == Brightness.light ? theme.colorScheme.primary : ownMessage - ? theme.colorScheme.onPrimary - : theme.colorScheme.onSurface; + ? theme.colorScheme.onPrimary + : theme.colorScheme.onSurface; final displayEvent = event.getDisplayEvent(timeline); const hardCorner = Radius.circular(4); @@ -103,13 +106,15 @@ class OverlayMessage extends StatelessWidget { final borderRadius = BorderRadius.only( topLeft: !ownMessage && nextEventSameSender ? hardCorner : roundedCorner, topRight: ownMessage && nextEventSameSender ? hardCorner : roundedCorner, - bottomLeft: - !ownMessage && previousEventSameSender ? hardCorner : roundedCorner, - bottomRight: - ownMessage && previousEventSameSender ? hardCorner : roundedCorner, + bottomLeft: !ownMessage && previousEventSameSender + ? hardCorner + : roundedCorner, + bottomRight: ownMessage && previousEventSameSender + ? hardCorner + : roundedCorner, ); - var color = theme.colorScheme.surfaceContainerHighest; + var color = theme.colorScheme.surfaceContainerHigh; if (ownMessage) { color = displayEvent.status.isError ? Colors.redAccent @@ -125,7 +130,8 @@ class OverlayMessage extends StatelessWidget { : theme.colorScheme.primary; } - final noBubble = ({ + final noBubble = + ({ MessageTypes.Video, MessageTypes.Image, MessageTypes.Sticker, @@ -145,9 +151,7 @@ class OverlayMessage extends StatelessWidget { final content = Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), width: messageWidth, height: messageHeight, @@ -155,23 +159,15 @@ class OverlayMessage extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (event.relationshipType == RelationshipTypes.reply) + if (event.inReplyToEventId(includingFallback: false) != null) FutureBuilder( - future: event.getReplyEvent( - timeline, - ), - builder: ( - BuildContext context, - snapshot, - ) { + future: event.getReplyEvent(timeline), + builder: (BuildContext context, snapshot) { final replyEvent = snapshot.hasData ? snapshot.data! : Event( eventId: event.relationshipEventId!, - content: { - 'msgtype': 'm.text', - 'body': '...', - }, + content: {'msgtype': 'm.text', 'body': '...'}, senderId: "", type: 'm.room.message', room: event.room, @@ -179,19 +175,14 @@ class OverlayMessage extends StatelessWidget { originServerTs: DateTime.now(), ); return Padding( - padding: const EdgeInsets.only( - left: 16, - right: 16, - top: 8, - ), + padding: const EdgeInsets.only(left: 16, right: 16, top: 8), child: Material( color: Colors.transparent, borderRadius: ReplyContent.borderRadius, child: InkWell( borderRadius: ReplyContent.borderRadius, - onTap: () => controller.scrollToEventId( - replyEvent.eventId, - ), + onTap: () => + controller.scrollToEventId(replyEvent.eventId), child: AbsorbPointer( child: ReplyContent( replyEvent, @@ -221,10 +212,7 @@ class OverlayMessage extends StatelessWidget { selected: true, ), ), - if (event.hasAggregatedEvents( - timeline, - RelationshipTypes.edit, - )) + if (event.hasAggregatedEvents(timeline, RelationshipTypes.edit)) Padding( padding: const EdgeInsets.only( bottom: 8.0, @@ -241,14 +229,10 @@ class OverlayMessage extends StatelessWidget { size: 14, ), Text( - displayEvent.originServerTs.localizedTimeShort( - context, - ), + displayEvent.originServerTs.localizedTimeShort(context), textScaler: TextScaler.noScaling, style: TextStyle( - color: textColor.withAlpha( - 164, - ), + color: textColor.withAlpha(164), fontSize: 11, ), ), @@ -261,7 +245,7 @@ class OverlayMessage extends StatelessWidget { final maxWidth = min( FluffyThemes.columnWidth * 1.5, - MediaQuery.of(context).size.width - + MediaQuery.widthOf(context) - (ownMessage ? 0 : Avatar.defaultSize) - 32.0 - (FluffyThemes.isColumnMode(context) @@ -269,10 +253,7 @@ class OverlayMessage extends StatelessWidget { : 0.0), ); - final style = AppConfig.messageTextStyle( - event, - textColor, - ); + final style = AppConfig.messageTextStyle(event, textColor); return Material( key: MatrixState.pAnyState.layerLinkAndKey(overlayKey).key, @@ -293,7 +274,8 @@ class OverlayMessage extends StatelessWidget { children: [ _MessageBubbleTranscription( controller: selectModeController, - enabled: event.messageType == MessageTypes.Audio && + enabled: + event.messageType == MessageTypes.Audio && !event.redacted && isSubscribed != false, maxWidth: maxWidth, @@ -341,12 +323,10 @@ class _MessageSelectModeContent extends StatelessWidget { @override Widget build(BuildContext context) { return ListenableBuilder( - listenable: Listenable.merge( - [ - controller.selectedMode, - controller.currentModeStateNotifier, - ], - ), + listenable: Listenable.merge([ + controller.selectedMode, + controller.currentModeStateNotifier, + ]), builder: (context, _) { final mode = controller.selectedMode.value; if (mode == null) { @@ -380,42 +360,38 @@ class _MessageSelectModeContent extends StatelessWidget { padding: const EdgeInsets.all(12.0), child: switch (state) { AsyncLoading() => Row( - mainAxisSize: MainAxisSize.min, - children: [ - CircularProgressIndicator.adaptive( - backgroundColor: style.color, - ), - ], - ), + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator.adaptive( + backgroundColor: style.color, + ), + ], + ), AsyncError(error: final _) => Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.error_outline, - color: Theme.of(context).colorScheme.error, - ), - const SizedBox(width: 8), - Text( - L10n.of(context).translationError, - textScaler: TextScaler.noScaling, - style: style.copyWith(fontStyle: FontStyle.italic), - ), - ], - ), + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.error_outline, + color: Theme.of(context).colorScheme.error, + ), + const SizedBox(width: 8), + Text( + L10n.of(context).translationError, + textScaler: TextScaler.noScaling, + style: style.copyWith(fontStyle: FontStyle.italic), + ), + ], + ), AsyncLoaded(value: final value) => Container( - constraints: BoxConstraints( - maxWidth: maxWidth, - ), - child: SingleChildScrollView( - child: Text( - value, - textScaler: TextScaler.noScaling, - style: style.copyWith( - fontStyle: FontStyle.italic, - ), - ), + constraints: BoxConstraints(maxWidth: maxWidth), + child: SingleChildScrollView( + child: Text( + value, + textScaler: TextScaler.noScaling, + style: style.copyWith(fontStyle: FontStyle.italic), ), ), + ), _ => const SizedBox(), }, ); diff --git a/lib/pangea/toolbar/layout/practice_mode_transition_animation.dart b/lib/pangea/toolbar/layout/practice_mode_transition_animation.dart index 7c8a06f5d..83eb29664 100644 --- a/lib/pangea/toolbar/layout/practice_mode_transition_animation.dart +++ b/lib/pangea/toolbar/layout/practice_mode_transition_animation.dart @@ -75,30 +75,26 @@ class PracticeModeTransitionAnimationState duration: widget.controller.transitionAnimationDuration, ); - _offsetAnimation = Tween( - begin: startOffset, - end: endOffset, - ).animate( - CurvedAnimation( - parent: _animationController!, - curve: FluffyThemes.animationCurve, - ), - ); + _offsetAnimation = Tween(begin: startOffset, end: endOffset) + .animate( + CurvedAnimation( + parent: _animationController!, + curve: FluffyThemes.animationCurve, + ), + ); final startSize = Size( widget.controller.originalMessageSize.width, widget.controller.originalMessageSize.height, ); - _sizeAnimation = Tween( - begin: startSize, - end: _centerMessageSize!, - ).animate( - CurvedAnimation( - parent: _animationController!, - curve: FluffyThemes.animationCurve, - ), - ); + _sizeAnimation = Tween(begin: startSize, end: _centerMessageSize!) + .animate( + CurvedAnimation( + parent: _animationController!, + curve: FluffyThemes.animationCurve, + ), + ); widget.controller.onStartedTransition(); setState(() {}); @@ -163,16 +159,13 @@ class PracticeModeTransitionAnimationState class CenteredMessage extends StatelessWidget { final MessageSelectionPositionerState controller; - const CenteredMessage({ - super.key, - required this.controller, - }); + const CenteredMessage({super.key, required this.controller}); @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: controller.finishedTransition, - builder: (context, finished, __) { + builder: (context, finished, _) { return Opacity( opacity: finished ? 1.0 : 0.0, child: GestureDetector( @@ -182,8 +175,8 @@ class CenteredMessage extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ SizedBox( - width: controller.mediaQuery!.size.width - - controller.columnWidth, + width: + controller.screenSize!.width - controller.columnWidth, height: 20.0, ), OverlayCenterContent( diff --git a/lib/pangea/toolbar/message_practice/message_audio_card.dart b/lib/pangea/toolbar/message_practice/message_audio_card.dart index 6a3661b61..6f218ab21 100644 --- a/lib/pangea/toolbar/message_practice/message_audio_card.dart +++ b/lib/pangea/toolbar/message_practice/message_audio_card.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; @@ -17,11 +18,7 @@ class MessageAudioCard extends StatefulWidget { final PangeaMessageEvent messageEvent; final VoidCallback? onError; - const MessageAudioCard({ - super.key, - required this.messageEvent, - this.onError, - }); + const MessageAudioCard({super.key, required this.messageEvent, this.onError}); @override MessageAudioCardState createState() => MessageAudioCardState(); @@ -72,20 +69,20 @@ class MessageAudioCardState extends State { child: _isLoading ? const TextLoadingShimmer(width: 200) : audioFile != null - ? AudioPlayerWidget( - null, - eventId: "${widget.messageEvent.eventId}_practice", - roomId: widget.messageEvent.room.id, - senderId: widget.messageEvent.senderId, - matrixFile: audioFile, - color: Theme.of(context).colorScheme.onPrimaryContainer, - fontSize: - AppConfig.messageFontSize * AppConfig.fontSizeFactor, - linkColor: Theme.of(context).brightness == Brightness.light - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onPrimary, - ) - : const SizedBox(), + ? AudioPlayerWidget( + null, + eventId: "${widget.messageEvent.eventId}_practice", + roomId: widget.messageEvent.room.id, + senderId: widget.messageEvent.senderId, + matrixFile: audioFile, + color: Theme.of(context).colorScheme.onPrimaryContainer, + fontSize: + AppConfig.messageFontSize * AppSettings.fontSizeFactor.value, + linkColor: Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onPrimary, + ) + : const SizedBox(), ); } } diff --git a/lib/pangea/toolbar/message_practice/message_morph_choice.dart b/lib/pangea/toolbar/message_practice/message_morph_choice.dart index 458932773..fcdf626e7 100644 --- a/lib/pangea/toolbar/message_practice/message_morph_choice.dart +++ b/lib/pangea/toolbar/message_practice/message_morph_choice.dart @@ -64,25 +64,25 @@ class MessageMorphInputBarContentState } TextStyle? textStyle(BuildContext context) => widget.maxWidth > 600 - ? Theme.of(context).textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ) - : Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.bold, - ); + ? Theme.of( + context, + ).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold) + : Theme.of( + context, + ).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold); @override Widget build(BuildContext context) { final iconSize = widget.maxWidth > 600 ? 28.0 : widget.maxWidth > 600 - ? 24.0 - : 16.0; + ? 24.0 + : 16.0; final spacing = widget.maxWidth > 600 ? 16.0 : widget.maxWidth > 600 - ? 8.0 - : 4.0; + ? 8.0 + : 4.0; return Column( spacing: spacing, @@ -114,41 +114,42 @@ class MessageMorphInputBarContentState runAlignment: WrapAlignment.center, spacing: spacing, runSpacing: spacing, - children: widget.activity.multipleChoiceContent.choices.mapIndexed( - (index, choice) { - final wasCorrect = widget.controller.wasCorrectChoice(choice); + children: widget.activity.multipleChoiceContent.choices.mapIndexed(( + index, + choice, + ) { + final wasCorrect = widget.controller.wasCorrectChoice(choice); - return ChoiceAnimationWidget( - isSelected: selectedTag == choice, - isCorrect: wasCorrect, - child: MessageMorphChoiceItem( - cId: ConstructIdentifier( - lemma: choice, - type: ConstructTypeEnum.morph, - category: morph.name, - ), - onTap: () { - setState(() => selectedTag = choice); - widget.controller.onMatch( - token, - PracticeChoice( - choiceContent: choice, - form: ConstructForm( - cId: widget.activity.tokens.first.morphIdByFeature( - widget.activity.morphFeature, - )!, - form: token.text.content, - ), - ), - ); - }, - isSelected: selectedTag == choice, - isGold: wasCorrect, - shimmer: widget.controller.showChoiceShimmer, + return ChoiceAnimationWidget( + isSelected: selectedTag == choice, + isCorrect: wasCorrect, + child: MessageMorphChoiceItem( + cId: ConstructIdentifier( + lemma: choice, + type: ConstructTypeEnum.morph, + category: morph.name, ), - ); - }, - ).toList(), + onTap: () { + setState(() => selectedTag = choice); + widget.controller.onMatch( + token, + PracticeChoice( + choiceContent: choice, + form: ConstructForm( + cId: widget.activity.tokens.first.morphIdByFeature( + widget.activity.morphFeature, + )!, + form: token.text.content, + ), + ), + ); + }, + isSelected: selectedTag == choice, + isGold: wasCorrect, + shimmer: widget.controller.showChoiceShimmer, + ), + ); + }).toList(), ), if (selectedTag != null) Container( diff --git a/lib/pangea/toolbar/message_practice/message_morph_choice_item.dart b/lib/pangea/toolbar/message_practice/message_morph_choice_item.dart index 7aa99a354..b5053fd45 100644 --- a/lib/pangea/toolbar/message_practice/message_morph_choice_item.dart +++ b/lib/pangea/toolbar/message_practice/message_morph_choice_item.dart @@ -51,10 +51,9 @@ class MessageMorphChoiceItemState extends State { : AppConfig.warning.withAlpha((0.4 * 255).toInt()); } if (widget.isSelected) { - return Theme.of(context) - .colorScheme - .primary - .withAlpha((0.4 * 255).toInt()); + return Theme.of( + context, + ).colorScheme.primary.withAlpha((0.4 * 255).toInt()); } return _isHovered ? Theme.of(context).colorScheme.primary.withAlpha((0.2 * 255).toInt()) diff --git a/lib/pangea/toolbar/message_practice/message_practice_mode_enum.dart b/lib/pangea/toolbar/message_practice/message_practice_mode_enum.dart index e8d851186..1a1fdcb78 100644 --- a/lib/pangea/toolbar/message_practice/message_practice_mode_enum.dart +++ b/lib/pangea/toolbar/message_practice/message_practice_mode_enum.dart @@ -44,10 +44,7 @@ enum MessagePracticeMode { } } - Color iconButtonColor( - BuildContext context, - bool done, - ) => + Color iconButtonColor(BuildContext context, bool done) => done ? AppConfig.gold : Theme.of(context).colorScheme.primaryContainer; ActivityTypeEnum? get associatedActivityType { @@ -66,11 +63,11 @@ enum MessagePracticeMode { } static List get practiceModes => [ - MessagePracticeMode.listening, - MessagePracticeMode.wordMorph, - MessagePracticeMode.wordMeaning, - MessagePracticeMode.wordEmoji, - ]; + MessagePracticeMode.listening, + MessagePracticeMode.wordMorph, + MessagePracticeMode.wordMeaning, + MessagePracticeMode.wordEmoji, + ]; InstructionsEnum? get instruction { switch (this) { diff --git a/lib/pangea/toolbar/message_practice/morph_selection.dart b/lib/pangea/toolbar/message_practice/morph_selection.dart index 612a1698c..2821be0dd 100644 --- a/lib/pangea/toolbar/message_practice/morph_selection.dart +++ b/lib/pangea/toolbar/message_practice/morph_selection.dart @@ -5,10 +5,7 @@ class MorphSelection { PangeaToken token; MorphFeaturesEnum morph; - MorphSelection( - this.token, - this.morph, - ); + MorphSelection(this.token, this.morph); @override bool operator ==(Object other) { diff --git a/lib/pangea/toolbar/message_practice/practice_activity_card.dart b/lib/pangea/toolbar/message_practice/practice_activity_card.dart index 8561ea27a..176f5c072 100644 --- a/lib/pangea/toolbar/message_practice/practice_activity_card.dart +++ b/lib/pangea/toolbar/message_practice/practice_activity_card.dart @@ -43,9 +43,7 @@ class PracticeActivityCardState extends State { @override void initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback( - (_) => _fetchActivity(), - ); + WidgetsBinding.instance.addPostFrameCallback((_) => _fetchActivity()); } @override @@ -87,31 +85,28 @@ class PracticeActivityCardState extends State { Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: _activityState, - builder: (context, state, __) { + builder: (context, state, _) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ switch (state) { - AsyncLoading() => const ContentLoadingIndicator( - height: 40, - ), + AsyncLoading() => const ContentLoadingIndicator(height: 40), AsyncError() => CardErrorWidget( - L10n.of(context).errorFetchingActivity, - ), + L10n.of(context).errorFetchingActivity, + ), AsyncLoaded() => switch (state.value) { - MultipleChoicePracticeActivityModel() => - MessageMorphInputBarContent( - controller: widget.controller, - activity: state.value as MorphPracticeActivityModel, - selectedToken: widget.selectedToken, - maxWidth: widget.maxWidth, - ), - MatchPracticeActivityModel() => MatchActivityCard( - currentActivity: - state.value as MatchPracticeActivityModel, - controller: widget.controller, - ), - }, + MultipleChoicePracticeActivityModel() => + MessageMorphInputBarContent( + controller: widget.controller, + activity: state.value as MorphPracticeActivityModel, + selectedToken: widget.selectedToken, + maxWidth: widget.maxWidth, + ), + MatchPracticeActivityModel() => MatchActivityCard( + currentActivity: state.value as MatchPracticeActivityModel, + controller: widget.controller, + ), + }, _ => const SizedBox.shrink(), }, ], diff --git a/lib/pangea/toolbar/message_practice/practice_controller.dart b/lib/pangea/toolbar/message_practice/practice_controller.dart index e642be6f6..03d3af822 100644 --- a/lib/pangea/toolbar/message_practice/practice_controller.dart +++ b/lib/pangea/toolbar/message_practice/practice_controller.dart @@ -84,19 +84,14 @@ class PracticeController with ChangeNotifier { } return target == null || - PracticeRecordController.isCompleteByToken( - target, - token, - ); + PracticeRecordController.isCompleteByToken(target, token); } bool get showChoiceShimmer { if (_activity == null) return false; if (_activity is MorphMatchPracticeActivityModel) { return selectedMorph != null && - !PracticeRecordController.hasResponse( - _activity!.practiceTarget, - ); + !PracticeRecordController.hasResponse(_activity!.practiceTarget); } return selectedChoice == null && @@ -179,13 +174,16 @@ class PracticeController with ChangeNotifier { "message-token-${token.text.uniqueKey}-${pangeaMessageEvent.eventId}"; final updateService = MatrixState - .pangeaController.matrixState.analyticsDataService.updateService; + .pangeaController + .matrixState + .analyticsDataService + .updateService; // we don't take off points for incorrect emoji matches if (_activity is! EmojiPracticeActivityModel || isCorrect) { - final constructUseType = - PracticeRecordController.lastResponse(_activity!.practiceTarget)! - .useType(_activity!.activityType); + final constructUseType = PracticeRecordController.lastResponse( + _activity!.practiceTarget, + )!.useType(_activity!.activityType); final constructs = [ OneConstructUse( @@ -204,10 +202,7 @@ class PracticeController with ChangeNotifier { ), ]; - updateService.addAnalytics( - targetId, - constructs, - ); + updateService.addAnalytics(targetId, constructs); } if (isCorrect) { diff --git a/lib/pangea/toolbar/message_practice/practice_match_card.dart b/lib/pangea/toolbar/message_practice/practice_match_card.dart index 60506e821..189b59508 100644 --- a/lib/pangea/toolbar/message_practice/practice_match_card.dart +++ b/lib/pangea/toolbar/message_practice/practice_match_card.dart @@ -40,17 +40,15 @@ class MatchActivityCard extends StatelessWidget { case WordListeningPracticeActivityModel(): return Padding( padding: const EdgeInsets.all(8), - child: Icon( - Icons.volume_up, - size: fontSize, - ), + child: Icon(Icons.volume_up, size: fontSize), ); } } @override Widget build(BuildContext context) { - double fontSize = (FluffyThemes.isColumnMode(context) + double fontSize = + (FluffyThemes.isColumnMode(context) ? Theme.of(context).textTheme.titleLarge?.fontSize : Theme.of(context).textTheme.titleMedium?.fontSize) ?? 26; @@ -72,31 +70,34 @@ class MatchActivityCard extends StatelessWidget { alignment: WrapAlignment.center, spacing: 4.0, runSpacing: 4.0, - children: currentActivity.matchContent.choices.map( - (PracticeChoice cf) { - final bool? wasCorrect = controller.wasCorrectMatch(cf); - return ChoiceAnimationWidget( + children: currentActivity.matchContent.choices.map(( + PracticeChoice cf, + ) { + final bool? wasCorrect = controller.wasCorrectMatch(cf); + return ChoiceAnimationWidget( + isSelected: controller.selectedChoice == cf, + isCorrect: wasCorrect, + child: PracticeMatchItem( + token: currentActivity.tokens.firstWhereOrNull( + (t) => t.vocabConstructID == cf.form.cId, + ), isSelected: controller.selectedChoice == cf, isCorrect: wasCorrect, - child: PracticeMatchItem( - token: currentActivity.tokens.firstWhereOrNull( - (t) => t.vocabConstructID == cf.form.cId, - ), - isSelected: controller.selectedChoice == cf, - isCorrect: wasCorrect, - constructForm: cf, - content: - choiceDisplayContent(context, cf.choiceContent, fontSize), - audioContent: - currentActivity is WordListeningPracticeActivityModel - ? cf.choiceContent - : null, - controller: controller, - shimmer: controller.showChoiceShimmer, + constructForm: cf, + content: choiceDisplayContent( + context, + cf.choiceContent, + fontSize, ), - ); - }, - ).toList(), + audioContent: + currentActivity is WordListeningPracticeActivityModel + ? cf.choiceContent + : null, + controller: controller, + shimmer: controller.showChoiceShimmer, + ), + ); + }).toList(), ), ], ); diff --git a/lib/pangea/toolbar/message_practice/practice_match_item.dart b/lib/pangea/toolbar/message_practice/practice_match_item.dart index 055250ed9..13b3547b9 100644 --- a/lib/pangea/toolbar/message_practice/practice_match_item.dart +++ b/lib/pangea/toolbar/message_practice/practice_match_item.dart @@ -72,11 +72,7 @@ class PracticeMatchItemState extends State { } } catch (e, s) { debugger(when: kDebugMode); - ErrorHandler.logError( - e: e, - s: s, - data: {"text": widget.audioContent}, - ); + ErrorHandler.logError(e: e, s: s, data: {"text": widget.audioContent}); } finally { if (mounted) { setState(() => _isPlaying = false); @@ -128,14 +124,8 @@ class PracticeMatchItemState extends State { color: color(context).withAlpha((0.4 * 255).toInt()), borderRadius: BorderRadius.circular(AppConfig.borderRadius), border: isSelected - ? Border.all( - color: color(context).withAlpha(255), - width: 2, - ) - : Border.all( - color: Colors.transparent, - width: 2, - ), + ? Border.all(color: color(context).withAlpha(255), width: 2) + : Border.all(color: Colors.transparent, width: 2), ), child: widget.content, ), @@ -145,19 +135,13 @@ class PracticeMatchItemState extends State { return Draggable( data: widget.constructForm, - feedback: Material( - type: MaterialType.transparency, - child: content, - ), + feedback: Material(type: MaterialType.transparency, child: content), onDragStarted: onTap, child: InkWell( onHover: (isHovered) => setState(() => _isHovered = isHovered), borderRadius: BorderRadius.circular(AppConfig.borderRadius), onTap: onTap, - child: ShimmerBackground( - enabled: widget.shimmer, - child: content, - ), + child: ShimmerBackground(enabled: widget.shimmer, child: content), ), ); } diff --git a/lib/pangea/toolbar/message_practice/practice_record_controller.dart b/lib/pangea/toolbar/message_practice/practice_record_controller.dart index 9bb3f396e..831a08da1 100644 --- a/lib/pangea/toolbar/message_practice/practice_record_controller.dart +++ b/lib/pangea/toolbar/message_practice/practice_record_controller.dart @@ -30,10 +30,7 @@ class PracticeRecordController { ); } - static bool? wasCorrectMatch( - PracticeTarget target, - PracticeChoice choice, - ) { + static bool? wasCorrectMatch(PracticeTarget target, PracticeChoice choice) { final record = _recordByTarget(target); for (final response in record.responses) { if (response.text == choice.choiceContent && response.isCorrect) { @@ -48,10 +45,7 @@ class PracticeRecordController { return null; } - static bool? wasCorrectChoice( - PracticeTarget target, - String choice, - ) { + static bool? wasCorrectChoice(PracticeTarget target, String choice) { final record = _recordByTarget(target); for (final response in record.responses) { if (response.text == choice) { @@ -74,14 +68,11 @@ class PracticeRecordController { ); } - static bool isCompleteByToken( - PracticeTarget target, - PangeaToken token, - ) { + static bool isCompleteByToken(PracticeTarget target, PangeaToken token) { final cId = target.targetTokenConstructID(token); - return _recordByTarget(target).responses.any( - (res) => res.cId == cId && res.isCorrect, - ); + return _recordByTarget( + target, + ).responses.any((res) => res.cId == cId && res.isCorrect); } static bool hasAnyCorrectChoices(PracticeTarget target) { diff --git a/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart b/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart index 5eaf37f51..3385594e4 100644 --- a/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart +++ b/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart @@ -52,22 +52,21 @@ class ReadingAssistanceInputBarState extends State { spacing: 4.0, mainAxisSize: MainAxisSize.min, children: [ - ...MessagePracticeMode.practiceModes.map( - (m) { - final complete = widget.controller.isPracticeSessionDone( - m.associatedActivityType!, - ); - return ToolbarButton( - mode: m, - setMode: () => widget.controller.updateToolbarMode(m), - isComplete: complete, - isSelected: widget.controller.practiceMode == m, - shimmer: widget.controller.practiceMode == - MessagePracticeMode.noneSelected && - !complete, - ); - }, - ), + ...MessagePracticeMode.practiceModes.map((m) { + final complete = widget.controller.isPracticeSessionDone( + m.associatedActivityType!, + ); + return ToolbarButton( + mode: m, + setMode: () => widget.controller.updateToolbarMode(m), + isComplete: complete, + isSelected: widget.controller.practiceMode == m, + shimmer: + widget.controller.practiceMode == + MessagePracticeMode.noneSelected && + !complete, + ); + }), ], ), Padding( @@ -131,10 +130,7 @@ class _ReadingAssistanceBarContent extends StatelessWidget { case MessagePracticeMode.noneSelected: return controller.isTotallyDone ? const _AllDoneWidget() - : const Icon( - Symbols.fitness_center, - size: 60.0, - ); + : const Icon(Symbols.fitness_center, size: 60.0); case MessagePracticeMode.wordEmoji: case MessagePracticeMode.wordMeaning: @@ -180,12 +176,7 @@ class _ReadingAssistanceBarContent extends StatelessWidget { } if (target == null) { - return const Center( - child: Icon( - Symbols.fitness_center, - size: 60.0, - ), - ); + return const Center(child: Icon(Symbols.fitness_center, size: 60.0)); } return PracticeActivityCard( @@ -209,9 +200,9 @@ class _AllDoneWidget extends StatelessWidget { Text( L10n.of(context).allDone, style: Theme.of(context).textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - letterSpacing: 0.5, - ), + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + ), textAlign: TextAlign.center, ), ElevatedButton( diff --git a/lib/pangea/toolbar/message_practice/token_practice_button.dart b/lib/pangea/toolbar/message_practice/token_practice_button.dart index a585e1290..9777308b9 100644 --- a/lib/pangea/toolbar/message_practice/token_practice_button.dart +++ b/lib/pangea/toolbar/message_practice/token_practice_button.dart @@ -42,8 +42,8 @@ class TokenPracticeButton extends StatelessWidget { }); TextStyle get _emojiStyle => TextStyle( - fontSize: (textStyle.fontSize ?? tokenButtonDefaultFontSize) + 4, - ); + fontSize: (textStyle.fontSize ?? tokenButtonDefaultFontSize) + 4, + ); PracticeTarget? get _activity => controller.practiceTargetForToken(token); @@ -85,12 +85,10 @@ class TokenPracticeButton extends StatelessWidget { textColor: textColor, width: tokenButtonHeight, onTap: () => controller.onSelectMorph( - MorphSelection( - token, - _activity!.morphFeature!, - ), + MorphSelection(token, _activity!.morphFeature!), ), - shimmer: controller.selectedMorph == null && + shimmer: + controller.selectedMorph == null && _activity != null && !PracticeRecordController.hasAnyCorrectChoices(_activity!), ); @@ -141,7 +139,8 @@ class _StandardMatchButton extends StatelessWidget { Widget build(BuildContext context) { return DragTarget( builder: (BuildContext context, accepted, rejected) { - final double colorAlpha = 0.3 + + final double colorAlpha = + 0.3 + (selectedChoice != null ? 0.4 : 0.0) + (accepted.isNotEmpty ? 0.3 : 0.0); @@ -151,8 +150,9 @@ class _StandardMatchButton extends StatelessWidget { return Material( type: MaterialType.transparency, child: InkWell( - onTap: - selectedChoice != null ? () => onMatch(selectedChoice!) : null, + onTap: selectedChoice != null + ? () => onMatch(selectedChoice!) + : null, borderRadius: borderRadius, child: CustomPaint( painter: DottedBorderPainter( @@ -258,12 +258,9 @@ class _NoActivityContentButton extends StatelessWidget { if (practiceMode == MessagePracticeMode.wordEmoji && target != null) { final displayEmoji = PracticeRecordController.correctResponse(target!, token)?.text ?? - token.vocabConstructID.userSetEmoji ?? - ''; - return Text( - displayEmoji, - style: emojiStyle, - ); + token.vocabConstructID.userSetEmoji ?? + ''; + return Text(displayEmoji, style: emojiStyle); } if (practiceMode == MessagePracticeMode.wordMorph && target != null) { final morphFeature = target!.morphFeature!; @@ -282,8 +279,8 @@ class _NoActivityContentButton extends StatelessWidget { radius: width / 2, backgroundColor: Theme.of(context).brightness != Brightness.light - ? Theme.of(context).colorScheme.surface.withAlpha(100) - : null, + ? Theme.of(context).colorScheme.surface.withAlpha(100) + : null, child: Padding( padding: const EdgeInsets.all(4.0), child: MorphIcon( diff --git a/lib/pangea/toolbar/message_practice/toolbar_button.dart b/lib/pangea/toolbar/message_practice/toolbar_button.dart index c4981c87e..9c65be8f2 100644 --- a/lib/pangea/toolbar/message_practice/toolbar_button.dart +++ b/lib/pangea/toolbar/message_practice/toolbar_button.dart @@ -37,8 +37,9 @@ class ToolbarButton extends StatelessWidget { color: color, onPressed: setMode, playSound: true, - colorFactor: - Theme.of(context).brightness == Brightness.light ? 0.55 : 0.3, + colorFactor: Theme.of(context).brightness == Brightness.light + ? 0.55 + : 0.3, builder: (context, depressed, shadowColor) => Stack( alignment: Alignment.center, children: [ @@ -53,10 +54,9 @@ class ToolbarButton extends StatelessWidget { if (shimmer) Shimmer.fromColors( baseColor: Colors.transparent, - highlightColor: Theme.of(context) - .colorScheme - .primaryContainer - .withAlpha(0xAA), + highlightColor: Theme.of( + context, + ).colorScheme.primaryContainer.withAlpha(0xAA), child: Container( height: 40.0, width: 40.0, @@ -66,10 +66,7 @@ class ToolbarButton extends StatelessWidget { ), ), ), - Icon( - mode.icon, - size: 20, - ), + Icon(mode.icon, size: 20), ], ), ), diff --git a/lib/pangea/toolbar/message_selection_overlay.dart b/lib/pangea/toolbar/message_selection_overlay.dart index 3d0769fe8..fc0e2a242 100644 --- a/lib/pangea/toolbar/message_selection_overlay.dart +++ b/lib/pangea/toolbar/message_selection_overlay.dart @@ -43,11 +43,11 @@ class MessageSelectionOverlay extends StatefulWidget { required Event? prevEvent, required Timeline timeline, super.key, - }) : _initialSelectedToken = initialSelectedToken, - _nextEvent = nextEvent, - _prevEvent = prevEvent, - _event = event, - _timeline = timeline; + }) : _initialSelectedToken = initialSelectedToken, + _nextEvent = nextEvent, + _prevEvent = prevEvent, + _event = event, + _timeline = timeline; @override MessageOverlayController createState() => MessageOverlayController(); @@ -130,9 +130,7 @@ class MessageOverlayController extends State ErrorHandler.logError( e: e, s: s, - data: { - "eventID": pangeaMessageEvent.eventId, - }, + data: {"eventID": pangeaMessageEvent.eventId}, ); } finally { _initializeSelectedToken(); @@ -234,10 +232,10 @@ class MessageOverlayController extends State } PangeaMessageEvent get pangeaMessageEvent => PangeaMessageEvent( - event: widget._event, - timeline: widget._timeline, - ownMessage: widget._event.room.client.userID == widget._event.senderId, - ); + event: widget._event, + timeline: widget._timeline, + ownMessage: widget._event.room.client.userID == widget._event.senderId, + ); PangeaToken? get selectedToken { if (pangeaMessageEvent.isAudioMessage == true) { @@ -267,8 +265,9 @@ class MessageOverlayController extends State } if (const ListEquality().equals(textToSelect, _highlightedTokens)) return; - _highlightedTokens = - textToSelect.isEmpty ? null : textToSelect.map((t) => t.text).toList(); + _highlightedTokens = textToSelect.isEmpty + ? null + : textToSelect.map((t) => t.text).toList(); setState(() {}); } @@ -277,8 +276,8 @@ class MessageOverlayController extends State pangeaMessageEvent.messageDisplayRepresentation; if (repEvent != null) return repEvent; - final eventID = - await pangeaMessageEvent.requestRepresentationByDetectedLanguage(); + final eventID = await pangeaMessageEvent + .requestRepresentationByDetectedLanguage(); if (eventID == null) return null; final event = await widget._event.room.getEventById(eventID); @@ -290,14 +289,13 @@ class MessageOverlayController extends State ); } - void onClickOverlayMessageToken( - PangeaToken token, - ) => + void onClickOverlayMessageToken(PangeaToken token) => updateSelectedSpan(token.text); /// Whether the given token is currently selected or highlighted bool isTokenSelected(PangeaToken token) { - final isSelected = _selectedSpan?.offset == token.text.offset && + final isSelected = + _selectedSpan?.offset == token.text.offset && _selectedSpan?.length == token.text.length; return isSelected; } diff --git a/lib/pangea/toolbar/reading_assistance/lemma_emoji_choice_item.dart b/lib/pangea/toolbar/reading_assistance/lemma_emoji_choice_item.dart index b1d01164b..279ec4f74 100644 --- a/lib/pangea/toolbar/reading_assistance/lemma_emoji_choice_item.dart +++ b/lib/pangea/toolbar/reading_assistance/lemma_emoji_choice_item.dart @@ -23,10 +23,9 @@ class LemmaEmojiChoiceItemState extends State { Color color(BuildContext context) { if (_isHovered) { - return Theme.of(context) - .colorScheme - .primaryContainer - .withAlpha((0.4 * 255).toInt()); + return Theme.of( + context, + ).colorScheme.primaryContainer.withAlpha((0.4 * 255).toInt()); } return Colors.transparent; @@ -56,10 +55,7 @@ class LemmaEmojiChoiceItemState extends State { } class LemmaEmojiChoicePlaceholder extends StatelessWidget { - const LemmaEmojiChoicePlaceholder({ - super.key, - this.size = 40, - }); + const LemmaEmojiChoicePlaceholder({super.key, this.size = 40}); final double size; @@ -67,8 +63,9 @@ class LemmaEmojiChoicePlaceholder extends StatelessWidget { Widget build(BuildContext context) { return Shimmer.fromColors( baseColor: Colors.transparent, - highlightColor: - Theme.of(context).colorScheme.primaryContainer.withAlpha(0xAA), + highlightColor: Theme.of( + context, + ).colorScheme.primaryContainer.withAlpha(0xAA), child: Container( height: size, width: size, diff --git a/lib/pangea/toolbar/reading_assistance/lemma_emoji_picker.dart b/lib/pangea/toolbar/reading_assistance/lemma_emoji_picker.dart index 965787745..5162a0c76 100644 --- a/lib/pangea/toolbar/reading_assistance/lemma_emoji_picker.dart +++ b/lib/pangea/toolbar/reading_assistance/lemma_emoji_picker.dart @@ -20,10 +20,7 @@ class LemmaEmojiPicker extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.symmetric( - vertical: 4, - horizontal: 8, - ), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), width: (40 * 5) + (4 * 5) + 16, // 5 items max + padding child: Row( spacing: 4.0, diff --git a/lib/pangea/toolbar/reading_assistance/new_word_overlay.dart b/lib/pangea/toolbar/reading_assistance/new_word_overlay.dart index 3f66827bb..03541e7c1 100644 --- a/lib/pangea/toolbar/reading_assistance/new_word_overlay.dart +++ b/lib/pangea/toolbar/reading_assistance/new_word_overlay.dart @@ -39,29 +39,29 @@ class _NewWordOverlayState extends State _opacityAnim = TweenSequence([ TweenSequenceItem( - tween: Tween(begin: 0.0, end: 1.0) - .chain(CurveTween(curve: Curves.easeIn)), + tween: Tween( + begin: 0.0, + end: 1.0, + ).chain(CurveTween(curve: Curves.easeIn)), weight: 25, ), + TweenSequenceItem(tween: ConstantTween(1.0), weight: 25), TweenSequenceItem( - tween: ConstantTween(1.0), - weight: 25, - ), - TweenSequenceItem( - tween: Tween(begin: 1.0, end: 0.0) - .chain(CurveTween(curve: Curves.easeOut)), + tween: Tween( + begin: 1.0, + end: 0.0, + ).chain(CurveTween(curve: Curves.easeOut)), weight: 50, ), ]).animate(_controller!); _backgroundFadeAnim = TweenSequence([ + TweenSequenceItem(tween: ConstantTween(1.0), weight: 50), TweenSequenceItem( - tween: ConstantTween(1.0), - weight: 50, - ), - TweenSequenceItem( - tween: Tween(begin: 1.0, end: 0.0) - .chain(CurveTween(curve: Curves.easeOut)), + tween: Tween( + begin: 1.0, + end: 0.0, + ).chain(CurveTween(curve: Curves.easeOut)), weight: 50, ), ]).animate(_controller!); @@ -112,10 +112,7 @@ class _NewWordOverlayState extends State return Transform.translate( offset: Offset(0, moveY), - child: Opacity( - opacity: opacity, - child: const NewVocabBubble(), - ), + child: Opacity(opacity: opacity, child: const NewVocabBubble()), ); }, ), @@ -157,18 +154,11 @@ class NewVocabBubble extends StatelessWidget { color: theme.colorScheme.surfaceContainerHighest, borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), child: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon( - Symbols.dictionary, - color: theme.colorScheme.primary, - size: 24, - ), + Icon(Symbols.dictionary, color: theme.colorScheme.primary, size: 24), const SizedBox(width: 4.0), Text( "+ 1", diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart index 227a8781e..6db9b759e 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart @@ -198,8 +198,8 @@ class SelectModeButtonsState extends State { final updatedMode = controller.selectedMode.value == mode && mode != SelectMode.audio - ? null - : mode; + ? null + : mode; controller.setSelectMode(updatedMode); if (updatedMode == SelectMode.audio) { @@ -227,9 +227,7 @@ class SelectModeButtonsState extends State { } if (updatedMode == SelectMode.requestRegenerate) { - await widget.controller.requestRegeneration( - messageEvent.eventId, - ); + await widget.controller.requestRegeneration(messageEvent.eventId); if (mounted) { controller.setSelectMode(null); @@ -255,8 +253,9 @@ class SelectModeButtonsState extends State { if (target != null) TextButton( style: TextButton.styleFrom( - foregroundColor: - Theme.of(context).colorScheme.primaryContainer, + foregroundColor: Theme.of( + context, + ).colorScheme.primaryContainer, ), onPressed: () => widget.controller.updateLanguageOnMismatch(target), @@ -270,7 +269,8 @@ class SelectModeButtonsState extends State { Future playAudio() async { final playerID = "${messageEvent.eventId}_button"; - final isPlaying = matrix?.audioPlayer != null && + final isPlaying = + matrix?.audioPlayer != null && matrix?.voiceMessageEventId.value == playerID && matrix!.audioPlayer!.playerState.processingState != ProcessingState.completed; @@ -291,8 +291,9 @@ class SelectModeButtonsState extends State { matrix?.voiceMessageEventId.value = "${messageEvent.eventId}_button"; _playerStateSub?.cancel(); - _playerStateSub = - matrix?.audioPlayer?.playerStateStream.listen(_onUpdatePlayerState); + _playerStateSub = matrix?.audioPlayer?.playerStateStream.listen( + _onUpdatePlayerState, + ); _audioSub?.cancel(); _audioSub = matrix?.audioPlayer?.positionStream.listen(_onPlayAudio); @@ -311,10 +312,7 @@ class SelectModeButtonsState extends State { await matrix?.audioPlayer?.setFilePath(audioFile.path); } else { await matrix?.audioPlayer?.setAudioSource( - BytesAudioSource( - pangeaAudioFile.bytes, - pangeaAudioFile.mimeType, - ), + BytesAudioSource(pangeaAudioFile.bytes, pangeaAudioFile.mimeType), ); } @@ -330,9 +328,7 @@ class SelectModeButtonsState extends State { e: e, s: s, m: 'something wrong playing message audio', - data: { - 'event': messageEvent.event.toJson(), - }, + data: {'event': messageEvent.event.toJson()}, ); } } @@ -374,7 +370,8 @@ class SelectModeButtonsState extends State { if (ttsToken == null) return; - final isPlaying = matrix?.audioPlayer != null && + final isPlaying = + matrix?.audioPlayer != null && matrix!.audioPlayer!.playerState.processingState != ProcessingState.completed; @@ -408,12 +405,10 @@ class SelectModeButtonsState extends State { child: Tooltip( message: mode.tooltip(context), child: ListenableBuilder( - listenable: Listenable.merge( - [ - controller.selectedMode, - controller.modeStateNotifier(mode), - ], - ), + listenable: Listenable.merge([ + controller.selectedMode, + controller.modeStateNotifier(mode), + ]), builder: (context, _) { final selectedMode = controller.selectedMode.value; return Opacity( @@ -422,41 +417,50 @@ class SelectModeButtonsState extends State { borderRadius: BorderRadius.circular(20), depressed: mode == selectedMode || !enabled, color: theme.colorScheme.primaryContainer, - onPressed: - enabled ? () => updateMode(mode) : modeDisabled, + onPressed: enabled + ? () => updateMode(mode) + : modeDisabled, playSound: enabled && mode != SelectMode.audio, - colorFactor: - theme.brightness == Brightness.light ? 0.55 : 0.3, + colorFactor: theme.brightness == Brightness.light + ? 0.55 + : 0.3, builder: (context, depressed, shadowColor) => ShimmerBackground( - enabled: !InstructionsEnum - .shimmerTranslation.isToggledOff && - mode == SelectMode.translate && - enabled, - borderRadius: BorderRadius.circular(100), - child: AnimatedContainer( - duration: FluffyThemes.animationDuration, - height: buttonSize, - width: buttonSize, - decoration: BoxDecoration( - color: depressed - ? shadowColor - : theme.colorScheme.primaryContainer, - shape: BoxShape.circle, - ), - child: ValueListenableBuilder( - valueListenable: _isPlayingNotifier, - builder: (context, playing, __) => - _SelectModeButtonIcon( - mode: mode, - loading: controller.isLoading && - mode == selectedMode, - playing: mode == SelectMode.audio && playing, - color: theme.colorScheme.onPrimaryContainer, + enabled: + !InstructionsEnum + .shimmerTranslation + .isToggledOff && + mode == SelectMode.translate && + enabled, + borderRadius: BorderRadius.circular(100), + child: AnimatedContainer( + duration: FluffyThemes.animationDuration, + height: buttonSize, + width: buttonSize, + decoration: BoxDecoration( + color: depressed + ? shadowColor + : theme.colorScheme.primaryContainer, + shape: BoxShape.circle, + ), + child: ValueListenableBuilder( + valueListenable: _isPlayingNotifier, + builder: (context, playing, _) => + _SelectModeButtonIcon( + mode: mode, + loading: + controller.isLoading && + mode == selectedMode, + playing: + mode == SelectMode.audio && + playing, + color: theme + .colorScheme + .onPrimaryContainer, + ), + ), ), ), - ), - ), ), ); }, @@ -521,10 +525,7 @@ class _MoreButton extends StatelessWidget { final ChatController controller; final PangeaMessageEvent? messageEvent; - const _MoreButton({ - required this.controller, - this.messageEvent, - }); + const _MoreButton({required this.controller, this.messageEvent}); bool _messageActionEnabled(MessageActions action) { if (messageEvent == null) return false; @@ -544,7 +545,8 @@ class _MoreButton extends StatelessWidget { return false; } - final isPinned = events.length == 1 && + final isPinned = + events.length == 1 && controller.room.pinnedEventIds.contains(events.first.eventId); switch (action) { @@ -577,17 +579,14 @@ class _MoreButton extends StatelessWidget { Future _showMenu(BuildContext context) async { final RenderBox button = context.findRenderObject() as RenderBox; - final RenderBox overlay = Overlay.of(context, rootOverlay: true) - .context - .findRenderObject() as RenderBox; + final RenderBox overlay = + Overlay.of(context, rootOverlay: true).context.findRenderObject() + as RenderBox; final Offset offset = button.localToGlobal(Offset.zero, ancestor: overlay); final RelativeRect position = RelativeRect.fromRect( - Rect.fromPoints( - offset, - offset + button.size.bottomRight(Offset.zero), - ), + Rect.fromPoints(offset, offset + button.size.bottomRight(Offset.zero)), Offset.zero & overlay.size, ); @@ -616,10 +615,7 @@ class _MoreButton extends StatelessWidget { _onActionPressed(action, context); } - void _onActionPressed( - MessageActions action, - BuildContext context, - ) { + void _onActionPressed(MessageActions action, BuildContext context) { switch (action) { case MessageActions.reply: controller.replyAction(); @@ -646,11 +642,7 @@ class _MoreButton extends StatelessWidget { case MessageActions.report: final event = controller.selectedEvents.first; controller.clearSelectedEvents(); - reportEvent( - event, - controller, - controller.context, - ); + reportEvent(event, controller, controller.context); break; case MessageActions.info: controller.showEventInfo(); @@ -683,10 +675,7 @@ class _MoreButton extends StatelessWidget { color: depressed ? shadowColor : theme.colorScheme.primaryContainer, shape: BoxShape.circle, ), - child: const Icon( - Icons.more_horiz, - size: 20, - ), + child: const Icon(Icons.more_horiz, size: 20), ), ), ); diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart index 1f1a7890b..b748e4bc0 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart @@ -21,9 +21,9 @@ class _TranscriptionLoader extends AsyncLoader { @override Future fetch() => messageEvent.requestSpeechToText( - MatrixState.pangeaController.userController.userL1!.langCodeShort, - MatrixState.pangeaController.userController.userL2!.langCodeShort, - ); + MatrixState.pangeaController.userController.userL1!.langCodeShort, + MatrixState.pangeaController.userController.userL2!.langCodeShort, + ); } class _STTTranslationLoader extends AsyncLoader { @@ -32,13 +32,10 @@ class _STTTranslationLoader extends AsyncLoader { @override Future fetch() => messageEvent.requestSttTranslation( - langCode: - MatrixState.pangeaController.userController.userL1!.langCodeShort, - l1Code: - MatrixState.pangeaController.userController.userL1!.langCodeShort, - l2Code: - MatrixState.pangeaController.userController.userL2!.langCodeShort, - ); + langCode: MatrixState.pangeaController.userController.userL1!.langCodeShort, + l1Code: MatrixState.pangeaController.userController.userL1!.langCodeShort, + l2Code: MatrixState.pangeaController.userController.userL2!.langCodeShort, + ); } class _TranslationLoader extends AsyncLoader { @@ -81,12 +78,11 @@ class SelectModeController with LemmaEmojiSetter { final _AudioLoader _audioLoader; final _STTTranslationLoader _sttTranslationLoader; - SelectModeController( - this.messageEvent, - ) : _transcriptLoader = _TranscriptionLoader(messageEvent), - _translationLoader = _TranslationLoader(messageEvent), - _audioLoader = _AudioLoader(messageEvent), - _sttTranslationLoader = _STTTranslationLoader(messageEvent); + SelectModeController(this.messageEvent) + : _transcriptLoader = _TranscriptionLoader(messageEvent), + _translationLoader = _TranslationLoader(messageEvent), + _audioLoader = _AudioLoader(messageEvent), + _sttTranslationLoader = _STTTranslationLoader(messageEvent); ValueNotifier selectedMode = ValueNotifier(null); @@ -105,15 +101,13 @@ class SelectModeController with LemmaEmojiSetter { } static List get _textModes => [ - SelectMode.audio, - SelectMode.translate, - SelectMode.practice, - SelectMode.emoji, - ]; + SelectMode.audio, + SelectMode.translate, + SelectMode.practice, + SelectMode.emoji, + ]; - static List get _audioModes => [ - SelectMode.speechTranslation, - ]; + static List get _audioModes => [SelectMode.speechTranslation]; ValueNotifier> get translationState => _translationLoader.state; @@ -156,18 +150,20 @@ class SelectModeController with LemmaEmojiSetter { List modes = []; final lang = messageEvent.messageDisplayLangCode.split("-").first; - final matchesL1 = lang == + final matchesL1 = + lang == MatrixState.pangeaController.userController.userL1!.langCodeShort; if (messageEvent.event.messageType == MessageTypes.Text) { - final matchesL2 = lang == + final matchesL2 = + lang == MatrixState.pangeaController.userController.userL2!.langCodeShort; modes = matchesL2 ? _textModes : matchesL1 - ? [] - : [SelectMode.translate]; + ? [] + : [SelectMode.translate]; } else { modes = matchesL1 ? [] : _audioModes; } diff --git a/lib/pangea/toolbar/reading_assistance/stt_transcript_tokens.dart b/lib/pangea/toolbar/reading_assistance/stt_transcript_tokens.dart index 2a25706b9..56d7f2b6b 100644 --- a/lib/pangea/toolbar/reading_assistance/stt_transcript_tokens.dart +++ b/lib/pangea/toolbar/reading_assistance/stt_transcript_tokens.dart @@ -39,18 +39,15 @@ class SttTranscriptTokens extends StatelessWidget { } final messageCharacters = model.transcript.text.characters; - final newTokens = TokensUtil.getNewTokens( - eventId, - tokens, - model.langCode, - ); + final newTokens = TokensUtil.getNewTokens(eventId, tokens, model.langCode); return RichText( textScaler: TextScaler.noScaling, text: TextSpan( style: style ?? DefaultTextStyle.of(context).style, - children: - TokensUtil.getGlobalTokenPositions(tokens).map((tokenPosition) { + children: TokensUtil.getGlobalTokenPositions(tokens).map(( + tokenPosition, + ) { final text = messageCharacters .skip(tokenPosition.startIndex) .take(tokenPosition.endIndex - tokenPosition.startIndex) diff --git a/lib/pangea/toolbar/reading_assistance/token_emoji_button.dart b/lib/pangea/toolbar/reading_assistance/token_emoji_button.dart index f5f5cbc74..a74bc4997 100644 --- a/lib/pangea/toolbar/reading_assistance/token_emoji_button.dart +++ b/lib/pangea/toolbar/reading_assistance/token_emoji_button.dart @@ -68,10 +68,7 @@ class TokenEmojiButton extends StatelessWidget with LemmaEmojiSetter { final layer = MatrixState.pAnyState.layerLinkAndKey(targetId!); content = CompositedTransformTarget( link: layer.link, - child: KeyedSubtree( - key: layer.key, - child: content, - ), + child: KeyedSubtree(key: layer.key, child: content), ); } @@ -97,20 +94,16 @@ class _EmojiText extends StatelessWidget { if (!enabled || token == null) return const SizedBox.shrink(); return StreamBuilder( - stream: Matrix.of(context) - .analyticsDataService - .updateDispatcher + stream: Matrix.of(context).analyticsDataService.updateDispatcher .lemmaUpdateStream(token!.vocabConstructID), builder: (context, snapshot) { - final emoji = snapshot.data?.emojis?.firstOrNull ?? + final emoji = + snapshot.data?.emojis?.firstOrNull ?? token!.vocabConstructID.userSetEmoji; return Text( emoji ?? "-", - style: TextStyle( - fontSize: fontSize, - color: textColor, - ), + style: TextStyle(fontSize: fontSize, color: textColor), textScaler: TextScaler.noScaling, ); }, diff --git a/lib/pangea/toolbar/reading_assistance/token_rendering_util.dart b/lib/pangea/toolbar/reading_assistance/token_rendering_util.dart index 6891fe345..e7278ec11 100644 --- a/lib/pangea/toolbar/reading_assistance/token_rendering_util.dart +++ b/lib/pangea/toolbar/reading_assistance/token_rendering_util.dart @@ -19,10 +19,7 @@ class TokenRenderingUtil { } final textPainter = TextPainter( - text: TextSpan( - text: text, - style: style, - ), + text: TextSpan(text: text, style: style), maxLines: 1, textDirection: TextDirection.ltr, )..layout(); diff --git a/lib/pangea/toolbar/reading_assistance/tokens_util.dart b/lib/pangea/toolbar/reading_assistance/tokens_util.dart index 3f69baa4e..0fe4be59a 100644 --- a/lib/pangea/toolbar/reading_assistance/tokens_util.dart +++ b/lib/pangea/toolbar/reading_assistance/tokens_util.dart @@ -9,20 +9,14 @@ class _TokenPositionCacheItem { final List positions; final DateTime timestamp; - _TokenPositionCacheItem( - this.positions, - this.timestamp, - ); + _TokenPositionCacheItem(this.positions, this.timestamp); } class _NewTokenCacheItem { final List tokens; final DateTime timestamp; - _NewTokenCacheItem( - this.tokens, - this.timestamp, - ); + _NewTokenCacheItem(this.tokens, this.timestamp); } class TokenPosition { @@ -37,10 +31,10 @@ class TokenPosition { }); Map toJson() => { - 'token': token?.toJson(), - 'startIndex': startIndex, - 'endIndex': endIndex, - }; + 'token': token?.toJson(), + 'startIndex': startIndex, + 'endIndex': endIndex, + }; } class TokensUtil { @@ -66,10 +60,7 @@ class TokensUtil { String cacheKey, List tokens, ) { - _newTokenCache[cacheKey] = _NewTokenCacheItem( - tokens, - DateTime.now(), - ); + _newTokenCache[cacheKey] = _NewTokenCacheItem(tokens, DateTime.now()); } static List getNewTokens( @@ -79,11 +70,15 @@ class TokensUtil { int? maxTokens, }) { if (MatrixState - .pangeaController.matrixState.analyticsDataService.isInitializing) { + .pangeaController + .matrixState + .analyticsDataService + .isInitializing) { return []; } - final messageInUserL2 = tokensLangCode.split('-').first == + final messageInUserL2 = + tokensLangCode.split('-').first == MatrixState.pangeaController.userController.userL2?.langCodeShort; final cached = _getCachedNewTokens(cacheKey); @@ -120,18 +115,20 @@ class TokensUtil { return newTokens; } - static List getNewTokensByEvent( - PangeaMessageEvent event, - ) { + static List getNewTokensByEvent(PangeaMessageEvent event) { if (!event.eventId.isValidMatrixId || (MatrixState.pangeaController.subscriptionController.isSubscribed == false) || MatrixState - .pangeaController.matrixState.analyticsDataService.isInitializing) { + .pangeaController + .matrixState + .analyticsDataService + .isInitializing) { return []; } - final messageInUserL2 = event.messageDisplayLangCode.split("-")[0] == + final messageInUserL2 = + event.messageDisplayLangCode.split("-")[0] == MatrixState.pangeaController.userController.userL2?.langCodeShort; final cached = _getCachedNewTokens(event.eventId); @@ -249,9 +246,7 @@ class TokensUtil { } /// Given a list of tokens, reconstructs an original message, including gaps for non-token elements. - static List getGlobalTokenPositions( - List tokens, - ) { + static List getGlobalTokenPositions(List tokens) { final List tokenPositions = []; int tokenPointer = 0; int globalPointer = 0; @@ -264,10 +259,7 @@ class TokensUtil { // If the token starts after the current global pointer, we need to // create a new token position for the gap tokenPositions.add( - TokenPosition( - startIndex: globalPointer, - endIndex: token.text.offset, - ), + TokenPosition(startIndex: globalPointer, endIndex: token.text.offset), ); globalPointer = token.text.offset; @@ -279,9 +271,11 @@ class TokensUtil { final PangeaToken currentToken = tokens[endIndex]; final PangeaToken nextToken = tokens[endIndex + 1]; - final currentIsPunct = currentToken.pos == 'PUNCT' && + final currentIsPunct = + currentToken.pos == 'PUNCT' && currentToken.text.content.trim().isNotEmpty; - final nextIsPunct = nextToken.pos == 'PUNCT' && + final nextIsPunct = + nextToken.pos == 'PUNCT' && nextToken.text.content.trim().isNotEmpty; if (currentToken.text.offset + currentToken.text.length != diff --git a/lib/pangea/toolbar/token_rendering_mixin.dart b/lib/pangea/toolbar/token_rendering_mixin.dart index 81da0912e..4d89aab61 100644 --- a/lib/pangea/toolbar/token_rendering_mixin.dart +++ b/lib/pangea/toolbar/token_rendering_mixin.dart @@ -36,10 +36,7 @@ mixin TokenRenderingMixin { ), ]; - await analyticsService.updateService.addAnalytics( - targetId, - constructs, - ); + await analyticsService.updateService.addAnalytics(targetId, constructs); TokensUtil.clearNewTokenCache(); } } diff --git a/lib/pangea/toolbar/word_card/lemma_meaning_display.dart b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart index 70dd51f3e..2814056a4 100644 --- a/lib/pangea/toolbar/word_card/lemma_meaning_display.dart +++ b/lib/pangea/toolbar/word_card/lemma_meaning_display.dart @@ -34,42 +34,30 @@ class LemmaMeaningDisplay extends StatelessWidget { builder: (context, controller) { return switch (controller.state) { AsyncError() => ErrorIndicator( - message: L10n.of(context).errorFetchingDefinition, - style: const TextStyle(fontSize: 14.0), - ), + message: L10n.of(context).errorFetchingDefinition, + style: const TextStyle(fontSize: 14.0), + ), AsyncLoaded(value: final lemmaInfo) => RichText( - maxLines: 2, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.center, - text: TextSpan( - style: DefaultTextStyle.of(context).style.copyWith( - fontSize: 14.0, - ), - children: [ - TextSpan( - text: "${constructId.lemma} (${getGrammarCopy( - category: "POS", - lemma: constructId.category, - context: context, - ) ?? L10n.of(context).other})", - ), - const WidgetSpan( - child: SizedBox(width: 8.0), - ), - const TextSpan(text: ":"), - const WidgetSpan( - child: SizedBox(width: 8.0), - ), - TextSpan( - text: lemmaInfo.meaning, - ), - ], - ), - ), - _ => const TextLoadingShimmer( - width: 125.0, - height: 20.0, + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + text: TextSpan( + style: DefaultTextStyle.of( + context, + ).style.copyWith(fontSize: 14.0), + children: [ + TextSpan( + text: + "${constructId.lemma} (${getGrammarCopy(category: "POS", lemma: constructId.category, context: context) ?? L10n.of(context).other})", + ), + const WidgetSpan(child: SizedBox(width: 8.0)), + const TextSpan(text: ":"), + const WidgetSpan(child: SizedBox(width: 8.0)), + TextSpan(text: lemmaInfo.meaning), + ], ), + ), + _ => const TextLoadingShimmer(width: 125.0, height: 20.0), }; }, ); diff --git a/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart b/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart index cc823be64..3a0493314 100644 --- a/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart +++ b/lib/pangea/toolbar/word_card/lemma_reaction_picker.dart @@ -65,9 +65,7 @@ class LemmaReactionPickerState extends State { void _setEmojiSub() { _emojiSub?.cancel(); - _emojiSub = Matrix.of(context) - .analyticsDataService - .updateDispatcher + _emojiSub = Matrix.of(context).analyticsDataService.updateDispatcher .lemmaUpdateStream(widget.constructId) .listen((update) => _setSelectedEmoji(update.emojis?.firstOrNull)); } @@ -82,19 +80,14 @@ class LemmaReactionPickerState extends State { widget.event!.room.timeline!, RelationshipTypes.reaction, ) - .where( - (e) => e.senderId == Matrix.of(context).client.userID, - ); + .where((e) => e.senderId == Matrix.of(context).client.userID); return userSentEmojis.firstWhereOrNull( (e) => e.content.tryGetMap('m.relates_to')?['key'] == emoji, ); } - Future _setEmoji( - String emoji, - String targetId, - ) async { + Future _setEmoji(String emoji, String targetId) async { await widget.setLemmaEmoji( widget.constructId, widget.langCode, @@ -119,18 +112,12 @@ class LemmaReactionPickerState extends State { return; } - await widget.event!.room.sendReaction( - widget.event!.eventId, - emoji, - ); + await widget.event!.room.sendReaction(widget.event!.eventId, emoji); } catch (e, s) { ErrorHandler.logError( e: e, s: s, - data: { - 'emoji': emoji, - 'eventId': widget.event?.eventId, - }, + data: {'emoji': emoji, 'eventId': widget.event?.eventId}, ); } } @@ -147,13 +134,11 @@ class LemmaReactionPickerState extends State { : _sendOrRedactReaction(emoji), emoji: _selectedEmoji, messageInfo: widget.event?.content ?? {}, - selectedEmojiBadge: widget.event != null && + selectedEmojiBadge: + widget.event != null && _selectedEmoji != null && _sentReaction(_selectedEmoji!) == null - ? const Icon( - Icons.add_reaction, - size: 12.0, - ) + ? const Icon(Icons.add_reaction, size: 12.0) : null, enabled: _enabled, ); diff --git a/lib/pangea/toolbar/word_card/message_unsubscribed_card.dart b/lib/pangea/toolbar/word_card/message_unsubscribed_card.dart index d9b2528e5..fc7045696 100644 --- a/lib/pangea/toolbar/word_card/message_unsubscribed_card.dart +++ b/lib/pangea/toolbar/word_card/message_unsubscribed_card.dart @@ -11,9 +11,7 @@ class MessageUnsubscribedCard extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - constraints: const BoxConstraints( - maxWidth: AppConfig.toolbarMinWidth, - ), + constraints: const BoxConstraints(maxWidth: AppConfig.toolbarMinWidth), padding: const EdgeInsets.all(16), child: Column( children: [ @@ -27,8 +25,9 @@ class MessageUnsubscribedCard extends StatelessWidget { width: double.infinity, child: TextButton( onPressed: () { - MatrixState.pangeaController.subscriptionController - .showPaywall(context); + MatrixState.pangeaController.subscriptionController.showPaywall( + context, + ); }, style: ButtonStyle( backgroundColor: WidgetStateProperty.all( diff --git a/lib/pangea/toolbar/word_card/reading_assistance_content.dart b/lib/pangea/toolbar/word_card/reading_assistance_content.dart index 3096171c5..960f6ef48 100644 --- a/lib/pangea/toolbar/word_card/reading_assistance_content.dart +++ b/lib/pangea/toolbar/word_card/reading_assistance_content.dart @@ -23,19 +23,17 @@ class ReadingAssistanceContent extends StatelessWidget { @override Widget build(BuildContext context) { - if (![MessageTypes.Text, MessageTypes.Audio].contains( - overlayController.pangeaMessageEvent.event.messageType, - )) { + if (![ + MessageTypes.Text, + MessageTypes.Audio, + ].contains(overlayController.pangeaMessageEvent.event.messageType)) { return const SizedBox(); } final tokens = overlayController.pangeaMessageEvent.originalSent?.tokens; final selectedToken = overlayController.selectedToken; final selectedTokenIndex = selectedToken != null - ? tokens?.indexWhere( - (t) => t.text == selectedToken.text, - ) ?? - -1 + ? tokens?.indexWhere((t) => t.text == selectedToken.text) ?? -1 : -1; return WordZoomWidget( diff --git a/lib/pangea/toolbar/word_card/token_feedback_button.dart b/lib/pangea/toolbar/word_card/token_feedback_button.dart index c5a7ece94..fb379a50c 100644 --- a/lib/pangea/toolbar/word_card/token_feedback_button.dart +++ b/lib/pangea/toolbar/word_card/token_feedback_button.dart @@ -35,7 +35,8 @@ class TokenFeedbackButton extends StatelessWidget { textLanguage: textLanguage, text: text, builder: (context, transcriptController) { - final enabled = (lemmaController.lemmaInfo != null || + final enabled = + (lemmaController.lemmaInfo != null || lemmaController.isError) && (transcriptController.transcription != null || transcriptController.isError); @@ -50,10 +51,7 @@ class TokenFeedbackButton extends StatelessWidget { icon: const Icon(Icons.flag_outlined), onPressed: enabled ? () { - onFlagTokenInfo( - lemmaInfo, - transcript, - ); + onFlagTokenInfo(lemmaInfo, transcript); } : null, tooltip: enabled ? L10n.of(context).reportWordIssueTooltip : null, diff --git a/lib/pangea/toolbar/word_card/word_card_switcher.dart b/lib/pangea/toolbar/word_card/word_card_switcher.dart index 014273478..2ecbd9e63 100644 --- a/lib/pangea/toolbar/word_card/word_card_switcher.dart +++ b/lib/pangea/toolbar/word_card/word_card_switcher.dart @@ -13,7 +13,7 @@ class WordCardSwitcher extends StatelessWidget { Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: controller.widget.overlayController.selectedMode, - builder: (context, mode, __) { + builder: (context, mode, _) { return AnimatedSize( alignment: controller.ownMessage ? Alignment.bottomRight @@ -24,13 +24,13 @@ class WordCardSwitcher extends StatelessWidget { overlayController: controller.widget.overlayController, ) : mode != SelectMode.emoji - ? ValueListenableBuilder( - valueListenable: controller.reactionNotifier, - builder: (context, _, __) => MessageReactionPicker( - chatController: controller.widget.chatController, - ), - ) - : const SizedBox.shrink(), + ? ValueListenableBuilder( + valueListenable: controller.reactionNotifier, + builder: (context, _, _) => MessageReactionPicker( + chatController: controller.widget.chatController, + ), + ) + : const SizedBox.shrink(), ); }, ); diff --git a/lib/pangea/toolbar/word_card/word_zoom_widget.dart b/lib/pangea/toolbar/word_card/word_zoom_widget.dart index 4e9b40ffc..51a8baa65 100644 --- a/lib/pangea/toolbar/word_card/word_zoom_widget.dart +++ b/lib/pangea/toolbar/word_card/word_zoom_widget.dart @@ -86,10 +86,7 @@ class WordZoomWidget extends StatelessWidget { icon: const Icon(Icons.close), onPressed: onClose, ) - : const SizedBox( - width: 40.0, - height: 40.0, - ), + : const SizedBox(width: 40.0, height: 40.0), Flexible( child: Container( constraints: const BoxConstraints( @@ -103,7 +100,8 @@ class WordZoomWidget extends StatelessWidget { fontSize: 28.0, fontWeight: FontWeight.w600, height: 1.2, - color: Theme.of(context).brightness == + color: + Theme.of(context).brightness == Brightness.light ? AppConfig.yellowDark : AppConfig.yellowLight, @@ -114,19 +112,15 @@ class WordZoomWidget extends StatelessWidget { ), onFlagTokenInfo != null ? TokenFeedbackButton( - textLanguage: PLanguageStore.byLangCode( - langCode, - ) ?? + textLanguage: + PLanguageStore.byLangCode(langCode) ?? LanguageModel.unknown, constructId: construct, text: token.content, onFlagTokenInfo: onFlagTokenInfo!, messageInfo: event?.content ?? {}, ) - : const SizedBox( - width: 40.0, - height: 40.0, - ), + : const SizedBox(width: 40.0, height: 40.0), ], ), ), @@ -138,9 +132,8 @@ class WordZoomWidget extends StatelessWidget { showTranscript ? PhoneticTranscriptionWidget( text: token.content, - textLanguage: PLanguageStore.byLangCode( - langCode, - ) ?? + textLanguage: + PLanguageStore.byLangCode(langCode) ?? LanguageModel.unknown, style: const TextStyle(fontSize: 14.0), iconSize: 24.0, @@ -206,9 +199,7 @@ class WordZoomWidget extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, - children: [ - content, - ], + children: [content], ), ), ), diff --git a/lib/pangea/translation/full_text_translation_repo.dart b/lib/pangea/translation/full_text_translation_repo.dart index b1e621f11..4fbb5dd63 100644 --- a/lib/pangea/translation/full_text_translation_repo.dart +++ b/lib/pangea/translation/full_text_translation_repo.dart @@ -17,10 +17,7 @@ class _TranslateCacheItem { final Future response; final DateTime timestamp; - const _TranslateCacheItem({ - required this.response, - required this.timestamp, - }); + const _TranslateCacheItem({required this.response, required this.timestamp}); } class FullTextTranslationRepo { @@ -75,18 +72,12 @@ class FullTextTranslationRepo { return Result.value(res); } catch (e, s) { _cache.remove(request.hashCode.toString()); - ErrorHandler.logError( - e: e, - s: s, - data: request.toJson(), - ); + ErrorHandler.logError(e: e, s: s, data: request.toJson()); return Result.error(e); } } - static Future? _getCached( - FullTextTranslationRequestModel request, - ) { + static Future? _getCached(FullTextTranslationRequestModel request) { final cacheKeys = [..._cache.keys]; for (final key in cacheKeys) { if (DateTime.now().difference(_cache[key]!.timestamp) >= _cacheDuration) { @@ -100,9 +91,8 @@ class FullTextTranslationRepo { static void _setCached( FullTextTranslationRequestModel request, Future response, - ) => - _cache[request.hashCode.toString()] = _TranslateCacheItem( - response: response, - timestamp: DateTime.now(), - ); + ) => _cache[request.hashCode.toString()] = _TranslateCacheItem( + response: response, + timestamp: DateTime.now(), + ); } diff --git a/lib/pangea/translation/full_text_translation_request_model.dart b/lib/pangea/translation/full_text_translation_request_model.dart index 62375e56c..c14fbc9a8 100644 --- a/lib/pangea/translation/full_text_translation_request_model.dart +++ b/lib/pangea/translation/full_text_translation_request_model.dart @@ -22,15 +22,15 @@ class FullTextTranslationRequestModel { }); Map toJson() => { - ModelKey.text: text, - ModelKey.srcLang: srcLang, - ModelKey.tgtLang: tgtLang, - ModelKey.userL2: userL2, - ModelKey.userL1: userL1, - ModelKey.deepL: deepL, - ModelKey.offset: offset, - ModelKey.length: length, - }; + ModelKey.text: text, + ModelKey.srcLang: srcLang, + ModelKey.tgtLang: tgtLang, + ModelKey.userL2: userL2, + ModelKey.userL1: userL1, + ModelKey.deepL: deepL, + ModelKey.offset: offset, + ModelKey.length: length, + }; // override equals and hashcode @override diff --git a/lib/pangea/translation/full_text_translation_response_model.dart b/lib/pangea/translation/full_text_translation_response_model.dart index ebba1fea1..a46f650cc 100644 --- a/lib/pangea/translation/full_text_translation_response_model.dart +++ b/lib/pangea/translation/full_text_translation_response_model.dart @@ -14,9 +14,7 @@ class FullTextTranslationResponseModel { factory FullTextTranslationResponseModel.fromJson(Map json) { return FullTextTranslationResponseModel( translations: (json["translations"] as Iterable) - .map( - (e) => e, - ) + .map((e) => e) .toList() .cast(), source: json[ModelKey.srcLang], diff --git a/lib/pangea/user/about_me_display.dart b/lib/pangea/user/about_me_display.dart index 0a3cf5c12..f35d34d68 100644 --- a/lib/pangea/user/about_me_display.dart +++ b/lib/pangea/user/about_me_display.dart @@ -19,8 +19,9 @@ class AboutMeDisplay extends StatelessWidget { return ConstrainedBox( constraints: BoxConstraints(maxWidth: maxWidth), child: FutureBuilder( - future: MatrixState.pangeaController.userController - .getPublicProfile(userId), + future: MatrixState.pangeaController.userController.getPublicProfile( + userId, + ), builder: (context, snapshot) => snapshot.data?.about == null ? const SizedBox.shrink() : Padding( diff --git a/lib/pangea/user/pangea_push_rules_extension.dart b/lib/pangea/user/pangea_push_rules_extension.dart index c7e92462a..95cca5e85 100644 --- a/lib/pangea/user/pangea_push_rules_extension.dart +++ b/lib/pangea/user/pangea_push_rules_extension.dart @@ -6,8 +6,9 @@ import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; extension PangeaPushRulesExtension on Client { Future setPangeaPushRules() async { if (!isLogged()) return; - final List analyticsRooms = - rooms.where((room) => room.isAnalyticsRoom).toList(); + final List analyticsRooms = rooms + .where((room) => room.isAnalyticsRoom) + .toList(); for (final Room room in analyticsRooms) { final pushRule = room.pushRuleState; diff --git a/lib/pangea/user/public_profile_model.dart b/lib/pangea/user/public_profile_model.dart index 9f4ed71d9..66d3e17ba 100644 --- a/lib/pangea/user/public_profile_model.dart +++ b/lib/pangea/user/public_profile_model.dart @@ -8,11 +8,7 @@ class PublicProfileModel { final String? country; final String? about; - const PublicProfileModel({ - required this.analytics, - this.country, - this.about, - }); + const PublicProfileModel({required this.analytics, this.country, this.about}); String? get countryEmoji => country != null ? CountryService().findByName(country!)?.flagEmoji : null; diff --git a/lib/pangea/user/style_settings_repo.dart b/lib/pangea/user/style_settings_repo.dart index 65f452fec..e129b27be 100644 --- a/lib/pangea/user/style_settings_repo.dart +++ b/lib/pangea/user/style_settings_repo.dart @@ -51,13 +51,7 @@ class StyleSettingsRepo { try { return StyleSettings.fromJson(json); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - "settings_entry": json, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {"settings_entry": json}); _storage.remove(key); return const StyleSettings(); } diff --git a/lib/pangea/user/user_controller.dart b/lib/pangea/user/user_controller.dart index 79c9d7b70..668aa06a6 100644 --- a/lib/pangea/user/user_controller.dart +++ b/lib/pangea/user/user_controller.dart @@ -148,11 +148,7 @@ class UserController { await PLanguageStore.initialize(forceRefresh: true); } } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: {}, - ); + ErrorHandler.logError(e: err, s: s, data: {}); } finally { if (!initCompleter.isCompleted) { initCompleter.complete(); @@ -179,9 +175,7 @@ class UserController { } catch (e) { // getting a 404 error for some users without pre-existing profile // still want to set other properties, so catch this error - publicProfile = PublicProfileModel( - analytics: AnalyticsProfileModel(), - ); + publicProfile = PublicProfileModel(analytics: AnalyticsProfileModel()); } await updatePublicProfile(); @@ -230,11 +224,7 @@ class UserController { await initialize(); return profile.userSettings.targetLanguage != null; } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: {}, - ); + ErrorHandler.logError(e: err, s: s, data: {}); return false; } } @@ -264,8 +254,8 @@ class UserController { /// - The user's email address as a [String], or `null` if no email address /// is found. Future get userEmail async { - final List? identifiers = - await client.getAccount3PIDs(); + final List? identifiers = await client + .getAccount3PIDs(); final matrix.ThirdPartyIdentifier? email = identifiers?.firstWhereOrNull( (identifier) => identifier.medium == matrix.ThirdPartyIdentifierMedium.email, @@ -278,19 +268,12 @@ class UserController { Map content, ) async { try { - await client.setUserProfile( - client.userID!, - type, - content, - ); + await client.setUserProfile(client.userID!, type, content); } catch (e, s) { ErrorHandler.logError( e: e, s: s, - data: { - 'type': type, - 'content': content, - }, + data: {'type': type, 'content': content}, ); } } @@ -339,14 +322,14 @@ class UserController { if (lang == null || publicProfile?.analytics.languageAnalytics == null) { continue; } - final langKey = - publicProfile!.analytics.languageAnalytics!.keys.firstWhereOrNull( - (l) => l.langCodeShort == lang, - ); + final langKey = publicProfile!.analytics.languageAnalytics!.keys + .firstWhereOrNull((l) => l.langCodeShort == lang); if (langKey == null) continue; if (publicProfile! - .analytics.languageAnalytics![langKey]!.analyticsRoomId == + .analytics + .languageAnalytics![langKey]! + .analyticsRoomId == analyticsRoom.id) { continue; } @@ -397,9 +380,7 @@ class UserController { ); } - Future getPublicAnalyticsProfile( - String userId, - ) async { + Future getPublicAnalyticsProfile(String userId) async { try { if (userId == BotName.byEnvironment) { return AnalyticsProfileModel(); @@ -408,13 +389,7 @@ class UserController { final resp = await client.getUserProfile(userId); return AnalyticsProfileModel.fromJson(resp.additionalProperties); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - userId: userId, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {userId: userId}); return AnalyticsProfileModel(); } } @@ -422,21 +397,13 @@ class UserController { Future getPublicProfile(String userId) async { try { if (userId == BotName.byEnvironment) { - return PublicProfileModel( - analytics: AnalyticsProfileModel(), - ); + return PublicProfileModel(analytics: AnalyticsProfileModel()); } final resp = await client.getUserProfile(userId); return PublicProfileModel.fromJson(resp.additionalProperties); } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - userId: userId, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {userId: userId}); return null; } } diff --git a/lib/pangea/user/user_model.dart b/lib/pangea/user/user_model.dart index 8f7cc1976..19c312c83 100644 --- a/lib/pangea/user/user_model.dart +++ b/lib/pangea/user/user_model.dart @@ -36,29 +36,25 @@ class UserSettings { }); factory UserSettings.fromJson(Map json) => UserSettings( - dateOfBirth: json[ModelKey.userDateOfBirth] != null - ? DateTime.parse(json[ModelKey.userDateOfBirth]) - : null, - createdAt: json[ModelKey.userCreatedAt] != null - ? DateTime.parse(json[ModelKey.userCreatedAt]) - : null, - publicProfile: json[ModelKey.publicProfile], - targetLanguage: json[ModelKey.targetLanguage], - sourceLanguage: json[ModelKey.sourceLanguage], - gender: json[ModelKey.userGender] is String - ? GenderEnumExtension.fromString( - json[ModelKey.userGender], - ) - : GenderEnum.unselected, - country: json[ModelKey.userCountry], - about: json[ModelKey.userAbout], - cefrLevel: json[ModelKey.cefrLevel] is String - ? LanguageLevelTypeEnum.fromString( - json[ModelKey.cefrLevel], - ) - : LanguageLevelTypeEnum.a1, - voice: json[ModelKey.voice], - ); + dateOfBirth: json[ModelKey.userDateOfBirth] != null + ? DateTime.parse(json[ModelKey.userDateOfBirth]) + : null, + createdAt: json[ModelKey.userCreatedAt] != null + ? DateTime.parse(json[ModelKey.userCreatedAt]) + : null, + publicProfile: json[ModelKey.publicProfile], + targetLanguage: json[ModelKey.targetLanguage], + sourceLanguage: json[ModelKey.sourceLanguage], + gender: json[ModelKey.userGender] is String + ? GenderEnumExtension.fromString(json[ModelKey.userGender]) + : GenderEnum.unselected, + country: json[ModelKey.userCountry], + about: json[ModelKey.userAbout], + cefrLevel: json[ModelKey.cefrLevel] is String + ? LanguageLevelTypeEnum.fromString(json[ModelKey.cefrLevel]) + : LanguageLevelTypeEnum.a1, + voice: json[ModelKey.voice], + ); Map toJson() { final Map data = {}; @@ -109,15 +105,19 @@ class UserSettings { return UserSettings( dateOfBirth: dob, createdAt: createdAt, - publicProfile: (accountData[ModelKey.publicProfile] - ?.content[ModelKey.publicProfile] as bool?) ?? + publicProfile: + (accountData[ModelKey.publicProfile]?.content[ModelKey.publicProfile] + as bool?) ?? false, - targetLanguage: accountData[ModelKey.targetLanguage] - ?.content[ModelKey.targetLanguage] as String?, - sourceLanguage: accountData[ModelKey.sourceLanguage] - ?.content[ModelKey.sourceLanguage] as String?, - country: accountData[ModelKey.userCountry]?.content[ModelKey.userCountry] - as String?, + targetLanguage: + accountData[ModelKey.targetLanguage]?.content[ModelKey.targetLanguage] + as String?, + sourceLanguage: + accountData[ModelKey.sourceLanguage]?.content[ModelKey.sourceLanguage] + as String?, + country: + accountData[ModelKey.userCountry]?.content[ModelKey.userCountry] + as String?, ); } @@ -155,17 +155,17 @@ class UserSettings { @override int get hashCode => Object.hashAll([ - dateOfBirth.hashCode, - createdAt.hashCode, - (publicProfile ?? false).hashCode, - targetLanguage.hashCode, - sourceLanguage.hashCode, - gender.hashCode, - country.hashCode, - about.hashCode, - cefrLevel.hashCode, - voice.hashCode, - ]); + dateOfBirth.hashCode, + createdAt.hashCode, + (publicProfile ?? false).hashCode, + targetLanguage.hashCode, + sourceLanguage.hashCode, + gender.hashCode, + country.hashCode, + about.hashCode, + cefrLevel.hashCode, + voice.hashCode, + ]); } /// The user's language tool settings. @@ -219,20 +219,26 @@ class UserToolSettings { return UserToolSettings( interactiveTranslator: (accountData[ToolSetting.interactiveTranslator.toString()] - ?.content[ToolSetting.interactiveTranslator.toString()] - as bool?) ?? - true, + ?.content[ToolSetting.interactiveTranslator.toString()] + as bool?) ?? + true, interactiveGrammar: (accountData[ToolSetting.interactiveGrammar.toString()] - ?.content[ToolSetting.interactiveGrammar.toString()] - as bool?) ?? - true, - immersionMode: false, - definitions: (accountData[ToolSetting.definitions.toString()] - ?.content[ToolSetting.definitions.toString()] as bool?) ?? + ?.content[ToolSetting.interactiveGrammar.toString()] + as bool?) ?? true, - autoIGC: (accountData[ToolSetting.autoIGC.toString()] - ?.content[ToolSetting.autoIGC.toString()] as bool?) ?? + immersionMode: false, + definitions: + (accountData[ToolSetting.definitions.toString()]?.content[ToolSetting + .definitions + .toString()] + as bool?) ?? + true, + autoIGC: + (accountData[ToolSetting.autoIGC.toString()]?.content[ToolSetting + .autoIGC + .toString()] + as bool?) ?? true, ); } @@ -265,14 +271,14 @@ class UserToolSettings { @override int get hashCode => Object.hashAll([ - interactiveTranslator.hashCode, - interactiveGrammar.hashCode, - immersionMode.hashCode, - definitions.hashCode, - autoIGC.hashCode, - enableTTS.hashCode, - enableAutocorrect.hashCode, - ]); + interactiveTranslator.hashCode, + interactiveGrammar.hashCode, + immersionMode.hashCode, + definitions.hashCode, + autoIGC.hashCode, + enableTTS.hashCode, + enableAutocorrect.hashCode, + ]); } /// A wrapper around the matrix account data for the user profile. @@ -303,8 +309,9 @@ class Profile { profileData[ModelKey.instructionsSettings]; return Profile( - userSettings: - UserSettings.fromJson(userSettingsContent as Map), + userSettings: UserSettings.fromJson( + userSettingsContent as Map, + ), toolSettings: toolSettingsContent != null ? UserToolSettings.fromJson( toolSettingsContent as Map, @@ -345,9 +352,7 @@ class Profile { /// Saves the current configuration of the profile to the client's account data. /// If [waitForDataInSync] is true, the function will wait for the updated account /// data to come through in a sync, indicating that it has been set on the matrix server. - Future saveProfileData({ - waitForDataInSync = false, - }) async { + Future saveProfileData({bool waitForDataInSync = false}) async { final PangeaController pangeaController = MatrixState.pangeaController; final Client client = pangeaController.matrixState.client; final List profileKeys = [ @@ -366,11 +371,7 @@ class Profile { ), ); } - await client.setAccountData( - client.userID!, - ModelKey.userProfile, - toJson(), - ); + await client.setAccountData(client.userID!, ModelKey.userProfile, toJson()); if (waitForDataInSync) { await waitForUpdate; @@ -433,12 +434,8 @@ class PangeaProfile { }); factory PangeaProfile.fromJson(Map json) { - final l2 = LanguageModel.codeFromNameOrCode( - json[ModelKey.targetLanguage], - ); - final l1 = LanguageModel.codeFromNameOrCode( - json[ModelKey.sourceLanguage], - ); + final l2 = LanguageModel.codeFromNameOrCode(json[ModelKey.targetLanguage]); + final l1 = LanguageModel.codeFromNameOrCode(json[ModelKey.sourceLanguage]); return PangeaProfile( createdAt: json[ModelKey.userCreatedAt], @@ -468,10 +465,7 @@ class PangeaProfileResponse { final PangeaProfile profile; final String access; - PangeaProfileResponse({ - required this.profile, - required this.access, - }); + PangeaProfileResponse({required this.profile, required this.access}); factory PangeaProfileResponse.fromJson(Map json) { return PangeaProfileResponse( diff --git a/lib/utils/account_bundles.dart b/lib/utils/account_bundles.dart index 8bd296e05..7d5619267 100644 --- a/lib/utils/account_bundles.dart +++ b/lib/utils/account_bundles.dart @@ -7,9 +7,9 @@ class AccountBundles { AccountBundles({this.prefix, this.bundles}); AccountBundles.fromJson(Map json) - : prefix = json.tryGet('prefix'), - bundles = json['bundles'] is List - ? json['bundles'] + : prefix = json.tryGet('prefix'), + bundles = json['bundles'] is List + ? json['bundles'] .map((b) { try { return AccountBundle.fromJson(b); @@ -19,13 +19,12 @@ class AccountBundles { }) .whereType() .toList() - : null; + : null; Map toJson() => { - if (prefix != null) 'prefix': prefix, - if (bundles != null) - 'bundles': bundles!.map((v) => v.toJson()).toList(), - }; + if (prefix != null) 'prefix': prefix, + if (bundles != null) 'bundles': bundles!.map((v) => v.toJson()).toList(), + }; } class AccountBundle { @@ -35,13 +34,13 @@ class AccountBundle { AccountBundle({this.name, this.priority}); AccountBundle.fromJson(Map json) - : name = json.tryGet('name'), - priority = json.tryGet('priority'); + : name = json.tryGet('name'), + priority = json.tryGet('priority'); Map toJson() => { - if (name != null) 'name': name, - if (priority != null) 'priority': priority, - }; + if (name != null) 'name': name, + if (priority != null) 'priority': priority, + }; } const accountBundlesType = 'im.fluffychat.account_bundles'; @@ -50,24 +49,21 @@ extension AccountBundlesExtension on Client { List get accountBundles { List? ret; if (accountData.containsKey(accountBundlesType)) { - ret = AccountBundles.fromJson(accountData[accountBundlesType]!.content) - .bundles; + ret = AccountBundles.fromJson( + accountData[accountBundlesType]!.content, + ).bundles; } ret ??= []; if (ret.isEmpty) { - ret.add( - AccountBundle( - name: userID, - priority: 0, - ), - ); + ret.add(AccountBundle(name: userID, priority: 0)); } return ret; } Future setAccountBundle(String name, [int? priority]) async { - final data = - AccountBundles.fromJson(accountData[accountBundlesType]?.content ?? {}); + final data = AccountBundles.fromJson( + accountData[accountBundlesType]?.content ?? {}, + ); var foundBundle = false; final bundles = data.bundles ??= []; for (final bundle in bundles) { @@ -87,16 +83,18 @@ extension AccountBundlesExtension on Client { if (!accountData.containsKey(accountBundlesType)) { return; // nothing to do } - final data = - AccountBundles.fromJson(accountData[accountBundlesType]!.content); + final data = AccountBundles.fromJson( + accountData[accountBundlesType]!.content, + ); if (data.bundles == null) return; data.bundles!.removeWhere((b) => b.name == name); await setAccountData(userID!, accountBundlesType, data.toJson()); } String get sendPrefix { - final data = - AccountBundles.fromJson(accountData[accountBundlesType]?.content ?? {}); + final data = AccountBundles.fromJson( + accountData[accountBundlesType]?.content ?? {}, + ); return data.prefix!; } } diff --git a/lib/utils/account_config.dart b/lib/utils/account_config.dart index a5e2dfd00..7ac107376 100644 --- a/lib/utils/account_config.dart +++ b/lib/utils/account_config.dart @@ -8,19 +8,11 @@ extension ApplicationAccountConfigExtension on Client { accountData[accountDataKey]?.content ?? {}, ); - Future setApplicationAccountConfig( - ApplicationAccountConfig config, - ) => - setAccountData( - userID!, - accountDataKey, - config.toJson(), - ); + Future setApplicationAccountConfig(ApplicationAccountConfig config) => + setAccountData(userID!, accountDataKey, config.toJson()); /// Only updates the specified values in ApplicationAccountConfig - Future updateApplicationAccountConfig( - ApplicationAccountConfig config, - ) { + Future updateApplicationAccountConfig(ApplicationAccountConfig config) { final currentConfig = applicationAccountConfig; return setAccountData( userID!, @@ -57,14 +49,15 @@ class ApplicationAccountConfig { wallpaperUrl: json['wallpaper_url'] is String ? Uri.tryParse(json['wallpaper_url']) : null, - wallpaperOpacity: - _sanitizedOpacity(json.tryGet('wallpaper_opacity')), + wallpaperOpacity: _sanitizedOpacity( + json.tryGet('wallpaper_opacity'), + ), wallpaperBlur: json.tryGet('wallpaper_blur'), ); Map toJson() => { - 'wallpaper_url': wallpaperUrl?.toString(), - 'wallpaper_opacity': wallpaperOpacity, - 'wallpaper_blur': wallpaperBlur, - }; + 'wallpaper_url': wallpaperUrl?.toString(), + 'wallpaper_opacity': wallpaperOpacity, + 'wallpaper_blur': wallpaperBlur, + }; } diff --git a/lib/utils/adaptive_bottom_sheet.dart b/lib/utils/adaptive_bottom_sheet.dart index faa7f5ce8..a0e187d3e 100644 --- a/lib/utils/adaptive_bottom_sheet.dart +++ b/lib/utils/adaptive_bottom_sheet.dart @@ -21,10 +21,7 @@ Future showAdaptiveBottomSheet({ builder: (context) => Center( child: Container( margin: const EdgeInsets.all(16), - constraints: const BoxConstraints( - maxWidth: 480, - maxHeight: 720, - ), + constraints: const BoxConstraints(maxWidth: 480, maxHeight: 720), child: Material( elevation: Theme.of(context).dialogTheme.elevation ?? 4, shadowColor: Theme.of(context).dialogTheme.shadowColor, @@ -40,24 +37,18 @@ Future showAdaptiveBottomSheet({ return showModalBottomSheet( context: context, - builder: (context) => Padding( - padding: EdgeInsets.zero, - child: ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(AppConfig.borderRadius / 2), - topRight: Radius.circular(AppConfig.borderRadius / 2), - ), - child: builder(context), + builder: (context) => ConstrainedBox( + constraints: BoxConstraints( + maxHeight: + MediaQuery.viewInsetsOf(context).bottom + + min(MediaQuery.sizeOf(context).height - 32, 600), ), + child: builder(context), ), + useSafeArea: true, useRootNavigator: useRootNavigator, isDismissible: isDismissible, isScrollControlled: isScrollControlled, - constraints: BoxConstraints( - maxHeight: min(MediaQuery.of(context).size.height - 32, 600), - maxWidth: FluffyThemes.columnWidth * 1.25, - ), - backgroundColor: Colors.transparent, clipBehavior: Clip.hardEdge, ); } diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 98d1902f3..4cd7cde8f 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -20,6 +20,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; +import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -33,8 +35,10 @@ import 'package:unifiedpush/unifiedpush.dart'; import 'package:unifiedpush_ui/unifiedpush_ui.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/main.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/languages/language_constants.dart'; +import 'package:fluffychat/utils/notification_background_handler.dart'; import 'package:fluffychat/utils/push_helper.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart'; import '../config/app_config.dart'; @@ -61,11 +65,13 @@ class BackgroundPush { Future loadLocale() async { final context = matrix?.context; // inspired by _lookupL10n in .dart_tool/flutter_gen/gen_l10n/l10n.dart - l10n ??= (context != null ? L10n.of(context) : null) ?? + l10n ??= + (context != null ? L10n.of(context) : null) ?? (await L10n.delegate.load(PlatformDispatcher.instance.locale)); } final pendingTests = >{}; + bool firebaseEnabled = false; //final firebase = FcmSharedIsolate(); @@ -74,12 +80,45 @@ class BackgroundPush { bool upAction = false; void _init() async { + //firebaseEnabled = true; try { // #Pangea // Handle notifications when app is opened from terminated/background state FirebaseMessaging.instance.getInitialMessage().then(_onOpenNotification); FirebaseMessaging.onMessageOpenedApp.listen(_onOpenNotification); // Pangea# + mainIsolateReceivePort?.listen((message) async { + try { + await notificationTap( + NotificationResponseJson.fromJsonString(message), + client: client, + router: FluffyChatApp.router, + l10n: l10n, + ); + } catch (e, s) { + Logs().wtf('Main Notification Tap crashed', e, s); + } + }); + if (PlatformInfos.isAndroid) { + final port = ReceivePort(); + IsolateNameServer.removePortNameMapping('background_tab_port'); + IsolateNameServer.registerPortWithName( + port.sendPort, + 'background_tab_port', + ); + port.listen((message) async { + try { + await notificationTap( + NotificationResponseJson.fromJsonString(message), + client: client, + router: FluffyChatApp.router, + l10n: l10n, + ); + } catch (e, s) { + Logs().wtf('Main Notification Tap crashed', e, s); + } + }); + } await _flutterLocalNotificationsPlugin.initialize( const InitializationSettings( // #Pangea @@ -94,7 +133,13 @@ class BackgroundPush { ), // Pangea# ), - onDidReceiveNotificationResponse: goToRoom, + onDidReceiveNotificationResponse: (response) => notificationTap( + response, + client: client, + router: FluffyChatApp.router, + l10n: l10n, + ), + onDidReceiveBackgroundNotificationResponse: notificationTapBackground, ); // #Pangea @@ -135,7 +180,7 @@ class BackgroundPush { if (Platform.isAndroid) { await UnifiedPush.initialize( onNewEndpoint: _newUpEndpoint, - onRegistrationFailed: _upUnregistered, + onRegistrationFailed: (_, i) => _upUnregistered(i), onUnregistered: _upUnregistered, onMessage: _onUpMessage, ); @@ -272,7 +317,7 @@ class BackgroundPush { }) async { // #Pangea // if (PlatformInfos.isIOS) { - // await firebase.requestPermission(); + // //await firebase.requestPermission(); // } // if (PlatformInfos.isAndroid) { // _flutterLocalNotificationsPlugin @@ -284,7 +329,8 @@ class BackgroundPush { // Pangea# final clientName = PlatformInfos.clientName; oldTokens ??= {}; - final pushers = await (client.getPushers().catchError((e) { + final pushers = + await (client.getPushers().catchError((e) { Logs().w('[Push] Unable to request pushers', e); return []; })) ?? @@ -316,14 +362,12 @@ class BackgroundPush { currentPushers.first.data.url.toString() == gatewayUrl && currentPushers.first.data.format == // #Pangea - // AppSettings.pushNotificationsPusherFormat - // .getItem(matrix!.store) && + // AppSettings.pushNotificationsPusherFormat.value && null && // Pangea# - mapEquals( - currentPushers.single.data.additionalProperties, - {"data_message": pusherDataMessageFormat}, - )) { + mapEquals(currentPushers.single.data.additionalProperties, { + "data_message": pusherDataMessageFormat, + })) { Logs().i('[Push] Pusher already set'); } else { Logs().i('Need to set new pusher'); @@ -363,8 +407,7 @@ class BackgroundPush { data: PusherData( url: Uri.parse(gatewayUrl!), // #Pangea - // format: AppSettings.pushNotificationsPusherFormat - // .getItem(matrix!.store), + // format: AppSettings.pushNotificationsPusherFormat.value, // Pangea# additionalProperties: {"data_message": pusherDataMessageFormat}, ), @@ -375,11 +418,7 @@ class BackgroundPush { } catch (e, s) { Logs().e('[Push] Unable to set pushers', e, s); // #Pangea - ErrorHandler.logError( - e: e, - s: s, - data: {}, - ); + ErrorHandler.logError(e: e, s: s, data: {}); // Pangea# } } @@ -388,8 +427,8 @@ class BackgroundPush { final pusherDataMessageFormat = Platform.isAndroid ? 'android' : Platform.isIOS - ? 'ios' - : null; + ? 'ios' + : null; static bool _wentToRoomOnStartup = false; @@ -413,16 +452,24 @@ class BackgroundPush { } // ignore: unawaited_futures - _flutterLocalNotificationsPlugin - .getNotificationAppLaunchDetails() - .then((details) { + _flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails().then(( + details, + ) { if (details == null || !details.didNotificationLaunchApp || _wentToRoomOnStartup) { return; } _wentToRoomOnStartup = true; - goToRoom(details.notificationResponse); + final response = details.notificationResponse; + if (response != null) { + notificationTap( + response, + client: client, + router: FluffyChatApp.router, + l10n: l10n, + ); + } }); } @@ -430,7 +477,7 @@ class BackgroundPush { if (matrix == null) { return; } - if ((matrix?.store.getBool(SettingKeys.showNoGoogle) ?? false) == true) { + if (AppSettings.showNoGoogle.value) { return; } await loadLocale(); @@ -438,9 +485,7 @@ class BackgroundPush { if (PlatformInfos.isAndroid) { onFcmError?.call( l10n!.noGoogleServicesWarning, - link: Uri.parse( - AppConfig.enablePushTutorial, - ), + link: Uri.parse(AppConfig.enablePushTutorial), ); return; } @@ -461,66 +506,23 @@ class BackgroundPush { } } await setupPusher( - gatewayUrl: - AppSettings.pushNotificationsGatewayUrl.getItem(matrix!.store), + gatewayUrl: AppSettings.pushNotificationsGatewayUrl.value, token: _fcmToken, ); } - Future goToRoom(NotificationResponse? response) async { - try { - final payload = response?.payload; - Logs().v('[Push] Attempting to go to room with payload: $payload'); - if (payload == null) { - return; - } - - // #Pangea - Handle activity session data if present - String? roomId; - String? sessionRoomId; - String? activityId; - - try { - final payloadData = jsonDecode(payload) as Map; - roomId = payloadData['room_id'] as String?; - sessionRoomId = - payloadData['content_pangea.activity.session_room_id'] as String?; - activityId = payloadData['content_pangea.activity.id'] as String?; - } catch (_) { - // If payload is not JSON, treat it as a simple room ID - roomId = payload; - } - - if (roomId == null || roomId.isEmpty) { - return; - } - - await _navigateToActivityOrRoom( - roomId: roomId, - sessionRoomId: sessionRoomId, - activityId: activityId, - ); - // Pangea# - } catch (e, s) { - Logs().e('[Push] Failed to open room', e, s); - // #Pangea - ErrorHandler.logError( - e: e, - s: s, - data: { - "roomID": response?.payload, - }, - ); - // Pangea# - } - } - Future setupUp() async { - await UnifiedPushUi(matrix!.context, ["default"], UPFunctions()) - .registerAppWithDialog(); + await UnifiedPushUi( + context: matrix!.context, + instances: ["default"], + unifiedPushFunctions: UPFunctions(), + showNoDistribDialog: false, + onNoDistribDialogDismissed: () {}, // TODO: Implement me + ).registerAppWithDialog(); } - Future _newUpEndpoint(String newEndpoint, String i) async { + Future _newUpEndpoint(PushEndpoint newPushEndpoint, String i) async { + final newEndpoint = newPushEndpoint.url; upAction = true; if (newEndpoint.isEmpty) { await _upUnregistered(i); @@ -530,15 +532,13 @@ class BackgroundPush { 'https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify'; try { final url = Uri.parse(newEndpoint) - .replace( - path: '/_matrix/push/v1/notify', - query: '', - ) + .replace(path: '/_matrix/push/v1/notify', query: '') .toString() .split('?') .first; - final res = - json.decode(utf8.decode((await http.get(Uri.parse(url))).bodyBytes)); + final res = json.decode( + utf8.decode((await http.get(Uri.parse(url))).bodyBytes), + ); if (res['gateway'] == 'matrix' || (res['unifiedpush'] is Map && res['unifiedpush']['gateway'] == 'matrix')) { @@ -561,26 +561,27 @@ class BackgroundPush { oldTokens: oldTokens, useDeviceSpecificAppId: true, ); - await matrix?.store.setString(SettingKeys.unifiedPushEndpoint, newEndpoint); - await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, true); + await AppSettings.unifiedPushEndpoint.setItem(newEndpoint); + await AppSettings.unifiedPushRegistered.setItem(true); } Future _upUnregistered(String i) async { upAction = true; Logs().i('[Push] Removing UnifiedPush endpoint...'); - final oldEndpoint = - matrix?.store.getString(SettingKeys.unifiedPushEndpoint); - await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, false); - await matrix?.store.remove(SettingKeys.unifiedPushEndpoint); - if (oldEndpoint?.isNotEmpty ?? false) { + final oldEndpoint = AppSettings.unifiedPushEndpoint.value; + await AppSettings.unifiedPushEndpoint.setItem( + AppSettings.unifiedPushEndpoint.defaultValue, + ); + await AppSettings.unifiedPushRegistered.setItem(false); + if (oldEndpoint.isNotEmpty) { // remove the old pusher - await setupPusher( - oldTokens: {oldEndpoint}, - ); + await setupPusher(oldTokens: {oldEndpoint}); } } - Future _onUpMessage(Uint8List message, String i) async { + Future _onUpMessage(PushMessage pushMessage, String i) async { + Logs().wtf('Push Notification from UP received', pushMessage); + final message = pushMessage.content; upAction = true; final data = Map.from( json.decode(utf8.decode(message))['notification'], @@ -593,6 +594,8 @@ class BackgroundPush { l10n: l10n, activeRoomId: matrix?.activeRoomId, flutterLocalNotificationsPlugin: _flutterLocalNotificationsPlugin, + useNotificationActions: + false, // Buggy with UP: https://codeberg.org/UnifiedPush/flutter-connector/issues/34 ); } } @@ -614,7 +617,7 @@ class UPFunctions extends UnifiedPushFunctions { @override Future registerApp(String instance) async { - await UnifiedPush.registerApp(instance, features); + await UnifiedPush.register(instance: instance, features: features); } @override diff --git a/lib/utils/client_download_content_extension.dart b/lib/utils/client_download_content_extension.dart index 65e7e023c..b93357a0f 100644 --- a/lib/utils/client_download_content_extension.dart +++ b/lib/utils/client_download_content_extension.dart @@ -1,6 +1,7 @@ +import 'dart:math' show min; import 'dart:typed_data'; +import 'dart:ui'; -import 'package:image/image.dart'; import 'package:matrix/matrix.dart'; extension ClientDownloadContentExtension on Client { @@ -40,8 +41,9 @@ extension ClientDownloadContentExtension on Client { final response = await httpClient.get( httpUri, - headers: - accessToken == null ? null : {'authorization': 'Bearer $accessToken'}, + headers: accessToken == null + ? null + : {'authorization': 'Bearer $accessToken'}, ); if (response.statusCode != 200) { throw Exception(); @@ -49,10 +51,10 @@ extension ClientDownloadContentExtension on Client { var imageData = response.bodyBytes; if (rounded) { - final image = decodeImage(imageData); - if (image != null) { - imageData = encodePng(copyCropCircle(image)); - } + imageData = await _convertToCircularImage( + imageData, + min(width ?? 64, height ?? 64).round(), + ); } await database.storeFile(cacheKey, imageData, 0); @@ -60,3 +62,43 @@ extension ClientDownloadContentExtension on Client { return imageData; } } + +Future _convertToCircularImage( + Uint8List imageBytes, + int size, +) async { + final codec = await instantiateImageCodec(imageBytes); + final frame = await codec.getNextFrame(); + final originalImage = frame.image; + + final recorder = PictureRecorder(); + final canvas = Canvas(recorder); + + final paint = Paint(); + final rect = Rect.fromLTWH(0, 0, size.toDouble(), size.toDouble()); + + final clipPath = Path() + ..addOval( + Rect.fromCircle(center: Offset(size / 2, size / 2), radius: size / 2), + ); + + canvas.clipPath(clipPath); + + canvas.drawImageRect( + originalImage, + Rect.fromLTWH( + 0, + 0, + originalImage.width.toDouble(), + originalImage.height.toDouble(), + ), + rect, + paint, + ); + + final picture = recorder.endRecording(); + final circularImage = await picture.toImage(size, size); + + final byteData = await circularImage.toByteData(format: ImageByteFormat.png); + return byteData!.buffer.asUint8List(); +} diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index b64c855e7..94c76e676 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -6,19 +6,15 @@ import 'package:collection/collection.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod; -import 'package:hive_flutter/hive_flutter.dart'; import 'package:matrix/encryption/utils/key_verification.dart'; import 'package:matrix/matrix.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:universal_html/html.dart' as html; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/utils/custom_http_client.dart'; -import 'package:fluffychat/utils/custom_image_resizer.dart'; import 'package:fluffychat/utils/init_with_restore.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart'; @@ -30,14 +26,6 @@ abstract class ClientManager { bool initialize = true, required SharedPreferences store, }) async { - Logs().i('Getting clients from store'); - - if (PlatformInfos.isLinux) { - Hive.init((await getApplicationSupportDirectory()).path); - } else { - await Hive.initFlutter(); - } - final clientNames = {}; try { final clientNamesList = store.getStringList(clientNamespace) ?? []; @@ -54,24 +42,29 @@ abstract class ClientManager { clientNames.add(PlatformInfos.clientName); await store.setStringList(clientNamespace, clientNames.toList()); } - final clients = - await Future.wait(clientNames.map((name) => createClient(name, store))); + final clients = await Future.wait( + clientNames.map((name) => createClient(name, store)), + ); if (initialize) { await Future.wait( clients.map( - (client) => client.initWithRestore( - onMigration: () async { - // #Pangea - // final l10n = await lookupL10n(PlatformDispatcher.instance.locale); - // sendInitNotification( - // l10n.databaseMigrationTitle, - // l10n.databaseMigrationBody, - // ); - // Pangea# - }, - ).catchError( - (e, s) => Logs().e('Unable to initialize client', e, s), - ), + (client) => client + .initWithRestore( + onMigration: () async { + // #Pangea + // final l10n = await lookupL10n( + // PlatformDispatcher.instance.locale, + // ); + // sendInitNotification( + // l10n.databaseMigrationTitle, + // l10n.databaseMigrationBody, + // ); + // Pangea# + }, + ) + .catchError( + (e, s) => Logs().e('Unable to initialize client', e, s), + ), ), ); } @@ -109,7 +102,10 @@ abstract class ClientManager { } static NativeImplementations get nativeImplementations => kIsWeb - ? const NativeImplementationsDummy() + ? NativeImplementationsWebWorker( + Uri.parse('native_executor.js'), + timeout: const Duration(minutes: 1), + ) : NativeImplementationsIsolate( compute, vodozemacInit: () => vod.init(wasmPath: './assets/assets/vodozemac/'), @@ -119,13 +115,12 @@ abstract class ClientManager { String clientName, SharedPreferences store, ) async { - final shareKeysWith = AppSettings.shareKeysWith.getItem(store); - final enableSoftLogout = AppSettings.enableSoftLogout.getItem(store); + final shareKeysWith = AppSettings.shareKeysWith.value; + final enableSoftLogout = AppSettings.enableSoftLogout.value; return Client( clientName, - httpClient: - PlatformInfos.isAndroid ? CustomHttpClient.createHTTPClient() : null, + httpClient: CustomHttpClient.createHTTPClient(), verificationMethods: { KeyVerificationMethod.numbers, if (kIsWeb || PlatformInfos.isMobile || PlatformInfos.isLinux) @@ -162,15 +157,17 @@ abstract class ClientManager { AuthenticationTypes.sso, }, nativeImplementations: nativeImplementations, - customImageResizer: PlatformInfos.isMobile ? customImageResizer : null, defaultNetworkRequestTimeout: const Duration(minutes: 30), enableDehydratedDevices: true, - shareKeysWith: ShareKeysWith.values - .singleWhereOrNull((share) => share.name == shareKeysWith) ?? + shareKeysWith: + ShareKeysWith.values.singleWhereOrNull( + (share) => share.name == shareKeysWith, + ) ?? ShareKeysWith.all, convertLinebreaksInFormatting: false, - onSoftLogout: - enableSoftLogout ? (client) => client.refreshAccessToken() : null, + onSoftLogout: enableSoftLogout + ? (client) => client.refreshAccessToken() + : null, // #Pangea shouldReplaceRoomLastEvent: (_, event) { return event.content.tryGet(ModelKey.transcription) == null && @@ -186,20 +183,15 @@ abstract class ClientManager { static void sendInitNotification(String title, String body) async { if (kIsWeb) { - html.Notification( - title, - body: body, - ); + html.Notification(title, body: body); return; } if (Platform.isLinux) { await NotificationsClient().notify( title, body: body, - appName: AppConfig.applicationName, - hints: [ - NotificationHint.soundName('message-new-instant'), - ], + appName: AppSettings.applicationName.value, + hints: [NotificationHint.soundName('message-new-instant')], ); return; } diff --git a/lib/utils/code_highlight_theme.dart b/lib/utils/code_highlight_theme.dart new file mode 100644 index 000000000..ac3a19144 --- /dev/null +++ b/lib/utils/code_highlight_theme.dart @@ -0,0 +1,40 @@ +import 'package:flutter/widgets.dart'; + +const hightlightTextColor = Color(0xffabb2bf); +const atomOneBackgroundColor = Color(0xff282c34); +const atomOneDarkTheme = { + 'root': TextStyle(color: hightlightTextColor), + 'comment': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), + 'quote': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), + 'doctag': TextStyle(color: Color(0xffc678dd)), + 'keyword': TextStyle(color: Color(0xffc678dd)), + 'formula': TextStyle(color: Color(0xffc678dd)), + 'section': TextStyle(color: Color(0xffe06c75)), + 'name': TextStyle(color: Color(0xffe06c75)), + 'selector-tag': TextStyle(color: Color(0xffe06c75)), + 'deletion': TextStyle(color: Color(0xffe06c75)), + 'subst': TextStyle(color: Color(0xffe06c75)), + 'literal': TextStyle(color: Color(0xff56b6c2)), + 'string': TextStyle(color: Color(0xff98c379)), + 'regexp': TextStyle(color: Color(0xff98c379)), + 'addition': TextStyle(color: Color(0xff98c379)), + 'attribute': TextStyle(color: Color(0xff98c379)), + 'meta-string': TextStyle(color: Color(0xff98c379)), + 'built_in': TextStyle(color: Color(0xffe6c07b)), + 'attr': TextStyle(color: Color(0xffd19a66)), + 'variable': TextStyle(color: Color(0xffd19a66)), + 'template-variable': TextStyle(color: Color(0xffd19a66)), + 'type': TextStyle(color: Color(0xffd19a66)), + 'selector-class': TextStyle(color: Color(0xffd19a66)), + 'selector-attr': TextStyle(color: Color(0xffd19a66)), + 'selector-pseudo': TextStyle(color: Color(0xffd19a66)), + 'number': TextStyle(color: Color(0xffd19a66)), + 'symbol': TextStyle(color: Color(0xff61aeee)), + 'bullet': TextStyle(color: Color(0xff61aeee)), + 'link': TextStyle(color: Color(0xff61aeee)), + 'meta': TextStyle(color: Color(0xff61aeee)), + 'selector-id': TextStyle(color: Color(0xff61aeee)), + 'title': TextStyle(color: Color(0xff61aeee)), + 'emphasis': TextStyle(fontStyle: FontStyle.italic), + 'strong': TextStyle(fontWeight: FontWeight.bold), +}; diff --git a/lib/utils/custom_http_client.dart b/lib/utils/custom_http_client.dart index 479e5edb5..66b1dcad2 100644 --- a/lib/utils/custom_http_client.dart +++ b/lib/utils/custom_http_client.dart @@ -3,9 +3,15 @@ import 'dart:io'; import 'package:http/http.dart' as http; import 'package:http/io_client.dart'; +import 'package:http/retry.dart' as retry; import 'package:fluffychat/config/isrg_x1.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; +/// Custom Client to add an additional certificate. This is for the isrg X1 +/// certificate which is needed for LetsEncrypt certificates. It is shipped +/// on Android since OS version 7.1. As long as we support older versions we +/// still have to ship this certificate by ourself. class CustomHttpClient { static HttpClient customHttpClient(String? cert) { final context = SecurityContext.defaultContext; @@ -26,5 +32,9 @@ class CustomHttpClient { return HttpClient(context: context); } - static http.Client createHTTPClient() => IOClient(customHttpClient(ISRG_X1)); + static http.Client createHTTPClient() => retry.RetryClient( + PlatformInfos.isAndroid + ? IOClient(customHttpClient(ISRG_X1)) + : http.Client(), + ); } diff --git a/lib/utils/custom_image_resizer.dart b/lib/utils/custom_image_resizer.dart deleted file mode 100644 index f783d5cac..000000000 --- a/lib/utils/custom_image_resizer.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/painting.dart'; - -import 'package:matrix/matrix.dart'; -import 'package:native_imaging/native_imaging.dart' as native; - -(int, int) _scaleToBox(int width, int height, {required int boxSize}) { - final fit = applyBoxFit( - BoxFit.scaleDown, - Size(width.toDouble(), height.toDouble()), - Size(boxSize.toDouble(), boxSize.toDouble()), - ).destination; - return (fit.width.round(), fit.height.round()); -} - -Future customImageResizer( - MatrixImageFileResizeArguments arguments, -) async { - if (kIsWeb) { - throw UnsupportedError( - 'customImageResizer only supports non-web platforms.', - ); - } - - await native.init(); - - var imageBytes = arguments.bytes; - String? blurhash; - - var originalWidth = 0; - var originalHeight = 0; - var width = 0; - var height = 0; - - try { - // for the other platforms - final dartCodec = await instantiateImageCodec(arguments.bytes); - final frameCount = dartCodec.frameCount; - final dartFrame = await dartCodec.getNextFrame(); - final rgbaData = await dartFrame.image.toByteData(); - if (rgbaData == null) { - return null; - } - final rgba = Uint8List.view( - rgbaData.buffer, - rgbaData.offsetInBytes, - rgbaData.lengthInBytes, - ); - - width = originalWidth = dartFrame.image.width; - height = originalHeight = dartFrame.image.height; - - var nativeImg = native.Image.fromRGBA(width, height, rgba); - - dartFrame.image.dispose(); - dartCodec.dispose(); - - if (arguments.calcBlurhash) { - // scale down image for blurhashing to speed it up - final (blurW, blurH) = _scaleToBox(width, height, boxSize: 100); - final blurhashImg = nativeImg.resample( - blurW, blurH, - // nearest is unsupported... - native.Transform.bilinear, - ); - - blurhash = blurhashImg.toBlurhash(3, 3); - - blurhashImg.free(); - } - - if (frameCount > 1) { - // Don't scale down animated images, since those would lose frames. - nativeImg.free(); - } else { - final max = arguments.maxDimension; - if (width > max || height > max) { - (width, height) = _scaleToBox(width, height, boxSize: max); - - final scaledImg = - nativeImg.resample(width, height, native.Transform.lanczos); - nativeImg.free(); - nativeImg = scaledImg; - } - - imageBytes = await nativeImg.toJpeg(75); - nativeImg.free(); - } - } catch (e, s) { - Logs().e("Could not generate preview", e, s); - } - - return MatrixImageFileResizedResponse( - bytes: imageBytes, - width: width, - height: height, - originalWidth: originalWidth, - originalHeight: originalHeight, - blurhash: blurhash, - ); -} diff --git a/lib/utils/custom_scroll_behaviour.dart b/lib/utils/custom_scroll_behaviour.dart index ba25e9564..32baa332c 100644 --- a/lib/utils/custom_scroll_behaviour.dart +++ b/lib/utils/custom_scroll_behaviour.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; class CustomScrollBehavior extends MaterialScrollBehavior { @override Set get dragDevices => { - PointerDeviceKind.touch, - PointerDeviceKind.trackpad, - }; + PointerDeviceKind.touch, + PointerDeviceKind.trackpad, + }; } diff --git a/lib/utils/date_time_extension.dart b/lib/utils/date_time_extension.dart index dcd4aa851..508f13e29 100644 --- a/lib/utils/date_time_extension.dart +++ b/lib/utils/date_time_extension.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; /// Provides extra functionality for formatting the time. extension DateTimeExtension on DateTime { @@ -28,10 +29,9 @@ extension DateTimeExtension on DateTime { difference(prevTime) < const Duration(hours: 1); /// Returns a simple time String. - String localizedTimeOfDay(BuildContext context) => - L10n.of(context).alwaysUse24HourFormat == 'true' - ? DateFormat('HH:mm', L10n.of(context).localeName).format(this) - : DateFormat('h:mm a', L10n.of(context).localeName).format(this); + String localizedTimeOfDay(BuildContext context) => use24HourFormat(context) + ? DateFormat('HH:mm', L10n.of(context).localeName).format(this) + : DateFormat('h:mm a', L10n.of(context).localeName).format(this); /// Returns [localizedTimeOfDay()] if the ChatTime is today, the name of the week /// day if the ChatTime is this week and a date string else. @@ -42,7 +42,8 @@ extension DateTimeExtension on DateTime { final sameDay = sameYear && now.month == month && now.day == day; - final sameWeek = sameYear && + final sameWeek = + sameYear && !sameDay && now.millisecondsSinceEpoch - millisecondsSinceEpoch < 1000 * 60 * 60 * 24 * 7; @@ -50,14 +51,17 @@ extension DateTimeExtension on DateTime { if (sameDay) { return localizedTimeOfDay(context); } else if (sameWeek) { - return DateFormat.E(Localizations.localeOf(context).languageCode) - .format(this); + return DateFormat.E( + Localizations.localeOf(context).languageCode, + ).format(this); } else if (sameYear) { - return DateFormat.MMMd(Localizations.localeOf(context).languageCode) - .format(this); + return DateFormat.MMMd( + Localizations.localeOf(context).languageCode, + ).format(this); } - return DateFormat.yMMMd(Localizations.localeOf(context).languageCode) - .format(this); + return DateFormat.yMMMd( + Localizations.localeOf(context).languageCode, + ).format(this); } /// If the DateTime is today, this returns [localizedTimeOfDay()], if not it also @@ -76,4 +80,20 @@ extension DateTimeExtension on DateTime { localizedTimeOfDay(context), ); } + + /// Check if time needs to be in 24h format + bool use24HourFormat(BuildContext context) { + final mediaQuery24h = MediaQuery.alwaysUse24HourFormatOf(context); + + final l10n24h = L10n.of(context).alwaysUse24HourFormat == 'true'; + + // https://github.com/krille-chan/fluffychat/pull/1457#discussion_r1836817914 + if (PlatformInfos.isAndroid) { + return mediaQuery24h; + } else if (PlatformInfos.isIOS) { + return mediaQuery24h || l10n24h; + } + + return l10n24h; + } } diff --git a/lib/utils/error_reporter.dart b/lib/utils/error_reporter.dart index d529292da..bb402a5ad 100644 --- a/lib/utils/error_reporter.dart +++ b/lib/utils/error_reporter.dart @@ -6,18 +6,30 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; class ErrorReporter { - final BuildContext context; + final BuildContext? context; final String? message; const ErrorReporter(this.context, [this.message]); - void onErrorCallback(Object error, [StackTrace? stackTrace]) async { + static const Set ingoredTypes = { + "IOException", + "ClientException", + "SocketException", + "TlsException", + "HandshakeException", + }; + + void onErrorCallback(Object error, [StackTrace? stackTrace]) { + if (ingoredTypes.contains(error.runtimeType.toString())) return; Logs().e(message ?? 'Error caught', error, stackTrace); // #Pangea + // final text = '$error\n${stackTrace ?? ''}'; + // return _onErrorCallback(text); + if (context == null) return; try { // Attempt to retrieve the L10n instance using the current context - final L10n l10n = L10n.of(context); - ScaffoldMessenger.of(context).showSnackBar( + final L10n l10n = L10n.of(context!); + ScaffoldMessenger.of(context!).showSnackBar( SnackBar( content: Text( l10n.oopsSomethingWentWrong, // Use the non-null L10n instance to get the error message @@ -37,20 +49,22 @@ class ErrorReporter { data: {}, ); } + // Pangea# } - // final text = '$error\n${stackTrace ?? ''}'; + + // #Pangea + // void _onErrorCallback(String text) async { // await showAdaptiveDialog( - // context: context, + // context: context!, // builder: (context) => AlertDialog.adaptive( // title: Text(L10n.of(context).reportErrorDescription), // content: SizedBox( // height: 256, // width: 256, // child: SingleChildScrollView( - // child: HighlightView( + // child: Text( // text, - // language: 'sh', - // theme: shadesOfPurpleTheme, + // style: const TextStyle(fontSize: 14, fontFamily: 'RobotoMono'), // ), // ), // ), @@ -60,9 +74,7 @@ class ErrorReporter { // child: Text(L10n.of(context).close), // ), // AdaptiveDialogAction( - // onPressed: () => Clipboard.setData( - // ClipboardData(text: text), - // ), + // onPressed: () => Clipboard.setData(ClipboardData(text: text)), // child: Text(L10n.of(context).copy), // ), // AdaptiveDialogAction( diff --git a/lib/utils/event_checkbox_extension.dart b/lib/utils/event_checkbox_extension.dart index cf3832ba6..61e20c408 100644 --- a/lib/utils/event_checkbox_extension.dart +++ b/lib/utils/event_checkbox_extension.dart @@ -6,18 +6,17 @@ extension EventCheckboxRoomExtension on Room { String eventId, int checkboxId, { String? txid, - }) => - sendEvent( - { - 'm.relates_to': { - 'rel_type': relationshipType, - 'event_id': eventId, - 'checkbox_id': checkboxId, - }, - }, - type: EventTypes.Reaction, - txid: txid, - ); + }) => sendEvent( + { + 'm.relates_to': { + 'rel_type': relationshipType, + 'event_id': eventId, + 'checkbox_id': checkboxId, + }, + }, + type: EventTypes.Reaction, + txid: txid, + ); } extension EventCheckboxExtension on Event { diff --git a/lib/utils/feedback_dialog.dart b/lib/utils/feedback_dialog.dart index 36b0f0072..dbb06b362 100644 --- a/lib/utils/feedback_dialog.dart +++ b/lib/utils/feedback_dialog.dart @@ -24,10 +24,7 @@ Future showFeedbackDialog( child: Column( mainAxisSize: MainAxisSize.min, children: [ - const BotFace( - width: 60, - expression: BotExpression.addled, - ), + const BotFace(width: 60, expression: BotExpression.addled), const SizedBox(height: 10), Text(L10n.of(context).reportContentIssueDescription), const SizedBox(height: 10), @@ -35,9 +32,7 @@ Future showFeedbackDialog( padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.0), - border: Border.all( - color: AppConfig.warning, - ), + border: Border.all(color: AppConfig.warning), ), child: offendingContent, ), diff --git a/lib/utils/file_description.dart b/lib/utils/file_description.dart index a1d766aee..54f72e159 100644 --- a/lib/utils/file_description.dart +++ b/lib/utils/file_description.dart @@ -5,15 +5,23 @@ extension FileDescriptionExtension on Event { if (!{ MessageTypes.File, MessageTypes.Image, + MessageTypes.Sticker, + MessageTypes.Video, + MessageTypes.Audio, }.contains(messageType)) { return null; } final formattedBody = content.tryGet('formatted_body'); - if (formattedBody != null) return formattedBody; + if (formattedBody != null && formattedBody.isNotEmpty) return formattedBody; final filename = content.tryGet('filename'); final body = content.tryGet('body'); - if (filename != body && body != null && filename != null) return body; + if (filename != body && + body != null && + filename != null && + body.isNotEmpty) { + return body; + } return null; } } diff --git a/lib/utils/file_selector.dart b/lib/utils/file_selector.dart index f9507355a..97e229c2e 100644 --- a/lib/utils/file_selector.dart +++ b/lib/utils/file_selector.dart @@ -3,146 +3,24 @@ import 'package:flutter/widgets.dart'; import 'package:file_picker/file_picker.dart'; import 'package:file_selector/file_selector.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; Future> selectFiles( BuildContext context, { String? title, - FileSelectorType type = FileSelectorType.any, + FileType type = FileType.any, bool allowMultiple = false, }) async { - if (!PlatformInfos.isLinux) { - final result = await AppLock.of(context).pauseWhile( - showFutureLoadingDialog( - context: context, - future: () => FilePicker.platform.pickFiles( - compressionQuality: 0, - allowMultiple: allowMultiple, - type: type.filePickerType, - allowedExtensions: type.extensions, - ), + final result = await AppLock.of(context).pauseWhile( + showFutureLoadingDialog( + context: context, + future: () => FilePicker.platform.pickFiles( + compressionQuality: 0, + allowMultiple: allowMultiple, + type: type, ), - ); - return result.result?.xFiles ?? []; - } - - if (allowMultiple) { - return await AppLock.of(context).pauseWhile( - openFiles(confirmButtonText: title, acceptedTypeGroups: type.groups), - ); - } - final file = await AppLock.of(context).pauseWhile( - openFile(confirmButtonText: title, acceptedTypeGroups: type.groups), + ), ); - if (file == null) return []; - return [file]; -} - -enum FileSelectorType { - any([], FileType.any, null), - images( - [ - XTypeGroup( - label: 'Images', - extensions: [ - 'jpg', - 'JPG', - 'jpeg', - 'JPEG', - 'png', - 'PNG', - 'webp', - 'WebP', - 'WEBP', - 'gif', - 'GIF', - 'bmp', - 'BMP', - 'tiff', - 'TIFF', - 'tif', - 'TIF', - 'heic', - 'HEIC', - 'svg', - 'SVG', - ], - ), - XTypeGroup( - label: 'JPG', - extensions: ['jpg', 'JPG', 'jpeg', 'JPEG'], - ), - XTypeGroup(label: 'PNG', extensions: ['png', 'PNG']), - XTypeGroup(label: 'WebP', extensions: ['webp', 'WebP', 'WEBP']), - XTypeGroup(label: 'GIF', extensions: ['gif', 'GIF']), - XTypeGroup(label: 'BMP', extensions: ['bmp', 'BMP']), - XTypeGroup( - label: 'TIFF', - extensions: ['tiff', 'TIFF', 'tif', 'TIF'], - ), - XTypeGroup(label: 'HEIC', extensions: ['heic', 'HEIC']), - XTypeGroup(label: 'SVG', extensions: ['svg', 'SVG']), - ], - FileType.image, - null, - ), - videos( - [ - XTypeGroup( - label: 'Videos', - extensions: [ - 'mp4', - 'MP4', - 'avi', - 'AVI', - 'webm', - 'WebM', - 'WEBM', - 'mov', - 'MOV', - 'mkv', - 'MKV', - 'wmv', - 'WMV', - 'flv', - 'FLV', - 'mpeg', - 'MPEG', - '3gp', - '3GP', - 'ogg', - 'OGG', - ], - ), - XTypeGroup(label: 'MP4', extensions: ['mp4', 'MP4']), - XTypeGroup(label: 'WebM', extensions: ['webm', 'WebM', 'WEBM']), - XTypeGroup(label: 'AVI', extensions: ['avi', 'AVI']), - XTypeGroup(label: 'MOV', extensions: ['mov', 'MOV']), - XTypeGroup(label: 'MKV', extensions: ['mkv', 'MKV']), - XTypeGroup(label: 'WMV', extensions: ['wmv', 'WMV']), - XTypeGroup(label: 'FLV', extensions: ['flv', 'FLV']), - XTypeGroup(label: 'MPEG', extensions: ['mpeg', 'MPEG']), - XTypeGroup(label: '3GP', extensions: ['3gp', '3GP']), - XTypeGroup(label: 'OGG', extensions: ['ogg', 'OGG']), - ], - FileType.video, - null, - ), - zip( - [ - XTypeGroup(label: 'ZIP', extensions: ['zip', 'ZIP']), - ], - FileType.custom, - ['zip', 'ZIP'], - ), - // #Pangea - media([], FileType.media, null); - // Pangea# - - const FileSelectorType(this.groups, this.filePickerType, this.extensions); - final List groups; - final FileType filePickerType; - final List? extensions; + return result.result?.xFiles ?? []; } diff --git a/lib/utils/fluffy_share.dart b/lib/utils/fluffy_share.dart index d2a3cd032..ea09cbfba 100644 --- a/lib/utils/fluffy_share.dart +++ b/lib/utils/fluffy_share.dart @@ -16,18 +16,18 @@ abstract class FluffyShare { }) async { if (PlatformInfos.isMobile && !copyOnly) { final box = context.findRenderObject() as RenderBox; - await Share.share( - text, - sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size, + await SharePlus.instance.share( + ShareParams( + text: text, + sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size, + ), ); return; } - await Clipboard.setData( - ClipboardData(text: text), - ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context).copiedToClipboard)), - ); + await Clipboard.setData(ClipboardData(text: text)); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(L10n.of(context).copiedToClipboard))); return; } diff --git a/lib/utils/init_with_restore.dart b/lib/utils/init_with_restore.dart index 523b22f9e..71cc7f8b4 100644 --- a/lib/utils/init_with_restore.dart +++ b/lib/utils/init_with_restore.dart @@ -5,6 +5,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -30,22 +31,22 @@ class SessionBackup { SessionBackup.fromJson(jsonDecode(json)); factory SessionBackup.fromJson(Map json) => SessionBackup( - olmAccount: json['olm_account'], - accessToken: json['access_token'], - userId: json['user_id'], - homeserver: json['homeserver'], - deviceId: json['device_id'], - deviceName: json['device_name'], - ); + olmAccount: json['olm_account'], + accessToken: json['access_token'], + userId: json['user_id'], + homeserver: json['homeserver'], + deviceId: json['device_id'], + deviceName: json['device_name'], + ); Map toJson() => { - 'olm_account': olmAccount, - 'access_token': accessToken, - 'user_id': userId, - 'homeserver': homeserver, - 'device_id': deviceId, - if (deviceName != null) 'device_name': deviceName, - }; + 'olm_account': olmAccount, + 'access_token': accessToken, + 'user_id': userId, + 'homeserver': homeserver, + 'device_id': deviceId, + if (deviceName != null) 'device_name': deviceName, + }; @override String toString() => jsonEncode(toJson()); @@ -57,13 +58,13 @@ extension InitWithRestoreExtension on Client { ? const FlutterSecureStorage() : null; await storage?.delete( - key: '${AppConfig.applicationName}_session_backup_$clientName', + key: '${AppSettings.applicationName.value}_session_backup_$clientName', ); } Future initWithRestore({void Function()? onMigration}) async { final storageKey = - '${AppConfig.applicationName}_session_backup_$clientName'; + '${AppSettings.applicationName.value}_session_backup_$clientName'; final storage = PlatformInfos.isMobile || PlatformInfos.isLinux ? const FlutterSecureStorage() : null; @@ -81,7 +82,8 @@ extension InitWithRestoreExtension on Client { final homeserver = this.homeserver?.toString(); final deviceId = deviceID; final userId = userID; - final hasBackup = accessToken != null && + final hasBackup = + accessToken != null && homeserver != null && deviceId != null && userId != null; diff --git a/lib/utils/localized_exception_extension.dart b/lib/utils/localized_exception_extension.dart index 0b1989b46..b9fe247cc 100644 --- a/lib/utils/localized_exception_extension.dart +++ b/lib/utils/localized_exception_extension.dart @@ -8,7 +8,7 @@ import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pages/chat/recording_dialog.dart'; +import 'package:fluffychat/pages/chat/recording_view_model.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/utils/other_party_can_receive.dart'; @@ -23,8 +23,8 @@ extension LocalizedExceptionExtension on Object { final numString = round < 10 ? num.toStringAsFixed(2) : round < 100 - ? num.toStringAsFixed(1) - : round.toString(); + ? num.toStringAsFixed(1) + : round.toString(); return '$numString ${'kMGTPEZY'[i - 1]}B'; } @@ -47,9 +47,9 @@ extension LocalizedExceptionExtension on Object { // Pangea# if (this is FileTooBigMatrixException) { final exception = this as FileTooBigMatrixException; - return L10n.of(context).fileIsTooBigForServer( - _formatFileSize(exception.maxFileSize), - ); + return L10n.of( + context, + ).fileIsTooBigForServer(_formatFileSize(exception.maxFileSize)); } if (this is OtherPartyCanNotReceiveMessages) { return L10n.of(context).otherPartyNotLoggedIn; diff --git a/lib/utils/markdown_context_builder.dart b/lib/utils/markdown_context_builder.dart index 9404d3504..18b5d5117 100644 --- a/lib/utils/markdown_context_builder.dart +++ b/lib/utils/markdown_context_builder.dart @@ -64,8 +64,10 @@ Widget markdownContextBuilder( if (start == -1) start = 0; final end = selection.end; - final fullLineSelection = - TextSelection(baseOffset: start, extentOffset: end); + final fullLineSelection = TextSelection( + baseOffset: start, + extentOffset: end, + ); const checkBox = '- [ ]'; @@ -78,8 +80,11 @@ Widget markdownContextBuilder( : '$checkBox $line', ) .join('\n'); - controller.text = - controller.text.replaceRange(start, end, replacedRange); + controller.text = controller.text.replaceRange( + start, + end, + replacedRange, + ); ContextMenuController.removeAny(); }, ), diff --git a/lib/utils/matrix_sdk_extensions/device_extension.dart b/lib/utils/matrix_sdk_extensions/device_extension.dart index 55f3ebfc9..3872309ca 100644 --- a/lib/utils/matrix_sdk_extensions/device_extension.dart +++ b/lib/utils/matrix_sdk_extensions/device_extension.dart @@ -22,8 +22,13 @@ IconData _getIconFromName(String displayname) { }.any((s) => name.contains(s))) { return Icons.web_outlined; } - if ({'desktop', 'windows', 'macos', 'linux', 'ubuntu'} - .any((s) => name.contains(s))) { + if ({ + 'desktop', + 'windows', + 'macos', + 'linux', + 'ubuntu', + }.any((s) => name.contains(s))) { return Icons.desktop_mac_outlined; } return Icons.device_unknown_outlined; diff --git a/lib/utils/matrix_sdk_extensions/event_extension.dart b/lib/utils/matrix_sdk_extensions/event_extension.dart index b28ea258e..2da3a1f0c 100644 --- a/lib/utils/matrix_sdk_extensions/event_extension.dart +++ b/lib/utils/matrix_sdk_extensions/event_extension.dart @@ -14,7 +14,16 @@ extension LocalizedBody on Event { Future> _getFile(BuildContext context) => showFutureLoadingDialog( context: context, - future: downloadAndDecryptAttachment, + futureWithProgress: (onProgress) { + final fileSize = infoMap['size'] is int + ? infoMap['size'] as int + : null; + return downloadAndDecryptAttachment( + onDownloadProgress: fileSize == null + ? null + : (bytes) => onProgress(bytes / fileSize), + ); + }, ); void saveFile(BuildContext context) async { @@ -32,15 +41,18 @@ extension LocalizedBody on Event { bool get isAttachmentSmallEnough => infoMap['size'] is int && - infoMap['size'] < room.client.database.maxFileSize; + (infoMap['size'] as int) < room.client.database.maxFileSize; bool get isThumbnailSmallEnough => thumbnailInfoMap['size'] is int && - thumbnailInfoMap['size'] < room.client.database.maxFileSize; + (thumbnailInfoMap['size'] as int) < room.client.database.maxFileSize; bool get showThumbnail => - [MessageTypes.Image, MessageTypes.Sticker, MessageTypes.Video] - .contains(messageType) && + [ + MessageTypes.Image, + MessageTypes.Sticker, + MessageTypes.Video, + ].contains(messageType) && (kIsWeb || isAttachmentSmallEnough || isThumbnailSmallEnough || diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index f15e1f119..dc8b89584 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -1,64 +1,76 @@ import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; -import '../../config/app_config.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart'; extension VisibleInGuiExtension on List { - List filterByVisibleInGui({String? exceptionEventId}) => where( - // #Pangea - // (event) => event.isVisibleInGui || event.eventId == exceptionEventId, - (event) => - (event.isVisibleInGui || event.eventId == exceptionEventId) && - event.isVisibleInPangeaGui, - // Pangea# - ).toList(); + List filterByVisibleInGui({ + String? exceptionEventId, + String? threadId, + }) => where((event) { + if (threadId != null && + event.relationshipType != RelationshipTypes.reaction) { + if ((event.relationshipType != RelationshipTypes.thread || + event.relationshipEventId != threadId) && + event.eventId != threadId) { + return false; + } + } else if (event.relationshipType == RelationshipTypes.thread) { + return false; + } + // #Pangea + // return event.isVisibleInGui || event.eventId == exceptionEventId; + return (event.isVisibleInGui || event.eventId == exceptionEventId) && + event.isVisibleInPangeaGui; + // Pangea# + }).toList(); } extension IsStateExtension on Event { bool get isVisibleInGui => // always filter out edit and reaction relationships - !{RelationshipTypes.edit, RelationshipTypes.reaction} - .contains(relationshipType) && - // always filter out m.key.* events - !type.startsWith('m.key.verification.') && + !{ + RelationshipTypes.edit, + RelationshipTypes.reaction, + }.contains(relationshipType) && + // always filter out m.key.* and other known but unimportant events + !isKnownHiddenStates && // event types to hide: redaction and reaction events // if a reaction has been redacted we also want it to be hidden in the timeline !{EventTypes.Reaction, EventTypes.Redaction}.contains(type) && // if we enabled to hide all redacted events, don't show those - (!AppConfig.hideRedactedEvents || !redacted) && + (!AppSettings.hideRedactedEvents.value || !redacted) && // if we enabled to hide all unknown events, don't show those // #Pangea - // (!AppConfig.hideUnknownEvents || isEventTypeKnown) && - (!AppConfig.hideUnknownEvents || pangeaIsEventTypeKnown) && - // Pangea# - // remove state events that we don't want to render - (isState || !AppConfig.hideAllStateEvents) && - // #Pangea + // (!AppSettings.hideUnknownEvents.value || isEventTypeKnown); + (!AppSettings.hideUnknownEvents.value || pangeaIsEventTypeKnown) && content.tryGet(ModelKey.transcription) == null && - // if sending of transcription fails, - // don't show it as a errored audio event in timeline. ((unsigned?['extra_content'] as Map?)?[ModelKey.transcription] == null) && - // hide unimportant state events - (!AppConfig.hideUnimportantStateEvents || - !isState || - importantStateEvents.contains(type)) && - // Pangea# - // hide simple join/leave member events in public rooms - (!AppConfig.hideUnimportantStateEvents || - type != EventTypes.RoomMember || - room.joinRules != JoinRules.public || - content.tryGet('membership') == 'ban' || - stateKey != senderId); + (!isState || importantStateEvents.contains(type)); + // Pangea# bool get isState => !{ - EventTypes.Message, - EventTypes.Sticker, - EventTypes.Encrypted, - }.contains(type); + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + }.contains(type); + + bool get isCollapsedState => !{ + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + EventTypes.RoomCreate, + EventTypes.RoomTombstone, + }.contains(type); + + bool get isKnownHiddenStates => + {PollEventContent.responseType}.contains(type) || + type.startsWith('m.key.verification.'); // #Pangea bool get isVisibleInPangeaGui { @@ -85,6 +97,7 @@ extension IsStateExtension on Event { EventTypes.RoomMember, EventTypes.RoomTombstone, EventTypes.CallInvite, + PollEventContent.startType, PangeaEventTypes.activityPlan, PangeaEventTypes.activityRole, }; diff --git a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart index 996016bb6..8a850d212 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart @@ -32,47 +32,42 @@ Future flutterMatrixSdkDatabaseBuilder(String clientName) async { ); // Pangea# Logs().wtf('Unable to construct database!', e, s); - // Try to delete database so that it can created again on next init: - database?.delete().catchError( - // #Pangea - (err, s) { - ErrorHandler.logError( - e: e, - s: s, - data: {}, - m: "Failed to delete matrix database after failed construction.", - ); - } - // (e, s) => Logs().wtf( - // 'Unable to delete database, after failed construction', - // e, - // s, - // ), - // Pangea# - ); - - // Delete database file: - if (database == null && !kIsWeb) { - final dbFile = File(await _getDatabasePath(clientName)); - if (await dbFile.exists()) await dbFile.delete(); - } try { // Send error notification: // #Pangea // final l10n = await lookupL10n(PlatformDispatcher.instance.locale); - // ClientManager.sendInitNotification( - // l10n.initAppError, - // l10n.databaseBuildErrorBody( - // AppConfig.newIssueUrl.toString(), - // e.toString(), - // ), - // ); + // ClientManager.sendInitNotification(l10n.initAppError, e.toString()); // Pangea# } catch (e, s) { Logs().e('Unable to send error notification', e, s); } + // Try to delete database so that it can created again on next init: + database?.delete().catchError( + // #Pangea + (err, s) { + ErrorHandler.logError( + e: e, + s: s, + data: {}, + m: "Failed to delete matrix database after failed construction.", + ); + }, + // (e, s) => Logs().wtf( + // 'Unable to delete database, after failed construction', + // e, + // s, + // ), + // Pangea# + ); + + // Delete database file: + if (!kIsWeb) { + final dbFile = File(await _getDatabasePath(clientName)); + if (await dbFile.exists()) await dbFile.delete(); + } + rethrow; } } @@ -102,27 +97,25 @@ Future _constructDatabase(String clientName) async { // fix dlopen for old Android await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions(); // import the SQLite / SQLCipher shared objects / dynamic libraries - final factory = - createDatabaseFactoryFfi(ffiInit: SQfLiteEncryptionHelper.ffiInit); + final factory = createDatabaseFactoryFfi( + ffiInit: SQfLiteEncryptionHelper.ffiInit, + ); + // #Pangea Sentry.addBreadcrumb(Breadcrumb(message: 'Database path: $path')); // Pangea# - // migrate from potential previous SQLite database path to current one - await _migrateLegacyLocation(path, clientName); - // required for [getDatabasesPath] databaseFactory = factory; + // migrate from potential previous SQLite database path to current one + await _migrateLegacyLocation(path, clientName); + // in case we got a cipher, we use the encryption helper // to manage SQLite encryption final helper = cipher == null ? null - : SQfLiteEncryptionHelper( - factory: factory, - path: path, - cipher: cipher, - ); + : SQfLiteEncryptionHelper(factory: factory, path: path, cipher: cipher); // #Pangea Sentry.addBreadcrumb(Breadcrumb(message: 'Database cipher helper: $helper')); // Pangea# diff --git a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart index 4c3296c86..b890ef8ea 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/setting_keys.dart'; @@ -40,10 +39,7 @@ Future getDatabaseCipher() async { final list = Uint8List(32); list.setAll(0, Iterable.generate(list.length, (i) => rng.nextInt(256))); final newPassword = base64UrlEncode(list); - await secureStorage.write( - key: _passwordStorageKey, - value: newPassword, - ); + await secureStorage.write(key: _passwordStorageKey, value: newPassword); } // workaround for if we just wrote to the key and it still doesn't exist password = await secureStorage.read(key: _passwordStorageKey); @@ -89,8 +85,7 @@ Future getDatabaseCipher() async { } void _sendNoEncryptionWarning(Object exception) async { - final store = await SharedPreferences.getInstance(); - final isStored = AppSettings.noEncryptionWarningShown.getItem(store); + final isStored = AppSettings.noEncryptionWarningShown.value; if (isStored == true) return; @@ -108,5 +103,5 @@ void _sendNoEncryptionWarning(Object exception) async { // ); // Pangea# - await AppSettings.noEncryptionWarningShown.setItem(store, true); + await AppSettings.noEncryptionWarningShown.setItem(true); } diff --git a/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart b/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart index 01b2d85e4..dffea0c63 100644 --- a/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart +++ b/lib/utils/matrix_sdk_extensions/matrix_file_extension.dart @@ -1,53 +1,26 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; -import 'package:file_selector/file_selector.dart'; import 'package:matrix/matrix.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:universal_html/html.dart' as html; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/size_string.dart'; -import 'package:fluffychat/widgets/future_loading_dialog.dart'; extension MatrixFileExtension on MatrixFile { void save(BuildContext context) async { - if (PlatformInfos.isWeb) { - _webDownload(); - return; - } - - final downloadPath = !PlatformInfos.isMobile - ? (await getSaveLocation( - suggestedName: name, - confirmButtonText: L10n.of(context).saveFile, - )) - ?.path - : await FilePicker.platform.saveFile( - dialogTitle: L10n.of(context).saveFile, - fileName: name, - type: filePickerFileType, - bytes: bytes, - ); + final scaffoldMessenger = ScaffoldMessenger.of(context); + final l10n = L10n.of(context); + final downloadPath = await FilePicker.platform.saveFile( + dialogTitle: l10n.saveFile, + fileName: name, + type: filePickerFileType, + bytes: bytes, + ); if (downloadPath == null) return; - if (PlatformInfos.isDesktop) { - final result = await showFutureLoadingDialog( - context: context, - future: () => File(downloadPath).writeAsBytes(bytes), - ); - if (result.error != null) return; - } - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - L10n.of(context).fileHasBeenSavedAt(downloadPath), - ), - ), + scaffoldMessenger.showSnackBar( + SnackBar(content: Text(l10n.fileHasBeenSavedAt(downloadPath))), ); } @@ -58,28 +31,18 @@ extension MatrixFileExtension on MatrixFile { return FileType.any; } - void _webDownload() { - html.AnchorElement( - href: html.Url.createObjectUrlFromBlob( - html.Blob( - [bytes], - mimeType, - ), - ), - ) - ..download = name - ..click(); - } - void share(BuildContext context) async { // Workaround for iPad from // https://github.com/fluttercommunity/plus_plugins/tree/main/packages/share_plus/share_plus#ipad final box = context.findRenderObject() as RenderBox?; - await Share.shareXFiles( - [XFile.fromData(bytes, name: name, mimeType: mimeType)], - sharePositionOrigin: - box == null ? null : box.localToGlobal(Offset.zero) & box.size, + await SharePlus.instance.share( + ShareParams( + files: [XFile.fromData(bytes, name: name, mimeType: mimeType)], + sharePositionOrigin: box == null + ? null + : box.localToGlobal(Offset.zero) & box.size, + ), ); return; } diff --git a/lib/utils/matrix_sdk_extensions/matrix_locals.dart b/lib/utils/matrix_sdk_extensions/matrix_locals.dart index 123580134..118d46f42 100644 --- a/lib/utils/matrix_sdk_extensions/matrix_locals.dart +++ b/lib/utils/matrix_sdk_extensions/matrix_locals.dart @@ -37,13 +37,13 @@ class MatrixLocals extends MatrixLocalizations { } @override - String changedTheChatDescriptionTo(String senderName, String content) { - return l10n.changedTheChatDescriptionTo(senderName, content); + String changedTheChatDescriptionTo(String senderName, _) { + return l10n.changedTheChatDescription(senderName); } @override - String changedTheChatNameTo(String senderName, String content) { - return l10n.changedTheChatNameTo(senderName, content); + String changedTheChatNameTo(String senderName, _) { + return l10n.changedTheChatName(senderName); } @override @@ -356,11 +356,22 @@ class MatrixLocals extends MatrixLocalizations { String get cancelledSend => l10n.sendCanceled; @override - String voiceMessage(String senderName, Duration? duration) => - l10n.sentVoiceMessage( - senderName, - duration == null - ? '' - : '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}', - ); + String voiceMessage( + String senderName, + Duration? duration, + ) => l10n.sentVoiceMessage( + senderName, + duration == null + ? '' + : '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}', + ); + + @override + String get refreshingLastEvent => l10n.loadingPleaseWait; + + @override + String startedAPoll(String senderName) => '$senderName started a poll'; + + @override + String get pollHasBeenEnded => l10n.pollHasBeenEnded; } diff --git a/lib/utils/notification_background_handler.dart b/lib/utils/notification_background_handler.dart new file mode 100644 index 000000000..ddb105c4a --- /dev/null +++ b/lib/utils/notification_background_handler.dart @@ -0,0 +1,293 @@ +import 'dart:convert'; +import 'dart:isolate'; +import 'dart:ui'; + +import 'package:collection/collection.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod; +import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/utils/client_download_content_extension.dart'; +import 'package:fluffychat/utils/client_manager.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/push_helper.dart'; +import '../config/app_config.dart'; +import '../config/setting_keys.dart'; + +bool _vodInitialized = false; + +extension NotificationResponseJson on NotificationResponse { + String toJsonString() => jsonEncode({ + 'type': notificationResponseType.name, + 'id': id, + 'actionId': actionId, + 'input': input, + 'payload': payload, + 'data': data, + }); + + static NotificationResponse fromJsonString(String jsonString) { + final json = jsonDecode(jsonString) as Map; + return NotificationResponse( + notificationResponseType: NotificationResponseType.values.singleWhere( + (t) => t.name == json['type'], + ), + id: json['id'] as int?, + actionId: json['actionId'] as String?, + input: json['input'] as String?, + payload: json['payload'] as String?, + data: json['data'] as Map, + ); + } +} + +Future waitForPushIsolateDone() async { + if (IsolateNameServer.lookupPortByName(AppConfig.pushIsolatePortName) != + null) { + Logs().i('Wait for Push Isolate to be done...'); + await Future.delayed(const Duration(milliseconds: 300)); + } +} + +@pragma('vm:entry-point') +void notificationTapBackground( + NotificationResponse notificationResponse, +) async { + final sendPort = IsolateNameServer.lookupPortByName( + AppConfig.mainIsolatePortName, + ); + if (sendPort != null) { + sendPort.send(notificationResponse.toJsonString()); + Logs().i('Notification tap sent to main isolate!'); + return; + } + Logs().i( + 'Main isolate no up - Create temporary client for notification tap intend!', + ); + + final pushIsolateReceivePort = ReceivePort(); + IsolateNameServer.registerPortWithName( + pushIsolateReceivePort.sendPort, + AppConfig.pushIsolatePortName, + ); + + if (!_vodInitialized) { + await vod.init(); + _vodInitialized = true; + } + final store = await AppSettings.init(); + final client = (await ClientManager.getClients( + initialize: false, + store: store, + )).first; + await client.abortSync(); + await client.init( + waitForFirstSync: false, + waitUntilLoadCompletedLoaded: false, + ); + + if (!client.isLogged()) { + throw Exception('Notification tab in background but not logged in!'); + } + try { + await notificationTap(notificationResponse, client: client); + } finally { + await client.dispose(closeDatabase: false); + pushIsolateReceivePort.sendPort.send('DONE'); + IsolateNameServer.removePortNameMapping(AppConfig.pushIsolatePortName); + } + return; +} + +Future notificationTap( + NotificationResponse notificationResponse, { + GoRouter? router, + required Client client, + L10n? l10n, +}) async { + Logs().d( + 'Notification action handler started', + notificationResponse.notificationResponseType.name, + ); + final payload = FluffyChatPushPayload.fromString( + notificationResponse.payload ?? '', + ); + switch (notificationResponse.notificationResponseType) { + case NotificationResponseType.selectedNotification: + final roomId = payload.roomId; + if (roomId == null) return; + + if (router == null) { + Logs().v('Ignore select notification action in background mode'); + return; + } + Logs().v('Open room from notification tap', roomId); + await client.roomsLoading; + await client.accountDataLoading; + if (client.getRoomById(roomId) == null) { + await client + .waitForRoomInSync(roomId) + .timeout(const Duration(seconds: 30)); + } + + // #Pangea + const sessionIdKey = "content_pangea.activity.session_room_id"; + const activityIdKey = "content_pangea.activity.id"; + final sessionRoomId = payload.additionalData[sessionIdKey]; + final activityId = payload.additionalData[activityIdKey]; + if (sessionRoomId != null && + sessionRoomId.isNotEmpty && + activityId != null && + activityId.isNotEmpty) { + try { + final course = client.getRoomById(roomId); + if (course == null) return; + + final session = client.getRoomById(sessionRoomId); + if (session?.membership == Membership.join) { + router.go('/rooms/$sessionRoomId'); + return; + } + + router.go( + '/rooms/spaces/$roomId/activity/$activityId?roomid=$sessionRoomId', + ); + return; + } catch (err, s) { + ErrorHandler.logError(e: err, s: s, data: {"roomId": sessionRoomId}); + } + } + // Pangea# + + router.go( + client.getRoomById(roomId)?.membership == Membership.invite + ? '/rooms' + : '/rooms/$roomId', + ); + case NotificationResponseType.selectedNotificationAction: + final actionType = FluffyChatNotificationActions.values.singleWhereOrNull( + (action) => action.name == notificationResponse.actionId, + ); + if (actionType == null) { + throw Exception('Selected notification with action but no action ID'); + } + final roomId = payload.roomId; + if (roomId == null) { + throw Exception('Selected notification with action but no payload'); + } + await client.roomsLoading; + await client.accountDataLoading; + await client.userDeviceKeysLoading; + final room = client.getRoomById(roomId); + if (room == null) { + throw Exception( + 'Selected notification with action but unknown room $roomId', + ); + } + switch (actionType) { + case FluffyChatNotificationActions.markAsRead: + await room.setReadMarker( + payload.eventId ?? room.lastEvent!.eventId, + mRead: payload.eventId ?? room.lastEvent!.eventId, + public: AppSettings.sendPublicReadReceipts.value, + ); + case FluffyChatNotificationActions.reply: + final input = notificationResponse.input; + if (input == null || input.isEmpty) { + throw Exception( + 'Selected notification with reply action but without input', + ); + } + + final eventId = await room.sendTextEvent( + input, + parseCommands: false, + displayPendingEvent: false, + ); + + if (PlatformInfos.isAndroid) { + final ownProfile = await room.client.fetchOwnProfile(); + final avatar = ownProfile.avatarUrl; + final avatarFile = avatar == null + ? null + : await client + .downloadMxcCached( + avatar, + thumbnailMethod: ThumbnailMethod.crop, + width: notificationAvatarDimension, + height: notificationAvatarDimension, + animated: false, + isThumbnail: true, + rounded: true, + ) + .timeout(const Duration(seconds: 3)); + final messagingStyleInformation = + await AndroidFlutterLocalNotificationsPlugin() + .getActiveNotificationMessagingStyle(room.id.hashCode); + if (messagingStyleInformation == null) return; + l10n ??= await lookupL10n(PlatformDispatcher.instance.locale); + messagingStyleInformation.messages?.add( + Message( + input, + DateTime.now(), + Person( + key: room.client.userID, + name: l10n.you, + icon: avatarFile == null + ? null + : ByteArrayAndroidIcon(avatarFile), + ), + ), + ); + + await FlutterLocalNotificationsPlugin().show( + room.id.hashCode, + room.getLocalizedDisplayname(MatrixLocals(l10n)), + input, + NotificationDetails( + android: AndroidNotificationDetails( + AppConfig.pushNotificationsChannelId, + l10n.incomingMessages, + category: AndroidNotificationCategory.message, + shortcutId: room.id, + styleInformation: messagingStyleInformation, + groupKey: room.id, + playSound: false, + enableVibration: false, + actions: [ + AndroidNotificationAction( + FluffyChatNotificationActions.reply.name, + l10n.reply, + inputs: [ + AndroidNotificationActionInput( + label: l10n.writeAMessage, + ), + ], + cancelNotification: false, + allowGeneratedReplies: true, + semanticAction: SemanticAction.reply, + ), + AndroidNotificationAction( + FluffyChatNotificationActions.markAsRead.name, + l10n.markAsRead, + semanticAction: SemanticAction.markAsRead, + ), + ], + ), + ), + payload: FluffyChatPushPayload( + client.clientName, + room.id, + eventId, + ).toString(), + ); + } + } + } +} + +enum FluffyChatNotificationActions { markAsRead, reply } diff --git a/lib/utils/platform_infos.dart b/lib/utils/platform_infos.dart index 4913c1fe5..4ca1f7ae4 100644 --- a/lib/utils/platform_infos.dart +++ b/lib/utils/platform_infos.dart @@ -8,6 +8,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:universal_html/html.dart' as html; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import '../config/app_config.dart'; @@ -46,7 +47,7 @@ abstract class PlatformInfos { // Pangea# static String get clientName => - '${AppConfig.applicationName} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}'; + '${AppSettings.applicationName.value} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}'; static Future getVersion() async { var version = kIsWeb ? 'Web' : 'Unknown'; @@ -64,7 +65,7 @@ abstract class PlatformInfos { useRootNavigator: false, // Pangea# children: [ - Text('Version: $version'), + Text(L10n.of(context).versionWithNumber(version)), TextButton.icon( onPressed: () => launchUrlString(AppConfig.sourceCodeUrl), icon: const Icon(Icons.source_outlined), @@ -78,7 +79,7 @@ abstract class PlatformInfos { Navigator.of(innerContext).pop(); }, icon: const Icon(Icons.list_outlined), - label: const Text('Logs'), + label: Text(L10n.of(context).logs), ); }, ), @@ -90,7 +91,7 @@ abstract class PlatformInfos { Navigator.of(innerContext).pop(); }, icon: const Icon(Icons.settings_applications_outlined), - label: const Text('Advanced Configs'), + label: Text(L10n.of(context).advancedConfigs), ); }, ), @@ -101,7 +102,7 @@ abstract class PlatformInfos { height: 64, filterQuality: FilterQuality.medium, ), - applicationName: AppConfig.applicationName, + applicationName: AppSettings.applicationName.value, ); } @@ -119,5 +120,6 @@ abstract class PlatformInfos { } return null; } -// Pangea# + + // Pangea# } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 03a88e085..4c347beac 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -8,14 +8,15 @@ import 'package:collection/collection.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_shortcuts_new/flutter_shortcuts_new.dart'; import 'package:matrix/matrix.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/utils/notification_background_handler.dart'; import 'package:fluffychat/utils/platform_infos.dart'; const notificationAvatarDimension = 128; @@ -29,6 +30,7 @@ Future pushHelper( // #Pangea Map? additionalData, // Pangea# + bool useNotificationActions = true, }) async { try { await _tryPushHelper( @@ -40,11 +42,12 @@ Future pushHelper( // #Pangea additionalData: additionalData, // Pangea# + useNotificationActions: useNotificationActions, ); } catch (e, s) { - Logs().v('Push Helper has crashed!', e, s); + Logs().e('Push Helper has crashed! Writing into temporary file', e, s); - l10n ??= await lookupL10n(const Locale('en')); + l10n ??= await lookupL10n(PlatformDispatcher.instance.locale); flutterLocalNotificationsPlugin.show( notification.roomId?.hashCode ?? 0, // #Pangea @@ -59,7 +62,7 @@ Future pushHelper( l10n.incomingMessages, number: notification.counts?.unread, ticker: l10n.unreadChatsInApp( - AppConfig.applicationName, + AppSettings.applicationName.value, (notification.counts?.unread ?? 0).toString(), ), importance: Importance.high, @@ -81,6 +84,7 @@ Future _tryPushHelper( // #Pangea Map? additionalData, // Pangea# + bool useNotificationActions = true, }) async { final isBackgroundMessage = client == null; Logs().v( @@ -97,9 +101,8 @@ Future _tryPushHelper( client ??= (await ClientManager.getClients( initialize: false, - store: await SharedPreferences.getInstance(), - )) - .first; + store: await AppSettings.init(), + )).first; final event = await client.getEventByPushNotification( notification, storeInDatabase: false, @@ -114,8 +117,8 @@ Future _tryPushHelper( // Make sure client is fully loaded and synced before dismiss notifications: await client.roomsLoading; await client.oneShotSync(); - final activeNotifications = - await flutterLocalNotificationsPlugin.getActiveNotifications(); + final activeNotifications = await flutterLocalNotificationsPlugin + .getActiveNotifications(); for (final activeNotification in activeNotifications) { final room = client.rooms.singleWhereOrNull( (room) => room.id.hashCode == activeNotification.id, @@ -180,16 +183,16 @@ Future _tryPushHelper( roomAvatarFile = avatar == null ? null : await client - .downloadMxcCached( - avatar, - thumbnailMethod: ThumbnailMethod.crop, - width: notificationAvatarDimension, - height: notificationAvatarDimension, - animated: false, - isThumbnail: true, - rounded: true, - ) - .timeout(const Duration(seconds: 3)); + .downloadMxcCached( + avatar, + thumbnailMethod: ThumbnailMethod.crop, + width: notificationAvatarDimension, + height: notificationAvatarDimension, + animated: false, + isThumbnail: true, + rounded: true, + ) + .timeout(const Duration(seconds: 3)); } catch (e, s) { Logs().e('Unable to get avatar picture', e, s); // #Pangea @@ -200,18 +203,18 @@ Future _tryPushHelper( senderAvatarFile = event.room.isDirectChat ? roomAvatarFile : senderAvatar == null - ? null - : await client - .downloadMxcCached( - senderAvatar, - thumbnailMethod: ThumbnailMethod.crop, - width: notificationAvatarDimension, - height: notificationAvatarDimension, - animated: false, - isThumbnail: true, - rounded: true, - ) - .timeout(const Duration(seconds: 3)); + ? null + : await client + .downloadMxcCached( + senderAvatar, + thumbnailMethod: ThumbnailMethod.crop, + width: notificationAvatarDimension, + height: notificationAvatarDimension, + animated: false, + isThumbnail: true, + rounded: true, + ) + .timeout(const Duration(seconds: 3)); } catch (e, s) { Logs().e('Unable to get avatar picture', e, s); } @@ -236,14 +239,15 @@ Future _tryPushHelper( final messagingStyleInformation = PlatformInfos.isAndroid ? await AndroidFlutterLocalNotificationsPlugin() - .getActiveNotificationMessagingStyle(id) + .getActiveNotificationMessagingStyle(id) : null; messagingStyleInformation?.messages?.add(newMessage); final roomName = event.room.getLocalizedDisplayname(MatrixLocals(l10n)); - final notificationGroupId = - event.room.isDirectChat ? 'directChats' : 'groupChats'; + final notificationGroupId = event.room.isDirectChat + ? 'directChats' + : 'groupChats'; // #Pangea // final groupName = event.room.isDirectChat ? l10n.directChats : l10n.groups; final groupName = event.room.isDirectChat ? l10n.directChats : l10n.chats; @@ -261,11 +265,13 @@ Future _tryPushHelper( await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin + >() ?.createNotificationChannelGroup(messageRooms); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin + >() ?.createNotificationChannel(roomsChannel); final androidPlatformChannelSpecifics = AndroidNotificationDetails( @@ -274,7 +280,8 @@ Future _tryPushHelper( number: notification.counts?.unread, category: AndroidNotificationCategory.message, shortcutId: event.room.id, - styleInformation: messagingStyleInformation ?? + styleInformation: + messagingStyleInformation ?? MessagingStyleInformation( Person( name: senderName, @@ -299,6 +306,25 @@ Future _tryPushHelper( importance: Importance.high, priority: Priority.max, groupKey: event.room.spaceParents.firstOrNull?.roomId ?? 'rooms', + actions: event.type == EventTypes.RoomMember || !useNotificationActions + ? null + : [ + AndroidNotificationAction( + FluffyChatNotificationActions.reply.name, + l10n.reply, + inputs: [ + AndroidNotificationActionInput(label: l10n.writeAMessage), + ], + cancelNotification: false, + allowGeneratedReplies: true, + semanticAction: SemanticAction.reply, + ), + AndroidNotificationAction( + FluffyChatNotificationActions.markAsRead.name, + l10n.markAsRead, + semanticAction: SemanticAction.markAsRead, + ), + ], ); const iOSPlatformChannelSpecifics = DarwinNotificationDetails(); final platformChannelSpecifics = NotificationDetails( @@ -313,20 +339,20 @@ Future _tryPushHelper( } // #Pangea - Include activity session data in payload - String payload = event.roomId!; + final Map additionalDataMap = {}; if (additionalData != null) { - const sessionIdKey = "content_pangea.activity.session_room_id"; - const activityIdKey = "content_pangea.activity.id"; - final sessionRoomId = additionalData[sessionIdKey]; - final activityId = additionalData[activityIdKey]; - if (sessionRoomId is String && activityId is String) { - payload = jsonEncode({ - 'room_id': event.roomId, - sessionIdKey: sessionRoomId, - activityIdKey: activityId, - }); - } + additionalData.forEach((key, value) { + if (value is String) { + additionalDataMap[key] = value; + } + }); } + final payload = FluffyChatPushPayload( + client.clientName, + event.room.id, + event.eventId, + additionalData: additionalDataMap, + ).toString(); // Pangea# await flutterLocalNotificationsPlugin.show( @@ -335,13 +361,70 @@ Future _tryPushHelper( body, platformChannelSpecifics, // #Pangea - // payload: event.roomId, + // payload: FluffyChatPushPayload( + // client.clientName, + // event.room.id, + // event.eventId, + // ).toString(), payload: payload, // Pangea# ); Logs().v('Push helper has been completed!'); } +class FluffyChatPushPayload { + final String? clientName, roomId, eventId; + // #Pangea + final Map additionalData; + // Pangea# + + // #Pangea + // FluffyChatPushPayload(this.clientName, this.roomId, this.eventId); + FluffyChatPushPayload( + this.clientName, + this.roomId, + this.eventId, { + this.additionalData = const {}, + }); + // Pangea# + + factory FluffyChatPushPayload.fromString(String payload) { + final parts = payload.split('|'); + // #Pangea + // if (parts.length != 3) { + // return FluffyChatPushPayload(null, null, null); + // } + // return FluffyChatPushPayload(parts[0], parts[1], parts[2]); + if (parts.length < 3) { + return FluffyChatPushPayload(null, null, null); + } + + Map additionalData = {}; + if (parts.length > 3) { + try { + additionalData = Map.from(jsonDecode(parts[3])); + } catch (e, s) { + Logs().e('Unable to parse additional data from payload', e, s); + } + } + return FluffyChatPushPayload( + parts[0], + parts[1], + parts[2], + additionalData: additionalData, + ); + // Pangea# + } + + // #Pangea + // @override + // String toString() => '$clientName|$roomId|$eventId'; + @override + String toString() => + '$clientName|$roomId|$eventId|${jsonEncode(additionalData)}'; + // Pangea# +} + /// Creates a shortcut for Android platform but does not block displaying the /// notification. This is optional but provides a nicer view of the /// notification popup. diff --git a/lib/utils/resize_video.dart b/lib/utils/resize_video.dart index 99a4c6b0a..ae249818e 100644 --- a/lib/utils/resize_video.dart +++ b/lib/utils/resize_video.dart @@ -39,10 +39,7 @@ extension ResizeImage on XFile { try { final bytes = await VideoCompress.getByteThumbnail(path); if (bytes == null) return null; - return MatrixImageFile( - bytes: bytes, - name: name, - ); + return MatrixImageFile(bytes: bytes, name: name); } catch (e, s) { Logs().w('Error while compressing video', e, s); } diff --git a/lib/utils/room_status_extension.dart b/lib/utils/room_status_extension.dart index 7c978064b..3021a3147 100644 --- a/lib/utils/room_status_extension.dart +++ b/lib/utils/room_status_extension.dart @@ -19,8 +19,9 @@ extension RoomStatusExtension on Room { } else if (typingUsers.length == 1) { typingText = L10n.of(context).isTyping; if (typingUsers.first.id != directChatMatrixID) { - typingText = - L10n.of(context).userIsTyping(typingUsers.first.calcDisplayname()); + typingText = L10n.of( + context, + ).userIsTyping(typingUsers.first.calcDisplayname()); } } else if (typingUsers.length == 2) { typingText = L10n.of(context).userAndUserAreTyping( diff --git a/lib/utils/show_scaffold_dialog.dart b/lib/utils/show_scaffold_dialog.dart index 9a0a0c610..7e552f90d 100644 --- a/lib/utils/show_scaffold_dialog.dart +++ b/lib/utils/show_scaffold_dialog.dart @@ -10,28 +10,25 @@ Future showScaffoldDialog({ double maxWidth = 480, double maxHeight = 720, required Widget Function(BuildContext context) builder, -}) => - showDialog( - context: context, - useSafeArea: false, - builder: FluffyThemes.isColumnMode(context) - ? (context) => Center( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - color: containerColor ?? - Theme.of(context).scaffoldBackgroundColor, - ), - clipBehavior: Clip.hardEdge, - margin: const EdgeInsets.all(16), - constraints: BoxConstraints( - maxWidth: maxWidth, - maxHeight: maxHeight, - ), - child: builder(context), - ), - ) - : builder, - ); +}) => showDialog( + context: context, + useSafeArea: false, + builder: FluffyThemes.isColumnMode(context) + ? (context) => Center( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + color: + containerColor ?? Theme.of(context).scaffoldBackgroundColor, + ), + clipBehavior: Clip.hardEdge, + margin: const EdgeInsets.all(16), + constraints: BoxConstraints( + maxWidth: maxWidth, + maxHeight: maxHeight, + ), + child: builder(context), + ), + ) + : builder, +); diff --git a/lib/utils/sync_status_localization.dart b/lib/utils/sync_status_localization.dart index 6d3a10311..c48e71252 100644 --- a/lib/utils/sync_status_localization.dart +++ b/lib/utils/sync_status_localization.dart @@ -12,8 +12,9 @@ extension SyncStatusLocalization on SyncStatusUpdate { case SyncStatus.waitingForResponse: return L10n.of(context).waitingForServer; case SyncStatus.error: - return ((error?.exception ?? Object()) as Object) - .toLocalizedString(context); + return ((error?.exception ?? Object()) as Object).toLocalizedString( + context, + ); case SyncStatus.processing: case SyncStatus.cleaningUp: case SyncStatus.finished: diff --git a/lib/utils/tor_stub.dart b/lib/utils/tor_stub.dart deleted file mode 100644 index 3223d087f..000000000 --- a/lib/utils/tor_stub.dart +++ /dev/null @@ -1,7 +0,0 @@ -/// Stub class for [TorBrowserDetector] -/// -/// statically returns false as Tor **browser** can only be detected in a -/// **browser**. -abstract class TorBrowserDetector { - static Future get isTorBrowser => Future.value(false); -} diff --git a/lib/utils/uia_request_manager.dart b/lib/utils/uia_request_manager.dart index 3e457199e..e15fdbf67 100644 --- a/lib/utils/uia_request_manager.dart +++ b/lib/utils/uia_request_manager.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'package:flutter/material.dart'; + import 'package:matrix/matrix.dart'; -import 'package:url_launcher/url_launcher_string.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -14,7 +16,7 @@ extension UiaRequestManager on MatrixState { final l10n = L10n.of(context); final navigatorContext = FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ?? - context; + context; try { if (uiaRequest.state != UiaRequestState.waitForUser || uiaRequest.nextStages.isEmpty) { @@ -25,7 +27,8 @@ extension UiaRequestManager on MatrixState { Logs().d('Uia Request Stage: $stage'); switch (stage) { case AuthenticationTypes.password: - final input = cachedPassword ?? + final input = + cachedPassword ?? (await showTextInputDialog( context: navigatorContext, title: l10n.pleaseEnterYourPassword, @@ -83,24 +86,37 @@ extension UiaRequestManager on MatrixState { ), ); default: - final url = Uri.parse( - '${client.homeserver}/_matrix/client/r0/auth/$stage/fallback/web?session=${uiaRequest.session}', + final stageUrl = uiaRequest.params + .tryGetMap(stage) + ?.tryGet('url'); + final fallbackUrl = client.homeserver!.replace( + path: '/_matrix/client/v3/auth/$stage/fallback/web', + queryParameters: {'session': uiaRequest.session}, + ); + final url = stageUrl != null + ? (Uri.tryParse(stageUrl) ?? fallbackUrl) + : fallbackUrl; + + final consent = await showOkCancelAlertDialog( + useRootNavigator: false, + title: l10n.pleaseFollowInstructionsOnWeb, + context: navigatorContext, + okLabel: l10n.open, + cancelLabel: l10n.cancel, + ); + if (consent != OkCancelResult.ok) return uiaRequest.cancel(); + + launchUrl(url, mode: LaunchMode.inAppBrowserView); + final completer = Completer(); + final listener = AppLifecycleListener( + onResume: () => completer.complete(), + ); + await completer.future; + listener.dispose(); + + return uiaRequest.completeStage( + AuthenticationData(session: uiaRequest.session), ); - launchUrlString(url.toString()); - if (OkCancelResult.ok == - await showOkCancelAlertDialog( - useRootNavigator: false, - title: l10n.pleaseFollowInstructionsOnWeb, - context: navigatorContext, - okLabel: l10n.next, - cancelLabel: l10n.cancel, - )) { - return uiaRequest.completeStage( - AuthenticationData(session: uiaRequest.session), - ); - } else { - return uiaRequest.cancel(); - } } } catch (e, s) { Logs().e('Error while background UIA', e, s); diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart index 00ef99abc..eb41ae102 100644 --- a/lib/utils/url_launcher.dart +++ b/lib/utils/url_launcher.dart @@ -99,13 +99,16 @@ class UrlLauncher { // okay, we have either an http or an https URI. // As some platforms have issues with opening unicode URLs, we are going to help // them out by punycode-encoding them for them ourself. - final newHost = uri.host.split('.').map((hostPartEncoded) { - final hostPart = Uri.decodeComponent(hostPartEncoded); - final hostPartPunycode = punycodeEncode(hostPart); - return hostPartPunycode != '$hostPart-' - ? 'xn--$hostPartPunycode' - : hostPart; - }).join('.'); + final newHost = uri.host + .split('.') + .map((hostPartEncoded) { + final hostPart = Uri.decodeComponent(hostPartEncoded); + final hostPartPunycode = punycodeEncode(hostPart); + return hostPartPunycode != '$hostPart-' + ? 'xn--$hostPartPunycode' + : hostPart; + }) + .join('.'); // Force LaunchMode.externalApplication, otherwise url_launcher will default // to opening links in a webview on mobile platforms. launchUrlString( @@ -117,17 +120,17 @@ class UrlLauncher { void openMatrixToUrl() async { final matrix = Matrix.of(context); final url = this.url!.replaceFirst( - AppConfig.deepLinkPrefix, - AppConfig.inviteLinkPrefix, - ); + AppConfig.deepLinkPrefix, + AppConfig.inviteLinkPrefix, + ); // The identifier might be a matrix.to url and needs escaping. Or, it might have multiple // identifiers (room id & event id), or it might also have a query part. // All this needs parsing. - final identityParts = url.parseIdentifierIntoParts() ?? + final identityParts = + url.parseIdentifierIntoParts() ?? Uri.tryParse(url)?.host.parseIdentifierIntoParts() ?? - Uri.tryParse(url) - ?.pathSegments + Uri.tryParse(url)?.pathSegments .lastWhereOrNull((_) => true) ?.parseIdentifierIntoParts(); if (identityParts == null) { @@ -138,7 +141,8 @@ class UrlLauncher { // we got a room! Let's open that one final roomIdOrAlias = identityParts.primaryIdentifier; final event = identityParts.secondaryIdentifier; - var room = matrix.client.getRoomByAlias(roomIdOrAlias) ?? + var room = + matrix.client.getRoomByAlias(roomIdOrAlias) ?? matrix.client.getRoomById(roomIdOrAlias); var roomId = room?.id; // we make the servers a set and later on convert to a list, so that we can easily @@ -171,10 +175,7 @@ class UrlLauncher { // we have the room, so....just open it if (event != null) { context.go( - '/${Uri( - pathSegments: ['rooms', room.id], - queryParameters: {'event': event}, - )}', + '/${Uri(pathSegments: ['rooms', room.id], queryParameters: {'event': event})}', ); } else { context.go('/rooms/${room.id}'); @@ -184,9 +185,8 @@ class UrlLauncher { // #Pangea // await showAdaptiveDialog( // context: context, - // builder: (c) => PublicRoomDialog( - // roomAlias: identityParts.primaryIdentifier, - // ), + // builder: (c) => + // PublicRoomDialog(roomAlias: identityParts.primaryIdentifier), // ); await PublicRoomBottomSheet.show( context: context, @@ -231,12 +231,11 @@ class UrlLauncher { var noProfileWarning = false; final profileResult = await showFutureLoadingDialog( context: context, - future: () => matrix.client.getProfileFromUserId(userId).catchError( - (_) { - noProfileWarning = true; - return Profile(userId: userId); - }, - ), + future: () => + matrix.client.getProfileFromUserId(userId).catchError((_) { + noProfileWarning = true; + return Profile(userId: userId); + }), ); await UserDialog.show( context: context, diff --git a/lib/utils/voip/video_renderer.dart b/lib/utils/voip/video_renderer.dart index afdd9be2e..935b3984c 100644 --- a/lib/utils/voip/video_renderer.dart +++ b/lib/utils/voip/video_renderer.dart @@ -45,8 +45,9 @@ class _VideoRendererState extends State { @override void initState() { - _streamChangeSubscription = - widget.stream?.onStreamChanged.stream.listen((stream) { + _streamChangeSubscription = widget.stream?.onStreamChanged.stream.listen(( + stream, + ) { setState(() { _renderer?.srcObject = stream; }); diff --git a/lib/utils/voip_plugin.dart b/lib/utils/voip_plugin.dart index c5120d604..adef19dd6 100644 --- a/lib/utils/voip_plugin.dart +++ b/lib/utils/voip_plugin.dart @@ -33,13 +33,15 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { @override void didChangeAppLifecycleState(AppLifecycleState? state) { - background = (state == AppLifecycleState.detached || + background = + (state == AppLifecycleState.detached || state == AppLifecycleState.paused); } void addCallingOverlay(String callId, CallSession call) { - final context = - kIsWeb ? ChatList.contextForVoip! : this.context; // web is weird + final context = kIsWeb + ? ChatList.contextForVoip! + : this.context; // web is weird if (overlayEntry != null) { Logs().e('[VOIP] addCallingOverlay: The call session already exists?'); @@ -85,8 +87,7 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { Future createPeerConnection( Map configuration, [ Map constraints = const {}, - ]) => - webrtc_impl.createPeerConnection(configuration, constraints); + ]) => webrtc_impl.createPeerConnection(configuration, constraints); Future get hasCallingAccount async => false; diff --git a/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart b/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart index a80289032..40fc3f96d 100644 --- a/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart +++ b/lib/widgets/adaptive_dialogs/adaptive_dialog_action.dart @@ -1,11 +1,28 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:fluffychat/config/app_config.dart'; + class AdaptiveDialogAction extends StatelessWidget { final VoidCallback? onPressed; final bool autofocus; final Widget child; final bool bigButtons; + final BorderRadius? borderRadius; + + static const BorderRadius topRadius = BorderRadius.only( + topLeft: Radius.circular(AppConfig.borderRadius), + topRight: Radius.circular(AppConfig.borderRadius), + bottomLeft: Radius.circular(2), + bottomRight: Radius.circular(2), + ); + static const BorderRadius centerRadius = BorderRadius.all(Radius.circular(2)); + static const BorderRadius bottomRadius = BorderRadius.only( + bottomLeft: Radius.circular(AppConfig.borderRadius), + bottomRight: Radius.circular(AppConfig.borderRadius), + topLeft: Radius.circular(2), + topRight: Radius.circular(2), + ); const AdaptiveDialogAction({ super.key, @@ -13,6 +30,7 @@ class AdaptiveDialogAction extends StatelessWidget { required this.child, this.autofocus = false, this.bigButtons = false, + this.borderRadius, }); @override @@ -25,11 +43,16 @@ class AdaptiveDialogAction extends StatelessWidget { case TargetPlatform.windows: if (bigButtons) { return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), + padding: const EdgeInsets.symmetric(vertical: 2.0), child: SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: + borderRadius ?? + BorderRadius.circular(AppConfig.borderRadius), + ), backgroundColor: autofocus ? theme.colorScheme.primary : theme.colorScheme.surfaceBright, diff --git a/lib/widgets/adaptive_dialogs/dialog_text_field.dart b/lib/widgets/adaptive_dialogs/dialog_text_field.dart index 7838da05f..aaae9038f 100644 --- a/lib/widgets/adaptive_dialogs/dialog_text_field.dart +++ b/lib/widgets/adaptive_dialogs/dialog_text_field.dart @@ -98,10 +98,7 @@ class DialogTextField extends StatelessWidget { if (errorText != null) Text( errorText, - style: TextStyle( - fontSize: 11, - color: theme.colorScheme.error, - ), + style: TextStyle(fontSize: 11, color: theme.colorScheme.error), textAlign: TextAlign.left, ), ], diff --git a/lib/widgets/adaptive_dialogs/public_room_dialog.dart b/lib/widgets/adaptive_dialogs/public_room_dialog.dart index c7af78ba3..7fee87bfb 100644 --- a/lib/widgets/adaptive_dialogs/public_room_dialog.dart +++ b/lib/widgets/adaptive_dialogs/public_room_dialog.dart @@ -18,7 +18,7 @@ import 'adaptive_dialog_action.dart'; class PublicRoomDialog extends StatelessWidget { final String? roomAlias; - final PublicRoomsChunk? chunk; + final PublishedRoomsChunk? chunk; final List? via; const PublicRoomDialog({super.key, this.roomAlias, this.chunk, this.via}); @@ -30,15 +30,14 @@ class PublicRoomDialog extends StatelessWidget { final result = await showFutureLoadingDialog( context: context, future: () async { - if (chunk != null && client.getRoomById(chunk.roomId) != null) { + if (chunk != null && + client.getRoomById(chunk.roomId) != null && + client.getRoomById(chunk.roomId)?.membership != Membership.leave) { return chunk.roomId; } final roomId = chunk != null && knock ? await client.knockRoom(chunk.roomId, via: via) - : await client.joinRoom( - roomAlias ?? chunk!.roomId, - via: via, - ); + : await client.joinRoom(roomAlias ?? chunk!.roomId, via: via); if (!knock && client.getRoomById(roomId) == null) { await client.waitForRoomInSync(roomId); @@ -64,21 +63,21 @@ class PublicRoomDialog extends StatelessWidget { if (chunk?.roomType != 'm.space' && !client.getRoomById(result.result!)!.isSpace) { context.go('/rooms/$roomId'); + } else { + context.go('/rooms?spaceId=$roomId'); } return; } - bool _testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias; + bool _testRoom(PublishedRoomsChunk r) => r.canonicalAlias == roomAlias; - Future _search(BuildContext context) async { + Future _search(BuildContext context) async { final chunk = this.chunk; if (chunk != null) return chunk; final query = await Matrix.of(context).client.queryPublicRooms( - server: roomAlias!.domain, - filter: PublicRoomQueryFilter( - genericSearchTerm: roomAlias, - ), - ); + server: roomAlias!.domain, + filter: PublicRoomQueryFilter(genericSearchTerm: roomAlias), + ); if (!query.chunk.any(_testRoom)) { throw (L10n.of(context).noRoomsFound); } @@ -100,7 +99,7 @@ class PublicRoomDialog extends StatelessWidget { ), content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256), - child: FutureBuilder( + child: FutureBuilder( future: _search(context), builder: (context, snapshot) { final theme = Theme.of(context); @@ -111,8 +110,8 @@ class PublicRoomDialog extends StatelessWidget { return SingleChildScrollView( child: Column( spacing: 8, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: .min, + crossAxisAlignment: .stretch, children: [ if (roomLink != null) HoverBuilder( @@ -121,9 +120,7 @@ class PublicRoomDialog extends StatelessWidget { cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () { - Clipboard.setData( - ClipboardData(text: roomLink), - ); + Clipboard.setData(ClipboardData(text: roomLink)); setState(() { copied = true; }); @@ -133,8 +130,9 @@ class PublicRoomDialog extends StatelessWidget { children: [ WidgetSpan( child: Padding( - padding: - const EdgeInsets.only(right: 4.0), + padding: const EdgeInsets.only( + right: 4.0, + ), child: AnimatedScale( duration: FluffyThemes.animationDuration, @@ -142,8 +140,8 @@ class PublicRoomDialog extends StatelessWidget { scale: hovered ? 1.33 : copied - ? 1.25 - : 1.0, + ? 1.25 + : 1.0, child: Icon( copied ? Icons.check_circle @@ -156,8 +154,9 @@ class PublicRoomDialog extends StatelessWidget { ), TextSpan(text: roomLink), ], - style: theme.textTheme.bodyMedium - ?.copyWith(fontSize: 10), + style: theme.textTheme.bodyMedium?.copyWith( + fontSize: 10, + ), ), textAlign: TextAlign.center, ), @@ -172,25 +171,26 @@ class PublicRoomDialog extends StatelessWidget { size: Avatar.defaultSize * 2, onTap: avatar != null ? () => showDialog( - context: context, - builder: (_) => MxcImageViewer(avatar), - ) + context: context, + builder: (_) => MxcImageViewer(avatar), + ) : null, ), ), if (profile?.numJoinedMembers != null) Text( - L10n.of(context).countParticipants( - profile?.numJoinedMembers ?? 0, - ), + L10n.of( + context, + ).countParticipants(profile?.numJoinedMembers ?? 0), style: const TextStyle(fontSize: 10), textAlign: TextAlign.center, ), if (topic != null && topic.isNotEmpty) SelectableLinkify( text: topic, - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), + textScaleFactor: MediaQuery.textScalerOf( + context, + ).scale(1), textAlign: TextAlign.center, options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( @@ -210,18 +210,20 @@ class PublicRoomDialog extends StatelessWidget { actions: [ AdaptiveDialogAction( bigButtons: true, + borderRadius: AdaptiveDialogAction.topRadius, onPressed: () => _joinRoom(context), child: Text( chunk?.joinRule == 'knock' && Matrix.of(context).client.getRoomById(chunk!.roomId) == null ? L10n.of(context).knock : chunk?.roomType == 'm.space' - ? L10n.of(context).joinSpace - : L10n.of(context).joinRoom, + ? L10n.of(context).joinSpace + : L10n.of(context).joinRoom, ), ), AdaptiveDialogAction( bigButtons: true, + borderRadius: AdaptiveDialogAction.bottomRadius, onPressed: Navigator.of(context).pop, child: Text(L10n.of(context).close), ), diff --git a/lib/widgets/adaptive_dialogs/show_modal_action_popup.dart b/lib/widgets/adaptive_dialogs/show_modal_action_popup.dart index 7e6709871..f99e69353 100644 --- a/lib/widgets/adaptive_dialogs/show_modal_action_popup.dart +++ b/lib/widgets/adaptive_dialogs/show_modal_action_popup.dart @@ -23,7 +23,7 @@ Future showModalActionPopup({ clipBehavior: Clip.hardEdge, constraints: BoxConstraints( maxWidth: 512, - maxHeight: MediaQuery.of(context).size.height - 32, + maxHeight: MediaQuery.sizeOf(context).height - 32, ), builder: (context) => ListView( shrinkWrap: true, @@ -32,10 +32,7 @@ Future showModalActionPopup({ ListTile( title: title == null ? null - : Text( - title, - style: theme.textTheme.labelSmall, - ), + : Text(title, style: theme.textTheme.labelSmall), subtitle: message == null ? null : Text(message), ), const Divider(height: 1), @@ -49,8 +46,9 @@ Future showModalActionPopup({ style: action.isDestructive ? TextStyle( color: theme.colorScheme.error, - fontWeight: - action.isDefaultAction ? FontWeight.bold : null, + fontWeight: action.isDefaultAction + ? FontWeight.bold + : null, ) : null, ), diff --git a/lib/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart b/lib/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart index 542bdb7a0..3b6c72684 100644 --- a/lib/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart +++ b/lib/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart @@ -16,50 +16,49 @@ Future showOkCancelAlertDialog({ String? cancelLabel, bool isDestructive = false, bool useRootNavigator = true, -}) => - showAdaptiveDialog( - context: context, - useRootNavigator: useRootNavigator, - builder: (context) => AlertDialog.adaptive( - title: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 256), - child: Text(title), - ), - content: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 256), - child: message == null - ? null - : SelectableLinkify( - text: message, - textScaleFactor: MediaQuery.textScalerOf(context).scale(1), - linkStyle: TextStyle( - color: Theme.of(context).colorScheme.primary, - decorationColor: Theme.of(context).colorScheme.primary, - ), - options: const LinkifyOptions(humanize: false), - onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), - ), - ), - actions: [ - AdaptiveDialogAction( - onPressed: () => Navigator.of(context) - .pop(OkCancelResult.cancel), - child: Text(cancelLabel ?? L10n.of(context).cancel), - ), - AdaptiveDialogAction( - onPressed: () => - Navigator.of(context).pop(OkCancelResult.ok), - autofocus: true, - child: Text( - okLabel ?? L10n.of(context).ok, - style: isDestructive - ? TextStyle(color: Theme.of(context).colorScheme.error) - : null, +}) => showAdaptiveDialog( + context: context, + useRootNavigator: useRootNavigator, + builder: (context) => AlertDialog.adaptive( + title: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 256), + child: Text(title), + ), + content: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 256), + child: message == null + ? null + : SelectableLinkify( + text: message, + textScaleFactor: MediaQuery.textScalerOf(context).scale(1), + linkStyle: TextStyle( + color: Theme.of(context).colorScheme.primary, + decorationColor: Theme.of(context).colorScheme.primary, + ), + options: const LinkifyOptions(humanize: false), + onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), ), - ), - ], + ), + actions: [ + AdaptiveDialogAction( + onPressed: () => + Navigator.of(context).pop(OkCancelResult.cancel), + child: Text(cancelLabel ?? L10n.of(context).cancel), ), - ); + AdaptiveDialogAction( + onPressed: () => + Navigator.of(context).pop(OkCancelResult.ok), + autofocus: true, + child: Text( + okLabel ?? L10n.of(context).ok, + style: isDestructive + ? TextStyle(color: Theme.of(context).colorScheme.error) + : null, + ), + ), + ], + ), +); Future showOkAlertDialog({ required BuildContext context, @@ -67,37 +66,36 @@ Future showOkAlertDialog({ String? message, String? okLabel, bool useRootNavigator = true, -}) => - showAdaptiveDialog( - context: context, - useRootNavigator: useRootNavigator, - builder: (context) => AlertDialog.adaptive( - title: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 256), - child: Text(title), - ), - content: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 256), - child: message == null - ? null - : SelectableLinkify( - text: message, - textScaleFactor: MediaQuery.textScalerOf(context).scale(1), - linkStyle: TextStyle( - color: Theme.of(context).colorScheme.primary, - decorationColor: Theme.of(context).colorScheme.primary, - ), - options: const LinkifyOptions(humanize: false), - onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), - ), - ), - actions: [ - AdaptiveDialogAction( - onPressed: () => - Navigator.of(context).pop(OkCancelResult.ok), - autofocus: true, - child: Text(okLabel ?? L10n.of(context).close), - ), - ], +}) => showAdaptiveDialog( + context: context, + useRootNavigator: useRootNavigator, + builder: (context) => AlertDialog.adaptive( + title: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 256), + child: Text(title), + ), + content: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 256), + child: message == null + ? null + : SelectableLinkify( + text: message, + textScaleFactor: MediaQuery.textScalerOf(context).scale(1), + linkStyle: TextStyle( + color: Theme.of(context).colorScheme.primary, + decorationColor: Theme.of(context).colorScheme.primary, + ), + options: const LinkifyOptions(humanize: false), + onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), + ), + ), + actions: [ + AdaptiveDialogAction( + onPressed: () => + Navigator.of(context).pop(OkCancelResult.ok), + autofocus: true, + child: Text(okLabel ?? L10n.of(context).close), ), - ); + ], + ), +); diff --git a/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart b/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart index cba2d7247..4ac5b871b 100644 --- a/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart +++ b/lib/widgets/adaptive_dialogs/show_text_input_dialog.dart @@ -45,7 +45,7 @@ Future showTextInputDialog({ content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 256), child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ if (message != null) SelectableLinkify( diff --git a/lib/widgets/adaptive_dialogs/user_dialog.dart b/lib/widgets/adaptive_dialogs/user_dialog.dart index 866f53bcb..ec347dc02 100644 --- a/lib/widgets/adaptive_dialogs/user_dialog.dart +++ b/lib/widgets/adaptive_dialogs/user_dialog.dart @@ -22,15 +22,12 @@ class UserDialog extends StatelessWidget { required BuildContext context, required Profile profile, bool noProfileWarning = false, - }) => - showAdaptiveDialog( - context: context, - barrierDismissible: true, - builder: (context) => UserDialog( - profile, - noProfileWarning: noProfileWarning, - ), - ); + }) => showAdaptiveDialog( + context: context, + barrierDismissible: true, + builder: (context) => + UserDialog(profile, noProfileWarning: noProfileWarning), + ); final Profile profile; final bool noProfileWarning; @@ -41,7 +38,8 @@ class UserDialog extends StatelessWidget { Widget build(BuildContext context) { final client = Matrix.of(context).client; final dmRoomId = client.getDirectChatFromUserId(profile.userId); - final displayname = profile.displayName ?? + final displayname = + profile.displayName ?? profile.userId.localpart ?? L10n.of(context).user; var copied = false; @@ -66,16 +64,32 @@ class UserDialog extends StatelessWidget { final presenceText = presence.currentlyActive == true ? L10n.of(context).currentlyActive : lastActiveTimestamp != null - ? L10n.of(context).lastActiveAgo( - lastActiveTimestamp.localizedTimeShort(context), - ) - : null; + ? L10n.of(context).lastActiveAgo( + lastActiveTimestamp.localizedTimeShort(context), + ) + : null; return SingleChildScrollView( child: Column( spacing: 8, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: .min, + crossAxisAlignment: .stretch, children: [ + Center( + child: Avatar( + mxContent: avatar, + name: displayname, + size: Avatar.defaultSize * 2, + onTap: avatar != null + ? () => showDialog( + context: context, + builder: (_) => MxcImageViewer(avatar), + ) + : null, + // #Pangea + userId: profile.userId, + // Pangea# + ), + ), HoverBuilder( builder: (context, hovered) => StatefulBuilder( builder: (context, setState) => MouseRegion( @@ -101,8 +115,8 @@ class UserDialog extends StatelessWidget { scale: hovered ? 1.33 : copied - ? 1.25 - : 1.0, + ? 1.25 + : 1.0, child: Icon( copied ? Icons.check_circle @@ -115,8 +129,9 @@ class UserDialog extends StatelessWidget { ), TextSpan(text: profile.userId), ], - style: theme.textTheme.bodyMedium - ?.copyWith(fontSize: 10), + style: theme.textTheme.bodyMedium?.copyWith( + fontSize: 10, + ), ), textAlign: TextAlign.center, ), @@ -124,22 +139,6 @@ class UserDialog extends StatelessWidget { ), ), ), - Center( - child: Avatar( - mxContent: avatar, - name: displayname, - size: Avatar.defaultSize * 2, - onTap: avatar != null - ? () => showDialog( - context: context, - builder: (_) => MxcImageViewer(avatar), - ) - : null, - // #Pangea - userId: profile.userId, - // Pangea# - ), - ), if (presenceText != null) Text( presenceText, @@ -150,8 +149,9 @@ class UserDialog extends StatelessWidget { // if (statusMsg != null) // SelectableLinkify( // text: statusMsg, - // textScaleFactor: - // MediaQuery.textScalerOf(context).scale(1), + // textScaleFactor: MediaQuery.textScalerOf( + // context, + // ).scale(1), // textAlign: TextAlign.center, // options: const LinkifyOptions(humanize: false), // linkStyle: TextStyle( @@ -181,12 +181,10 @@ class UserDialog extends StatelessWidget { actions: [ if (client.userID != profile.userId) ...[ AdaptiveDialogAction( + borderRadius: AdaptiveDialogAction.topRadius, bigButtons: true, onPressed: () async { final router = GoRouter.of(context); - // #Pangea - // Navigator.of(context).pop(); - // Pangea# final roomIdResult = await showFutureLoadingDialog( context: context, // #Pangea @@ -197,11 +195,9 @@ class UserDialog extends StatelessWidget { ), // Pangea# ); - // #Pangea - Navigator.of(context).pop(); - // Pangea# final roomId = roomIdResult.result; if (roomId == null) return; + if (context.mounted) Navigator.of(context).pop(); router.go('/rooms/$roomId'); }, child: Text( @@ -212,6 +208,7 @@ class UserDialog extends StatelessWidget { ), AdaptiveDialogAction( bigButtons: true, + borderRadius: AdaptiveDialogAction.centerRadius, onPressed: () { final router = GoRouter.of(context); Navigator.of(context).pop(); @@ -228,6 +225,7 @@ class UserDialog extends StatelessWidget { ], AdaptiveDialogAction( bigButtons: true, + borderRadius: AdaptiveDialogAction.bottomRadius, onPressed: Navigator.of(context).pop, child: Text(L10n.of(context).close), ), diff --git a/lib/widgets/app_lock.dart b/lib/widgets/app_lock.dart index d337358d7..a8b933011 100644 --- a/lib/widgets/app_lock.dart +++ b/lib/widgets/app_lock.dart @@ -4,7 +4,6 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; -import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/widgets/lock_screen.dart'; class AppLockWidget extends StatefulWidget { @@ -42,7 +41,7 @@ class AppLock extends State with WidgetsBindingObserver { WidgetsBinding.instance.addPostFrameCallback(_checkLoggedIn); } - void _checkLoggedIn(_) async { + void _checkLoggedIn(dynamic _) async { if (widget.clients.any((client) => client.isLogged())) return; await changePincode(null); @@ -65,7 +64,7 @@ class AppLock extends State with WidgetsBindingObserver { Future changePincode(String? pincode) async { await const FlutterSecureStorage().write( - key: SettingKeys.appLockKey, + key: 'chat.fluffy.app_lock', value: pincode, ); _pincode = pincode; @@ -83,8 +82,8 @@ class AppLock extends State with WidgetsBindingObserver { } void showLockScreen() => setState(() { - _isLocked = true; - }); + _isLocked = true; + }); Future pauseWhile(Future future) async { _paused = true; @@ -95,20 +94,15 @@ class AppLock extends State with WidgetsBindingObserver { } } - static AppLock of(BuildContext context) => Provider.of( - context, - listen: false, - ); + static AppLock of(BuildContext context) => + Provider.of(context, listen: false); @override Widget build(BuildContext context) => Provider( - create: (_) => this, - child: Stack( - fit: StackFit.expand, - children: [ - widget.child, - if (isLocked) const LockScreen(), - ], - ), - ); + create: (_) => this, + child: Stack( + fit: StackFit.expand, + children: [widget.child, if (isLocked) const LockScreen()], + ), + ); } diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index 4745ba53d..7e1975a1c 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -21,6 +21,8 @@ class Avatar extends StatelessWidget { final BorderRadius? borderRadius; final IconData? icon; final BorderSide? border; + final Color? backgroundColor; + final Color? textColor; // #Pangea final bool useRive; final bool showPresence; @@ -42,6 +44,8 @@ class Avatar extends StatelessWidget { this.borderRadius, this.border, this.icon, + this.backgroundColor, + this.textColor, // #Pangea this.useRive = false, this.showPresence = true, @@ -58,10 +62,12 @@ class Avatar extends StatelessWidget { final theme = Theme.of(context); final name = this.name; - final fallbackLetters = - name == null || name.isEmpty ? '@' : name.substring(0, 1); + final fallbackLetters = name == null || name.isEmpty + ? '@' + : name.substring(0, 1); - final noPic = mxContent == null || + final noPic = + mxContent == null || mxContent.toString().isEmpty || mxContent.toString() == 'null'; final borderRadius = this.borderRadius ?? BorderRadius.circular(size / 2); @@ -80,62 +86,63 @@ class Avatar extends StatelessWidget { side: border ?? BorderSide.none, ), clipBehavior: Clip.antiAlias, - // #Pangea - // child: noPic + // child: MxcImage( child: (userId ?? presenceUserId) == BotName.byEnvironment ? BotFace( width: size, expression: BotExpression.idle, useRive: useRive, ) - : noPic + // #Pangea + : !(mxContent.toString().startsWith('mxc://')) + ? ImageByUrl( + imageUrl: mxContent, + width: size, + replacement: Center( + child: Icon( + icon ?? Icons.person_2, + color: theme.colorScheme.tertiary, + size: size / 1.5, + ), + ), + borderRadius: borderRadius, + ) + // Pangea# + : MxcImage( // Pangea# - ? Container( - decoration: - BoxDecoration(color: name?.lightColorAvatar), - alignment: Alignment.center, - child: Text( - fallbackLetters, - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'RobotoMono', - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: (size / 2.5).roundToDouble(), - ), - ), - ) - // #Pangea - : !(mxContent.toString().startsWith('mxc://')) - ? ImageByUrl( - imageUrl: mxContent, - width: size, - replacement: Center( - child: Icon( - icon ?? Icons.person_2, - color: theme.colorScheme.tertiary, - size: size / 1.5, + client: client, + borderRadius: borderRadius, + key: ValueKey(mxContent.toString()), + cacheKey: '${mxContent}_$size', + uri: mxContent, + fit: BoxFit.cover, + width: size, + height: size, + placeholder: (_) => noPic + ? Container( + decoration: BoxDecoration( + color: backgroundColor ?? name?.lightColorAvatar, + ), + alignment: Alignment.center, + child: Text( + fallbackLetters, + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'RobotoMono', + color: textColor ?? Colors.white, + fontWeight: FontWeight.bold, + fontSize: (size / 2.5).roundToDouble(), ), ), - borderRadius: borderRadius, ) - // Pangea# - : MxcImage( - client: client, - key: ValueKey(mxContent.toString()), - cacheKey: '${mxContent}_$size', - uri: mxContent, - fit: BoxFit.cover, - width: size, - height: size, - placeholder: (_) => Center( - child: Icon( - Icons.person_2, - color: theme.colorScheme.tertiary, - size: size / 1.5, - ), + : Center( + child: Icon( + Icons.person_2, + color: theme.colorScheme.tertiary, + size: size / 1.5, ), ), + ), ), ), // #Pangea @@ -160,8 +167,8 @@ class Avatar extends StatelessWidget { final dotColor = presence.presence.isOnline ? Colors.green : presence.presence.isUnavailable - ? Colors.orange - : Colors.grey; + ? Colors.orange + : Colors.grey; return Positioned( // #Pangea // bottom: -3, @@ -206,10 +213,7 @@ class Avatar extends StatelessWidget { if (onTap == null) return container; return MouseRegion( cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: onTap, - child: container, - ), + child: GestureDetector(onTap: onTap, child: container), ); } } diff --git a/lib/widgets/avatar_page_header.dart b/lib/widgets/avatar_page_header.dart new file mode 100644 index 000000000..bff0d3881 --- /dev/null +++ b/lib/widgets/avatar_page_header.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/config/themes.dart'; + +class AvatarPageHeader extends StatelessWidget { + final Widget avatar; + final void Function()? onAvatarEdit; + final Widget? textButtonLeft, textButtonRight; + final List iconButtons; + + const AvatarPageHeader({ + super.key, + required this.avatar, + this.onAvatarEdit, + this.iconButtons = const [], + this.textButtonLeft, + this.textButtonRight, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final onAvatarEdit = this.onAvatarEdit; + return Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: FluffyThemes.columnWidth), + child: Column( + mainAxisSize: .min, + crossAxisAlignment: .center, + spacing: 8.0, + children: [ + Stack( + children: [ + avatar, + if (onAvatarEdit != null) + Positioned( + bottom: 0, + right: 0, + child: FloatingActionButton.small( + elevation: 2, + onPressed: onAvatarEdit, + heroTag: null, + child: const Icon(Icons.camera_alt_outlined), + ), + ), + ], + ), + TextButtonTheme( + data: TextButtonThemeData( + style: TextButton.styleFrom( + disabledForegroundColor: theme.colorScheme.onSurface, + foregroundColor: theme.colorScheme.onSurface, + textStyle: const TextStyle(fontWeight: FontWeight.normal), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: LayoutBuilder( + builder: (context, constraints) { + return Row( + mainAxisAlignment: .center, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: constraints.maxWidth / 2, + ), + child: textButtonLeft, + ), + ConstrainedBox( + constraints: BoxConstraints( + maxWidth: constraints.maxWidth / 2, + ), + child: textButtonRight, + ), + ], + ); + }, + ), + ), + ), + IconButtonTheme( + data: IconButtonThemeData( + style: IconButton.styleFrom( + backgroundColor: theme.colorScheme.surfaceContainer, + iconSize: 24, + padding: const EdgeInsets.all(16), + ), + ), + child: Row( + mainAxisAlignment: .spaceEvenly, + children: iconButtons, + ), + ), + const SizedBox(height: 0.0), + ], + ), + ), + ); + } +} diff --git a/lib/widgets/blur_hash.dart b/lib/widgets/blur_hash.dart index d4ad1dd47..a7fc4fac0 100644 --- a/lib/widgets/blur_hash.dart +++ b/lib/widgets/blur_hash.dart @@ -25,9 +25,7 @@ class BlurHash extends StatefulWidget { class _BlurHashState extends State { Uint8List? _data; - static Future getBlurhashData( - BlurhashData blurhashData, - ) async { + static Future getBlurhashData(BlurhashData blurhashData) async { final blurhash = b.BlurHash.decode(blurhashData.hsh); final img = blurhash.toImage(blurhashData.w, blurhashData.h); return Uint8List.fromList(image.encodePng(img)); @@ -46,11 +44,7 @@ class _BlurHashState extends State { return _data ??= await compute( getBlurhashData, - BlurhashData( - hsh: widget.blurhash, - w: width, - h: height, - ), + BlurhashData(hsh: widget.blurhash, w: width, h: height), ); } @@ -84,21 +78,10 @@ class BlurhashData { final int w; final int h; - const BlurhashData({ - required this.hsh, - required this.w, - required this.h, - }); + const BlurhashData({required this.hsh, required this.w, required this.h}); - factory BlurhashData.fromJson(Map json) => BlurhashData( - hsh: json['hsh'], - w: json['w'], - h: json['h'], - ); + factory BlurhashData.fromJson(Map json) => + BlurhashData(hsh: json['hsh'], w: json['w'], h: json['h']); - Map toJson() => { - 'hsh': hsh, - 'w': w, - 'h': h, - }; + Map toJson() => {'hsh': hsh, 'w': w, 'h': h}; } diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index b82b571b5..eb2ee71db 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog. import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'matrix.dart'; -enum ChatPopupMenuActions { details, mute, unmute, leave, search } +enum ChatPopupMenuActions { details, mute, unmute, emote, leave, search } class ChatSettingsPopupMenu extends StatefulWidget { final Room room; @@ -31,12 +31,12 @@ class ChatSettingsPopupMenuState extends State { super.dispose(); } + void goToEmoteSettings() => + context.push('/rooms/${widget.room.id}/details/emotes'); + @override Widget build(BuildContext context) { - notificationChangeSub ??= Matrix.of(context) - .client - .onSync - .stream + notificationChangeSub ??= Matrix.of(context).client.onSync.stream .where( (syncUpdate) => syncUpdate.accountData?.any( @@ -44,9 +44,7 @@ class ChatSettingsPopupMenuState extends State { ) ?? false, ) - .listen( - (u) => setState(() {}), - ); + .listen((u) => setState(() {})); return Stack( alignment: Alignment.center, children: [ @@ -98,6 +96,8 @@ class ChatSettingsPopupMenuState extends State { case ChatPopupMenuActions.search: context.go('/rooms/${widget.room.id}/search'); break; + case ChatPopupMenuActions.emote: + goToEmoteSettings(); } }, itemBuilder: (BuildContext context) => [ @@ -144,6 +144,16 @@ class ChatSettingsPopupMenuState extends State { ], ), ), + PopupMenuItem( + value: ChatPopupMenuActions.emote, + child: Row( + children: [ + const Icon(Icons.emoji_emotions_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).emoteSettings), + ], + ), + ), PopupMenuItem( value: ChatPopupMenuActions.leave, child: Row( diff --git a/lib/widgets/config_viewer.dart b/lib/widgets/config_viewer.dart index 5026ec4cc..080ea494f 100644 --- a/lib/widgets/config_viewer.dart +++ b/lib/widgets/config_viewer.dart @@ -4,21 +4,26 @@ import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; -class ConfigViewer extends StatelessWidget { +class ConfigViewer extends StatefulWidget { const ConfigViewer({super.key}); + @override + State createState() => _ConfigViewerState(); +} + +class _ConfigViewerState extends State { void _changeSetting( - BuildContext context, AppSettings appSetting, SharedPreferences store, - Function setState, String initialValue, ) async { if (appSetting is AppSettings) { - appSetting.setItem(store, !(initialValue == 'true')); + await appSetting.setItem(!(initialValue == 'true')); + setState(() {}); return; } @@ -31,13 +36,13 @@ class ConfigViewer extends StatelessWidget { if (value == null) return; if (appSetting is AppSettings) { - appSetting.setItem(store, value); + await appSetting.setItem(value); } if (appSetting is AppSettings) { - appSetting.setItem(store, int.parse(value)); + await appSetting.setItem(int.parse(value)); } if (appSetting is AppSettings) { - appSetting.setItem(store, double.parse(value)); + await appSetting.setItem(double.parse(value)); } setState(() {}); @@ -48,10 +53,8 @@ class ConfigViewer extends StatelessWidget { final theme = Theme.of(context); return Scaffold( appBar: AppBar( - title: const Text('Advanced configurations'), - leading: BackButton( - onPressed: () => context.go('/'), - ), + title: Text(L10n.of(context).advancedConfigurations), + leading: BackButton(onPressed: () => context.go('/')), ), body: Column( children: [ @@ -61,44 +64,32 @@ class ConfigViewer extends StatelessWidget { color: theme.colorScheme.errorContainer, child: Text( 'Changing configs by hand is untested! Use without any warranty!', - style: TextStyle( - color: theme.colorScheme.onErrorContainer, - ), + style: TextStyle(color: theme.colorScheme.onErrorContainer), ), ), Expanded( - child: StatefulBuilder( - builder: (context, setState) { - return ListView.builder( - itemCount: AppSettings.values.length, - itemBuilder: (context, i) { - final store = Matrix.of(context).store; - final appSetting = AppSettings.values[i]; - var value = ''; - if (appSetting is AppSettings) { - value = appSetting.getItem(store); - } - if (appSetting is AppSettings) { - value = appSetting.getItem(store).toString(); - } - if (appSetting is AppSettings) { - value = appSetting.getItem(store).toString(); - } - if (appSetting is AppSettings) { - value = appSetting.getItem(store).toString(); - } - return ListTile( - title: Text(appSetting.name), - subtitle: Text(value), - onTap: () => _changeSetting( - context, - appSetting, - store, - setState, - value, - ), - ); - }, + child: ListView.builder( + itemCount: AppSettings.values.length, + itemBuilder: (context, i) { + final store = Matrix.of(context).store; + final appSetting = AppSettings.values[i]; + var value = ''; + if (appSetting is AppSettings) { + value = appSetting.value; + } + if (appSetting is AppSettings) { + value = appSetting.value.toString(); + } + if (appSetting is AppSettings) { + value = appSetting.value.toString(); + } + if (appSetting is AppSettings) { + value = appSetting.value.toString(); + } + return ListTile( + title: Text(appSetting.name), + subtitle: Text(value), + onTap: () => _changeSetting(appSetting, store, value), ); }, ), diff --git a/lib/widgets/error_widget.dart b/lib/widgets/error_widget.dart index 3bbab55e6..16637e01f 100644 --- a/lib/widgets/error_widget.dart +++ b/lib/widgets/error_widget.dart @@ -25,10 +25,10 @@ class _FluffyChatErrorWidgetState extends State { // related sentry issue: https://pangea-chat.sentry.io/issues/5970490357 if (!context.mounted) return; // Pangea# - ErrorReporter(context, 'Error Widget').onErrorCallback( - widget.details.exception, - widget.details.stack, - ); + ErrorReporter( + context, + 'Error Widget', + ).onErrorCallback(widget.details.exception, widget.details.stack); }); } diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index aa773d5d5..ce498d17a 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -8,13 +8,13 @@ import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/routes.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/theme_builder.dart'; -import '../config/app_config.dart'; import '../utils/custom_scroll_behaviour.dart'; import 'matrix.dart'; @@ -42,9 +42,7 @@ class FluffyChatApp extends StatelessWidget { static final GoRouter router = GoRouter( routes: AppRoutes.routes, // #Pangea - observers: [ - GoogleAnalytics.getAnalyticsObserver(), - ], + observers: [GoogleAnalytics.getAnalyticsObserver()], // Pangea# debugLogDiagnostics: true, ); @@ -53,11 +51,14 @@ class FluffyChatApp extends StatelessWidget { Widget build(BuildContext context) { return ThemeBuilder( builder: (context, themeMode, primaryColor) => MaterialApp.router( - title: AppConfig.applicationName, + title: AppSettings.applicationName.value, themeMode: themeMode, theme: FluffyThemes.buildTheme(context, Brightness.light, primaryColor), - darkTheme: - FluffyThemes.buildTheme(context, Brightness.dark, primaryColor), + darkTheme: FluffyThemes.buildTheme( + context, + Brightness.dark, + primaryColor, + ), scrollBehavior: CustomScrollBehavior(), // #Pangea locale: Provider.of(context).locale, diff --git a/lib/widgets/future_loading_dialog.dart b/lib/widgets/future_loading_dialog.dart index 47aa5ad57..e9575364a 100644 --- a/lib/widgets/future_loading_dialog.dart +++ b/lib/widgets/future_loading_dialog.dart @@ -14,7 +14,8 @@ import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart' /// null. Future> showFutureLoadingDialog({ required BuildContext context, - required Future Function() future, + Future Function()? future, + Future Function(void Function(double?) setProgress)? futureWithProgress, String? title, String? backLabel, bool barrierDismissible = false, @@ -28,7 +29,10 @@ Future> showFutureLoadingDialog({ VoidCallback? onDismiss, // Pangea# }) async { - final futureExec = future(); + assert(future != null || futureWithProgress != null); + final onProgressStream = StreamController(); + final futureExec = + futureWithProgress?.call(onProgressStream.add) ?? future!(); final resultFuture = ResultFuture(futureExec); if (delay) { @@ -44,38 +48,25 @@ Future> showFutureLoadingDialog({ } } - // #Pangea - if (context.mounted) { - // Pangea# - final result = await showAdaptiveDialog>( - context: context, - barrierDismissible: barrierDismissible, - builder: (BuildContext context) => LoadingDialog( - future: futureExec, - title: title, - backLabel: backLabel, - exceptionContext: exceptionContext, - // #Pangea - showError: showError, - onError: onError, - onDismiss: onDismiss, - onSuccess: onSuccess, - // Pangea# - ), - ); - return result ?? - Result.error( - Exception('FutureDialog canceled'), - StackTrace.current, - ); - } - - // #Pangea - return Result.error( - Exception('FutureDialog canceled'), - StackTrace.current, + final result = await showAdaptiveDialog>( + context: context, + barrierDismissible: barrierDismissible, + builder: (BuildContext context) => LoadingDialog( + future: futureExec, + title: title, + backLabel: backLabel, + exceptionContext: exceptionContext, + onProgressStream: onProgressStream.stream, + // #Pangea + showError: showError, + onError: onError, + onDismiss: onDismiss, + onSuccess: onSuccess, + // Pangea# + ), ); - // Pangea# + return result ?? + Result.error(Exception('FutureDialog canceled'), StackTrace.current); } class LoadingDialog extends StatefulWidget { @@ -83,6 +74,7 @@ class LoadingDialog extends StatefulWidget { final String? backLabel; final Future future; final ExceptionContext? exceptionContext; + final Stream onProgressStream; // #Pangea final bool Function(Object)? showError; final Object? Function(Object, StackTrace?)? onError; @@ -96,6 +88,7 @@ class LoadingDialog extends StatefulWidget { this.title, this.backLabel, this.exceptionContext, + required this.onProgressStream, // #Pangea this.showError, this.onError, @@ -181,13 +174,17 @@ class LoadingDialogState extends State { content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 256), child: Row( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: .center, children: [ // #Pangea // if (exception == null) ...[ if (exception == null && _successMessage == null) ...[ // Pangea# - const CircularProgressIndicator.adaptive(), + StreamBuilder( + stream: widget.onProgressStream, + builder: (context, snapshot) => + CircularProgressIndicator.adaptive(value: snapshot.data), + ), const SizedBox(width: 20), ], Expanded( @@ -211,47 +208,41 @@ class LoadingDialogState extends State { // ? null // : [ // AdaptiveDialogAction( - // onPressed: () => Navigator.of(context).pop>( - // Result.error( - // exception, - // stackTrace, - // ), - // ), + // onPressed: () => Navigator.of( + // context, + // ).pop>(Result.error(exception, stackTrace)), // child: Text(widget.backLabel ?? L10n.of(context).close), // ), // ], actions: _successMessage != null ? [ AdaptiveDialogAction( - onPressed: () => Navigator.of(context).pop>( - Result.value(_result as T), - ), + onPressed: () => Navigator.of( + context, + ).pop>(Result.value(_result as T)), child: Text(L10n.of(context).close), ), ] : exception == null - ? widget.onDismiss != null - ? [ - AdaptiveDialogAction( - onPressed: () { - widget.onDismiss!(); - Navigator.of(context).pop(); - }, - child: Text(L10n.of(context).cancel), - ), - ] - : null - : [ - AdaptiveDialogAction( - onPressed: () => Navigator.of(context).pop>( - Result.error( - exception, - stackTrace, - ), + ? widget.onDismiss != null + ? [ + AdaptiveDialogAction( + onPressed: () { + widget.onDismiss!(); + Navigator.of(context).pop(); + }, + child: Text(L10n.of(context).cancel), ), - child: Text(widget.backLabel ?? L10n.of(context).close), - ), - ], + ] + : null + : [ + AdaptiveDialogAction( + onPressed: () => Navigator.of( + context, + ).pop>(Result.error(exception, stackTrace)), + child: Text(widget.backLabel ?? L10n.of(context).close), + ), + ], // Pangea# ); } diff --git a/lib/widgets/layouts/empty_page.dart b/lib/widgets/layouts/empty_page.dart index 0ac51a111..8f91e7fde 100644 --- a/lib/widgets/layouts/empty_page.dart +++ b/lib/widgets/layouts/empty_page.dart @@ -9,7 +9,7 @@ class EmptyPage extends StatelessWidget { const EmptyPage({super.key}); @override Widget build(BuildContext context) { - final width = min(MediaQuery.of(context).size.width, EmptyPage._width) / 2; + final width = min(MediaQuery.sizeOf(context).width, EmptyPage._width) / 2; // #Pangea // final theme = Theme.of(context); // Pangea# diff --git a/lib/widgets/layouts/login_scaffold.dart b/lib/widgets/layouts/login_scaffold.dart index 963315e5e..ebf40384f 100644 --- a/lib/widgets/layouts/login_scaffold.dart +++ b/lib/widgets/layouts/login_scaffold.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:particles_network/particles_network.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; class LoginScaffold extends StatelessWidget { final Widget body; @@ -28,74 +31,62 @@ class LoginScaffold extends StatelessWidget { return Scaffold( key: const Key('LoginScaffold'), appBar: appBar, - body: SafeArea( - // #Pangea - child: Container( - decoration: const BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: AssetImage('assets/login_wallpaper.png'), - ), - ), - // Pangea# - child: body, - ), - ), + body: SafeArea(child: body), ); } return Container( - // #Pangea - // decoration: BoxDecoration( - // gradient: LinearGradient( - // colors: [ - // theme.colorScheme.surfaceContainerLow, - // theme.colorScheme.surfaceContainer, - // theme.colorScheme.surfaceContainerHighest, - // ], - // begin: Alignment.topLeft, - // end: Alignment.bottomRight, - // ), - // ), - decoration: const BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: AssetImage('assets/login_wallpaper.png'), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + theme.colorScheme.surfaceContainerLow, + theme.colorScheme.surfaceContainer, + theme.colorScheme.surfaceContainerHighest, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, ), ), - // Pangea# - child: Column( + child: Stack( children: [ - const SizedBox(height: 16), - Expanded( - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Material( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - clipBehavior: Clip.hardEdge, - elevation: theme.appBarTheme.scrolledUnderElevation ?? 4, - shadowColor: theme.appBarTheme.shadowColor, - child: ConstrainedBox( - constraints: isMobileMode - ? const BoxConstraints() - : const BoxConstraints( - maxWidth: 480, - // #Pangea - // maxHeight: 640, - maxHeight: 700, - // Pangea# - ), - child: Scaffold( - key: const Key('LoginScaffold'), - appBar: appBar, - body: SafeArea(child: body), + if (!MediaQuery.of(context).disableAnimations) + ParticleNetwork( + particleColor: theme.colorScheme.primary, + lineColor: theme.colorScheme.secondary, + ), + Column( + children: [ + const SizedBox(height: 16), + Expanded( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Material( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + clipBehavior: Clip.hardEdge, + elevation: theme.appBarTheme.scrolledUnderElevation ?? 4, + shadowColor: theme.appBarTheme.shadowColor, + child: ConstrainedBox( + constraints: isMobileMode + ? const BoxConstraints() + : const BoxConstraints( + maxWidth: 480, + maxHeight: 640, + ), + child: Scaffold( + key: const Key('LoginScaffold'), + appBar: appBar, + body: SafeArea(child: body), + ), + ), ), ), ), ), - ), + const _PrivacyButtons(mainAxisAlignment: .center), + ], ), - const _PrivacyButtons(mainAxisAlignment: MainAxisAlignment.center), ], ), ); @@ -119,34 +110,20 @@ class _PrivacyButtons extends StatelessWidget { children: [ TextButton( onPressed: () => launchUrlString(AppConfig.website), - child: Text( - L10n.of(context).website, - style: shadowTextStyle, - ), + child: Text(L10n.of(context).website, style: shadowTextStyle), ), TextButton( onPressed: () => launchUrlString(AppConfig.supportUrl), - child: Text( - L10n.of(context).help, - style: shadowTextStyle, - ), + child: Text(L10n.of(context).help, style: shadowTextStyle), ), TextButton( - onPressed: () => launchUrlString(AppConfig.privacyUrl), - child: Text( - L10n.of(context).privacy, - style: shadowTextStyle, - ), + onPressed: () => launchUrl(AppConfig.privacyUrl), + child: Text(L10n.of(context).privacy, style: shadowTextStyle), + ), + TextButton( + onPressed: () => PlatformInfos.showDialog(context), + child: Text(L10n.of(context).about, style: shadowTextStyle), ), - // #Pangea - // TextButton( - // onPressed: () => PlatformInfos.showDialog(context), - // child: Text( - // L10n.of(context).about, - // style: shadowTextStyle, - // ), - // ), - // Pangea# ], ), ), diff --git a/lib/widgets/layouts/max_width_body.dart b/lib/widgets/layouts/max_width_body.dart index 0a8631cc8..b75272a3d 100644 --- a/lib/widgets/layouts/max_width_body.dart +++ b/lib/widgets/layouts/max_width_body.dart @@ -47,22 +47,21 @@ class MaxWidthBody extends StatelessWidget { // constraints: const BoxConstraints( // maxWidth: FluffyThemes.columnWidth * 1.5, // ), - constraints: BoxConstraints( - maxWidth: maxWidth, - ), + constraints: BoxConstraints(maxWidth: maxWidth), // Pangea# child: Material( shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + // #Pangea + // side: BorderSide(color: theme.dividerColor), side: BorderSide( - // #Pangea - // color: theme.dividerColor, color: showBorder ? theme.dividerColor : Colors.transparent, - // Pangea# ), + // Pangea# ), clipBehavior: Clip.hardEdge, child: Padding( diff --git a/lib/widgets/layouts/two_column_layout.dart b/lib/widgets/layouts/two_column_layout.dart index c9ad03897..13fcd477a 100644 --- a/lib/widgets/layouts/two_column_layout.dart +++ b/lib/widgets/layouts/two_column_layout.dart @@ -31,9 +31,10 @@ class TwoColumnLayout extends StatelessWidget { final spaceID = state.pathParameters['spaceid']; if (roomID == null && spaceID == null) { - showNavRail = !["newcourse", ":construct"].any( - (p) => state.fullPath?.contains(p) ?? false, - ); + showNavRail = ![ + "newcourse", + ":construct", + ].any((p) => state.fullPath?.contains(p) ?? false); } else if (roomID == null) { showNavRail = state.fullPath?.endsWith(':spaceid') == true; } @@ -41,7 +42,7 @@ class TwoColumnLayout extends StatelessWidget { final columnWidth = (showNavRail ? (FluffyThemes.navRailWidth + 1.0) : 0.0) + - (isColumnMode ? (FluffyThemes.columnWidth + 1.0) : 0.0); + (isColumnMode ? (FluffyThemes.columnWidth + 1.0) : 0.0); // Pangea# return ScaffoldMessenger( child: Scaffold( @@ -56,16 +57,7 @@ class TwoColumnLayout extends StatelessWidget { left: columnWidth, child: ClipRRect(child: sideView), ), - SpaceNavigationColumn( - state: state, - showNavRail: showNavRail, - ), - // Container( - // clipBehavior: Clip.antiAlias, - // decoration: const BoxDecoration(), - // width: FluffyThemes.columnWidth + FluffyThemes.navRailWidth, - // child: mainView, - // ), + SpaceNavigationColumn(state: state, showNavRail: showNavRail), // Container(width: 1.0, color: theme.dividerColor), // Expanded(child: ClipRRect(child: sideView)), // Pangea# diff --git a/lib/widgets/local_notifications_extension.dart b/lib/widgets/local_notifications_extension.dart index ff78aca9a..8eb051730 100644 --- a/lib/widgets/local_notifications_extension.dart +++ b/lib/widgets/local_notifications_extension.dart @@ -5,16 +5,17 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; +import 'package:image/image.dart'; import 'package:matrix/matrix.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:universal_html/html.dart' as html; -import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/push_helper.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -26,18 +27,18 @@ extension LocalNotificationsExtension on MatrixState { void showLocalNotification(Event event) async { final roomId = event.room.id; if (activeRoomId == roomId) { - if (kIsWeb && webHasFocus) return; - if (PlatformInfos.isDesktop && - WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) { + if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) { return; } } - final title = - event.room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))); + final title = event.room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)), + ); final body = await event.calcLocalizedBody( MatrixLocals(L10n.of(context)), - withSenderNamePrefix: !event.room.isDirectChat || + withSenderNamePrefix: + !event.room.isDirectChat || event.room.lastEvent?.senderId == client.userID, plaintextBody: true, hideReply: true, @@ -53,28 +54,33 @@ extension LocalNotificationsExtension on MatrixState { Uri? thumbnailUri; if (avatarUrl != null) { - const size = 64; + const size = 128; const thumbnailMethod = ThumbnailMethod.crop; // Pre-cache so that we can later just set the thumbnail uri as icon: - await client.downloadMxcCached( - avatarUrl, - width: size, - height: size, - thumbnailMethod: thumbnailMethod, - isThumbnail: true, - ); + try { + await client.downloadMxcCached( + avatarUrl, + width: size, + height: size, + thumbnailMethod: thumbnailMethod, + isThumbnail: true, + rounded: true, + ); + } catch (e, s) { + Logs().d('Unable to pre-download avatar for web notification', e, s); + } - thumbnailUri = - await event.senderFromMemoryOrFallback.avatarUrl?.getThumbnailUri( - client, - width: size, - height: size, - method: thumbnailMethod, - ); + thumbnailUri = await event.senderFromMemoryOrFallback.avatarUrl + ?.getThumbnailUri( + client, + width: size, + height: size, + method: thumbnailMethod, + ); } // #Pangea - _audioPlayer.volume = AppConfig.volume; + _audioPlayer.volume = AppSettings.volume.value; // Pangea# _audioPlayer.play(); @@ -85,11 +91,41 @@ extension LocalNotificationsExtension on MatrixState { tag: event.room.id, ); } else if (Platform.isLinux) { + final avatarUrl = event.room.avatar; + final hints = [NotificationHint.soundName('message-new-instant')]; + + if (avatarUrl != null) { + const size = notificationAvatarDimension; + const thumbnailMethod = ThumbnailMethod.crop; + // Pre-cache so that we can later just set the thumbnail uri as icon: + final data = await client.downloadMxcCached( + avatarUrl, + width: size, + height: size, + thumbnailMethod: thumbnailMethod, + isThumbnail: true, + rounded: true, + ); + + final image = decodeImage(data); + if (image != null) { + final realData = image.getBytes(order: ChannelOrder.rgba); + hints.add( + NotificationHint.imageData( + image.width, + image.height, + realData, + hasAlpha: true, + channels: 4, + ), + ); + } + } final notification = await linuxNotifications!.notify( title, body: body, replacesId: linuxNotificationIds[roomId] ?? 0, - appName: AppConfig.applicationName, + appName: AppSettings.applicationName.value, appIcon: 'fluffychat', actions: [ NotificationAction( @@ -101,13 +137,12 @@ extension LocalNotificationsExtension on MatrixState { L10n.of(context).markAsRead, ), ], - hints: [ - NotificationHint.soundName('message-new-instant'), - ], + hints: hints, ); notification.action.then((actionStr) { - var action = DesktopNotificationActions.values - .singleWhereOrNull((a) => a.name == actionStr); + var action = DesktopNotificationActions.values.singleWhereOrNull( + (a) => a.name == actionStr, + ); if (action == null && actionStr == "default") { action = DesktopNotificationActions.openChat; } @@ -116,10 +151,12 @@ extension LocalNotificationsExtension on MatrixState { event.room.setReadMarker( event.eventId, mRead: event.eventId, - public: AppConfig.sendPublicReadReceipts, + public: AppSettings.sendPublicReadReceipts.value, ); break; case DesktopNotificationActions.openChat: + setActiveClient(event.room.client); + FluffyChatApp.router.go('/rooms/${event.room.id}'); break; } @@ -154,15 +191,10 @@ extension LocalNotificationsExtension on MatrixState { notifPermissionNotifier.value = notifPermissionNotifier.value + 1; } catch (e, s) { final permission = await notificationsEnabled; - ErrorHandler.logError( - e: e, - s: s, - data: { - 'permission': permission, - }, - ); + ErrorHandler.logError(e: e, s: s, data: {'permission': permission}); } } + // Pangea# } diff --git a/lib/widgets/lock_screen.dart b/lib/widgets/lock_screen.dart index 852455458..ea4bcf8c2 100644 --- a/lib/widgets/lock_screen.dart +++ b/lib/widgets/lock_screen.dart @@ -21,6 +21,7 @@ class _LockScreenState extends State { final TextEditingController _textEditingController = TextEditingController(); void tryUnlock(String text) async { + text = text.trim(); setState(() { _errorText = null; }); @@ -35,7 +36,7 @@ class _LockScreenState extends State { return; } - if (AppLock.of(context).unlock(enteredPin.toString())) { + if (AppLock.of(context).unlock(text)) { setState(() { _inputBlocked = false; _errorText = null; @@ -60,57 +61,54 @@ class _LockScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).pleaseEnterYourPin), - centerTitle: true, - ), - extendBodyBehindAppBar: true, - body: Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth, - ), - child: ListView( - shrinkWrap: true, - children: [ - Center( - child: Image.asset( - 'assets/info-logo.png', - width: 256, + return ScaffoldMessenger( + child: Scaffold( + appBar: AppBar( + title: Text(L10n.of(context).pleaseEnterYourPin), + centerTitle: true, + ), + extendBodyBehindAppBar: true, + body: Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth, + ), + child: ListView( + shrinkWrap: true, + children: [ + Center( + child: Image.asset('assets/info-logo.png', width: 256), ), - ), - TextField( - controller: _textEditingController, - textInputAction: TextInputAction.done, - keyboardType: TextInputType.number, - obscureText: true, - autofocus: true, - textAlign: TextAlign.center, - readOnly: _inputBlocked, - onChanged: tryUnlock, - onSubmitted: tryUnlock, - style: const TextStyle(fontSize: 40), - inputFormatters: [ - LengthLimitingTextInputFormatter(4), - ], - decoration: InputDecoration( - errorText: _errorText, - hintText: '****', - suffix: IconButton( - icon: const Icon(Icons.lock_open_outlined), - onPressed: () => tryUnlock(_textEditingController.text), + TextField( + controller: _textEditingController, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.number, + obscureText: true, + autofocus: true, + textAlign: TextAlign.center, + readOnly: _inputBlocked, + onChanged: tryUnlock, + onSubmitted: tryUnlock, + style: const TextStyle(fontSize: 40), + inputFormatters: [LengthLimitingTextInputFormatter(4)], + decoration: InputDecoration( + errorText: _errorText, + hintText: '****', + suffix: IconButton( + icon: const Icon(Icons.lock_open_outlined), + onPressed: () => tryUnlock(_textEditingController.text), + ), ), ), - ), - if (_inputBlocked) - const Padding( - padding: EdgeInsets.all(8.0), - child: LinearProgressIndicator(), - ), - ], + if (_inputBlocked) + const Padding( + padding: EdgeInsets.all(8.0), + child: LinearProgressIndicator(), + ), + ], + ), ), ), ), diff --git a/lib/widgets/log_view.dart b/lib/widgets/log_view.dart index 0779af6e6..402e64064 100644 --- a/lib/widgets/log_view.dart +++ b/lib/widgets/log_view.dart @@ -15,17 +15,14 @@ class LogViewerState extends State { double fontSize = 14; @override Widget build(BuildContext context) { - final outputEvents = Logs() - .outputEvents + final outputEvents = Logs().outputEvents .where((e) => e.level.index <= logLevel.index) .toList(); return Scaffold( backgroundColor: Colors.black, appBar: AppBar( title: Text(logLevel.toString()), - leading: BackButton( - onPressed: () => context.go('/'), - ), + leading: BackButton(onPressed: () => context.go('/')), actions: [ IconButton( icon: const Icon(Icons.zoom_in_outlined), @@ -55,9 +52,7 @@ class LogViewerState extends State { scrollDirection: Axis.horizontal, child: SelectableText( outputEvents[i].toDisplayString(), - style: TextStyle( - color: outputEvents[i].color, - ), + style: TextStyle(color: outputEvents[i].color), ), ), ), diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index c67c4a933..6aa59679a 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:app_links/app_links.dart'; import 'package:collection/collection.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; -import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; import 'package:just_audio/just_audio.dart'; @@ -19,13 +18,13 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:universal_html/html.dart' as html; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; -import 'package:fluffychat/pangea/user/style_settings_repo.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -34,7 +33,6 @@ import 'package:fluffychat/utils/voip_plugin.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; -import '../config/app_config.dart'; import '../config/setting_keys.dart'; import '../pages/key_verification/key_verification_dialog.dart'; import '../utils/account_bundles.dart'; @@ -92,15 +90,17 @@ class MatrixState extends State with WidgetsBindingObserver { Client get client { if (_activeClient < 0 || _activeClient >= widget.clients.length) { // #Pangea - currentBundle!.first!.homeserver = - Uri.parse("https://${AppConfig.defaultHomeserver}"); + currentBundle!.first!.homeserver = Uri.parse( + "https://${AppConfig.defaultHomeserver}", + ); // Pangea# return currentBundle!.first!; } // #Pangea - widget.clients[_activeClient].homeserver = - Uri.parse("https://${AppConfig.defaultHomeserver}"); + widget.clients[_activeClient].homeserver = Uri.parse( + "https://${AppConfig.defaultHomeserver}", + ); // Pangea# return widget.clients[_activeClient]; } @@ -159,10 +159,7 @@ class MatrixState extends State with WidgetsBindingObserver { } resBundles[bundle.name] ??= []; resBundles[bundle.name]!.add( - _AccountBundleWithClient( - client: widget.clients[i], - bundle: bundle, - ), + _AccountBundleWithClient(client: widget.clients[i], bundle: bundle), ); } } @@ -171,12 +168,13 @@ class MatrixState extends State with WidgetsBindingObserver { (a, b) => a.bundle!.priority == null ? 1 : b.bundle!.priority == null - ? -1 - : a.bundle!.priority!.compareTo(b.bundle!.priority!), + ? -1 + : a.bundle!.priority!.compareTo(b.bundle!.priority!), ); } - return resBundles - .map((k, v) => MapEntry(k, v.map((vv) => vv.client).toList())); + return resBundles.map( + (k, v) => MapEntry(k, v.map((vv) => vv.client).toList()), + ); } bool get hasComplexBundles => accountBundles.values.any((v) => v.length > 1); @@ -190,48 +188,49 @@ class MatrixState extends State with WidgetsBindingObserver { if (widget.clients.isNotEmpty && !client.isLogged()) { return client; } - final candidate = - _loginClientCandidate ??= await ClientManager.createClient( - '${AppConfig.applicationName}-${DateTime.now().millisecondsSinceEpoch}', - store, - ) - ..onLoginStateChanged - .stream + final candidate = _loginClientCandidate ??= + await ClientManager.createClient( + '${AppSettings.applicationName.value}-${DateTime.now().millisecondsSinceEpoch}', + store, + ) + ..onLoginStateChanged.stream .where((l) => l == LoginState.loggedIn) .first .then((_) async { - // #Pangea - MatrixState.pangeaController.handleLoginStateChange( - LoginState.loggedIn, - _loginClientCandidate!.userID, - context, - ); - // Pangea# - if (!widget.clients.contains(_loginClientCandidate)) { - widget.clients.add(_loginClientCandidate!); - } - ClientManager.addClientNameToStore( - _loginClientCandidate!.clientName, - store, - ); - _registerSubs(_loginClientCandidate!.clientName); - _loginClientCandidate = null; - // #Pangea - // FluffyChatApp.router.go('/rooms'); - final isL2Set = await pangeaController.userController.isUserL2Set; - FluffyChatApp.router.go( - isL2Set ? '/rooms' : '/registration/create', - ); - // Pangea# - }); + // #Pangea + MatrixState.pangeaController.handleLoginStateChange( + LoginState.loggedIn, + _loginClientCandidate!.userID, + context, + ); + // Pangea# + if (!widget.clients.contains(_loginClientCandidate)) { + widget.clients.add(_loginClientCandidate!); + } + ClientManager.addClientNameToStore( + _loginClientCandidate!.clientName, + store, + ); + _registerSubs(_loginClientCandidate!.clientName); + _loginClientCandidate = null; + // #Pangea + // FluffyChatApp.router.go('/backup'); + final isL2Set = + await pangeaController.userController.isUserL2Set; + FluffyChatApp.router.go( + isL2Set ? '/rooms' : '/registration/create', + ); + // Pangea# + }); // #Pangea candidate.homeserver = Uri.parse("https://${AppConfig.defaultHomeserver}"); // This listener is not set for the new login client until the user is logged in, // but if the user tries to sign up without this listener set, the signup UIA request // will hang. So set the listener here. - onUiaRequest[candidate.clientName] ??= - candidate.onUiaRequest.stream.listen(uiaRequestHandler); + onUiaRequest[candidate.clientName] ??= candidate.onUiaRequest.stream.listen( + uiaRequestHandler, + ); // Pangea# if (widget.clients.isEmpty) widget.clients.add(candidate); return candidate; @@ -245,8 +244,6 @@ class MatrixState extends State with WidgetsBindingObserver { final onNotification = {}; final onLoginStateChanged = >{}; final onUiaRequest = >{}; - StreamSubscription? onFocusSub; - StreamSubscription? onBlurSub; String? _cachedPassword; Timer? _cachedPasswordClearTimer; @@ -263,8 +260,6 @@ class MatrixState extends State with WidgetsBindingObserver { }); } - bool webHasFocus = true; - String? get activeRoomId { final route = FluffyChatApp.router.routeInformationProvider.value.uri.path; if (!route.startsWith('/rooms/')) return null; @@ -274,8 +269,9 @@ class MatrixState extends State with WidgetsBindingObserver { // Pangea# } - final linuxNotifications = - PlatformInfos.isLinux ? NotificationsClient() : null; + final linuxNotifications = PlatformInfos.isLinux + ? NotificationsClient() + : null; final Map linuxNotificationIds = {}; @override @@ -283,19 +279,10 @@ class MatrixState extends State with WidgetsBindingObserver { super.initState(); WidgetsBinding.instance.addObserver(this); initMatrix(); - if (PlatformInfos.isWeb) { - initConfig().then((_) => initSettings()); - } else { - initSettings(); - } // #Pangea Sentry.configureScope( - (scope) => scope.setUser( - SentryUser( - id: client.userID, - name: client.userID, - ), - ), + (scope) => + scope.setUser(SentryUser(id: client.userID, name: client.userID)), ); pangeaController = PangeaController(matrixState: this); WidgetsBinding.instance.addPostFrameCallback((_) { @@ -335,7 +322,7 @@ class MatrixState extends State with WidgetsBindingObserver { await showOkAlertDialog( context: FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ?? - context, + context, title: L10n.of(context).screenSizeWarning, ); _lastShownPopupHeight = MediaQuery.heightOf(context); @@ -346,11 +333,11 @@ class MatrixState extends State with WidgetsBindingObserver { Future _setLanguageListener() async { await pangeaController.userController.initialize(); _languageListener?.cancel(); - _languageListener = - pangeaController.userController.languageStream.stream.listen((update) { - _setAppLanguage(); - analyticsDataService.updateService.onUpdateLanguages(update); - }); + _languageListener = pangeaController.userController.languageStream.stream + .listen((update) { + _setAppLanguage(); + analyticsDataService.updateService.onUpdateLanguages(update); + }); } void _setAppLanguage() { @@ -360,28 +347,11 @@ class MatrixState extends State with WidgetsBindingObserver { ); } catch (e, s) { Logs().e('Error setting app language', e); - ErrorHandler.logError( - e: e, - s: s, - data: {}, - ); + ErrorHandler.logError(e: e, s: s, data: {}); } } // Pangea# - Future initConfig() async { - try { - final configJsonString = - utf8.decode((await http.get(Uri.parse('config.json'))).bodyBytes); - final configJson = json.decode(configJsonString); - AppConfig.loadFromJson(configJson); - } on FormatException catch (_) { - Logs().v('[ConfigLoader] config.json not found'); - } catch (e) { - Logs().v('[ConfigLoader] config.json not found', e); - } - } - void _registerSubs(String name) { final c = getClientByName(name); if (c == null) { @@ -390,8 +360,9 @@ class MatrixState extends State with WidgetsBindingObserver { ); return; } - onRoomKeyRequestSub[name] ??= - c.onRoomKeyRequest.stream.listen((RoomKeyRequest request) async { + onRoomKeyRequestSub[name] ??= c.onRoomKeyRequest.stream.listen(( + RoomKeyRequest request, + ) async { if (widget.clients.any( ((cl) => cl.userID == request.requestingDevice.userId && @@ -405,24 +376,27 @@ class MatrixState extends State with WidgetsBindingObserver { }); onKeyVerificationRequestSub[name] ??= c.onKeyVerificationRequest.stream .listen((KeyVerification request) async { - var hidPopup = false; - request.onUpdate = () { - if (!hidPopup && - {KeyVerificationState.done, KeyVerificationState.error} - .contains(request.state)) { - FluffyChatApp.router.pop('dialog'); - } - hidPopup = true; - }; - request.onUpdate = null; - hidPopup = true; - await KeyVerificationDialog(request: request).show( - FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ?? - context, - ); - }); - onLoginStateChanged[name] ??= - c.onLoginStateChanged.stream.listen((state) async { + var hidPopup = false; + request.onUpdate = () { + if (!hidPopup && + { + KeyVerificationState.done, + KeyVerificationState.error, + }.contains(request.state)) { + FluffyChatApp.router.pop('dialog'); + } + hidPopup = true; + }; + request.onUpdate = null; + hidPopup = true; + await KeyVerificationDialog(request: request).show( + FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ?? + context, + ); + }); + onLoginStateChanged[name] ??= c.onLoginStateChanged.stream.listen(( + state, + ) async { // #Pangea MatrixState.pangeaController.handleLoginStateChange( state, @@ -444,9 +418,7 @@ class MatrixState extends State with WidgetsBindingObserver { FluffyChatApp.router.routerDelegate.navigatorKey.currentContext ?? context, ).showSnackBar( - SnackBar( - content: Text(L10n.of(context).oneClientLoggedOut), - ), + SnackBar(content: Text(L10n.of(context).oneClientLoggedOut)), ); if (state != LoginState.loggedIn) { @@ -454,16 +426,15 @@ class MatrixState extends State with WidgetsBindingObserver { } } else { // #Pangea + // FluffyChatApp.router.go( + // state == LoginState.loggedIn ? '/backup' : '/home', + // ); if (state == LoginState.loggedIn) { final isL2Set = await pangeaController.userController.isUserL2Set; - FluffyChatApp.router.go( - isL2Set ? '/rooms' : '/registration/create', - ); + FluffyChatApp.router.go(isL2Set ? '/rooms' : '/registration/create'); } else { FluffyChatApp.router.go('/home'); } - // FluffyChatApp.router - // .go(state == LoginState.loggedIn ? '/rooms' : '/home'); // Pangea# } }); @@ -471,8 +442,9 @@ class MatrixState extends State with WidgetsBindingObserver { if (PlatformInfos.isWeb || PlatformInfos.isLinux) { c.onSync.stream.first.then((s) { html.Notification.requestPermission(); - onNotification[name] ??= - c.onNotification.stream.listen(showLocalNotification); + onNotification[name] ??= c.onNotification.stream.listen( + showLocalNotification, + ); }); } // #Pangea @@ -502,23 +474,23 @@ class MatrixState extends State with WidgetsBindingObserver { _registerSubs(c.clientName); } - if (kIsWeb) { - onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true); - onBlurSub = html.window.onBlur.listen((_) => webHasFocus = false); - } - if (PlatformInfos.isMobile) { backgroundPush = BackgroundPush( this, onFcmError: (errorMsg, {Uri? link}) async { final result = await showOkCancelAlertDialog( - context: FluffyChatApp - .router.routerDelegate.navigatorKey.currentContext ?? + context: + FluffyChatApp + .router + .routerDelegate + .navigatorKey + .currentContext ?? context, title: L10n.of(context).pushNotificationsNotAvailable, message: errorMsg, - okLabel: - link == null ? L10n.of(context).ok : L10n.of(context).learnMore, + okLabel: link == null + ? L10n.of(context).ok + : L10n.of(context).learnMore, cancelLabel: L10n.of(context).doNotShowAgain, ); if (result == OkCancelResult.ok && link != null) { @@ -528,7 +500,7 @@ class MatrixState extends State with WidgetsBindingObserver { ); } if (result == OkCancelResult.cancel) { - await store.setBool(SettingKeys.showNoGoogle, true); + await AppSettings.showNoGoogle.setItem(true); } }, ); @@ -538,7 +510,7 @@ class MatrixState extends State with WidgetsBindingObserver { } void createVoipPlugin() async { - if (store.getBool(SettingKeys.experimentalVoip) == false) { + if (AppSettings.experimentalVoip.value) { voipPlugin = null; return; } @@ -547,12 +519,13 @@ class MatrixState extends State with WidgetsBindingObserver { @override void didChangeAppLifecycleState(AppLifecycleState state) { - Logs().v('AppLifecycleState = $state'); - final foreground = state != AppLifecycleState.inactive && + final foreground = + state != AppLifecycleState.inactive && state != AppLifecycleState.paused; for (final client in widget.clients) { - client.syncPresence = - state == AppLifecycleState.resumed ? null : PresenceType.unavailable; + client.syncPresence = state == AppLifecycleState.resumed + ? null + : PresenceType.unavailable; if (PlatformInfos.isMobile) { client.backgroundSync = foreground; client.requestHistoryOnLimitedTimeline = !foreground; @@ -561,76 +534,6 @@ class MatrixState extends State with WidgetsBindingObserver { } } - void initSettings() { - // #Pangea - // AppConfig.fontSizeFactor = - // double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ?? - // AppConfig.fontSizeFactor; - if (client.isLogged()) { - StyleSettingsRepo.settings(client.userID!).then((settings) { - AppConfig.fontSizeFactor = settings.fontSizeFactor; - AppConfig.useActivityImageAsChatBackground = - settings.useActivityImageBackground; - }); - } - // Pangea# - - AppConfig.renderHtml = - store.getBool(SettingKeys.renderHtml) ?? AppConfig.renderHtml; - - AppConfig.swipeRightToLeftToReply = - store.getBool(SettingKeys.swipeRightToLeftToReply) ?? - AppConfig.swipeRightToLeftToReply; - - AppConfig.hideRedactedEvents = - store.getBool(SettingKeys.hideRedactedEvents) ?? - AppConfig.hideRedactedEvents; - - AppConfig.hideUnknownEvents = - store.getBool(SettingKeys.hideUnknownEvents) ?? - AppConfig.hideUnknownEvents; - - AppConfig.hideUnimportantStateEvents = - store.getBool(SettingKeys.hideUnimportantStateEvents) ?? - AppConfig.hideUnimportantStateEvents; - - AppConfig.separateChatTypes = - store.getBool(SettingKeys.separateChatTypes) ?? - AppConfig.separateChatTypes; - - AppConfig.autoplayImages = - store.getBool(SettingKeys.autoplayImages) ?? AppConfig.autoplayImages; - - AppConfig.sendTypingNotifications = - store.getBool(SettingKeys.sendTypingNotifications) ?? - AppConfig.sendTypingNotifications; - - AppConfig.sendPublicReadReceipts = - store.getBool(SettingKeys.sendPublicReadReceipts) ?? - AppConfig.sendPublicReadReceipts; - - AppConfig.sendOnEnter = - store.getBool(SettingKeys.sendOnEnter) ?? AppConfig.sendOnEnter; - - AppConfig.experimentalVoip = store.getBool(SettingKeys.experimentalVoip) ?? - AppConfig.experimentalVoip; - - AppConfig.showPresences = - store.getBool(SettingKeys.showPresences) ?? AppConfig.showPresences; - - AppConfig.displayNavigationRail = - store.getBool(SettingKeys.displayNavigationRail) ?? - AppConfig.displayNavigationRail; - - // #Pangea - AppConfig.volume = store.getDouble(SettingKeys.volume) ?? AppConfig.volume; - - AppConfig.showedActivityMenu = - store.getBool(SettingKeys.showedActivityMenu) ?? - AppConfig.showedActivityMenu; - // Pangea# - } - @override void dispose() { WidgetsBinding.instance.removeObserver(this); @@ -640,8 +543,6 @@ class MatrixState extends State with WidgetsBindingObserver { onLoginStateChanged.values.map((s) => s.cancel()); onNotification.values.map((s) => s.cancel()); client.httpClient.close(); - onFocusSub?.cancel(); - onBlurSub?.cancel(); linuxNotifications?.close(); // #Pangea @@ -655,10 +556,7 @@ class MatrixState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - return Provider( - create: (_) => this, - child: widget.child, - ); + return Provider(create: (_) => this, child: widget.child); } Future dehydrateAction(BuildContext context) async { @@ -678,9 +576,7 @@ class MatrixState extends State with WidgetsBindingObserver { final export = result.result; if (export == null) return; - final exportBytes = Uint8List.fromList( - const Utf8Codec().encode(export), - ); + final exportBytes = Uint8List.fromList(const Utf8Codec().encode(export)); final exportFileName = 'fluffychat-export-${DateFormat(DateFormat.YEAR_MONTH_DAY).format(DateTime.now())}.fluffybackup'; @@ -693,13 +589,15 @@ class MatrixState extends State with WidgetsBindingObserver { Future _processIncomingUris(Uri? uri) async { if (uri == null || uri.fragment.isEmpty) return; - final path = - uri.fragment.startsWith('/') ? uri.fragment : '/${uri.fragment}'; + final path = uri.fragment.startsWith('/') + ? uri.fragment + : '/${uri.fragment}'; WidgetsBinding.instance.addPostFrameCallback((_) { FluffyChatApp.router.go(path); }); } + // Pangea# } diff --git a/lib/widgets/member_actions_popup_menu_button.dart b/lib/widgets/member_actions_popup_menu_button.dart index f555a192f..57058c2f2 100644 --- a/lib/widgets/member_actions_popup_menu_button.dart +++ b/lib/widgets/member_actions_popup_menu_button.dart @@ -32,9 +32,9 @@ void showMemberActionsPopupMenu({ // #Pangea // final overlay = Overlay.of(context).context.findRenderObject() as RenderBox; - final overlay = Overlay.of(context, rootOverlay: true) - .context - .findRenderObject() as RenderBox; + final overlay = + Overlay.of(context, rootOverlay: true).context.findRenderObject() + as RenderBox; // Pangea# final button = context.findRenderObject() as RenderBox; @@ -101,9 +101,7 @@ void showMemberActionsPopupMenu({ padding: const EdgeInsets.all(4.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - LevelDisplayName(userId: user.id), - ], + children: [LevelDisplayName(userId: user.id)], ), ), // Pangea# @@ -120,10 +118,7 @@ void showMemberActionsPopupMenu({ if (user.id == BotName.byEnvironment && room != null && room.isRoomAdmin) PopupMenuItem( enabled: false, - padding: const EdgeInsets.only( - left: 12.0, - right: 12.0, - ), + padding: const EdgeInsets.only(left: 12.0, right: 12.0), child: BotChatSettingsDialog(room: room), ), const PopupMenuDivider(), @@ -174,16 +169,16 @@ void showMemberActionsPopupMenu({ const Icon(Icons.admin_panel_settings_outlined), const SizedBox(width: 18), Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: .min, + crossAxisAlignment: .start, children: [ Text(L10n.of(context).chatPermissions), Text( user.powerLevel < 50 ? L10n.of(context).userLevel(user.powerLevel) : user.powerLevel < 100 - ? L10n.of(context).moderatorLevel(user.powerLevel) - : L10n.of(context).adminLevel(user.powerLevel), + ? L10n.of(context).moderatorLevel(user.powerLevel) + : L10n.of(context).adminLevel(user.powerLevel), style: const TextStyle(fontSize: 10), ), ], @@ -300,7 +295,8 @@ void showMemberActionsPopupMenu({ cancelLabel: L10n.of(context).no, // #Pangea // message: L10n.of(context).kickUserDescription, - message: user.id == BotName.byEnvironment && + message: + user.id == BotName.byEnvironment && !user.room.isSpace && !user.room.isDirectChat ? L10n.of(context).kickBotWarning @@ -342,10 +338,7 @@ void showMemberActionsPopupMenu({ // final result = await showFutureLoadingDialog( // context: context, - // future: () => user.room.client.reportUser( - // user.id, - // reason, - // ), + // future: () => user.room.client.reportUser(user.id, reason), // ); // if (result.error != null) return; // ScaffoldMessenger.of(context).showSnackBar( @@ -356,10 +349,8 @@ void showMemberActionsPopupMenu({ final router = GoRouter.of(context); final roomIdResult = await showFutureLoadingDialog( context: context, - future: () => user.room.client.startDirectChat( - user.id, - enableEncryption: false, - ), + future: () => + user.room.client.startDirectChat(user.id, enableEncryption: false), ); final roomId = roomIdResult.result; if (roomId == null) return; diff --git a/lib/widgets/mxc_image.dart b/lib/widgets/mxc_image.dart index 33af91abf..b8a7cbc70 100644 --- a/lib/widgets/mxc_image.dart +++ b/lib/widgets/mxc_image.dart @@ -26,6 +26,7 @@ class MxcImage extends StatefulWidget { final Widget Function(BuildContext context)? placeholder; final String? cacheKey; final Client? client; + final BorderRadius borderRadius; const MxcImage({ this.uri, @@ -42,6 +43,7 @@ class MxcImage extends StatefulWidget { this.thumbnailMethod = ThumbnailMethod.scale, this.cacheKey, this.client, + this.borderRadius = BorderRadius.zero, super.key, }); @@ -77,7 +79,7 @@ class _MxcImageState extends State { final event = widget.event; if (uri != null) { - final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; + final devicePixelRatio = MediaQuery.devicePixelRatioOf(context); final width = widget.width; final realWidth = width == null ? null : width * devicePixelRatio; final height = widget.height; @@ -165,53 +167,42 @@ class _MxcImageState extends State { final data = _imageData; final hasData = data != null && data.isNotEmpty; - return AnimatedCrossFade( - crossFadeState: - // #Pangea - // hasData ? CrossFadeState.showSecond : CrossFadeState.showFirst, - hasData || _error != null - ? CrossFadeState.showSecond - : CrossFadeState.showFirst, - // Pangea# - duration: const Duration(milliseconds: 128), - firstChild: placeholder(context), - // #Pangea - // secondChild: hasData - secondChild: _error != null - ? SizedBox( - width: widget.width, - height: widget.height, - ) - : hasData - // Pangea# - ? Image.memory( - data, - width: widget.width, - height: widget.height, - fit: widget.fit, - filterQuality: widget.isThumbnail - ? FilterQuality.low - : FilterQuality.medium, - errorBuilder: (context, e, s) { - Logs().d('Unable to render mxc image', e, s); - return SizedBox( - width: widget.width, - height: widget.height, - child: Material( - color: Theme.of(context).colorScheme.surfaceContainer, - child: Icon( - Icons.broken_image_outlined, - size: min(widget.height ?? 64, 64), - color: Theme.of(context).colorScheme.onSurface, - ), + return AnimatedSwitcher( + duration: FluffyThemes.animationDuration, + child: hasData + ? ClipRRect( + borderRadius: widget.borderRadius, + child: Image.memory( + data, + width: widget.width, + height: widget.height, + fit: widget.fit, + filterQuality: widget.isThumbnail + ? FilterQuality.low + : FilterQuality.medium, + errorBuilder: (context, e, s) { + Logs().d('Unable to render mxc image', e, s); + return SizedBox( + width: widget.width, + height: widget.height, + child: Material( + color: Theme.of(context).colorScheme.surfaceContainer, + child: Icon( + Icons.broken_image_outlined, + size: min(widget.height ?? 64, 64), + color: Theme.of(context).colorScheme.onSurface, ), - ); - }, - ) - : SizedBox( - width: widget.width, - height: widget.height, - ), + ), + ); + }, + ), + ) + // #Pangea + // : placeholder(context), + : _error != null + ? SizedBox(width: widget.width, height: widget.height) + : placeholder(context), + // Pangea# ); } } diff --git a/lib/widgets/mxc_image_viewer.dart b/lib/widgets/mxc_image_viewer.dart index 1d806d858..be075d79b 100644 --- a/lib/widgets/mxc_image_viewer.dart +++ b/lib/widgets/mxc_image_viewer.dart @@ -35,7 +35,7 @@ class MxcImageViewer extends StatelessWidget { maxScale: 10.0, onInteractionEnd: (endDetails) { if (endDetails.velocity.pixelsPerSecond.dy > - MediaQuery.of(context).size.height * 1.5) { + MediaQuery.sizeOf(context).height * 1.5) { Navigator.of(context, rootNavigator: false).pop(); } }, diff --git a/lib/widgets/navigation_rail.dart b/lib/widgets/navigation_rail.dart index 3fc977dee..4b891123b 100644 --- a/lib/widgets/navigation_rail.dart +++ b/lib/widgets/navigation_rail.dart @@ -47,12 +47,9 @@ class SpacesNavigationRail extends StatelessWidget { @override Widget build(BuildContext context) { final client = Matrix.of(context).client; - final isSettings = GoRouter.of(context) - .routeInformationProvider - .value - .uri - .path - .startsWith('/rooms/settings'); + final isSettings = GoRouter.of( + context, + ).routeInformationProvider.value.uri.path.startsWith('/rooms/settings'); // #Pangea final isAnalytics = path?.contains('analytics') ?? false; final isCourse = path?.contains('course') ?? false; @@ -66,21 +63,13 @@ class SpacesNavigationRail extends StatelessWidget { child: SafeArea( child: StreamBuilder( // Pangea# - key: ValueKey( - client.userID.toString(), - ), + key: ValueKey(client.userID.toString()), stream: client.onSync.stream .where((s) => s.hasRoomUpdate) .rateLimit(const Duration(seconds: 1)), builder: (context, _) { - final allSpaces = client.rooms.where((room) => room.isSpace); - final rootSpaces = allSpaces - .where( - (space) => !allSpaces.any( - (parentSpace) => parentSpace.spaceChildren - .any((child) => child.roomId == space.id), - ), - ) + final allSpaces = client.rooms + .where((room) => room.isSpace) .toList(); // #Pangea @@ -99,8 +88,8 @@ class SpacesNavigationRail extends StatelessWidget { child: ListView.builder( scrollDirection: Axis.vertical, // #Pangea - // itemCount: rootSpaces.length + 2, - itemCount: rootSpaces.length + 3, + // itemCount: allSpaces.length + 2, + itemCount: allSpaces.length + 3, // Pangea# itemBuilder: (context, i) { // #Pangea @@ -138,9 +127,11 @@ class SpacesNavigationRail extends StatelessWidget { borderRadius: BorderRadius.circular(99), child: Avatar( mxContent: snapshot.data?.avatarUrl, - name: snapshot.data?.displayName ?? + name: + snapshot.data?.displayName ?? client.userID!.localpart, - size: width - + size: + width - (isColumnMode ? 32.0 : 24.0), ), ), @@ -160,7 +151,8 @@ class SpacesNavigationRail extends StatelessWidget { return NaviRailItem( // #Pangea // isSelected: activeSpaceId == null && !isSettings, - isSelected: activeSpaceId == null && + isSelected: + activeSpaceId == null && !isSettings && !isAnalytics && !isCourse, @@ -189,7 +181,7 @@ class SpacesNavigationRail extends StatelessWidget { ); } i--; - if (i == rootSpaces.length) { + if (i == allSpaces.length) { return NaviRailItem( // #Pangea // isSelected: false, @@ -212,12 +204,12 @@ class SpacesNavigationRail extends StatelessWidget { width: width - (isColumnMode ? 32.0 : 24.0), height: width - (isColumnMode ? 32.0 : 24.0), color: isCourse - ? Theme.of(context) - .colorScheme - .primaryContainer - : Theme.of(context) - .colorScheme - .surfaceContainerHigh, + ? Theme.of( + context, + ).colorScheme.primaryContainer + : Theme.of( + context, + ).colorScheme.surfaceContainerHigh, child: const Icon(Icons.add), ), ), @@ -226,31 +218,29 @@ class SpacesNavigationRail extends StatelessWidget { // Pangea# ); } - final space = rootSpaces[i]; - final displayname = - rootSpaces[i].getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), - ); - final spaceChildrenIds = - space.spaceChildren.map((c) => c.roomId).toSet(); + final space = allSpaces[i]; + final displayname = allSpaces[i] + .getLocalizedDisplayname( + MatrixLocals(L10n.of(context)), + ); + final spaceChildrenIds = space.spaceChildren + .map((c) => c.roomId) + .toSet(); return NaviRailItem( toolTip: displayname, isSelected: activeSpaceId == space.id, // #Pangea backgroundColor: Colors.transparent, borderRadius: BorderRadius.circular(0), - // onTap: () => onGoToSpaceId(rootSpaces[i].id), + // onTap: () => onGoToSpaceId(allSpaces[i].id), onTap: () { collapse(); - final room = client.getRoomById(rootSpaces[i].id); + final room = client.getRoomById(allSpaces[i].id); if (room != null) { - chatListHandleSpaceTap( - context, - room, - ); + chatListHandleSpaceTap(context, room); } else { context.go( - "/rooms/spaces/${rootSpaces[i].id}/details", + "/rooms/spaces/${allSpaces[i].id}/details", ); } }, @@ -259,7 +249,7 @@ class SpacesNavigationRail extends StatelessWidget { spaceChildrenIds.contains(room.id), // #Pangea // icon: Avatar( - // mxContent: rootSpaces[i].avatar, + // mxContent: allSpaces[i].avatar, // name: displayname, // border: BorderSide( // width: 1, @@ -271,7 +261,7 @@ class SpacesNavigationRail extends StatelessWidget { // ), icon: b.Badge( showBadge: - rootSpaces[i].membership == Membership.invite, + allSpaces[i].membership == Membership.invite, badgeStyle: b.BadgeStyle( badgeColor: Theme.of(context).colorScheme.error, elevation: 4, @@ -283,14 +273,11 @@ class SpacesNavigationRail extends StatelessWidget { color: Theme.of(context).colorScheme.onPrimary, size: 16, ), - position: b.BadgePosition.topEnd( - top: -5, - end: -7, - ), + position: b.BadgePosition.topEnd(top: -5, end: -7), child: ClipPath( clipper: MapClipper(), child: Avatar( - mxContent: rootSpaces[i].avatar, + mxContent: allSpaces[i].avatar, name: displayname, border: BorderSide( width: 1, diff --git a/lib/widgets/permission_slider_dialog.dart b/lib/widgets/permission_slider_dialog.dart index f4b629029..0fdc1432e 100644 --- a/lib/widgets/permission_slider_dialog.dart +++ b/lib/widgets/permission_slider_dialog.dart @@ -14,12 +14,12 @@ Future showPermissionChooser( return await showAdaptiveDialog( context: context, builder: (context) => AlertDialog.adaptive( - title: Text(L10n.of(context).chatPermissions), + title: Center(child: Text(L10n.of(context).chatPermissions)), content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 256, maxHeight: 256), child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: .min, + crossAxisAlignment: .stretch, spacing: 12.0, children: [ Text(L10n.of(context).setPermissionsLevelDescription), @@ -39,6 +39,7 @@ Future showPermissionChooser( actions: [ AdaptiveDialogAction( bigButtons: true, + borderRadius: AdaptiveDialogAction.topRadius, onPressed: () { final level = int.tryParse(controller.text.trim()); if (level == null) { @@ -55,18 +56,23 @@ Future showPermissionChooser( ), if (maxLevel >= 100 && currentLevel != 100) AdaptiveDialogAction( + borderRadius: AdaptiveDialogAction.centerRadius, bigButtons: true, onPressed: () => Navigator.of(context).pop(100), child: Text(L10n.of(context).admin), ), if (maxLevel >= 50 && currentLevel != 50) AdaptiveDialogAction( + borderRadius: maxLevel != 0 + ? AdaptiveDialogAction.centerRadius + : AdaptiveDialogAction.bottomRadius, bigButtons: true, onPressed: () => Navigator.of(context).pop(50), child: Text(L10n.of(context).moderator), ), if (currentLevel != 0) AdaptiveDialogAction( + borderRadius: AdaptiveDialogAction.bottomRadius, bigButtons: true, onPressed: () => Navigator.of(context).pop(0), child: Text(L10n.of(context).normalUser), diff --git a/lib/widgets/presence_builder.dart b/lib/widgets/presence_builder.dart index 4332485ab..a2d46c35f 100644 --- a/lib/widgets/presence_builder.dart +++ b/lib/widgets/presence_builder.dart @@ -27,12 +27,10 @@ class _PresenceBuilderState extends State { StreamSubscription? _sub; void _updatePresence(CachedPresence? presence) { - // #Pangea - // setState(() { - // _presence = presence; - // }); - if (mounted) setState(() => _presence = presence); - // Pangea# + if (!mounted) return; + setState(() { + _presence = presence; + }); } @override diff --git a/lib/widgets/profile_bottom_sheet.dart b/lib/widgets/profile_bottom_sheet.dart index 40afa25ba..440e8d870 100644 --- a/lib/widgets/profile_bottom_sheet.dart +++ b/lib/widgets/profile_bottom_sheet.dart @@ -65,10 +65,7 @@ class ProfileBottomSheet extends StatelessWidget { profile?.displayName ?? userId.localpart ?? userId, style: const TextStyle(fontSize: 18), ), - Text( - userId, - style: const TextStyle(fontSize: 12), - ), + Text(userId, style: const TextStyle(fontSize: 12)), ], ), // Pangea# diff --git a/lib/widgets/qr_code_viewer.dart b/lib/widgets/qr_code_viewer.dart index 374cfc212..eb1cc378d 100644 --- a/lib/widgets/qr_code_viewer.dart +++ b/lib/widgets/qr_code_viewer.dart @@ -14,10 +14,7 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dar import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../config/themes.dart'; -Future showQrCodeViewer( - BuildContext context, - String content, -) => +Future showQrCodeViewer(BuildContext context, String content) => showDialog( context: context, builder: (context) => QrCodeViewer(content: content), @@ -36,11 +33,7 @@ class QrCodeViewer extends StatelessWidget { // final inviteLink = 'https://matrix.to/#/$content'; final inviteLink = '${Environment.frontendURL}/#/rooms/$content'; // Pangea# - final image = QRImage( - inviteLink, - size: 256, - radius: 1, - ).generate(); + final image = QRImage(inviteLink, size: 256, radius: 1).generate(); return compute(encodePng, image); }, ); @@ -80,10 +73,7 @@ class QrCodeViewer extends StatelessWidget { backgroundColor: Colors.black.withAlpha(128), ), icon: Icon(Icons.adaptive.share_outlined), - onPressed: () => FluffyShare.share( - inviteLink, - context, - ), + onPressed: () => FluffyShare.share(inviteLink, context), color: Colors.white, tooltip: L10n.of(context).share, ), @@ -109,11 +99,12 @@ class QrCodeViewer extends StatelessWidget { borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), child: Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: .min, children: [ ConstrainedBox( - constraints: - const BoxConstraints(maxWidth: FluffyThemes.columnWidth), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth, + ), child: PrettyQrView.data( data: inviteLink, decoration: PrettyQrDecoration( diff --git a/lib/widgets/settings_switch_list_tile.dart b/lib/widgets/settings_switch_list_tile.dart index f49b97598..f625f9e64 100644 --- a/lib/widgets/settings_switch_list_tile.dart +++ b/lib/widgets/settings_switch_list_tile.dart @@ -1,18 +1,16 @@ import 'package:flutter/material.dart'; -import 'matrix.dart'; +import 'package:fluffychat/config/setting_keys.dart'; class SettingsSwitchListTile extends StatefulWidget { - final bool defaultValue; - final String storeKey; + final AppSettings setting; final String title; final String? subtitle; final Function(bool)? onChanged; const SettingsSwitchListTile.adaptive({ super.key, - this.defaultValue = false, - required this.storeKey, + required this.setting, required this.title, this.subtitle, this.onChanged, @@ -27,13 +25,12 @@ class SettingsSwitchListTileState extends State { Widget build(BuildContext context) { final subtitle = widget.subtitle; return SwitchListTile.adaptive( - value: Matrix.of(context).store.getBool(widget.storeKey) ?? - widget.defaultValue, + value: widget.setting.value, title: Text(widget.title), subtitle: subtitle == null ? null : Text(subtitle), onChanged: (bool newValue) async { widget.onChanged?.call(newValue); - await Matrix.of(context).store.setBool(widget.storeKey, newValue); + await widget.setting.setItem(newValue); setState(() {}); }, ); diff --git a/lib/widgets/share_scaffold_dialog.dart b/lib/widgets/share_scaffold_dialog.dart index b7a63c0bc..dab24a933 100644 --- a/lib/widgets/share_scaffold_dialog.dart +++ b/lib/widgets/share_scaffold_dialog.dart @@ -65,9 +65,7 @@ class _ShareScaffoldDialogState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final rooms = Matrix.of(context) - .client - .rooms + final rooms = Matrix.of(context).client.rooms .where( (room) => room.canSendDefaultMessages && @@ -142,15 +140,29 @@ class _ShareScaffoldDialogState extends State { ), controlAffinity: ListTileControlAffinity.trailing, shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), ), secondary: Avatar( mxContent: room.avatar, name: displayname, size: Avatar.defaultSize * 0.75, ), - title: Text(displayname), + title: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + room.directChatMatrixID ?? + L10n.of(context).countParticipants( + (room.summary.mJoinedMemberCount ?? 0) + + (room.summary.mInvitedMemberCount ?? 0), + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), value: selectedRoomId == room.id, onChanged: (_) => _toggleRoom(room.id), ), diff --git a/lib/widgets/theme_builder.dart b/lib/widgets/theme_builder.dart index 1ce0a6f05..669c88913 100644 --- a/lib/widgets/theme_builder.dart +++ b/lib/widgets/theme_builder.dart @@ -12,7 +12,8 @@ class ThemeBuilder extends StatefulWidget { BuildContext context, ThemeMode themeMode, Color? primaryColor, - ) builder; + ) + builder; final String themeModeSettingsKey; final String primaryColorSettingsKey; @@ -38,28 +39,26 @@ class ThemeController extends State { Color? get primaryColor => _primaryColor; static ThemeController of(BuildContext context) => - Provider.of( - context, - listen: false, - ); + Provider.of(context, listen: false); - void _loadData(_) async { - final preferences = - _sharedPreferences ??= await SharedPreferences.getInstance(); + void _loadData(dynamic _) async { + final preferences = _sharedPreferences ??= + await SharedPreferences.getInstance(); final rawThemeMode = preferences.getString(widget.themeModeSettingsKey); final rawColor = preferences.getInt(widget.primaryColorSettingsKey); setState(() { - _themeMode = ThemeMode.values - .singleWhereOrNull((value) => value.name == rawThemeMode); + _themeMode = ThemeMode.values.singleWhereOrNull( + (value) => value.name == rawThemeMode, + ); _primaryColor = rawColor == null ? null : Color(rawColor); }); } Future setThemeMode(ThemeMode newThemeMode) async { - final preferences = - _sharedPreferences ??= await SharedPreferences.getInstance(); + final preferences = _sharedPreferences ??= + await SharedPreferences.getInstance(); await preferences.setString(widget.themeModeSettingsKey, newThemeMode.name); setState(() { _themeMode = newThemeMode; @@ -67,8 +66,8 @@ class ThemeController extends State { } Future setPrimaryColor(Color? newPrimaryColor) async { - final preferences = - _sharedPreferences ??= await SharedPreferences.getInstance(); + final preferences = _sharedPreferences ??= + await SharedPreferences.getInstance(); if (newPrimaryColor == null) { await preferences.remove(widget.primaryColorSettingsKey); } else { @@ -93,11 +92,8 @@ class ThemeController extends State { return Provider( create: (_) => this, child: DynamicColorBuilder( - builder: (light, _) => widget.builder( - context, - themeMode, - primaryColor ?? light?.primary, - ), + builder: (light, _) => + widget.builder(context, themeMode, primaryColor ?? light?.primary), ), ); } diff --git a/lib/widgets/unread_rooms_badge.dart b/lib/widgets/unread_rooms_badge.dart index 4e93e1acc..746933171 100644 --- a/lib/widgets/unread_rooms_badge.dart +++ b/lib/widgets/unread_rooms_badge.dart @@ -23,10 +23,9 @@ class UnreadRoomsBadge extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); - final unreadCount = Matrix.of(context) - .client - .rooms - // #Pangea + // #Pangea + // final unreadCount = Matrix.of(context).client.rooms + final unreadCount = Matrix.of(context).client.rooms .where((r) => !r.isHiddenRoom && !r.isSpace) // Pangea# .where(filter) @@ -39,18 +38,12 @@ class UnreadRoomsBadge extends StatelessWidget { // Pangea# badgeColor: theme.colorScheme.primary, elevation: 4, - borderSide: BorderSide( - color: theme.colorScheme.surface, - width: 2, - ), + borderSide: BorderSide(color: theme.colorScheme.surface, width: 2), ), // #Pangea // badgeContent: Text( // unreadCount.toString(), - // style: TextStyle( - // color: theme.colorScheme.onPrimary, - // fontSize: 12, - // ), + // style: TextStyle(color: theme.colorScheme.onPrimary, fontSize: 12), // ), badgeContent: SizedBox( width: 15, @@ -61,10 +54,7 @@ class UnreadRoomsBadge extends StatelessWidget { unreadCount < 100 ? unreadCount.toString() : L10n.of(context).unreadPlus, - style: TextStyle( - color: theme.colorScheme.onPrimary, - fontSize: 12, - ), + style: TextStyle(color: theme.colorScheme.onPrimary, fontSize: 12), ), ), ), diff --git a/licenses.yaml b/licenses.yaml index 02b7ad97e..fe53d876b 100644 --- a/licenses.yaml +++ b/licenses.yaml @@ -12,6 +12,7 @@ permittedLicenses: - BSD-2-Clause - BSD-3-Clause - EUPL-1.2 + - ISC - LGPL-3.0 - MIT - MPL-2.0 @@ -26,6 +27,7 @@ packageLicenseOverride: platform_detect: Apache-2.0 rxdart: Apache-2.0 flutter_new_badger: MIT + sqlcipher_flutter_libs: MIT # flutter's internal packages flutter_driver: BSD-3-Clause diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 2bf30a5e5..bd25d6822 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -15,17 +16,22 @@ #include #include #include -#include #include +#include #include #include #include +#include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); + g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin"); + desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar); g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); @@ -50,12 +56,12 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) open_file_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin"); open_file_linux_plugin_register_with_registrar(open_file_linux_registrar); - g_autoptr(FlPluginRegistrar) pasteboard_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); - pasteboard_plugin_register_with_registrar(pasteboard_registrar); g_autoptr(FlPluginRegistrar) record_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin"); record_linux_plugin_register_with_registrar(record_linux_registrar); + g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); + screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); @@ -65,6 +71,12 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) webcrypto_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WebcryptoPlugin"); + webcrypto_plugin_register_with_registrar(webcrypto_registrar); + g_autoptr(FlPluginRegistrar) window_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); + window_manager_plugin_register_with_registrar(window_manager_registrar); g_autoptr(FlPluginRegistrar) window_to_front_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin"); window_to_front_plugin_register_with_registrar(window_to_front_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 4806ed127..03136b174 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux + desktop_webview_window dynamic_color emoji_picker_flutter file_selector_linux @@ -12,11 +13,13 @@ list(APPEND FLUTTER_PLUGIN_LIST gtk handy_window open_file_linux - pasteboard record_linux + screen_retriever_linux sentry_flutter sqlcipher_flutter_libs url_launcher_linux + webcrypto + window_manager window_to_front ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index fb476d2e2..398940f74 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import app_links import app_settings import audio_session import audioplayers_darwin +import desktop_webview_window import device_info_plus import dynamic_color import emoji_picker_flutter @@ -28,11 +29,11 @@ import in_app_purchase_storekit import just_audio import open_file_mac import package_info_plus -import pasteboard import path_provider_foundation import purchases_flutter import record_macos import rive_common +import screen_retriever_macos import sentry_flutter import share_plus import shared_preferences_foundation @@ -42,6 +43,8 @@ import url_launcher_macos import video_compress import video_player_avfoundation import wakelock_plus +import webcrypto +import window_manager import window_to_front func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -49,6 +52,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppSettingsPlugin.register(with: registry.registrar(forPlugin: "AppSettingsPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) + DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin")) @@ -68,11 +72,11 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) - PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PurchasesFlutterPlugin.register(with: registry.registrar(forPlugin: "PurchasesFlutterPlugin")) RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) + ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) @@ -82,5 +86,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) + WebcryptoPlugin.register(with: registry.registrar(forPlugin: "WebcryptoPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 0c59e46c0..3e928cef2 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,10 +1,12 @@ PODS: - - app_links (1.0.0): + - app_links (6.4.1): - FlutterMacOS - audio_session (0.0.1): - FlutterMacOS - desktop_drop (0.0.1): - FlutterMacOS + - desktop_webview_window (0.0.1): + - FlutterMacOS - device_info_plus (0.0.1): - FlutterMacOS - dynamic_color (0.0.2): @@ -25,9 +27,9 @@ PODS: - FlutterMacOS - flutter_web_auth_2 (3.0.0): - FlutterMacOS - - flutter_webrtc (0.12.6): + - flutter_webrtc (1.2.0): - FlutterMacOS - - WebRTC-SDK (= 125.6422.06) + - WebRTC-SDK (= 137.7151.04) - FlutterMacOS (1.0.0) - geolocator_apple (1.2.0): - Flutter @@ -37,29 +39,26 @@ PODS: - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS - - pasteboard (0.0.1): - - FlutterMacOS - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - record_macos (1.0.0): + - record_macos (1.1.0): + - FlutterMacOS + - screen_retriever_macos (0.0.1): - FlutterMacOS - share_plus (0.0.1): - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - - sqflite_darwin (0.0.4): - - Flutter - - FlutterMacOS - - SQLCipher (4.8.0): - - SQLCipher/standard (= 4.8.0) - - SQLCipher/common (4.8.0) - - SQLCipher/standard (4.8.0): + - SQLCipher (4.10.0): + - SQLCipher/standard (= 4.10.0) + - SQLCipher/common (4.10.0) + - SQLCipher/standard (4.10.0): - SQLCipher/common - sqlcipher_flutter_libs (0.0.1): - FlutterMacOS - - SQLCipher (~> 4.8.0) + - SQLCipher (~> 4.10.0) - url_launcher_macos (0.0.1): - FlutterMacOS - video_compress (0.3.0): @@ -69,7 +68,12 @@ PODS: - FlutterMacOS - wakelock_plus (0.0.1): - FlutterMacOS - - WebRTC-SDK (125.6422.06) + - webcrypto (0.1.1): + - Flutter + - FlutterMacOS + - WebRTC-SDK (137.7151.04) + - window_manager (0.5.0): + - FlutterMacOS - window_to_front (0.0.1): - FlutterMacOS @@ -77,6 +81,7 @@ DEPENDENCIES: - app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`) - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) + - desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`) - emoji_picker_flutter (from `Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos`) @@ -92,17 +97,18 @@ DEPENDENCIES: - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin`) - just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/darwin`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`) + - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) - sqlcipher_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlcipher_flutter_libs/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - video_compress (from `Flutter/ephemeral/.symlinks/plugins/video_compress/macos`) - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) + - webcrypto (from `Flutter/ephemeral/.symlinks/plugins/webcrypto/darwin`) + - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) - window_to_front (from `Flutter/ephemeral/.symlinks/plugins/window_to_front/macos`) SPEC REPOS: @@ -117,6 +123,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos desktop_drop: :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos + desktop_webview_window: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos device_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos dynamic_color: @@ -147,18 +155,16 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/just_audio/darwin package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos - pasteboard: - :path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin record_macos: :path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos + screen_retriever_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos share_plus: :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin - sqflite_darwin: - :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin sqlcipher_flutter_libs: :path: Flutter/ephemeral/.symlinks/plugins/sqlcipher_flutter_libs/macos url_launcher_macos: @@ -169,41 +175,47 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin wakelock_plus: :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos + webcrypto: + :path: Flutter/ephemeral/.symlinks/plugins/webcrypto/darwin + window_manager: + :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos window_to_front: :path: Flutter/ephemeral/.symlinks/plugins/window_to_front/macos SPEC CHECKSUMS: - app_links: afe860c55c7ef176cea7fb630a2b7d7736de591d + app_links: 05a6ec2341985eb05e9f97dc63f5837c39895c3f audio_session: eaca2512cf2b39212d724f35d11f46180ad3a33e - desktop_drop: e0b672a7d84c0a6cbc378595e82cdb15f2970a43 - device_info_plus: a56e6e74dbbd2bb92f2da12c64ddd4f67a749041 - dynamic_color: b820c000cc68df65e7ba7ff177cb98404ce56651 - emoji_picker_flutter: b9d4f4d08bdf3168fa3827f1290d435083745a14 + desktop_drop: 10a3e6a7fa9dbe350541f2574092fecfa345a07b + desktop_webview_window: 7e37af677d6d19294cb433d9b1d878ef78dffa4d + device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76 + dynamic_color: cb7c2a300ee67ed3bd96c3e852df3af0300bf610 + emoji_picker_flutter: 51ca408e289d84d1e460016b2a28721ec754fcf7 file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a - file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31 - flutter_local_notifications: 7e5a17a1dbc00d83dc10d43c2c4c05f2ceed233c + file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7 + flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0 flutter_new_badger: 6fe9bf7e42793a164032c21f164c0ad9985cd0f2 flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54 flutter_vodozemac: fd2ea9cb3e2a37beaac883a369811fbfe042fc53 flutter_web_auth_2: 62b08da29f15a20fa63f144234622a1488d45b65 - flutter_webrtc: 377dbcebdde6fed0fc40de87bcaaa2bffcec9a88 - FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + flutter_webrtc: 718eae22a371cd94e5d56aa4f301443ebc5bb737 + FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed package_info_plus: f0052d280d17aa382b932f399edf32507174e870 - pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - record_macos: 295d70bd5fb47145df78df7b80e6697cd18403c0 + record_macos: 43194b6c06ca6f8fa132e2acea72b202b92a0f5b + screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 - sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - SQLCipher: 908f846ca79d74be4e1776b3b86c6ad9e6c0b04f - sqlcipher_flutter_libs: 72569ed27a3f8d3502571be15fdc3e28f8f8570c + SQLCipher: eb79c64049cb002b4e9fcb30edb7979bf4706dfc + sqlcipher_flutter_libs: 01ead34db27ae5e49987cae46c8a34199eb22cfe url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 video_compress: 752b161da855df2492dd1a8fa899743cc8fe9534 video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b - wakelock_plus: 21ddc249ac4b8d018838dbdabd65c5976c308497 - WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db + wakelock_plus: 917609be14d812ddd9e9528876538b2263aaa03b + webcrypto: a5f5eb3e375cf0a99993e207e97cdcab5c94ce2e + WebRTC-SDK: 40d4f5ba05cadff14e4db5614aec402a633f007e + window_manager: b729e31d38fb04905235df9ea896128991cad99e window_to_front: 9e76fd432e36700a197dac86a0011e49c89abe0a PODFILE CHECKSUM: d0975b16fbdecb73b109d8fbc88aa77ffe4c7a8d diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 41d360c6f..5b297f8ab 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -216,7 +216,6 @@ 33CC10EC2044A3C60003C045 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.Sandbox = { enabled = 1; @@ -427,7 +426,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 4NXF6Z997G; @@ -559,7 +558,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 4NXF6Z997G; @@ -585,7 +584,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 4NXF6Z997G; diff --git a/pubspec.lock b/pubspec.lock index cc9dce04b..e7d3e9179 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "91.0.0" _flutterfire_internals: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "8.4.1" android_intent_plus: dependency: "direct main" description: @@ -45,10 +45,10 @@ packages: dependency: "direct main" description: name: animations - sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb + sha256: "18938cefd7dcc04e1ecac0db78973761a01e4bc2d6bfae0cfa596bfeac9e96ab" url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.1.1" ansicolor: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: "direct main" description: name: app_links - sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" + sha256: "5f88447519add627fe1cbcab4fd1da3d4fed15b9baf29f28b22535c95ecee3e8" url: "https://pub.dev" source: hosted - version: "6.4.0" + version: "6.4.1" app_links_linux: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: "direct main" description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.7" args: dependency: transitive description: @@ -301,10 +301,10 @@ packages: dependency: "direct main" description: name: chewie - sha256: "4d9554a8f87cc2dc6575dfd5ad20a4375015a29edd567fd6733febe6365e2566" + sha256: "44bcfc5f0dfd1de290c87c9d86a61308b3282a70b63435d5557cfd60f54a69ca" url: "https://pub.dev" source: hosted - version: "1.11.3" + version: "1.13.0" cli_config: dependency: transitive description: @@ -353,14 +353,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" - console: - dependency: transitive - description: - name: console - sha256: e04e7824384c5b39389acdd6dc7d33f3efe6b232f6f16d7626f194f6a01ad69a - url: "https://pub.dev" - source: hosted - version: "4.1.0" convert: dependency: transitive description: @@ -389,10 +381,10 @@ packages: dependency: "direct main" description: name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239" url: "https://pub.dev" source: hosted - version: "0.3.4+2" + version: "0.3.5" crypto: dependency: transitive description: @@ -425,14 +417,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_earcut: + dependency: transitive + description: + name: dart_earcut + sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b + url: "https://pub.dev" + source: hosted + version: "1.2.0" + dart_polylabel2: + dependency: transitive + description: + name: dart_polylabel2 + sha256: "7eeab15ce72894e4bdba6a8765712231fc81be0bd95247de4ad9966abc57adc6" + url: "https://pub.dev" + source: hosted + version: "1.0.0" dart_webrtc: dependency: transitive description: name: dart_webrtc - sha256: "5b76fd85ac95d6f5dee3e7d7de8d4b51bfbec1dc73804647c6aebb52d6297116" + sha256: "51bcda4ba5d7dd9e65a309244ce3ac0b58025e6e1f6d7442cee4cd02134ef65f" url: "https://pub.dev" source: hosted - version: "1.5.3+hotfix.2" + version: "1.6.0" dbus: dependency: transitive description: @@ -449,22 +457,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.3" + desktop_webview_window: + dependency: transitive + description: + name: desktop_webview_window + sha256: "57cf20d81689d5cbb1adfd0017e96b669398a669d927906073b0e42fc64111c0" + url: "https://pub.dev" + source: hosted + version: "0.2.3" device_info_plus: dependency: "direct main" description: name: device_info_plus - sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 + sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c" url: "https://pub.dev" source: hosted - version: "10.1.2" + version: "12.3.0" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f url: "https://pub.dev" source: hosted - version: "7.0.2" + version: "7.0.3" diacritic: dependency: "direct main" description: @@ -485,34 +501,18 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d + sha256: "43a5a6679649a7731ab860334a5812f2067c2d9ce6452cf069c5e0c25336c17c" url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.8.1" emoji_picker_flutter: dependency: "direct main" description: name: emoji_picker_flutter - sha256: "08567e6f914d36c32091a96cf2f51d2558c47aa2bd47a590dc4f50e42e0965f6" + sha256: "984d3e9b9cf3175df9a868ce4a2d9611491e80e5d3b8e2b1e8991a4998972885" url: "https://pub.dev" source: hosted - version: "3.1.0" - emojis: - dependency: "direct main" - description: - name: emojis - sha256: "2e4d847c3f1e2670f30dc355909ce6fa7808b4e626c34a4dd503a360995a38bf" - url: "https://pub.dev" - source: hosted - version: "0.9.9" - enhanced_enum: - dependency: transitive - description: - name: enhanced_enum - sha256: "074c5a8b9664799ca91e1e8b68003b8694cb19998671cbafd9c7779c13fcdecf" - url: "https://pub.dev" - source: hosted - version: "0.2.4" + version: "4.4.0" equatable: dependency: transitive description: @@ -524,10 +524,11 @@ packages: excel: dependency: "direct main" description: - name: excel - sha256: "1a15327dcad260d5db21d1f6e04f04838109b39a2f6a84ea486ceda36e468780" - url: "https://pub.dev" - source: hosted + path: "." + ref: efd70f3086d12105dad0029f71663e6a4ed295f2 + resolved-ref: efd70f3086d12105dad0029f71663e6a4ed295f2 + url: "https://github.com/busslina/excel.git" + source: git version: "4.0.6" fake_async: dependency: transitive @@ -537,13 +538,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" - fcm_shared_isolate: - dependency: "direct overridden" - description: - path: "pangea_packages/fcm_shared_isolate" - relative: true - source: path - version: "0.2.0" ffi: dependency: transitive description: @@ -564,18 +558,18 @@ packages: dependency: "direct main" description: name: file_picker - sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810 + sha256: d974b6ba2606371ac71dd94254beefb6fa81185bde0b59bdc1df09885da85fde url: "https://pub.dev" source: hosted - version: "8.3.7" + version: "10.3.8" file_selector: dependency: "direct main" description: name: file_selector - sha256: "5019692b593455127794d5718304ff1ae15447dea286cdda9f0db2a796a1b828" + sha256: bd15e43e9268db636b53eeaca9f56324d1622af30e5c34d6e267649758c84d9a url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.1.0" file_selector_android: dependency: transitive description: @@ -596,26 +590,26 @@ packages: dependency: transitive description: name: file_selector_linux - sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" + sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0" url: "https://pub.dev" source: hosted - version: "0.9.3+2" + version: "0.9.4" file_selector_macos: dependency: transitive description: name: file_selector_macos - sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711" + sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a" url: "https://pub.dev" source: hosted - version: "0.9.4+3" + version: "0.9.5" file_selector_platform_interface: dependency: transitive description: name: file_selector_platform_interface - sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" url: "https://pub.dev" source: hosted - version: "2.6.2" + version: "2.7.0" file_selector_web: dependency: transitive description: @@ -718,7 +712,7 @@ packages: source: sdk version: "0.0.0" flutter_cache_manager: - dependency: "direct main" + dependency: transitive description: name: flutter_cache_manager sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" @@ -742,18 +736,10 @@ packages: dependency: "direct main" description: name: flutter_foreground_task - sha256: "6cf10a27f5e344cd2ecad0752d3a5f4ec32846d82fda8753b3fe2480ebb832a3" + sha256: "48ea45056155a99fb30b15f14f4039a044d925bc85f381ed0b2d3b00a60b99de" url: "https://pub.dev" source: hosted - version: "6.5.0" - flutter_highlighter: - dependency: "direct main" - description: - name: flutter_highlighter - sha256: "93173afd47a9ada53f3176371755e7ea4a1065362763976d06d6adfb4d946e10" - url: "https://pub.dev" - source: hosted - version: "0.1.1" + version: "9.2.0" flutter_html: dependency: "direct main" description: @@ -762,54 +748,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" - flutter_keyboard_visibility: - dependency: transitive - description: - name: flutter_keyboard_visibility - sha256: "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8" - url: "https://pub.dev" - source: hosted - version: "6.0.0" - flutter_keyboard_visibility_linux: - dependency: transitive - description: - name: flutter_keyboard_visibility_linux - sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - flutter_keyboard_visibility_macos: - dependency: transitive - description: - name: flutter_keyboard_visibility_macos - sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086 - url: "https://pub.dev" - source: hosted - version: "1.0.0" - flutter_keyboard_visibility_platform_interface: - dependency: transitive - description: - name: flutter_keyboard_visibility_platform_interface - sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4 - url: "https://pub.dev" - source: hosted - version: "2.0.0" - flutter_keyboard_visibility_web: - dependency: transitive - description: - name: flutter_keyboard_visibility_web - sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1 - url: "https://pub.dev" - source: hosted - version: "2.0.0" - flutter_keyboard_visibility_windows: - dependency: transitive - description: - name: flutter_keyboard_visibility_windows - sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73 - url: "https://pub.dev" - source: hosted - version: "1.0.0" flutter_linkify: dependency: "direct main" description: @@ -822,34 +760,42 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "6.0.0" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications - sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35" + sha256: "19ffb0a8bb7407875555e5e98d7343a633bb73707bae6c6a5f37c90014077875" url: "https://pub.dev" source: hosted - version: "17.2.4" + version: "19.5.0" flutter_local_notifications_linux: dependency: transitive description: name: flutter_local_notifications_linux - sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af + sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5 url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "6.0.0" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface - sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66" + sha256: "277d25d960c15674ce78ca97f57d0bae2ee401c844b6ac80fcd972a9c99d09fe" url: "https://pub.dev" source: hosted - version: "7.2.0" + version: "9.1.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: "8d658f0d367c48bd420e7cf2d26655e2d1130147bca1eea917e576ca76668aaf" + url: "https://pub.dev" + source: hosted + version: "1.0.3" flutter_localizations: dependency: "direct main" description: flutter @@ -859,18 +805,18 @@ packages: dependency: "direct main" description: name: flutter_map - sha256: "87cc8349b8fa5dccda5af50018c7374b6645334a0d680931c1fe11bce88fa5bb" + sha256: "391e7dc95cc3f5190748210a69d4cfeb5d8f84dcdfa9c3235d0a9d7742ccb3f8" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "8.2.2" flutter_native_splash: dependency: "direct dev" description: name: flutter_native_splash - sha256: "7062602e0dbd29141fb8eb19220b5871ca650be5197ab9c1f193a28b17537bc7" + sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.7" flutter_new_badger: dependency: "direct main" description: @@ -879,14 +825,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" - flutter_openssl_crypto: - dependency: "direct main" - description: - name: flutter_openssl_crypto - sha256: "293b4fcda13ab0710645a16e82f3d5b7de19bfc0ab2d06bcdb87637222eda5e1" - url: "https://pub.dev" - source: hosted - version: "0.5.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -899,10 +837,10 @@ packages: dependency: transitive description: name: flutter_rust_bridge - sha256: b416ff56002789e636244fb4cc449f587656eff995e5a7169457eb0593fcaddb + sha256: "37ef40bc6f863652e865f0b2563ea07f0d3c58d8efad803cc01933a4b2ee067e" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.1" flutter_secure_storage: dependency: "direct main" description: @@ -912,14 +850,13 @@ packages: source: hosted version: "9.2.4" flutter_secure_storage_linux: - dependency: "direct overridden" + dependency: transitive description: - path: flutter_secure_storage_linux - ref: patch-2 - resolved-ref: f076cbb65b075afd6e3b648122987a67306dc298 - url: "https://github.com/m-berto/flutter_secure_storage.git" - source: git - version: "2.0.1" + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" flutter_secure_storage_macos: dependency: transitive description: @@ -929,13 +866,13 @@ packages: source: hosted version: "3.1.3" flutter_secure_storage_platform_interface: - dependency: "direct overridden" + dependency: transitive description: name: flutter_secure_storage_platform_interface - sha256: b8337d3d52e429e6c0a7710e38cf9742a3bb05844bd927450eb94f80c11ef85d + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.1.2" flutter_secure_storage_web: dependency: transitive description: @@ -981,40 +918,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.3" - flutter_typeahead: - dependency: "direct main" - description: - path: "." - ref: main - resolved-ref: "3e209e67aa6e780cba61ced06cf49d2babbbcaa4" - url: "https://github.com/famedly/flutter_typeahead.git" - source: git - version: "5.2.0" flutter_vodozemac: dependency: "direct main" description: name: flutter_vodozemac - sha256: "2405ca121b84d1cd83200a14021022e1691b123a23bcefc36adc7740cefbc1f9" + sha256: "16d4b44dd338689441fe42a80d0184e5c864e9563823de9e7e6371620d2c0590" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.4.1" flutter_web_auth_2: dependency: "direct main" description: - path: flutter_web_auth_2 - ref: "3.x-without-v1" - resolved-ref: "48682f19576001e50104a602d891343850adb67f" - url: "https://github.com/ThexXTURBOXx/flutter_web_auth_2.git" - source: git - version: "3.1.2-without-v1" + name: flutter_web_auth_2 + sha256: "3c14babeaa066c371f3a743f204dd0d348b7d42ffa6fae7a9847a521aff33696" + url: "https://pub.dev" + source: hosted + version: "4.1.0" flutter_web_auth_2_platform_interface: dependency: transitive description: name: flutter_web_auth_2_platform_interface - sha256: e8669e262005a8354389ba2971f0fc1c36188481234ff50d013aaf993f30f739 + sha256: c63a472c8070998e4e422f6b34a17070e60782ac442107c70000dd1bed645f4d url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.1.0" flutter_web_plugins: dependency: transitive description: flutter @@ -1024,10 +951,10 @@ packages: dependency: "direct main" description: name: flutter_webrtc - sha256: b832dc76c0d1577f14aaf35e9c38d4ed7667cbc89c492b7bf4505d8d5f62e08b + sha256: "71a38363a5b50603e405c275f30de2eb90f980b0cc94b0e1e9d8b9d6a6b03bf0" url: "https://pub.dev" source: hosted - version: "0.12.12+hotfix.1" + version: "1.2.1" freezed_annotation: dependency: transitive description: @@ -1049,22 +976,30 @@ packages: description: flutter source: sdk version: "0.0.0" + geoclue: + dependency: transitive + description: + name: geoclue + sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f + url: "https://pub.dev" + source: hosted + version: "0.1.1" geolocator: dependency: "direct main" description: name: geolocator - sha256: f62bcd90459e63210bbf9c35deb6a51c521f992a78de19a1fe5c11704f9530e2 + sha256: "79939537046c9025be47ec645f35c8090ecadb6fe98eba146a0d25e8c1357516" url: "https://pub.dev" source: hosted - version: "13.0.4" + version: "14.0.2" geolocator_android: dependency: transitive description: name: geolocator_android - sha256: fcb1760a50d7500deca37c9a666785c047139b5f9ee15aa5469fae7dbbe3170d + sha256: "179c3cb66dfa674fc9ccbf2be872a02658724d1c067634e2c427cf6df7df901a" url: "https://pub.dev" source: hosted - version: "4.6.2" + version: "5.0.2" geolocator_apple: dependency: transitive description: @@ -1073,6 +1008,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.13" + geolocator_linux: + dependency: transitive + description: + name: geolocator_linux + sha256: d64112a205931926f4363bb6bd48f14cb38e7326833041d170615586cd143797 + url: "https://pub.dev" + source: hosted + version: "0.2.4" geolocator_platform_interface: dependency: transitive description: @@ -1105,14 +1048,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.7.2" - get_it: - dependency: transitive - description: - name: get_it - sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 - url: "https://pub.dev" - source: hosted - version: "7.7.0" get_storage: dependency: "direct main" description: @@ -1141,10 +1076,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: "0b1e06223bee260dee31a171fb1153e306907563a0b0225e8c1733211911429a" + sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896" url: "https://pub.dev" source: hosted - version: "15.1.2" + version: "17.1.0" graphs: dependency: transitive description: @@ -1153,6 +1088,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + gsettings: + dependency: transitive + description: + name: gsettings + sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c" + url: "https://pub.dev" + source: hosted + version: "0.2.8" gtk: dependency: transitive description: @@ -1169,30 +1112,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.0" - highlighter: - dependency: transitive - description: - name: highlighter - sha256: "92180c72b9da8758e1acf39a45aa305a97dcfe2fdc8f3d1d2947c23f2772bfbc" - url: "https://pub.dev" - source: hosted - version: "0.1.1" - hive: + highlight: dependency: "direct main" description: - name: hive - sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + name: highlight + sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" url: "https://pub.dev" source: hosted - version: "2.2.3" - hive_flutter: - dependency: "direct main" - description: - name: hive_flutter - sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc - url: "https://pub.dev" - source: hosted - version: "1.1.0" + version: "0.7.0" html: dependency: "direct main" description: @@ -1213,10 +1140,10 @@ packages: dependency: "direct main" description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -1237,74 +1164,74 @@ packages: dependency: "direct main" description: name: image - sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d + sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c" url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.7.2" image_picker: dependency: "direct main" description: name: image_picker - sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + sha256: "784210112be18ea55f69d7076e2c656a4e24949fa9e76429fe53af0c0f4fa320" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.1" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: "317a5d961cec5b34e777b9252393f2afbd23084aa6e60fcf601dcf6341b9ebeb" + sha256: e83b2b05141469c5e19d77e1dfa11096b6b1567d09065b2265d7c6904560050c url: "https://pub.dev" source: hosted - version: "0.8.12+23" + version: "0.8.13" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" + sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.0" image_picker_ios: dependency: transitive description: name: image_picker_ios - sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100" + sha256: eb06fe30bab4c4497bad449b66448f50edcc695f1c59408e78aa3a8059eb8f0e url: "https://pub.dev" source: hosted - version: "0.8.12+2" + version: "0.8.13" image_picker_linux: dependency: transitive description: name: image_picker_linux - sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9" + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" url: "https://pub.dev" source: hosted - version: "0.2.1+2" + version: "0.2.2" image_picker_macos: dependency: transitive description: name: image_picker_macos - sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1" + sha256: d58cd9d67793d52beefd6585b12050af0a7663c0c2a6ece0fb110a35d6955e04 url: "https://pub.dev" source: hosted - version: "0.2.1+2" + version: "0.2.2" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" + sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665" url: "https://pub.dev" source: hosted - version: "2.10.1" + version: "2.11.0" image_picker_windows: dependency: transitive description: name: image_picker_windows - sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae url: "https://pub.dev" source: hosted - version: "0.2.1+1" + version: "0.2.2" import_sorter: dependency: "direct dev" description: @@ -1394,18 +1321,18 @@ packages: dependency: "direct main" description: name: just_audio - sha256: f978d5b4ccea08f267dae0232ec5405c1b05d3f3cd63f82097ea46c015d5c09e + sha256: "9694e4734f515f2a052493d1d7e0d6de219ee0427c7c29492e246ff32a219908" url: "https://pub.dev" source: hosted - version: "0.9.46" + version: "0.10.5" just_audio_platform_interface: dependency: transitive description: name: just_audio_platform_interface - sha256: "4cd94536af0219fa306205a58e78d67e02b0555283c1c094ee41e402a14a5c4a" + sha256: "2532c8d6702528824445921c5ff10548b518b13f808c2e34c2fd54793b999a6a" url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.6.0" just_audio_web: dependency: transitive description: @@ -1458,10 +1385,10 @@ packages: dependency: "direct dev" description: name: license_checker - sha256: eea27638e42bc98fd91a6a8187eb57e5617e2c3c8b313a5d51b14bec7a8685e1 + sha256: "8a35b6946e50811e070ac6fe4717ee431cd1a334e080df2116956b54b0bb0d0f" url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "1.6.2" linkify: dependency: "direct main" description: @@ -1474,10 +1401,10 @@ packages: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "6.1.0" list_counter: dependency: transitive description: @@ -1546,11 +1473,11 @@ packages: dependency: "direct main" description: path: "." - ref: merge-upstream - resolved-ref: "1e5975c8dae6af46444f07868bb2ddf5689c3cbe" + ref: merge-upstream-2 + resolved-ref: "503d9831d8d1acc467f6cdb71d455297f05c5a3c" url: "https://github.com/pangeachat/matrix-dart-sdk.git" source: git - version: "1.0.1" + version: "4.1.0" meta: dependency: transitive description: @@ -1571,26 +1498,10 @@ packages: dependency: "direct main" description: name: mime - sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.6" - msix: - dependency: "direct dev" - description: - name: msix - sha256: edde648a8133bf301883c869d19d127049683037c65ff64173ba526ac7a8af2f - url: "https://pub.dev" - source: hosted - version: "3.16.9" - native_imaging: - dependency: "direct main" - description: - name: native_imaging - sha256: "93573afdcab070011d78a40fc1f69b61967f1f8485d2b81a7a2ee585a85f4c04" - url: "https://pub.dev" - source: hosted - version: "0.2.0" + version: "2.0.0" nested: dependency: transitive description: @@ -1699,34 +1610,34 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "9.0.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" pana: dependency: transitive description: name: pana - sha256: "3fc3fe8e7a9fd4827fa4d625a423eec95d305b2bc3538a3adf7fd6c49217af97" + sha256: eb816d35b80d3880335c3f2d139b376e81fd98a9ea273faf39f2c8914c4afba5 url: "https://pub.dev" source: hosted - version: "0.21.45" - pasteboard: + version: "0.23.3" + particles_network: dependency: "direct main" description: - name: pasteboard - sha256: "1c8b6a8b3f1d12e55d4e9404433cda1b4abe66db6b17bc2d2fb5965772c04674" + name: particles_network + sha256: "10350bbf446b504acf591b928b1a39ba687237a8895d73e6567abba1a2740c4c" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "1.9.0" path: dependency: "direct main" description: @@ -1803,18 +1714,18 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" + sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 url: "https://pub.dev" source: hosted - version: "11.4.0" + version: "12.0.1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" url: "https://pub.dev" source: hosted - version: "12.1.0" + version: "13.0.1" permission_handler_apple: dependency: transitive description: @@ -1851,10 +1762,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "7.0.1" platform: dependency: transitive description: @@ -1871,46 +1782,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - pointer_interceptor: - dependency: transitive - description: - name: pointer_interceptor - sha256: "57210410680379aea8b1b7ed6ae0c3ad349bfd56fe845b8ea934a53344b9d523" - url: "https://pub.dev" - source: hosted - version: "0.10.1+2" - pointer_interceptor_ios: - dependency: transitive - description: - name: pointer_interceptor_ios - sha256: a6906772b3205b42c44614fcea28f818b1e5fdad73a4ca742a7bd49818d9c917 - url: "https://pub.dev" - source: hosted - version: "0.10.1" - pointer_interceptor_platform_interface: - dependency: transitive - description: - name: pointer_interceptor_platform_interface - sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506" - url: "https://pub.dev" - source: hosted - version: "0.10.0+1" - pointer_interceptor_web: - dependency: transitive - description: - name: pointer_interceptor_web - sha256: "460b600e71de6fcea2b3d5f662c92293c049c4319e27f0829310e5a953b3ee2a" - url: "https://pub.dev" - source: hosted - version: "0.10.3" - polylabel: - dependency: transitive - description: - name: polylabel - sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b" - url: "https://pub.dev" - source: hosted - version: "1.0.1" pool: dependency: transitive description: @@ -1919,14 +1790,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" pretty_qr_code: dependency: "direct main" description: name: pretty_qr_code - sha256: b078bd5d51956dea4342378af1b092ad962b81bdbb55b10fffce03461da8db74 + sha256: "2291db3f68d70a3dcd46c6bd599f30991ae4c02f27f36215fbb3f4865a609259" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.5.0" process: dependency: transitive description: @@ -1955,10 +1834,10 @@ packages: dependency: "direct main" description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.1.5+1" pub_semver: dependency: transitive description: @@ -2003,10 +1882,10 @@ packages: dependency: "direct main" description: name: qr_code_scanner_plus - sha256: "39696b50d277097ee4d90d4292de36f38c66213a4f5216a06b2bdd2b63117859" + sha256: b764e5004251c58d9dee0c295e6006e05bd8d249e78ac3383abdb5afe0a996cd url: "https://pub.dev" source: hosted - version: "2.0.10+1" + version: "2.0.14" qr_image: dependency: "direct main" description: @@ -2035,66 +1914,66 @@ packages: dependency: "direct main" description: name: record - sha256: daeb3f9b3fea9797094433fe6e49a879d8e4ca4207740bc6dc7e4a58764f0817 + sha256: "6bad72fb3ea6708d724cf8b6c97c4e236cf9f43a52259b654efeb6fd9b737f1f" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.2" record_android: dependency: transitive description: name: record_android - sha256: "97d7122455f30de89a01c6c244c839085be6b12abca251fc0e78f67fed73628b" + sha256: fb54ee4e28f6829b8c580252a9ef49d9c549cfd263b0660ad7eeac0908658e9f url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.4.4" record_ios: dependency: transitive description: name: record_ios - sha256: "73706ebbece6150654c9d6f57897cf9b622c581148304132ba85dba15df0fdfb" + sha256: "765b42ac1be019b1674ddd809b811fc721fe5a93f7bb1da7803f0d16772fd6d7" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.4" record_linux: dependency: transitive description: name: record_linux - sha256: fcb5964a84292813de70d52253663c1caca00a15f849fb5d0fdf9b929b28a7b9 + sha256: "235b1f1fb84e810f8149cc0c2c731d7d697f8d1c333b32cb820c449bf7bb72d8" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.2.1" record_macos: dependency: transitive description: name: record_macos - sha256: "02240833fde16c33fcf2c589f3e08d4394b704761b4a3bb609d872ff3043fbbd" + sha256: "842ea4b7e95f4dd237aacffc686d1b0ff4277e3e5357865f8d28cd28bc18ed95" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.2" record_platform_interface: dependency: transitive description: name: record_platform_interface - sha256: "8a575828733d4c3cb5983c914696f40db8667eab3538d4c41c50cbb79e722ef4" + sha256: b0065fdf1ec28f5a634d676724d388a77e43ce7646fb049949f58c69f3fcb4ed url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.0" record_web: dependency: transitive description: name: record_web - sha256: "024c81eb7f51468b1833a3eca8b461c7ca25c04899dba37abe580bb57afd32e4" + sha256: "3feeffbc0913af3021da9810bb8702a068db6bc9da52dde1d19b6ee7cb9edb51" url: "https://pub.dev" source: hosted - version: "1.1.8" + version: "1.2.2" record_windows: dependency: transitive description: name: record_windows - sha256: "85a22fc97f6d73ecd67c8ba5f2f472b74ef1d906f795b7970f771a0914167e99" + sha256: "223258060a1d25c62bae18282c16783f28581ec19401d17e56b5205b9f039d78" url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.7" retry: dependency: transitive description: @@ -2135,6 +2014,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" scroll_to_index: dependency: "direct main" description: @@ -2179,26 +2098,26 @@ packages: dependency: "direct main" description: name: share_plus - sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840" url: "https://pub.dev" source: hosted - version: "10.1.4" + version: "12.0.1" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.1.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_android: dependency: transitive description: @@ -2360,10 +2279,10 @@ packages: dependency: "direct main" description: name: sqflite_common_ffi - sha256: "1f3ef3888d3bfbb47785cc1dda0dc7dd7ebd8c1955d32a9e8e9dae1e38d1c4c1" + sha256: "8d7b8749a516cbf6e9057f9b480b716ad14fc4f3d3873ca6938919cc626d9025" url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.3.7+1" sqflite_darwin: dependency: transitive description: @@ -2384,18 +2303,18 @@ packages: dependency: "direct main" description: name: sqlcipher_flutter_libs - sha256: "777c3469ada8fe6b808bd50f1c752cdd2ca1b1f3cf751d434502ead15334f3a5" + sha256: dd1fcc74d5baf3c36ad53e2652b2d06c9f8747494a3ccde0076e88b159dfe622 url: "https://pub.dev" source: hosted - version: "0.6.6" + version: "0.6.8" sqlite3: dependency: transitive description: name: sqlite3 - sha256: c0503c69b44d5714e6abbf4c1f51a3c3cc42b75ce785f44404765e4635481d38 + sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" url: "https://pub.dev" source: hosted - version: "2.7.6" + version: "2.9.4" stack_trace: dependency: transitive description: @@ -2460,14 +2379,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" - tar: - dependency: transitive - description: - name: tar - sha256: "22f67e2d77b51050436620b2a5de521c58ca6f0b75af1d9ab3c8cae2eae58fcd" - url: "https://pub.dev" - source: hosted - version: "1.0.5" term_glyph: dependency: transitive description: @@ -2504,10 +2415,10 @@ packages: dependency: transitive description: name: timezone - sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" + sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 url: "https://pub.dev" source: hosted - version: "0.9.4" + version: "0.10.1" tint: dependency: transitive description: @@ -2516,22 +2427,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - tor_detector_web: - dependency: "direct main" - description: - name: tor_detector_web - sha256: c4acbd6c0fecd2cd0e8fe00b1a37332422e041021a42488dfddcb3e7ec809b3f - url: "https://pub.dev" - source: hosted - version: "1.1.0" translations_cleaner: dependency: "direct dev" description: name: translations_cleaner - sha256: "060f4a8cd782e271509719741dd3540fe81ddaad49bd79e1d8fc4598299a6b84" + sha256: "811f42be32f024fdf083903f198d3625f6ee6927601e3a53a29b85b90508b88c" url: "https://pub.dev" source: hosted - version: "0.0.5" + version: "0.1.0" typed_data: dependency: transitive description: @@ -2552,50 +2455,66 @@ packages: dependency: "direct main" description: name: unifiedpush - sha256: "6dbed5a6305ca33f1865c7a3d814ae39476b79a2d23ca76a5708f023f405730f" + sha256: "8ed9767f750a1dc6159a77e2171641d0cb825dc87682d1ce1b8618689b79f58e" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.2.0" unifiedpush_android: dependency: transitive description: name: unifiedpush_android - sha256: "7443dece0a850ae956514f809983eb2b39fc518c2c7d24dbfe817198bec89134" + sha256: "556796c81e8151ee8e4275baea2f7191119e8b1412ec35523cc2ac1c44c348bf" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "3.4.0" + unifiedpush_linux: + dependency: transitive + description: + name: unifiedpush_linux + sha256: c062d5eedd1cec70bcd33270cc4e01ae0ff6501f33d471167c06b34a968adfeb + url: "https://pub.dev" + source: hosted + version: "1.0.0" unifiedpush_platform_interface: dependency: transitive description: name: unifiedpush_platform_interface - sha256: dd588d78a8b2bfc10430e30035526e98caa543d0b7364a6344b5eb4815721c6d + sha256: "83372bc8d794b8b12ef6993b518d7be907dcfc2191bdf6de0ece5c4445d89880" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "4.0.0" + unifiedpush_storage_interface: + dependency: transitive + description: + name: unifiedpush_storage_interface + sha256: b8d423a4695efc616aa21d8ab48fb5ef99d6288c68b56282b8faac1579ceabd9 + url: "https://pub.dev" + source: hosted + version: "1.0.0" unifiedpush_ui: dependency: "direct main" description: name: unifiedpush_ui - sha256: cf86f0214f37debd41f25c0425c8489df85e27f9f8784fed571eb7a86d39ba11 + sha256: "1b36b2aa0bc6b61577e2661c1183bd3442969ecf77b4c78174796d324f66dd1d" url: "https://pub.dev" source: hosted - version: "0.1.0" + version: "0.2.0" universal_html: dependency: "direct main" description: name: universal_html - sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" + sha256: c0bcae5c733c60f26c7dfc88b10b0fd27cbcc45cb7492311cdaa6067e21c9cd4 url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.3.0" universal_io: dependency: transitive description: name: universal_io - sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.1" unorm_dart: dependency: transitive description: @@ -2608,10 +2527,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.2" url_launcher_android: dependency: transitive description: @@ -2696,10 +2615,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + sha256: "201e876b5d52753626af64b6359cd13ac6011b80728731428fd34bc840f71c9b" url: "https://pub.dev" source: hosted - version: "1.1.17" + version: "1.1.20" vector_math: dependency: transitive description: @@ -2720,10 +2639,10 @@ packages: dependency: "direct main" description: name: video_player - sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a" + sha256: "096bc28ce10d131be80dfb00c223024eb0fba301315a406728ab43dd99c45bdf" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.10.1" video_player_android: dependency: transitive description: @@ -2768,26 +2687,26 @@ packages: dependency: transitive description: name: vodozemac - sha256: dba14017e042748fb22d270e8ab1d3e46965b89788dd3857dba938ec07571968 + sha256: "39144e20740807731871c9248d811ed5a037b21d0aa9ffcfa630954de74139d9" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.4.0" wakelock_plus: dependency: "direct main" description: name: wakelock_plus - sha256: a474e314c3e8fb5adef1f9ae2d247e57467ad557fa7483a2b895bc1b421c5678 + sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" wakelock_plus_platform_interface: dependency: transitive description: name: wakelock_plus_platform_interface - sha256: e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207 + sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2" url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.3.0" watcher: dependency: transitive description: @@ -2820,6 +2739,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + webcrypto: + dependency: transitive + description: + name: webcrypto + sha256: e393b3d0b01694a8f81efecf278ed7392877130e6e7b29f578863e4f2d0b2ebd + url: "https://pub.dev" + source: hosted + version: "0.5.8" webdriver: dependency: transitive description: @@ -2836,30 +2763,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + webpush_encryption: + dependency: transitive + description: + name: webpush_encryption + sha256: "63046b7d6909f4a72ce3c153fa574726e257aaf21b1995ba063dc241a1b1520b" + url: "https://pub.dev" + source: hosted + version: "1.0.0" webrtc_interface: dependency: "direct main" description: name: webrtc_interface - sha256: "86fe3afc81a08481dfb25cf14a5a94e27062ecef25544783f352c914e0bbc1ca" + sha256: "2e604a31703ad26781782fb14fa8a4ee621154ee2c513d2b9938e486fa695233" url: "https://pub.dev" source: hosted - version: "1.2.2+hotfix.2" + version: "1.3.0" win32: - dependency: "direct overridden" + dependency: transitive description: name: win32 - sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9" + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" url: "https://pub.dev" source: hosted - version: "5.5.3" + version: "5.14.0" win32_registry: dependency: transitive description: name: win32_registry - sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" url: "https://pub.dev" source: hosted - version: "1.1.5" + version: "2.1.0" + window_manager: + dependency: transitive + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" window_to_front: dependency: transitive description: @@ -2888,10 +2831,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" yaml: dependency: transitive description: @@ -2901,5 +2844,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.0-0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.10.0 <4.0.0" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index 620576dd3..188e91b74 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,103 +9,92 @@ publish_to: none version: 4.1.17+7 environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: - animations: ^2.0.11 - app_links: ^6.3.3 - archive: ^3.4.10 + animations: ^2.1.1 + app_links: ^6.4.1 + archive: ^4.0.7 async: ^2.11.0 badges: ^3.1.2 blurhash_dart: ^1.2.1 - chewie: ^1.11.3 + chewie: ^1.13.0 collection: ^1.18.0 - cross_file: ^0.3.4+2 + cross_file: ^0.3.5 confetti: ^0.8.0 cupertino_icons: any # #Pangea - # desktop_drop: ^0.4.4 + # desktop_drop: ^0.7.0 # Pangea# desktop_notifications: ^0.6.3 - device_info_plus: ^10.0.1 + device_info_plus: ^12.3.0 diacritic: ^0.1.6 - dynamic_color: ^1.7.0 - emoji_picker_flutter: ^3.1.0 - emojis: ^0.9.9 - file_picker: ^8.1.2 - file_selector: ^1.0.3 + dynamic_color: ^1.8.1 + emoji_picker_flutter: ^4.4.0 + file_picker: ^10.3.8 + file_selector: ^1.1.0 flutter: sdk: flutter - flutter_cache_manager: ^3.4.1 - flutter_foreground_task: ^6.1.3 - flutter_highlighter: ^0.1.1 + flutter_foreground_task: ^9.2.0 flutter_linkify: ^6.0.0 - flutter_local_notifications: ^17.2.3 + flutter_local_notifications: ^19.5.0 flutter_localizations: sdk: flutter - flutter_map: ^6.1.0 + flutter_map: ^8.2.2 flutter_new_badger: ^1.1.1 - flutter_openssl_crypto: ^0.5.0 flutter_secure_storage: ^9.2.4 flutter_shortcuts_new: ^2.0.0 - flutter_typeahead: ## Custom fork from flutter_typeahead since the package is not maintain well. - git: - url: https://github.com/famedly/flutter_typeahead.git - ref: main - flutter_vodozemac: ^0.2.2 - flutter_web_auth_2: ^3.1.1 # Version 4 blocked by https://github.com/MixinNetwork/flutter-plugins/issues/379 - flutter_webrtc: ^0.12.9 - geolocator: ^13.0.1 - go_router: ^15.1.2 + flutter_vodozemac: ^0.4.1 + flutter_web_auth_2: ^4.1.0 + flutter_webrtc: ^1.2.1 + geolocator: ^14.0.2 + go_router: ^17.0.1 handy_window: ^0.4.0 - hive: ^2.2.3 - hive_flutter: ^1.1.0 + highlight: ^0.7.0 html: ^0.15.4 - http: ^1.2.0 - image: ^4.1.7 - image_picker: ^1.1.0 + http: ^1.6.0 + image: ^4.7.2 + image_picker: ^1.2.1 intl: any - just_audio: ^0.9.39 + just_audio: ^0.10.5 latlong2: ^0.9.1 linkify: ^5.0.0 # #Pangea + # matrix: ^4.1.0 matrix: git: url: https://github.com/pangeachat/matrix-dart-sdk.git # repo - ref: merge-upstream - # matrix: ^1.0.1 + ref: merge-upstream-2 # Pangea# - mime: ^1.0.6 - native_imaging: ^0.2.0 + mime: ^2.0.0 opus_caf_converter_dart: ^1.0.1 - package_info_plus: ^8.1.1 - pasteboard: ^0.2.0 + package_info_plus: ^9.0.0 + particles_network: ^1.9.0 path: ^1.9.0 path_provider: ^2.1.2 - permission_handler: ^11.4.0 - pretty_qr_code: ^3.2.1 + permission_handler: ^12.0.1 + pretty_qr_code: ^3.5.0 provider: ^6.0.2 punycode: ^1.0.0 - qr_code_scanner_plus: ^2.0.10+1 + qr_code_scanner_plus: ^2.0.14 qr_image: ^1.0.0 receive_sharing_intent: ^1.8.1 - record: ^6.0.0 + record: ^6.1.2 scroll_to_index: ^3.0.1 - share_plus: ^10.0.2 - shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401 + share_plus: ^12.0.1 + shared_preferences: ^2.5.4 # Pinned because https://github.com/flutter/flutter/issues/118401 slugify: ^2.0.0 - sqflite_common_ffi: ^2.3.3 - sqlcipher_flutter_libs: ^0.6.1 + sqflite_common_ffi: ^2.3.7+1 + sqlcipher_flutter_libs: ^0.6.8 swipe_to_action: ^0.3.0 - tor_detector_web: ^1.1.0 - unifiedpush: ^5.0.1 - unifiedpush_ui: ^0.1.0 - universal_html: ^2.2.4 - url_launcher: ^6.2.5 + unifiedpush: ^6.2.0 + unifiedpush_ui: ^0.2.0 + universal_html: ^2.3.0 + url_launcher: ^6.3.2 video_compress: ^3.1.4 - video_player: ^2.9.5 - wakelock_plus: ^1.2.2 - webrtc_interface: ^1.0.13 + video_player: ^2.10.1 + wakelock_plus: ^1.3.3 + webrtc_interface: ^1.3.0 # #Pangea android_intent_plus: ^5.2.0 app_settings: ^6.1.1 @@ -115,7 +104,11 @@ dependencies: country_picker: ^2.0.25 csv: ^6.0.0 dropdown_button2: ^2.3.9 - excel: ^4.0.6 + # https://github.com/justkawal/excel/pull/409 + excel: + git: + url: https://github.com/busslina/excel.git + ref: efd70f3086d12105dad0029f71663e6a4ed295f2 firebase_analytics: ^11.0.1 firebase_core: ^3.1.0 firebase_messaging: ^15.1.5 @@ -136,18 +129,17 @@ dependencies: # Pangea# dev_dependencies: - flutter_lints: ^3.0.0 - flutter_native_splash: ^2.0.3+1 + flutter_lints: ^6.0.0 + flutter_native_splash: ^2.4.7 flutter_test: sdk: flutter import_sorter: ^4.6.0 integration_test: sdk: flutter - license_checker: ^1.6.0 - msix: ^3.6.2 + license_checker: ^1.6.2 pedantic: ^1.11.0 sentry_dart_plugin: ^1.0.0 - translations_cleaner: ^0.0.5 + translations_cleaner: ^0.1.0 import_sorter: ignored_files: # Optional, defaults to [] @@ -174,36 +166,8 @@ flutter: - assets/sounds/ - assets/vodozemac/ -msix_config: - display_name: FluffyChat - publisher_display_name: FluffyChat - publisher: CN=FluffyChat, O=Head of bad integration tests, L=Matrix, S=Internet, C=EU - # #Pangea - # identity_name: chat.fluffy.fluffychat - identity_name: com.talktolearn.chat - # Pangea# - logo_path: assets\logo.png - capabilities: internetClient, location, microphone, webcam - protocol_activation: https - app_uri_handler_hosts: fluffychat.im, matrix.to - execution_alias: fluffychat - sign_msix: false - install_certificate: false - -dependency_overrides: - fcm_shared_isolate: - path: pangea_packages/fcm_shared_isolate - # https://github.com/juliansteenbakker/flutter_secure_storage/issues/920 - flutter_secure_storage_linux: - git: - url: https://github.com/m-berto/flutter_secure_storage.git - ref: patch-2 - path: flutter_secure_storage_linux - flutter_secure_storage_platform_interface: 2.0.0 - # https://github.com/ThexXTURBOXx/flutter_web_auth_2/issues/155 - flutter_web_auth_2: - git: - url: https://github.com/ThexXTURBOXx/flutter_web_auth_2.git - ref: 3.x-without-v1 - path: flutter_web_auth_2 - win32: 5.5.3 +# Guidelines for adding a dependency override: +# 1. Don't do it if you can avoid it or fix it upstream in a manageable time +# 2. Always link an (upstream?) issue +# 3. Explain how and when this can be removed (overrides must be temporarily) +dependency_overrides: \ No newline at end of file diff --git a/scripts/enable-android-google-services.patch b/scripts/enable-android-google-services.patch new file mode 100644 index 000000000..fb806d14e --- /dev/null +++ b/scripts/enable-android-google-services.patch @@ -0,0 +1,144 @@ +diff --git a/android/app/build.gradle b/android/app/build.gradle +index bb8e015cd..3ff4a7579 100644 +--- a/android/app/build.gradle ++++ b/android/app/build.gradle +@@ -2,7 +2,7 @@ plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +- //id "com.google.gms.google-services" ++ id "com.google.gms.google-services" + } + + def localProperties = new Properties() +@@ -97,11 +97,12 @@ flutter { + } + + dependencies { +- //implementation 'com.google.firebase:firebase-messaging:19.0.1' // Workaround for https://github.com/microg/android_packages_apps_GmsCore/issues/313#issuecomment-617651698 ++ implementation 'com.google.firebase:firebase-messaging:19.0.1' ++ // Workaround for https://github.com/microg/android_packages_apps_GmsCore/issues/313#issuecomment-617651698 + implementation 'androidx.multidex:multidex:2.0.1' + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") + } + + configurations.all { +- exclude group: 'com.google.android.gms' ++ //exclude group: 'com.google.android.gms' + } +\ No newline at end of file +diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro +index d0e0fbc9..0a546da0 100644 +--- a/android/app/proguard-rules.pro ++++ b/android/app/proguard-rules.pro +@@ -1 +1,42 @@ +--keep class net.sqlcipher.** { *; } +\ No newline at end of file ++-optimizationpasses 5 ++## Flutter wrapper ++-keep class net.sqlcipher.** { *; } ++-keep class io.flutter.app.** { *; } ++-keep class io.flutter.plugin.** { *; } ++-keep class io.flutter.util.** { *; } ++-keep class io.flutter.view.** { *; } ++-keep class io.flutter.** { *; } ++-keep class io.flutter.plugins.** { *; } ++-dontwarn io.flutter.embedding.** ++ ++##---------------Begin: proguard configuration for Gson (Needed for flutter_local_notifications) ---------- ++# Gson uses generic type information stored in a class file when working with fields. Proguard ++# removes such information by default, so configure it to keep all of it. ++-keepattributes Signature ++ ++# For using GSON @Expose annotation ++-keepattributes *Annotation* ++ ++# Gson specific classes ++-dontwarn sun.misc.** ++ ++# Application classes that will be serialized/deserialized over Gson ++-keep class com.google.gson.examples.android.model.** { ; } ++ ++# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, ++# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) ++-keep class * extends com.google.gson.TypeAdapter ++-keep class * implements com.google.gson.TypeAdapterFactory ++-keep class * implements com.google.gson.JsonSerializer ++-keep class * implements com.google.gson.JsonDeserializer ++ ++# Prevent R8 from leaving Data object members always null ++-keepclassmembers,allowobfuscation class * { ++ @com.google.gson.annotations.SerializedName ; ++} ++ ++# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. ++-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken ++-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken ++ ++##---------------End: proguard configuration for Gson (Needed for flutter_local_notifications) ---------- +\ No newline at end of file +diff --git a/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt b/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt +index d9930f55..510e9845 100644 +--- a/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt ++++ b/android/app/src/main/kotlin/chat/fluffy/fluffychat/FcmPushService.kt +@@ -1,4 +1,4 @@ +-/*package chat.fluffy.fluffychat ++package chat.fluffy.fluffychat + + import com.famedly.fcm_shared_isolate.FcmSharedIsolateService + +@@ -33,4 +33,3 @@ class FcmPushService : FcmSharedIsolateService() { + } + } + } +-*/ +\ No newline at end of file +diff --git a/android/settings.gradle b/android/settings.gradle +index b2fd960a..fdb01a4d 100644 +--- a/android/settings.gradle ++++ b/android/settings.gradle +@@ -20,7 +20,7 @@ plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.7.3" apply false + id "org.jetbrains.kotlin.android" version "2.1.10" apply false +- // id "com.google.gms.google-services" version "4.3.8" apply false ++ id "com.google.gms.google-services" version "4.3.8" apply false + } + + include ":app" +\ No newline at end of file +diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart +index 1ba2659a..989f458e 100644 +--- a/lib/utils/background_push.dart ++++ b/lib/utils/background_push.dart +@@ -39,7 +39,7 @@ import '../config/setting_keys.dart'; + import '../widgets/matrix.dart'; + import 'platform_infos.dart'; + +-//import 'package:fcm_shared_isolate/fcm_shared_isolate.dart'; ++import 'package:fcm_shared_isolate/fcm_shared_isolate.dart'; + + class NoTokenException implements Exception { + String get cause => 'Cannot get firebase token'; +@@ -64,7 +64,7 @@ class BackgroundPush { + + final pendingTests = >{}; + +- final dynamic firebase = null; //FcmSharedIsolate(); ++ final dynamic firebase = FcmSharedIsolate(); + + DateTime? lastReceivedPush; + +diff --git a/pubspec.yaml b/pubspec.yaml +index fb3e3ca4..039b2ccc 100644 +--- a/pubspec.yaml ++++ b/pubspec.yaml +@@ -25,7 +25,7 @@ dependencies: + dynamic_color: ^1.7.0 + emoji_picker_flutter: ^4.3.0 + emojis: ^0.9.9 +- #fcm_shared_isolate: ^0.2.0 ++ fcm_shared_isolate: ^0.2.0 + file_picker: ^8.1.2 + file_selector: ^1.0.3 + flutter: diff --git a/scripts/generate_command_hints_glue.sh b/scripts/generate_command_hints_glue.sh index bdcc443d1..436dc41ed 100644 --- a/scripts/generate_command_hints_glue.sh +++ b/scripts/generate_command_hints_glue.sh @@ -9,7 +9,7 @@ # Looking to add descriptions for a new command, but don't know what it does? # It is likely defined here (in registerDefaultCommands()): -# https://gitlab.com/famedly/company/frontend/famedlysdk/-/blob/main/lib/src/utils/commands_extension.dart +# https://github.com/famedly/matrix-dart-sdk/tree/main/lib/src/utils/commands_extension.dart echo "\ // This file is auto-generated using scripts/generate_command_hints_glue.sh. diff --git a/scripts/package-windows.ps1 b/scripts/package-windows.ps1 deleted file mode 100644 index 7fe516a97..000000000 --- a/scripts/package-windows.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -Write-Output "$WINDOWN_PFX" -Move-Item -Path $WINDOWS_PFX -Destination fluffychat.pem -certutil -decode fluffychat.pem fluffychat.pfx - -flutter pub run msix:create -c fluffychat.pfx -p $WINDOWS_PFX_PASS --sign-msix true --install-certificate false \ No newline at end of file diff --git a/scripts/prepare-web.sh b/scripts/prepare-web.sh index bc47f0237..085274204 100755 --- a/scripts/prepare-web.sh +++ b/scripts/prepare-web.sh @@ -1,10 +1,15 @@ #!/bin/sh -ve -git clone https://github.com/famedly/dart-vodozemac.git .vodozemac +version=$(yq ".dependencies.flutter_vodozemac" < pubspec.yaml) +version=$(expr "$version" : '\^*\(.*\)') +git clone https://github.com/famedly/dart-vodozemac.git -b ${version} .vodozemac cd .vodozemac cargo install flutter_rust_bridge_codegen flutter_rust_bridge_codegen build-web --dart-root dart --rust-root $(readlink -f rust) --release cd .. rm -f ./assets/vodozemac/vodozemac_bindings_dart* mv .vodozemac/dart/web/pkg/vodozemac_bindings_dart* ./assets/vodozemac/ -rm -rf .vodozemac \ No newline at end of file +rm -rf .vodozemac + +flutter pub get +dart compile js ./web/native_executor.dart -o ./web/native_executor.js -m \ No newline at end of file diff --git a/scripts/release-ios-testflight.sh b/scripts/release-ios-testflight.sh index 14e6ea08d..5eef16622 100644 --- a/scripts/release-ios-testflight.sh +++ b/scripts/release-ios-testflight.sh @@ -1,5 +1,5 @@ #!/bin/sh -ve -flutter pub add fcm_shared_isolate:0.1.0 +flutter pub add fcm_shared_isolate:0.2.0 sed -i '' 's,//,,g' lib/utils/background_push.dart flutter clean flutter pub get diff --git a/scripts/remove_intl_keys_from_file.py b/scripts/remove_intl_keys_from_file.py new file mode 100644 index 000000000..cf9aa9f01 --- /dev/null +++ b/scripts/remove_intl_keys_from_file.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +""" +Script to remove all translation keys from one .arb file that exist in another .arb file. + +This script: +1. Takes two .arb files as input: + - A source file containing keys to remove + - A target file to clean +2. Removes all matching keys from the target file +3. Removes corresponding metadata entries (keys starting with @) +4. Preserves key order and file structure + +Usage: + python3 scripts/remove_intl_keys_from_file.py + +Example: + python3 scripts/remove_intl_keys_from_file.py app_en.arb app_es.arb +""" + +import json +import sys +from pathlib import Path +from collections import OrderedDict + + +def load_arb(path: Path) -> OrderedDict: + with open(path, "r", encoding="utf-8") as f: + return json.load(f, object_pairs_hook=OrderedDict) + + +def remove_keys(source_arb: OrderedDict, target_arb: OrderedDict) -> int: + """ + Remove all keys from target_arb that exist in source_arb. + + Includes both normal keys and metadata keys. + """ + keys_to_remove = set(source_arb.keys()) + removed = 0 + + for key in list(target_arb.keys()): + if key in keys_to_remove: + del target_arb[key] + removed += 1 + + return removed + + +def main() -> int: + if len(sys.argv) != 3: + print("Usage: python3 scripts/remove_intl_keys_from_file.py ") + return 1 + + repo_path = Path(__file__).parent.parent.absolute() + l10n_dir = repo_path / 'lib' / 'l10n' + + source_path = l10n_dir / sys.argv[1] + target_path = l10n_dir / sys.argv[2] + + if not source_path.exists(): + print(f"Error: Source file not found: {source_path}") + return 1 + + if not target_path.exists(): + print(f"Error: Target file not found: {target_path}") + return 1 + + source_arb = load_arb(source_path) + target_arb = load_arb(target_path) + + removed_count = remove_keys(source_arb, target_arb) + + if removed_count == 0: + print("No matching keys found. Target file unchanged.") + return 0 + + with open(target_path, "w", encoding="utf-8") as f: + json.dump(target_arb, f, indent=2, ensure_ascii=False) + f.write("\n") + + print(f"Removed {removed_count} entries from {target_path.name}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 779770c98..532b026a0 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,7 +1,7 @@ name: fluffychat title: FluffyChat base: core24 -version: 1.27.0 +version: 2.4.0 license: AGPL-3.0 summary: The cutest messenger in the Matrix network description: | @@ -37,8 +37,8 @@ description: | And a world where apps are made for fluffyness and not for profit. ♥ - Join the community: https://matrix.to/#/#fluffychat:matrix.org - Website: http://fluffychat.im + Join the community: https://matrix.to/#/#fluffy-space:matrix.org + Website: http://fluffy.chat Microblog: https://mastodon.art/@krille grade: stable @@ -53,7 +53,7 @@ platforms: parts: flutter-git: source: https://github.com/flutter/flutter.git - source-tag: 3.32.4 + source-tag: 3.38.4 source-depth: 1 plugin: nil override-build: | @@ -128,4 +128,4 @@ apps: XDG_DATA_HOME: $SNAP_USER_DATA XDG_DATA_DIRS: $SNAP/usr/share GDK_GL: gles - LD_LIBRARY_PATH: "$LD_LIBRARY_PATH:$SNAP/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET" \ No newline at end of file + LD_LIBRARY_PATH: "$LD_LIBRARY_PATH:$SNAP/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET" diff --git a/test/command_hint_test.dart b/test/command_hint_test.dart index 68812be5f..84f558635 100644 --- a/test/command_hint_test.dart +++ b/test/command_hint_test.dart @@ -8,8 +8,7 @@ import 'utils/test_client.dart'; void main() async { test('Check for missing /command hints', () async { final translated = - jsonDecode(File('lib/l10n/intl_en.arb').readAsStringSync()) - .keys + jsonDecode(File('lib/l10n/intl_en.arb').readAsStringSync()).keys .where((String k) => k.startsWith('commandHint_')) .map((k) => k.replaceFirst('commandHint_', '')); final commands = (await prepareTestClient()).commands.keys; diff --git a/test/pangea/choreo_record_test.dart b/test/pangea/choreo_record_test.dart index cd8ee065e..4c6c215f9 100644 --- a/test/pangea/choreo_record_test.dart +++ b/test/pangea/choreo_record_test.dart @@ -14,9 +14,7 @@ void main() async { editedText: editedText, ); - assert( - edits.offset == 1 && edits.length == 3 && edits.insert == "erri", - ); + assert(edits.offset == 1 && edits.length == 3 && edits.insert == "erri"); }); test("Test that data saved via ChoreoEdit can be accurately retrieved", () { @@ -30,9 +28,7 @@ void main() async { final String retrieved = edits.editedText(originalText); - assert( - retrieved == editedText, - ); + assert(retrieved == editedText); }); test("Test that addRecord and lastText work correctly", () { diff --git a/test/pangea/span_data_model_test.dart b/test/pangea/span_data_model_test.dart index cef2755c2..3e3cc1e24 100644 --- a/test/pangea/span_data_model_test.dart +++ b/test/pangea/span_data_model_test.dart @@ -4,25 +4,25 @@ import 'package:fluffychat/pangea/choreographer/igc/replacement_type_enum.dart'; import 'package:fluffychat/pangea/choreographer/igc/span_data_model.dart'; void main() { - test('SpanData.fromJson handles legacy correction type (maps to grammar)', - () { - final Map legacyJson = { - 'message': null, - 'short_message': null, - 'choices': [], - 'offset': 0, - 'length': 4, - 'full_text': 'Test', - 'type': { - 'type_name': 'correction', - }, - }; + test( + 'SpanData.fromJson handles legacy correction type (maps to grammar)', + () { + final Map legacyJson = { + 'message': null, + 'short_message': null, + 'choices': [], + 'offset': 0, + 'length': 4, + 'full_text': 'Test', + 'type': {'type_name': 'correction'}, + }; - expect(() => SpanData.fromJson(legacyJson), returnsNormally); - final SpanData span = SpanData.fromJson(legacyJson); - // 'correction' is mapped to 'grammar' for backward compatibility - expect(span.type, ReplacementTypeEnum.subjectVerbAgreement); - }); + expect(() => SpanData.fromJson(legacyJson), returnsNormally); + final SpanData span = SpanData.fromJson(legacyJson); + // 'correction' is mapped to 'grammar' for backward compatibility + expect(span.type, ReplacementTypeEnum.subjectVerbAgreement); + }, + ); test('SpanData.fromJson handles legacy typeName object', () { final Map legacyJson = { @@ -32,9 +32,7 @@ void main() { 'offset': 0, 'length': 4, 'full_text': 'Test', - 'type': { - 'typeName': 'itStart', - }, + 'type': {'typeName': 'itStart'}, }; expect(() => SpanData.fromJson(legacyJson), returnsNormally); @@ -58,22 +56,24 @@ void main() { expect(span.type, ReplacementTypeEnum.didYouMean); }); - test('SpanData.fromJson handles legacy vocabulary type (maps to wordChoice)', - () { - final Map legacyJson = { - 'message': null, - 'short_message': null, - 'choices': [], - 'offset': 0, - 'length': 4, - 'full_text': 'Test', - 'type': 'vocabulary', - }; + test( + 'SpanData.fromJson handles legacy vocabulary type (maps to wordChoice)', + () { + final Map legacyJson = { + 'message': null, + 'short_message': null, + 'choices': [], + 'offset': 0, + 'length': 4, + 'full_text': 'Test', + 'type': 'vocabulary', + }; - expect(() => SpanData.fromJson(legacyJson), returnsNormally); - final SpanData span = SpanData.fromJson(legacyJson); - expect(span.type, ReplacementTypeEnum.other); - }); + expect(() => SpanData.fromJson(legacyJson), returnsNormally); + final SpanData span = SpanData.fromJson(legacyJson); + expect(span.type, ReplacementTypeEnum.other); + }, + ); test('SpanData.fromJson handles new grammar type directly', () { final Map jsonData = { @@ -144,20 +144,22 @@ void main() { expect(span.fullText, 'Text from parent'); }); - test('uses empty string when neither full_text nor parentFullText present', - () { - final Map jsonData = { - 'message': null, - 'short_message': null, - 'choices': [], - 'offset': 0, - 'length': 4, - 'type': 'grammar', - }; + test( + 'uses empty string when neither full_text nor parentFullText present', + () { + final Map jsonData = { + 'message': null, + 'short_message': null, + 'choices': [], + 'offset': 0, + 'length': 4, + 'type': 'grammar', + }; - final SpanData span = SpanData.fromJson(jsonData); - expect(span.fullText, ''); - }); + final SpanData span = SpanData.fromJson(jsonData); + expect(span.fullText, ''); + }, + ); test('prefers sentence over full_text (legacy field name)', () { final Map jsonData = { diff --git a/test/pangea/text_normalization_test.dart b/test/pangea/text_normalization_test.dart index b8aef771a..1eac52ac8 100644 --- a/test/pangea/text_normalization_test.dart +++ b/test/pangea/text_normalization_test.dart @@ -339,8 +339,10 @@ void main() { final expected = testCase['expected']!; test('Test ${i + 1}: "$input" should normalize to "$expected"', () { - final actual = - normalizeString(input, 'en'); // Default to English for tests + final actual = normalizeString( + input, + 'en', + ); // Default to English for tests expect( actual, equals(expected), diff --git a/web/index.html b/web/index.html index 8100dd616..69fc37dd8 100644 --- a/web/index.html +++ b/web/index.html @@ -43,6 +43,7 @@ + diff --git a/web/native_executor.dart b/web/native_executor.dart new file mode 100644 index 000000000..888af44a9 --- /dev/null +++ b/web/native_executor.dart @@ -0,0 +1,3 @@ +import 'package:matrix/matrix.dart'; + +void main() => startWebWorker(); diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 65f8c87c5..8b853c62a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -16,14 +17,16 @@ #include #include #include -#include #include #include #include +#include #include #include #include #include +#include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -31,6 +34,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("AppLinksPluginCApi")); AudioplayersWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); + DesktopWebviewWindowPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); EmojiPickerFlutterPluginCApiRegisterWithRegistrar( @@ -47,14 +52,14 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FlutterWebRTCPlugin")); GeolocatorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("GeolocatorWindows")); - PasteboardPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("PasteboardPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); RecordWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("RecordWindowsPluginCApi")); RivePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("RivePlugin")); + ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); SentryFlutterPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("SentryFlutterPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( @@ -63,6 +68,10 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WebcryptoPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WebcryptoPlugin")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); WindowToFrontPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowToFrontPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index bb75d6852..4f57c7fc3 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST app_links audioplayers_windows + desktop_webview_window dynamic_color emoji_picker_flutter file_selector_windows @@ -13,18 +14,21 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_tts flutter_webrtc geolocator_windows - pasteboard permission_handler_windows record_windows rive_common + screen_retriever_windows sentry_flutter share_plus sqlcipher_flutter_libs url_launcher_windows + webcrypto + window_manager window_to_front ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_local_notifications_windows flutter_vodozemac ) diff --git a/winuwp/.gitignore b/winuwp/.gitignore deleted file mode 100644 index d492d0d98..000000000 --- a/winuwp/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/winuwp/CMakeLists.txt b/winuwp/CMakeLists.txt deleted file mode 100644 index 7530c80c6..000000000 --- a/winuwp/CMakeLists.txt +++ /dev/null @@ -1,64 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.8) -set(CMAKE_SYSTEM_NAME WindowsStore) -set(CMAKE_SYSTEM_VERSION 10.0) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED YES) -project(fluffychat LANGUAGES CXX) - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0079 NEW) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "fluffychat") - -# Define build configuration options. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() -# Define settings for the Profile build mode. -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100" /await) - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") - target_compile_definitions(${TARGET} PRIVATE WINUWP) - set_target_properties(${TARGET} PROPERTIES VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION 10.0.18362.0) -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build; see runner/CMakeLists.txt. -add_subdirectory("runner_uwp") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) diff --git a/winuwp/flutter/CMakeLists.txt b/winuwp/flutter/CMakeLists.txt deleted file mode 100644 index 9adbd9ddb..000000000 --- a/winuwp/flutter/CMakeLists.txt +++ /dev/null @@ -1,92 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.8) -set(CMAKE_SYSTEM_NAME WindowsStore) -set(CMAKE_SYSTEM_VERSION 10.0) -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -include(CMakePrintHelpers) - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows_winuwp.dll") - -# === Assets === -set(CMAKE_INSTALL_MANIFEST "${EPHEMERAL_DIR}/install_manifest") -file(STRINGS ${CMAKE_INSTALL_MANIFEST} INSTALL_MANIFEST_CONTENT) - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(INSTALL_MANIFEST_CONTENT ${INSTALL_MANIFEST_CONTENT} PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/winuwp/flutter/flutter_windows.h b/winuwp/flutter/flutter_windows.h deleted file mode 100644 index 0aeb6fdc1..000000000 --- a/winuwp/flutter/flutter_windows.h +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_H_ -#define FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_H_ - -#include -#include -#include - -#include "flutter_export.h" -#include "flutter_messenger.h" -#include "flutter_plugin_registrar.h" - -#ifdef WINUWP -#include -#include -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - -// Opaque reference to a Flutter window controller. -typedef struct FlutterDesktopViewControllerState* - FlutterDesktopViewControllerRef; - -// Opaque reference to a Flutter window. -struct FlutterDesktopView; -typedef struct FlutterDesktopView* FlutterDesktopViewRef; - -// Opaque reference to a Flutter engine instance. -struct FlutterDesktopEngine; -typedef struct FlutterDesktopEngine* FlutterDesktopEngineRef; - -// Properties for configuring a Flutter engine instance. -typedef struct { - // The path to the flutter_assets folder for the application to be run. - // This can either be an absolute path or a path relative to the directory - // containing the executable. - const wchar_t* assets_path; - - // The path to the icudtl.dat file for the version of Flutter you are using. - // This can either be an absolute path or a path relative to the directory - // containing the executable. - const wchar_t* icu_data_path; - - // The path to the AOT library file for your application, if any. - // This can either be an absolute path or a path relative to the directory - // containing the executable. This can be nullptr for a non-AOT build, as - // it will be ignored in that case. - const wchar_t* aot_library_path; - - // Number of elements in the array passed in as dart_entrypoint_argv. - int dart_entrypoint_argc; - - // Array of Dart entrypoint arguments. This is deep copied during the call - // to FlutterDesktopEngineCreate. - const char** dart_entrypoint_argv; - -} FlutterDesktopEngineProperties; - -// ========== View Controller ========== - -// Creates a view that hosts and displays the given engine instance. -// -// This takes ownership of |engine|, so FlutterDesktopEngineDestroy should no -// longer be called on it, as it will be called internally when the view -// controller is destroyed. If creating the view controller fails, the engine -// will be destroyed immediately. -// -// If |engine| is not already running, the view controller will start running -// it automatically before displaying the window. -// -// The caller owns the returned reference, and is responsible for calling -// FlutterDesktopViewControllerDestroy. Returns a null pointer in the event of -// an error. -#ifdef WINUWP -// The CoreApplicationView implementation accepts a pointer to the host -// CoreApplicationView and view hookup is performed in the construction path. -FLUTTER_EXPORT FlutterDesktopViewControllerRef -FlutterDesktopViewControllerCreateFromCoreApplicationView( - ABI::Windows::ApplicationModel::Core::CoreApplicationView* window, - ABI::Windows::ApplicationModel::Activation::IActivatedEventArgs* args, - FlutterDesktopEngineRef engine); -#else //! WINUWP -// The Win32 implementation accepts width, height -// with view hookup explicitly performed using the caller using HWND parenting. -FLUTTER_EXPORT FlutterDesktopViewControllerRef -FlutterDesktopViewControllerCreate(int width, - int height, - FlutterDesktopEngineRef engine); -#endif - -// Shuts down the engine instance associated with |controller|, and cleans up -// associated state. -// -// |controller| is no longer valid after this call. -FLUTTER_EXPORT void FlutterDesktopViewControllerDestroy( - FlutterDesktopViewControllerRef controller); - -// Returns the handle for the engine running in FlutterDesktopViewControllerRef. -// -// Its lifetime is the same as the |controller|'s. -FLUTTER_EXPORT FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine( - FlutterDesktopViewControllerRef controller); -// Returns the view managed by the given controller. - -FLUTTER_EXPORT FlutterDesktopViewRef -FlutterDesktopViewControllerGetView(FlutterDesktopViewControllerRef controller); - -// Requests new frame from engine and repaints the view -FLUTTER_EXPORT void FlutterDesktopViewControllerForceRedraw( - FlutterDesktopViewControllerRef controller); - -#ifndef WINUWP -// Allows the Flutter engine and any interested plugins an opportunity to -// handle the given message. -// -// If the WindowProc was handled and further handling should stop, this returns -// true and |result| will be populated. |result| is not set if returning false. -FLUTTER_EXPORT bool FlutterDesktopViewControllerHandleTopLevelWindowProc( - FlutterDesktopViewControllerRef controller, - HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result); -#endif - -// ========== Engine ========== - -// Creates a Flutter engine with the given properties. -// -// The caller owns the returned reference, and is responsible for calling -// FlutterDesktopEngineDestroy. The lifetime of |engine_properties| is required -// to extend only until the end of this call. -FLUTTER_EXPORT FlutterDesktopEngineRef FlutterDesktopEngineCreate( - const FlutterDesktopEngineProperties* engine_properties); - -// Shuts down and destroys the given engine instance. Returns true if the -// shutdown was successful, or if the engine was not running. -// -// |engine| is no longer valid after this call. -FLUTTER_EXPORT bool FlutterDesktopEngineDestroy(FlutterDesktopEngineRef engine); - -// Starts running the given engine instance and optional entry point in the Dart -// project. If the entry point is null, defaults to main(). -// -// If provided, entry_point must be the name of a top-level function from the -// same Dart library that contains the app's main() function, and must be -// decorated with `@pragma(vm:entry-point)` to ensure the method is not -// tree-shaken by the Dart compiler. -// -// Returns false if running the engine failed. -FLUTTER_EXPORT bool FlutterDesktopEngineRun(FlutterDesktopEngineRef engine, - const char* entry_point); - -#ifndef WINUWP -// DEPRECATED: This is no longer necessary to call, Flutter will take care of -// processing engine messages transparently through DispatchMessage. -// -// Processes any pending events in the Flutter engine, and returns the -// number of nanoseconds until the next scheduled event (or max, if none). -// -// This should be called on every run of the application-level runloop, and -// a wait for native events in the runloop should never be longer than the -// last return value from this function. -FLUTTER_EXPORT uint64_t -FlutterDesktopEngineProcessMessages(FlutterDesktopEngineRef engine); -#endif - -FLUTTER_EXPORT void FlutterDesktopEngineReloadSystemFonts( - FlutterDesktopEngineRef engine); - -FLUTTER_EXPORT void FlutterDesktopEngineReloadPlatformBrightness( - FlutterDesktopEngineRef engine); - -// Returns the plugin registrar handle for the plugin with the given name. -// -// The name must be unique across the application. -FLUTTER_EXPORT FlutterDesktopPluginRegistrarRef -FlutterDesktopEngineGetPluginRegistrar(FlutterDesktopEngineRef engine, - const char* plugin_name); - -// Returns the messenger associated with the engine. -FLUTTER_EXPORT FlutterDesktopMessengerRef -FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine); - -// Returns the texture registrar associated with the engine. -FLUTTER_EXPORT FlutterDesktopTextureRegistrarRef -FlutterDesktopEngineGetTextureRegistrar( - FlutterDesktopTextureRegistrarRef texture_registrar); - -// ========== View ========== - -#ifdef WINUWP -// Return backing CoreApplicationView for manipulation of CoreWindow and -// CoreTitleBar in host application. -FLUTTER_EXPORT ABI::Windows::ApplicationModel::Core::CoreApplicationView* -FlutterDesktopViewGetCoreApplicationView(FlutterDesktopViewRef view); -#else -// Return backing HWND for manipulation in host application. -FLUTTER_EXPORT HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view); -#endif - -// ========== Plugin Registrar (extensions) ========== -// These are Windows-specific extensions to flutter_plugin_registrar.h - -// Function pointer type for top level WindowProc delegate registration. -// -// The user data will be whatever was passed to -// FlutterDesktopRegisterTopLevelWindowProcHandler. -// -// Implementations should populate |result| and return true if the WindowProc -// was handled and further handling should stop. |result| is ignored if the -// function returns false. -typedef bool (*FlutterDesktopWindowProcCallback)(HWND /* hwnd */, - UINT /* uMsg */, - WPARAM /*wParam*/, - LPARAM /* lParam*/, - void* /* user data */, - LRESULT* result); - -// Returns the view associated with this registrar's engine instance. -FLUTTER_EXPORT FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( - FlutterDesktopPluginRegistrarRef registrar); - -#ifndef WINUWP -FLUTTER_EXPORT void -FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate( - FlutterDesktopPluginRegistrarRef registrar, - FlutterDesktopWindowProcCallback delegate, - void* user_data); - -FLUTTER_EXPORT void -FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate( - FlutterDesktopPluginRegistrarRef registrar, - FlutterDesktopWindowProcCallback delegate); -#endif - -// ========== Freestanding Utilities ========== - -// Gets the DPI for a given |hwnd|, depending on the supported APIs per -// windows version and DPI awareness mode. If nullptr is passed, returns the DPI -// of the primary monitor. -// -// This uses the same logic and fallback for older Windows versions that is used -// internally by Flutter to determine the DPI to use for displaying Flutter -// content, so should be used by any code (e.g., in plugins) that translates -// between Windows and Dart sizes/offsets. -FLUTTER_EXPORT UINT FlutterDesktopGetDpiForHWND(HWND hwnd); - -// Gets the DPI for a given |monitor|. If the API is not available, a default -// DPI of 96 is returned. -// -// See FlutterDesktopGetDpiForHWND for more information. -FLUTTER_EXPORT UINT FlutterDesktopGetDpiForMonitor(HMONITOR monitor); - -// Reopens stdout and stderr and resysncs the standard library output streams. -// Should be called if output is being directed somewhere in the runner process -// (e.g., after an AllocConsole call). -FLUTTER_EXPORT void FlutterDesktopResyncOutputStreams(); - -#if defined(__cplusplus) -} // extern "C" -#endif - -#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_WINDOWS_H_ diff --git a/winuwp/flutter/generated_plugin_registrant.cc b/winuwp/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 8b6d4680a..000000000 --- a/winuwp/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void RegisterPlugins(flutter::PluginRegistry* registry) { -} diff --git a/winuwp/flutter/generated_plugin_registrant.h b/winuwp/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a..000000000 --- a/winuwp/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/winuwp/flutter/generated_plugins.cmake b/winuwp/flutter/generated_plugins.cmake deleted file mode 100644 index 4d10c2518..000000000 --- a/winuwp/flutter/generated_plugins.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) diff --git a/winuwp/project_version b/winuwp/project_version deleted file mode 100644 index c22708346..000000000 --- a/winuwp/project_version +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file diff --git a/winuwp/runner_uwp/Assets/LargeTile.scale-100.png b/winuwp/runner_uwp/Assets/LargeTile.scale-100.png deleted file mode 100644 index fa651eb71..000000000 Binary files a/winuwp/runner_uwp/Assets/LargeTile.scale-100.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/LargeTile.scale-125.png b/winuwp/runner_uwp/Assets/LargeTile.scale-125.png deleted file mode 100644 index 649e07696..000000000 Binary files a/winuwp/runner_uwp/Assets/LargeTile.scale-125.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/LargeTile.scale-150.png b/winuwp/runner_uwp/Assets/LargeTile.scale-150.png deleted file mode 100644 index fd14c60cb..000000000 Binary files a/winuwp/runner_uwp/Assets/LargeTile.scale-150.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/LargeTile.scale-200.png b/winuwp/runner_uwp/Assets/LargeTile.scale-200.png deleted file mode 100644 index 873537f33..000000000 Binary files a/winuwp/runner_uwp/Assets/LargeTile.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/LargeTile.scale-400.png b/winuwp/runner_uwp/Assets/LargeTile.scale-400.png deleted file mode 100644 index 979878f73..000000000 Binary files a/winuwp/runner_uwp/Assets/LargeTile.scale-400.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/LockScreenLogo.scale-200.png b/winuwp/runner_uwp/Assets/LockScreenLogo.scale-200.png deleted file mode 100644 index 735f57adb..000000000 Binary files a/winuwp/runner_uwp/Assets/LockScreenLogo.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SmallTile.scale-100.png b/winuwp/runner_uwp/Assets/SmallTile.scale-100.png deleted file mode 100644 index 35e7f6242..000000000 Binary files a/winuwp/runner_uwp/Assets/SmallTile.scale-100.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SmallTile.scale-125.png b/winuwp/runner_uwp/Assets/SmallTile.scale-125.png deleted file mode 100644 index 2a74cc67e..000000000 Binary files a/winuwp/runner_uwp/Assets/SmallTile.scale-125.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SmallTile.scale-150.png b/winuwp/runner_uwp/Assets/SmallTile.scale-150.png deleted file mode 100644 index 157124823..000000000 Binary files a/winuwp/runner_uwp/Assets/SmallTile.scale-150.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SmallTile.scale-200.png b/winuwp/runner_uwp/Assets/SmallTile.scale-200.png deleted file mode 100644 index 07ec2ddcb..000000000 Binary files a/winuwp/runner_uwp/Assets/SmallTile.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SmallTile.scale-400.png b/winuwp/runner_uwp/Assets/SmallTile.scale-400.png deleted file mode 100644 index c205729b7..000000000 Binary files a/winuwp/runner_uwp/Assets/SmallTile.scale-400.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SplashScreen.scale-100.png b/winuwp/runner_uwp/Assets/SplashScreen.scale-100.png deleted file mode 100644 index 601257981..000000000 Binary files a/winuwp/runner_uwp/Assets/SplashScreen.scale-100.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SplashScreen.scale-125.png b/winuwp/runner_uwp/Assets/SplashScreen.scale-125.png deleted file mode 100644 index 0c35be827..000000000 Binary files a/winuwp/runner_uwp/Assets/SplashScreen.scale-125.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SplashScreen.scale-150.png b/winuwp/runner_uwp/Assets/SplashScreen.scale-150.png deleted file mode 100644 index f1e60f33c..000000000 Binary files a/winuwp/runner_uwp/Assets/SplashScreen.scale-150.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SplashScreen.scale-200.png b/winuwp/runner_uwp/Assets/SplashScreen.scale-200.png deleted file mode 100644 index 73d2461ce..000000000 Binary files a/winuwp/runner_uwp/Assets/SplashScreen.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/SplashScreen.scale-400.png b/winuwp/runner_uwp/Assets/SplashScreen.scale-400.png deleted file mode 100644 index b2b7ca9c4..000000000 Binary files a/winuwp/runner_uwp/Assets/SplashScreen.scale-400.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-100.png b/winuwp/runner_uwp/Assets/Square150x150Logo.scale-100.png deleted file mode 100644 index cdc3e9732..000000000 Binary files a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-100.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-125.png b/winuwp/runner_uwp/Assets/Square150x150Logo.scale-125.png deleted file mode 100644 index 71e5a1153..000000000 Binary files a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-125.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-150.png b/winuwp/runner_uwp/Assets/Square150x150Logo.scale-150.png deleted file mode 100644 index 60f2e1827..000000000 Binary files a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-150.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-200.png b/winuwp/runner_uwp/Assets/Square150x150Logo.scale-200.png deleted file mode 100644 index 20814081d..000000000 Binary files a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-400.png b/winuwp/runner_uwp/Assets/Square150x150Logo.scale-400.png deleted file mode 100644 index 605aacb02..000000000 Binary files a/winuwp/runner_uwp/Assets/Square150x150Logo.scale-400.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png b/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png deleted file mode 100644 index 1c78d96bf..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png deleted file mode 100644 index d49d3d899..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png deleted file mode 100644 index 889622468..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png b/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png deleted file mode 100644 index 6389ede5a..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-100.png b/winuwp/runner_uwp/Assets/Square44x44Logo.scale-100.png deleted file mode 100644 index 47e3cd29b..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-100.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-125.png b/winuwp/runner_uwp/Assets/Square44x44Logo.scale-125.png deleted file mode 100644 index 50faa9328..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-125.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-150.png b/winuwp/runner_uwp/Assets/Square44x44Logo.scale-150.png deleted file mode 100644 index f0293ed81..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-150.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-200.png b/winuwp/runner_uwp/Assets/Square44x44Logo.scale-200.png deleted file mode 100644 index e54a56dbc..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-400.png b/winuwp/runner_uwp/Assets/Square44x44Logo.scale-400.png deleted file mode 100644 index 4b5fb179f..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.scale-400.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-16.png b/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-16.png deleted file mode 100644 index a1a6ec70a..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-16.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-24.png b/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-24.png deleted file mode 100644 index c67a8e12f..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-24.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png deleted file mode 100644 index 47d36f63e..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-256.png b/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-256.png deleted file mode 100644 index 50efc008d..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-256.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-32.png b/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-32.png deleted file mode 100644 index f86682c9f..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-32.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-48.png b/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-48.png deleted file mode 100644 index 7561269af..000000000 Binary files a/winuwp/runner_uwp/Assets/Square44x44Logo.targetsize-48.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/StoreLogo.png b/winuwp/runner_uwp/Assets/StoreLogo.png deleted file mode 100644 index 7385b56c0..000000000 Binary files a/winuwp/runner_uwp/Assets/StoreLogo.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/StoreLogo.scale-100.png b/winuwp/runner_uwp/Assets/StoreLogo.scale-100.png deleted file mode 100644 index fcefe81ba..000000000 Binary files a/winuwp/runner_uwp/Assets/StoreLogo.scale-100.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/StoreLogo.scale-125.png b/winuwp/runner_uwp/Assets/StoreLogo.scale-125.png deleted file mode 100644 index 4381be777..000000000 Binary files a/winuwp/runner_uwp/Assets/StoreLogo.scale-125.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/StoreLogo.scale-150.png b/winuwp/runner_uwp/Assets/StoreLogo.scale-150.png deleted file mode 100644 index e49390bf3..000000000 Binary files a/winuwp/runner_uwp/Assets/StoreLogo.scale-150.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/StoreLogo.scale-200.png b/winuwp/runner_uwp/Assets/StoreLogo.scale-200.png deleted file mode 100644 index fb740e846..000000000 Binary files a/winuwp/runner_uwp/Assets/StoreLogo.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/StoreLogo.scale-400.png b/winuwp/runner_uwp/Assets/StoreLogo.scale-400.png deleted file mode 100644 index d1472741e..000000000 Binary files a/winuwp/runner_uwp/Assets/StoreLogo.scale-400.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/Wide310x150Logo.scale-200.png b/winuwp/runner_uwp/Assets/Wide310x150Logo.scale-200.png deleted file mode 100644 index 288995b39..000000000 Binary files a/winuwp/runner_uwp/Assets/Wide310x150Logo.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/WideTile.scale-100.png b/winuwp/runner_uwp/Assets/WideTile.scale-100.png deleted file mode 100644 index 1cb688c7b..000000000 Binary files a/winuwp/runner_uwp/Assets/WideTile.scale-100.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/WideTile.scale-125.png b/winuwp/runner_uwp/Assets/WideTile.scale-125.png deleted file mode 100644 index 729239615..000000000 Binary files a/winuwp/runner_uwp/Assets/WideTile.scale-125.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/WideTile.scale-150.png b/winuwp/runner_uwp/Assets/WideTile.scale-150.png deleted file mode 100644 index d4b275a57..000000000 Binary files a/winuwp/runner_uwp/Assets/WideTile.scale-150.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/WideTile.scale-200.png b/winuwp/runner_uwp/Assets/WideTile.scale-200.png deleted file mode 100644 index 601257981..000000000 Binary files a/winuwp/runner_uwp/Assets/WideTile.scale-200.png and /dev/null differ diff --git a/winuwp/runner_uwp/Assets/WideTile.scale-400.png b/winuwp/runner_uwp/Assets/WideTile.scale-400.png deleted file mode 100644 index 73d2461ce..000000000 Binary files a/winuwp/runner_uwp/Assets/WideTile.scale-400.png and /dev/null differ diff --git a/winuwp/runner_uwp/CMakeLists.txt b/winuwp/runner_uwp/CMakeLists.txt deleted file mode 100644 index b4362a4fc..000000000 --- a/winuwp/runner_uwp/CMakeLists.txt +++ /dev/null @@ -1,141 +0,0 @@ -cmake_minimum_required (VERSION 3.8) -set(CMAKE_SYSTEM_NAME WindowsStore) -set(CMAKE_SYSTEM_VERSION 10.0) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED YES) - -include(CMakePrintHelpers) - -project (runner LANGUAGES CXX) - -# UWP tile and icon assets. -set(ASSET_FILES ${ASSET_FILES} - Assets/LargeTile.scale-100.png - Assets/LargeTile.scale-125.png - Assets/LargeTile.scale-150.png - Assets/LargeTile.scale-200.png - Assets/LargeTile.scale-400.png - Assets/LockScreenLogo.scale-200.png - Assets/SmallTile.scale-100.png - Assets/SmallTile.scale-125.png - Assets/SmallTile.scale-150.png - Assets/SmallTile.scale-200.png - Assets/SmallTile.scale-400.png - Assets/SplashScreen.scale-100.png - Assets/SplashScreen.scale-125.png - Assets/SplashScreen.scale-150.png - Assets/SplashScreen.scale-200.png - Assets/SplashScreen.scale-400.png - Assets/Square44x44Logo.altform-unplated_targetsize-16.png - Assets/Square44x44Logo.altform-unplated_targetsize-32.png - Assets/Square44x44Logo.altform-unplated_targetsize-48.png - Assets/Square44x44Logo.altform-unplated_targetsize-256.png - Assets/Square44x44Logo.scale-100.png - Assets/Square44x44Logo.scale-125.png - Assets/Square44x44Logo.scale-150.png - Assets/Square44x44Logo.scale-200.png - Assets/Square44x44Logo.scale-400.png - Assets/Square44x44Logo.targetsize-16.png - Assets/Square44x44Logo.targetsize-24.png - Assets/Square44x44Logo.targetsize-24_altform-unplated.png - Assets/Square44x44Logo.targetsize-32.png - Assets/Square44x44Logo.targetsize-48.png - Assets/Square44x44Logo.targetsize-256.png - Assets/Square150x150Logo.scale-100.png - Assets/Square150x150Logo.scale-125.png - Assets/Square150x150Logo.scale-150.png - Assets/Square150x150Logo.scale-200.png - Assets/Square150x150Logo.scale-400.png - Assets/StoreLogo.png - Assets/StoreLogo.scale-100.png - Assets/StoreLogo.scale-125.png - Assets/StoreLogo.scale-150.png - Assets/StoreLogo.scale-200.png - Assets/StoreLogo.scale-400.png - Assets/Wide310x150Logo.scale-200.png - Assets/WideTile.scale-100.png - Assets/WideTile.scale-125.png - Assets/WideTile.scale-150.png - Assets/WideTile.scale-200.png - Assets/WideTile.scale-400.png -) - -# Configure package manifest file. -set(APP_MANIFEST_NAME Package.appxmanifest) -set(APP_MANIFEST_TARGET_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${APP_MANIFEST_NAME}) -set(SHORT_NAME ${BINARY_NAME}) -set(PACKAGE_GUID "086F9B60-CB52-4D0B-9B4E-AE891E7859D1") - -configure_file( - appxmanifest.in - ${APP_MANIFEST_TARGET_LOCATION} - @ONLY) - -set(CONTENT_FILES ${APP_MANIFEST_TARGET_LOCATION}) - -# Configure package content files. -set_property(SOURCE ${CONTENT_FILES} PROPERTY VS_DEPLOYMENT_CONTENT 1) - -set(RESOURCE_FILES ${ASSET_FILES} ${CONTENT_FILES} Windows_TemporaryKey.pfx) -set_property(SOURCE ${ASSET_FILES} PROPERTY VS_DEPLOYMENT_CONTENT 1) -set_property(SOURCE ${ASSET_FILES} PROPERTY VS_DEPLOYMENT_LOCATION "Assets") - -set(STRING_FILES Resources.pri) -set_property(SOURCE ${STRING_FILES} PROPERTY VS_TOOL_OVERRIDE "PRIResource") - -source_group("Resource Files" FILES ${RESOURCE_FILES} ${CONTENT_FILES} ${STRING_FILES}) - -# Configure Flutter assets using tool generated install manifest -foreach(ITEM ${INSTALL_MANIFEST_CONTENT}) - get_filename_component(ITEM_REL ${CMAKE_BINARY_DIR} DIRECTORY) - file(RELATIVE_PATH RELPATH ${ITEM_REL} ${ITEM}) - - get_filename_component(RELPATH ${RELPATH} DIRECTORY) - get_filename_component(ITEMEXT ${ITEM} LAST_EXT) - - if("${ITEMEXT}" STREQUAL ".dll" OR "${ITEMEXT}" STREQUAL ".pdb") - string(CONCAT RELPATH "") - elseif ("${ITEMEXT}" STREQUAL ".so") - file(RELATIVE_PATH RELPATH "${ITEM_REL}/winuwp" ${ITEM}) - string(REGEX REPLACE "/" "\\\\" RELPATH ${RELPATH}) - string(CONCAT RELPATH "Assets\\Data") - elseif("${ITEMEXT}" STREQUAL ".dat") - string(CONCAT RELPATH "Assets\\Data") - else() - string(REGEX REPLACE "/" "\\\\" RELPATH ${RELPATH}) - string(CONCAT RELPATH "Assets\\Data\\" ${RELPATH}) - endif() - - cmake_print_variables(${RELPATH}) - - set_property(SOURCE ${ITEM} PROPERTY VS_DEPLOYMENT_CONTENT 1) - set_property(SOURCE ${ITEM} PROPERTY VS_DEPLOYMENT_LOCATION ${RELPATH}) -endforeach() - -# Define the application target. To change its name, change BINARY_NAME in the -# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer -# work. -# -# Any new source files that you add to the application should be added here. -add_executable (${BINARY_NAME} WIN32 - main.cpp - flutter_frameworkview.cpp - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - ${RESOURCE_FILES} - ${INSTALL_MANIFEST_CONTENT} -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Disable Windows macros that collide with C++ standard library functions. -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") - -# Add dependency libraries and include directories. Add any application-specific -# dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE WindowsApp flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/winuwp/runner_uwp/CMakeSettings.json b/winuwp/runner_uwp/CMakeSettings.json deleted file mode 100644 index ba63a530d..000000000 --- a/winuwp/runner_uwp/CMakeSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file. - "configurations": [ - { - "name": "Debug", - "generator": "Visual Studio 15 2017 Win64", - "configurationType": "Debug", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", - "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "" - }, - { - "name": "Release", - "generator": "Visual Studio 15 2017 Win64", - "configurationType": "Release", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", - "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "" - } - ] -} diff --git a/winuwp/runner_uwp/Windows_TemporaryKey.pfx b/winuwp/runner_uwp/Windows_TemporaryKey.pfx deleted file mode 100644 index 1cad9993d..000000000 Binary files a/winuwp/runner_uwp/Windows_TemporaryKey.pfx and /dev/null differ diff --git a/winuwp/runner_uwp/appxmanifest.in b/winuwp/runner_uwp/appxmanifest.in deleted file mode 100644 index 570d424ee..000000000 --- a/winuwp/runner_uwp/appxmanifest.in +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - @SHORT_NAME@ - CMake Test Cert - Assets/StoreLogo.png - - - - - - - - - - - - - - - - - - - - - - diff --git a/winuwp/runner_uwp/flutter_frameworkview.cpp b/winuwp/runner_uwp/flutter_frameworkview.cpp deleted file mode 100644 index bcdc73adb..000000000 --- a/winuwp/runner_uwp/flutter_frameworkview.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "winrt/Windows.ApplicationModel.Core.h" -#include "winrt/Windows.Foundation.h" -#include "winrt/Windows.System.Profile.h" -#include "winrt/Windows.System.Threading.h" -#include "winrt/Windows.UI.Core.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -struct FlutterFrameworkView - : winrt::implements< - FlutterFrameworkView, - winrt::Windows::ApplicationModel::Core::IFrameworkView> { - // |winrt::Windows::ApplicationModel::Core::IFrameworkView| - void - Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const - &applicationView) { - - // Layout scaling must be disabled in the appinitialization phase in order - // to take effect correctly. - if (winrt::Windows::System::Profile::AnalyticsInfo::VersionInfo() - .DeviceFamily() == L"Windows.Xbox") { - - bool result = winrt::Windows::UI::ViewManagement::ApplicationViewScaling:: - TrySetDisableLayoutScaling(true); - if (!result) { - OutputDebugString(L"Couldn't disable layout scaling"); - } - } - - main_view_ = applicationView; - main_view_.Activated({this, &FlutterFrameworkView::OnActivated}); - } - - // |winrt::Windows::ApplicationModel::Core::IFrameworkView| - void Uninitialize() { - main_view_.Activated(nullptr); - main_view_ = nullptr; - } - - // |winrt::Windows::ApplicationModel::Core::IFrameworkView| - void Load(winrt::hstring const &) {} - - // |winrt::Windows::ApplicationModel::Core::IFrameworkView| - void Run() { - winrt::Windows::UI::Core::CoreWindow window = - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread(); - - winrt::Windows::UI::Core::CoreDispatcher dispatcher = window.Dispatcher(); - dispatcher.ProcessEvents( - winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessUntilQuit); - } - - // |winrt::Windows::ApplicationModel::Core::IFrameworkView| - winrt::Windows::Foundation::IAsyncAction - SetWindow(winrt::Windows::UI::Core::CoreWindow const &window) { - - // Capture reference to window. - window_ = window; - - // Lay out the window's content within the region occupied by the - // CoreWindow. - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView:: - GetForCurrentView(); - - appView.SetDesiredBoundsMode(winrt::Windows::UI::ViewManagement:: - ApplicationViewBoundsMode::UseCoreWindow); - - // Configure folder paths. - try { - winrt::Windows::Storage::StorageFolder folder = - winrt::Windows::ApplicationModel::Package::Current() - .InstalledLocation(); - - winrt::Windows::Storage::StorageFolder assets = - co_await folder.GetFolderAsync(L"Assets"); - winrt::Windows::Storage::StorageFolder data = - co_await assets.GetFolderAsync(L"data"); - winrt::Windows::Storage::StorageFolder flutter_assets = - co_await data.GetFolderAsync(L"flutter_assets"); - winrt::Windows::Storage::StorageFile icu_data = - co_await data.GetFileAsync(L"icudtl.dat"); - -#if NDEBUG - winrt::Windows::Storage::StorageFile aot_data = - co_await data.GetFileAsync(L"app.so"); -#endif - - std::wstring flutter_assets_path{flutter_assets.Path()}; - std::wstring icu_data_path{icu_data.Path()}; - std::wstring aot_data_path { -#if NDEBUG - aot_data.Path() -#endif - }; - - flutter::DartProject project(flutter_assets_path, icu_data_path, - aot_data_path); - - // Construct viewcontroller using the Window and project - flutter_view_controller_ = std::make_unique( - static_cast(winrt::get_abi(main_view_)), - static_cast(winrt::get_abi(launch_args_)), - project); - - // If plugins present, register them. - RegisterPlugins(flutter_view_controller_.get()->engine()); - } catch (winrt::hresult_error &err) { - winrt::Windows::UI::Popups::MessageDialog md = - winrt::Windows::UI::Popups::MessageDialog::MessageDialog( - L"There was a problem starting the engine: " + err.message()); - md.ShowAsync(); - } - } - - void OnActivated( - winrt::Windows::ApplicationModel::Core::CoreApplicationView const - &applicationView, - winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const - &args) { - // Activate the application window, making it visible and enabling it to - // receive events. - applicationView.CoreWindow().Activate(); - - // Capture launch args to later pass to Flutter. - launch_args_ = args; - } - - // Current CoreApplicationView. - winrt::Windows::ApplicationModel::Core::CoreApplicationView main_view_{ - nullptr}; - - // Current CoreWindow. - winrt::Windows::UI::Core::CoreWindow window_{nullptr}; - - // Current FlutterViewController. - std::unique_ptr flutter_view_controller_{ - nullptr}; - - // Launch args that were passed in on activation. - winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs - launch_args_; -}; diff --git a/winuwp/runner_uwp/main.cpp b/winuwp/runner_uwp/main.cpp deleted file mode 100644 index 1ce54b1fc..000000000 --- a/winuwp/runner_uwp/main.cpp +++ /dev/null @@ -1,30 +0,0 @@ - -#include - -#include "winrt/Windows.ApplicationModel.Core.h" -#include "winrt/Windows.Foundation.h" -#include -#include -#include - -#include - -#include "flutter_frameworkview.cpp" - -struct App - : winrt::implements< - App, winrt::Windows::ApplicationModel::Core::IFrameworkViewSource> { - App() { view_ = winrt::make_self(); } - - // |winrt::Windows::ApplicationModel::Core::IFrameworkViewSource| - winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { - return view_.as(); - } - - winrt::com_ptr view_; -}; - -int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) { - winrt::Windows::ApplicationModel::Core::CoreApplication::Run( - winrt::make()); -} diff --git a/winuwp/runner_uwp/resources.pri b/winuwp/runner_uwp/resources.pri deleted file mode 100644 index 7de03c9dc..000000000 Binary files a/winuwp/runner_uwp/resources.pri and /dev/null differ