Compare commits

..

No commits in common. "main" and "v1.10.0" have entirely different histories.

195 changed files with 40925 additions and 43601 deletions

View File

@ -1,7 +1,9 @@
variables:
FLUTTER_VERSION: 3.10.0
FLUTTER_VERSION: 3.7.7
image: ghcr.io/cirruslabs/flutter:${FLUTTER_VERSION}
image:
name: cirrusci/flutter:${FLUTTER_VERSION}
pull_policy: if-not-present
.shared_windows_runners:
tags:
@ -16,25 +18,23 @@ stages:
code_analyze:
stage: test
script:
- flutter pub get
- dart run import_sorter:main --no-comments --exit-if-changed
- dart format lib/ test/ --set-exit-if-changed
- flutter analyze
- git apply ./scripts/enable-android-google-services.patch
- flutter pub get
- flutter analyze
- flutter pub run dart_code_metrics:metrics lib -r gitlab > code-quality-report.json || true
script: [ ./scripts/code_analyze.sh ]
artifacts:
reports:
codequality: code-quality-report.json
tags:
- docker
- famedly
widget_test:
stage: test
script: [flutter test]
script: [ flutter test ]
tags:
- docker
- famedly
# the basic integration test configuration testing FLOSS builds on Synapse
.integration_test:
integration_test:
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/integration/stable:${FLUTTER_VERSION}
stage: test
services:
@ -74,8 +74,9 @@ widget_test:
- ffmpeg -i video.mkv -vf scale=iw/2:-2 -crf 40 -b:v 2000k -preset fast video.mp4 || true
timeout: 30m
retry: 2
only:
- tags
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: always
artifacts:
when: always
paths:
@ -84,10 +85,12 @@ widget_test:
- docker
- famedly
# integration tests for Linux builds
### disabled because of Linux headless issues
.integration_test_linux:
extends: .integration_test
image: cirrusci/flutter:${FLUTTER_VERSION}
extends: integration_test
parallel:
matrix:
- HOMESERVER_IMPLEMENTATION:
@ -97,12 +100,12 @@ widget_test:
- apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libsecret-1-dev libjsoncpp-dev
- flutter pub get
- flutter test integration_test -d linux --dart-define=HOMESERVER=$HOMESERVER --dart-define=USER1_NAME=$USER1_NAME --dart-define=USER2_NAME=$USER2_NAME --dart-define=USER1_PW=$USER1_PW --dart-define=USER2_PW=$USER2_PW || ( sleep 10 && exit 1 )
after_script: []
after_script: [ ]
artifacts:
# extending the default tests to test the Google-flavored builds
.integration_test_proprietary:
extends: .integration_test
integration_test_proprietary:
extends: integration_test
parallel:
matrix:
- HOMESERVER_IMPLEMENTATION:
@ -134,8 +137,6 @@ release_mode_launches:
# generate temporary release build configuration and ensure app launches
- scripts/integration-check-release-build.sh
timeout: 20m
only:
- tags
tags:
- docker
- famedly
@ -143,12 +144,14 @@ release_mode_launches:
build_web:
stage: build
before_script:
[sudo apt update && sudo apt install curl -y, ./scripts/prepare-web.sh]
script:
- flutter build web --release --verbose --source-maps
[ sudo apt update && sudo apt install curl -y, ./scripts/prepare-web.sh ]
script: [ ./scripts/build-web.sh ]
artifacts:
paths:
- build/web/
tags:
- docker
- famedly
# yes, we *do* build a Windows DLL on Linux. More reliable.
build_olm_windows:
@ -194,28 +197,28 @@ build_windows:
build_android_debug:
stage: build
script: [flutter build apk --debug]
script: [ ./scripts/build-android-debug.sh ]
artifacts:
when: on_success
paths:
- build/app/outputs/apk/debug/app-debug.apk
tags:
- docker
- famedly
except:
- main
- tags
tags:
- docker
- famedly
build_android_apk:
stage: build
before_script:
- git apply ./scripts/enable-android-google-services.patch
- ./scripts/prepare-android-release.sh
script: [flutter build apk --release]
script: [ ./scripts/build-android-apk.sh ]
artifacts:
when: on_success
paths:
- build/app/outputs/apk/release/app-release.apk
- build/android/app-release.apk
tags:
- docker
- famedly
@ -228,7 +231,7 @@ deploy_playstore_internal:
before_script:
- git apply ./scripts/enable-android-google-services.patch
- ./scripts/prepare-android-release.sh
script: [./scripts/release-playstore-beta.sh]
script: [ ./scripts/release-playstore-beta.sh ]
artifacts:
when: on_success
paths:
@ -255,6 +258,9 @@ fdroid_repo:
needs:
- "build_android_apk"
resource_group: playstore_release
tags:
- docker
- famedly
allow_failure: true
only:
- main
@ -283,11 +289,12 @@ pages:
build_linux_x86:
stage: build
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/flutter-linux/stable:${FLUTTER_VERSION}
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/flutter-linux/stable
before_script:
- sudo apt-get update
- sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 -y
script: [flutter build linux --release]
[
sudo apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install keyboard-configuration -y && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 -y,
]
script: [ ./scripts/build-linux.sh ]
tags:
- docker
- famedly
@ -298,10 +305,9 @@ build_linux_x86:
build_linux_arm64:
stage: build
before_script:
- flutter upgrade $FLUTTER_VERSION --force
script: [flutter build linux --release]
tags: [docker_arm64]
before_script: [ flutter upgrade ]
script: [ ./scripts/build-linux.sh ]
tags: [ docker_arm64 ]
only:
- main
- tags
@ -313,7 +319,9 @@ build_linux_arm64:
update_dependencies:
stage: build
needs: []
needs: [ ]
tags:
- docker
only:
- schedules
variables:
@ -338,6 +346,9 @@ update_dependencies:
.release:
stage: deploy
image: curlimages/curl:latest
tags:
- docker
- famedly
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
- if: '$CI_COMMIT_TAG =~ /^rc\d+\.\d+\.\d+-\d+$/'
@ -350,29 +361,29 @@ upload_android:
extends: .release
script:
- |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file build/app/outputs/apk/release/app-release.apk ${PACKAGE_REGISTRY_URL}/fluffychat.apk
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file build/android/app-release.apk ${PACKAGE_REGISTRY_URL}/fluffychat.apk
upload_web:
extends: .release
script:
- tar czf package.tar.gz -C build/web/ .
# workaround bug of Flutter engine
- tar czf package.tar.gz --ignore-failed-read -C build/web/ .
- |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-web.tar.gz
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-web.tar.gz
upload_linux_x86:
extends: .release
script:
- tar czf package.tar.gz -C build/linux/x64/release/bundle/ .
- |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-x86.tar.gz
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-x86.tar.gz
upload_linux_arm64:
extends: .release
script:
- tar czf package.tar.gz -C build/linux/arm64/release/bundle/ .
- |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-arm64.tar.gz
allow_failure: true
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.tar.gz ${PACKAGE_REGISTRY_URL}/fluffychat-linux-arm64.tar.gz
upload_windows:
extends: .release
@ -382,16 +393,15 @@ upload_windows:
- mv build/windows/runner/Release/fluffychat.msix fluffychat.msix
- cd build/windows/runner/Release; zip -r ../../../../package.zip . ; cd -
- |
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.zip ${PACKAGE_REGISTRY_URL}/fluffychat-windows.zip
curl --fail-with-body --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file fluffychat.msix ${PACKAGE_REGISTRY_URL}/fluffychat-windows.msix
allow_failure: true
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file package.zip ${PACKAGE_REGISTRY_URL}/fluffychat-windows.zip
curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file fluffychat.msix ${PACKAGE_REGISTRY_URL}/fluffychat-windows.msix
deploy_playstore:
stage: deploy
before_script:
- git apply ./scripts/enable-android-google-services.patch
- ./scripts/prepare-android-release.sh
script: [./scripts/release-playstore.sh]
script: [ ./scripts/release-playstore.sh ]
resource_group: playstore_release
only:
- tags

View File

@ -4,7 +4,7 @@
# This file should be version controlled.
version:
revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
channel: stable
project_type: app
@ -13,11 +13,26 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: android
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: ios
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: linux
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: macos
create_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
base_revision: d3d8effc686d73e0114d71abdcccef63fa1f25d2
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: web
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: windows
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
# User provided section

View File

@ -1,111 +1,3 @@
## v1.12.0
- Added translation using Weblate (Toki Pona) (Mæve Rey)
- Translated using Weblate (Arabic) (Rex_sa)
- Translated using Weblate (Chinese (Simplified)) (Eric)
- Translated using Weblate (Croatian) (Milo Ivir)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Polish) (lauren n. liberda)
- Translated using Weblate (Romanian) (Riley)
- Translated using Weblate (Russian) (DarkCoder15)
- Translated using Weblate (Spanish) (José Muñoz)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- build: Remove dependency overwrite for ffi (Krille)
- build: Update dependencies (Krille)
- builds: Change minsdkversion of Android from 16 to 19 (Krille)
- builds: Do not allow failure for linux x86 (Krille)
- builds: Do not use verbose mode on building linux (Krille)
- builds: Linux with flutter 3.10 (Krille)
- builds: Remove workaround for building linux arm64 (Krille)
- builds: Update file_picker to 5.3.0 (Krille)
- builds: Update flutter table html (Krille)
- builds: Update flutter_html (Krille)
- builds: migrate to dart 3.0/flutter 3.10 (lauren n. liberda)
- chore: Add missing blockquote style (Krille)
- chore: Allow failure in build linux for now (Krille)
- chore: Ask for storage persistence (Krille)
- chore: Clean unused translations (Malin Errenst)
- chore: Enhance room pills (Krille)
- chore: Minor code clean up (Krille)
- chore: Update flutter webrtc (Krille)
- chore: Upgrade to Flutter 3.10.1 (Malin Errenst)
- chore: change release curl calls to use --fail-with-body (Tim Flink)
- chore: update macOS icons and add build script (TheOneWithTheBraid)
- design: Replace anime images with neutral cupertino icons (Krille)
- feat: Add toggle to mute notifications from chat groups (fbievan)
- feat: Allow ruby tags in html (Krille)
- feat: Display progress value for initial sync (Krille)
- feat: Implement new error reporting tool when critical features break like playing audio or video messages or opening a chat (Krille)
- feat: clean up macOS build metadata (TheOneWithTheBraid)
- feat: set display information correctly (TheOneWithTheBraid)
- feat: update macOS build files (TheOneWithTheBraid)
- feat: update macOS build information for macOS Ventura (TheOneWithTheBraid)
- fix "Unhandled Exception: VRouter.of(context) was called with a context which does not contain a VRouter." (Lauren N. Liberda)
- fix: Broken arb file (Krille)
- fix: Do not unnecessary request all members in public rooms (Krille)
- fix: Remove wrong rendered linebreak in html (Krille)
- fix: Scroll down button (Krille)
- fix: Scroll up and scroll down buttons in chat list (Krille)
- fix: Scrolldown button (Krille)
- fix: Too long file name cause a render overflow (Skying)
- fix: Try to reload timeline on IOException (Krille)
- fix: User pills (Krille)
- fix: broken CI artifact uploads (TheOneWithTheBraid)
- fix: custom emote placeholder (TheOneWithTheBraid)
- fix: path of libolm (TheOneWithTheBraid)
- fix: Quick account switching (JHansen)
- fix: read reciepts (JHansen)
- perf: Use valuenotifier to not rebuild chatlist (Krille)
- refactor: Reimplement flutter matrix html locally (Krille)
- refactor: Update Roboto and Noto Emoji (The one with the Braid)
- refactor: Use AnimatedSize for FAB (Krille)
- refactor: Use DateTime for weekday localization (Malin Errenst)
## v1.11.2
- Translated using Weblate (Croatian) (Milo Ivir)
- Translated using Weblate (Dutch) (Jelv)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Polish) (Eryk Michalak)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
- feat: Permission dialog before open link in browser (Krille)
- fix: Chats do not load (Krille)
## v1.11.1 - 2023-04-20
- fix: Download files on web and iOS with correct mimetype
## v1.11.0 - 2023-04-14
- feat: Add visual read marker (Krille)
- feat: Jump to last read event (Krille)
- feat: Use fragmented timeline to jump to event (Krille)
- feat: change to flutterwebauth2 (ShootingStarDragons)
- fix: Join public room (Krille)
- fix: Set fcm priority to max on android (Krille)
- refactor: CI scripts and old workarounds for build scripts (Krille)
- refactor: Client in ChatPage (Krille)
- refactor: Not nullable room in ChatPage (Krille)
- refactor: Switch to file_picker package and get rid of some dependency overrides (Krille)
- refactor: Use correct Matrix instance (Krille)
- style: Make emptypage logo bigger (Krille)
- style: Minor adjustments for modal bottom sheets (Krille)
- style: Move chats to top (Krille)
- style: Use SliverList for chatlist (Krille)
- refactor: Container -> SizedBox.shrink() (noob_tea)
- Translated using Weblate (Chinese (Simplified)) (Eric)
- Translated using Weblate (Dutch) (Jelv)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (French) (Anne Onyme 017)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (Indonesian) (Linerly)
- Translated using Weblate (Persian) (Parsa)
- Translated using Weblate (Persian) (Siavash)
- Translated using Weblate (Polish) (Luna)
- Translated using Weblate (Swedish) (Kristoffer Grundström)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
## v1.10.0 - 2023-02-25
- Added translation using Weblate (Thai) (Wphaoka)
- Added translation using Weblate (Tibetan) (Nathan Freitas)

View File

@ -1,4 +1,4 @@
FROM ghcr.io/cirruslabs/flutter as builder
FROM cirrusci/flutter as builder
RUN sudo apt update && sudo apt install curl -y
COPY . /app
WORKDIR /app

View File

@ -44,7 +44,7 @@ android {
defaultConfig {
applicationId "chat.fluffy.fluffychat"
minSdkVersion 19
minSdkVersion 16
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

View File

@ -93,9 +93,8 @@
</intent-filter>
</activity>
<activity
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
android:exported="true">
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity"
android:exported="true">
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />

View File

@ -27,6 +27,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
task clean(type: Delete) {
delete rootProject.buildDir
}

BIN
assets/encryption.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
{}
{}

View File

@ -71,6 +71,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Sala arxivada",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Accés dels usuaris convidats",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -395,6 +400,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Signatura creuada activada",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Actiu actualment",
"@currentlyActive": {
"type": "text",
@ -592,6 +602,11 @@
"type": "text",
"placeholders": {}
},
"friday": "divendres",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Des de la unió",
"@fromJoining": {
"type": "text",
@ -757,6 +772,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Les claus estan desades a la memòria cau",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} ha expulsat a {targetName}",
"@kicked": {
"type": "text",
@ -886,6 +906,11 @@
"type": "text",
"placeholders": {}
},
"monday": "dilluns",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Silencia el xat",
"@muteChat": {
"type": "text",
@ -1176,6 +1201,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "dissabte",
"@saturday": {
"type": "text",
"placeholders": {}
},
"security": "Seguretat",
"@security": {
"type": "text",
@ -1364,6 +1394,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "diumenge",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "Sistema",
"@systemTheme": {
"type": "text",
@ -1379,6 +1414,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Aquesta sala ha estat arxivada.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "dijous",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1400,6 +1445,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "dimarts",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "No disponible",
"@unavailable": {
"type": "text",
@ -1560,6 +1610,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "dimecres",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Us hem enviat un missatge de correu electrònic",
"@weSentYouAnEmail": {
"type": "text",
@ -1746,6 +1801,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "El vostre id. dusuari:",
"@yourUserId": {},
"enableEncryption": "Activa el xifratge",
"@enableEncryption": {
"type": "text",
@ -2008,6 +2065,11 @@
"supportedVersions": {}
}
},
"discover": "Descobreix",
"@discover": {
"type": "text",
"placeholders": {}
},
"editChatPermissions": "Edita els permisos del xat",
"@editChatPermissions": {
"type": "text",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
{}

View File

@ -134,6 +134,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Archived Room",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Are guest users allowed to join",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -383,6 +388,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "Your user ID:",
"@yourUserId": {},
"yourChatBackupHasBeenSetUp": "Your chat backup has been set up.",
"@yourChatBackupHasBeenSetUp": {},
"chatBackup": "Chat backup",
@ -626,6 +633,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Cross-signing on",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Currently active",
"@currentlyActive": {
"type": "text",
@ -711,11 +723,6 @@
"type": "text",
"placeholders": {}
},
"allRooms": "All Group Chats",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"discover": "Discover",
"@discover": {
"type": "text",
@ -889,6 +896,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Friday",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "From joining",
"@fromJoining": {
"type": "text",
@ -1079,6 +1091,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Keys are cached",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username} kicked {targetName}",
"@kicked": {
"type": "text",
@ -1143,6 +1160,8 @@
"@dehydrate": {},
"dehydrateWarning": "This action cannot be undone. Ensure you safely store the backup file.",
"@dehydrateWarning": {},
"dehydrateShare": "This is your private FluffyChat export. Ensure you don't lose it and keep it private.",
"@dehydrateShare": {},
"dehydrateTor": "TOR Users: Export session",
"@dehydrateTor": {},
"dehydrateTorLong": "For TOR users, it is recommended to export the session before closing the window.",
@ -1217,11 +1236,17 @@
"type": "text",
"placeholders": {}
},
"noSearchResult": "No matching search results.",
"moderator": "Moderator",
"@moderator": {
"type": "text",
"placeholders": {}
},
"monday": "Monday",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Mute chat",
"@muteChat": {
"type": "text",
@ -1287,6 +1312,8 @@
},
"shareYourInviteLink": "Share your invite link",
"@shareYourInviteLink": {},
"typeInInviteLinkManually": "Type in invite link manually...",
"@typeInInviteLinkManually": {},
"scanQrCode": "Scan QR code",
"@scanQrCode": {},
"none": "None",
@ -1644,6 +1671,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Saturday",
"@saturday": {
"type": "text",
"placeholders": {}
},
"saveFile": "Save file",
"@saveFile": {
"type": "text",
@ -1897,6 +1929,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Sunday",
"@sunday": {
"type": "text",
"placeholders": {}
},
"synchronizingPleaseWait": "Synchronizing… Please wait.",
"@synchronizingPleaseWait": {
"type": "text",
@ -1917,6 +1954,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "This room has been archived.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Thursday",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1953,6 +2000,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Tuesday",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Unavailable",
"@unavailable": {
"type": "text",
@ -2125,6 +2177,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Wednesday",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "We sent you an email",
"@weSentYouAnEmail": {
"type": "text",
@ -2379,6 +2436,8 @@
"@stories": {},
"users": "Users",
"@users": {},
"enableAutoBackups": "Enable auto backups",
"@enableAutoBackups": {},
"unlockOldMessages": "Unlock old messages",
"@unlockOldMessages": {},
"storeInSecureStorageDescription": "Store the recovery key in the secure storage of this device.",
@ -2460,21 +2519,5 @@
"reopenChat": "Reopen chat",
"noBackupWarning": "Warning! Without enabling chat backup, you will lose access to your encrypted messages. It is highly recommended to enable the chat backup first before logging out.",
"noOtherDevicesFound": "No other devices found",
"fileIsTooBigForServer": "The server reports that the file is too large to be sent.",
"fileHasBeenSavedAt": "File has been saved at {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"jumpToLastReadMessage": "Jump to last read message",
"readUpToHere": "Read up to here",
"jump": "Jump",
"openLinkInBrowser": "Open link in browser",
"reportErrorDescription": "Oh no. Something went wrong. Please try again later. If you want, you can report the bug to the developers.",
"report": "report",
"signInWithPassword": "Sign in with password",
"continueWith": "Continue with:",
"pleaseTryAgainLaterOrChooseDifferentServer": "Please try again later or choose a different server."
"fileIsTooBigForServer": "The server reports that the file is too large to be sent."
}

View File

@ -81,6 +81,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Arĥivita ĉambro",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Ĉu gastoj rajtas aliĝi",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -528,6 +533,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Delegaj subskriboj estas ŝaltitaj",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Nun aktiva",
"@currentlyActive": {
"type": "text",
@ -613,6 +623,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Trovi",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Prezenta nomo ŝanĝiĝis",
"@displaynameHasBeenChanged": {
"type": "text",
@ -779,6 +794,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Vendredo",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Ekde aliĝo",
"@fromJoining": {
"type": "text",
@ -969,6 +989,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Ŝlosiloj estas kaŝmemoritaj",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} forpelis uzanton {targetName}",
"@kicked": {
"type": "text",
@ -1093,6 +1118,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Lundo",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Silentigi babilon",
"@muteChat": {
"type": "text",
@ -1473,6 +1503,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Sabato",
"@saturday": {
"type": "text",
"placeholders": {}
},
"saveFile": "Konservi dosieron",
"@saveFile": {
"type": "text",
@ -1707,6 +1742,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Dimanĉo",
"@sunday": {
"type": "text",
"placeholders": {}
},
"synchronizingPleaseWait": "Spegulante… Bonvolu atendi.",
"@synchronizingPleaseWait": {
"type": "text",
@ -1727,6 +1767,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Ĉi tiu ĉambro arĥiviĝis.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Ĵaŭdo",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1763,6 +1813,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Mardo",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Nedisponeble",
"@unavailable": {
"type": "text",
@ -1933,6 +1988,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Merkredo",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Ni sendis retleteron al vi",
"@weSentYouAnEmail": {
"type": "text",

File diff suppressed because it is too large Load Diff

View File

@ -82,6 +82,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Arhiveeritud jututuba",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Kas külalised võivad liituda",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -539,6 +544,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Risttunnustamine on kasutusel",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Hetkel aktiivne",
"@currentlyActive": {
"type": "text",
@ -624,6 +634,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Avasta",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Kuvatav nimi on muudetud",
"@displaynameHasBeenChanged": {
"type": "text",
@ -790,6 +805,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Reede",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Alates liitumise hetkest",
"@fromJoining": {
"type": "text",
@ -980,6 +1000,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Krüptovõtmed on puhverdatud",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username} müksas kasutaja {targetName} välja",
"@kicked": {
"type": "text",
@ -1114,6 +1139,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Esmaspäev",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Summuta vestlus",
"@muteChat": {
"type": "text",
@ -1507,6 +1537,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Laupäev",
"@saturday": {
"type": "text",
"placeholders": {}
},
"saveFile": "Salvesta fail",
"@saveFile": {
"type": "text",
@ -1745,6 +1780,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Pühapäev",
"@sunday": {
"type": "text",
"placeholders": {}
},
"synchronizingPleaseWait": "Sünkroniseerin andmeid… Palun oota.",
"@synchronizingPleaseWait": {
"type": "text",
@ -1765,6 +1805,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "See jututuba on arhiveeritud.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Neljapäev",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1801,6 +1851,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Teisipäev",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Eemal",
"@unavailable": {
"type": "text",
@ -1971,6 +2026,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Kolmapäev",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Me saatsime sulle e-kirja",
"@weSentYouAnEmail": {
"type": "text",
@ -2047,6 +2107,8 @@
"@addToSpace": {},
"scanQrCode": "Skaneeri QR-koodi",
"@scanQrCode": {},
"typeInInviteLinkManually": "Sisesta kutse link käsitsi...",
"@typeInInviteLinkManually": {},
"shareYourInviteLink": "Jaga oma kutselinki",
"@shareYourInviteLink": {},
"sendOnEnter": "Saada sõnum sisestusklahvi vajutusel",
@ -2073,6 +2135,8 @@
"@link": {},
"yourChatBackupHasBeenSetUp": "Sinu vestluste varundus on seadistatud.",
"@yourChatBackupHasBeenSetUp": {},
"yourUserId": "Sinu kasutajatunnus:",
"@yourUserId": {},
"unverified": "Verifitseerimata",
"@unverified": {},
"repeatPassword": "Korda salasõna",
@ -2310,6 +2374,8 @@
"@recoveryKey": {},
"users": "Kasutajad",
"@users": {},
"enableAutoBackups": "Võta kasutusele automaatne varundus",
"@enableAutoBackups": {},
"stories": "Jutustused",
"@stories": {},
"storeInSecureStorageDescription": "Salvesta taastevõti selle seadme turvahoidlas.",
@ -2336,6 +2402,8 @@
},
"dehydrate": "Ekspordi sessiooni teave ja kustuta nutiseadmest rakenduse andmed",
"@dehydrate": {},
"dehydrateShare": "See on sinu FluffyChat'i privaatsete andmete eksport. Palun hoia teda turvaliselt ja vaata, et sa seda ei kaotaks.",
"@dehydrateShare": {},
"dehydrateTor": "TOR'i kasutajad: Ekspordi sessioon",
"@dehydrateTor": {},
"hydrateTor": "TOR'i kasutajatele: impordi viimati eksporditud sessiooni andmed",
@ -2464,6 +2532,8 @@
"@deviceKeys": {},
"newSpaceDescription": "Kogukonnad võimaldavad sul koondada erinevaid vestlusi ning korraldada avalikku või privaatset ühistegevust.",
"@newSpaceDescription": {},
"noSearchResult": "Sobivaid otsingutulemusi ei leidu.",
"@noSearchResult": {},
"enterInviteLinkOrMatrixId": "Sisesta kutse link või Matrix ID...",
"@enterInviteLinkOrMatrixId": {},
"letsStart": "Sõidame!",
@ -2475,40 +2545,5 @@
"noBackupWarning": "Hoiatus! Kui sa ei lülita sisse vestluse varundust, siis sul puudub hiljem ligipääs krüptitud sõnumitele. Me tungivalt soovitame, et palun lülita vestluse varundamine sisse enne väljalogimist.",
"@noBackupWarning": {},
"fileIsTooBigForServer": "Serveri seadistuste alusel on see fail saatmiseks liiga suur.",
"@fileIsTooBigForServer": {},
"fileHasBeenSavedAt": "Fail on salvestatud kausta: {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"jumpToLastReadMessage": "Liigu viimase loetud sõnumini",
"@jumpToLastReadMessage": {},
"readUpToHere": "Siiamaani on loetud",
"@readUpToHere": {},
"jump": "Hüppa",
"@jump": {},
"openLinkInBrowser": "Ava link veebibrauseris",
"@openLinkInBrowser": {},
"discover": "Otsi ja leia",
"@discover": {
"type": "text",
"placeholders": {}
},
"report": "teata",
"@report": {},
"allRooms": "Kõik vestlusrühmad",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"reportErrorDescription": "Oh appike! Midagi läks valesti. Palun proovi hiljem uuesti. Kui soovid, võid sellest veast arendajatele teatada.",
"@reportErrorDescription": {},
"continueWith": "Jätkamiseks kasuta:",
"@continueWith": {},
"signInWithPassword": "Logi sisse salasõnaga",
"@signInWithPassword": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Palun proovi hiljem uuesti või muuda serveri nime.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {}
"@fileIsTooBigForServer": {}
}

View File

@ -62,6 +62,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Artxibatutako gela",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Ba al dute gonbidatutako erabiltzaileek bat egiteko baimenik?",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -266,12 +271,12 @@
"type": "text",
"placeholders": {}
},
"compareEmojiMatch": "Konparatu ondorengo emojiak:",
"compareEmojiMatch": "Konparatu eta egiaztatu ondorengo emojiak beste gailukoarekin bat datozela:",
"@compareEmojiMatch": {
"type": "text",
"placeholders": {}
},
"compareNumbersMatch": "Konparatu ondorengo zenbakiak:",
"compareNumbersMatch": "Konparatu eta egiaztatu ondorengo zenbakiak beste gailukoarekin bat datozela:",
"@compareNumbersMatch": {
"type": "text",
"placeholders": {}
@ -332,6 +337,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Zeharkako sinadura gaituta dago",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Unean aktibo",
"@currentlyActive": {
"type": "text",
@ -484,12 +494,17 @@
"type": "text",
"placeholders": {}
},
"fromJoining": "Bat egiteaz geroztik",
"friday": "Ostirala",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "sartzeaz",
"@fromJoining": {
"type": "text",
"placeholders": {}
},
"fromTheInvitation": "Gonbidapenaz geroztik",
"fromTheInvitation": "gonbidapenaz",
"@fromTheInvitation": {
"type": "text",
"placeholders": {}
@ -614,6 +629,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Gakoak katxean daude",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username}(e)k {targetName} kaleratu du",
"@kicked": {
"type": "text",
@ -716,6 +736,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Astelehena",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Mututu berriketa",
"@muteChat": {
"type": "text",
@ -924,6 +949,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Larunbata",
"@saturday": {
"type": "text",
"placeholders": {}
},
"seenByUser": "{username}(e)k ikusi du",
"@seenByUser": {
"type": "text",
@ -1080,6 +1110,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Igandea",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "Sistemak darabilena",
"@systemTheme": {
"type": "text",
@ -1095,6 +1130,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Gela hau artxibatu da.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Osteguna",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1106,6 +1151,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Asteartea",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unbannedUser": "{username}(e)k {targetName} baimendu du",
"@unbannedUser": {
"type": "text",
@ -1254,6 +1304,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Asteazkena",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"whoIsAllowedToJoinThisGroup": "Nork duen baimena talde honetara batzeko",
"@whoIsAllowedToJoinThisGroup": {
"type": "text",
@ -1470,7 +1525,7 @@
"type": "text",
"placeholders": {}
},
"unreadChats": "{unreadCount, plural, =1{irakurri gabeko txat 1} other {irakurri gabeko {unreadCount} txat}}",
"unreadChats": "{unreadCount, plural, =1{irakurri gabeko txat 1} other {{unreadCount} txat irakurri gabe}}",
"@unreadChats": {
"type": "text",
"placeholders": {
@ -1685,6 +1740,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "Zure erabiltzaile IDa:",
"@yourUserId": {},
"chatBackup": "Txataren babeskopia",
"@chatBackup": {
"type": "text",
@ -1816,6 +1873,8 @@
"type": "text",
"placeholders": {}
},
"typeInInviteLinkManually": "Idatzi eskuz gonbidapen esteka…",
"@typeInInviteLinkManually": {},
"online": "Linean",
"@online": {
"type": "text",
@ -1950,6 +2009,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Deskubritu",
"@discover": {
"type": "text",
"placeholders": {}
},
"emotePacks": "Emote sortak gelarako",
"@emotePacks": {
"type": "text",
@ -2000,7 +2064,7 @@
"type": "text",
"placeholders": {}
},
"noEncryptionForPublicRooms": "Zifraketa aktiba dezakezu soilik gelak publikoa izateari utzi badio.",
"noEncryptionForPublicRooms": "Zifraketa aktiba dezakezu soilik gela publikoa ez bada.",
"@noEncryptionForPublicRooms": {
"type": "text",
"placeholders": {}
@ -2232,7 +2296,7 @@
"@widgetEtherpad": {},
"widgetJitsi": "Jitsi Meet",
"@widgetJitsi": {},
"widgetCustom": "Norberak ezarritakoa",
"widgetCustom": "Neurrira egindakoa",
"@widgetCustom": {},
"widgetName": "Izena",
"@widgetName": {},
@ -2343,6 +2407,10 @@
"@hydrateTorLong": {},
"noEmailWarning": "Sartu baliozko posta helbide bat. Bestela ezingo duzu pasahitza berrezarri. Hala ere nahi ez baduzu, sakatu berriro botoia aurrera egiteko.",
"@noEmailWarning": {},
"enableAutoBackups": "Gaitu babeskopia automatikoak",
"@enableAutoBackups": {},
"dehydrateShare": "Hau zure FluffyChaten esportazio pribatua da. Ez galdu eta ez partekatu inorekin.",
"@dehydrateShare": {},
"dehydrateTor": "TOR Erabiltzaileak: Esportatu saioa",
"@dehydrateTor": {},
"hydrateTor": "TOR Erabiltzaileak: Inportatu esportatutako saioa",
@ -2430,7 +2498,7 @@
"@startFirstChat": {},
"newSpaceDescription": "Guneek txatak taldekatzea ahalbidetzen dute eta komunitate pribatu edo publikoak osatzea.",
"@newSpaceDescription": {},
"endToEndEncryption": "Ertzetik ertzerako zifratzea",
"endToEndEncryption": "Puntuz puntuko zifraketa",
"@endToEndEncryption": {},
"disableEncryptionWarning": "Segurtasun arrazoiak direla-eta, ezin duzu lehendik zifratuta zegoen txat bateko zifraketa ezgaitu.",
"@disableEncryptionWarning": {},
@ -2451,57 +2519,6 @@
"@sorryThatsNotPossible": {},
"reopenChat": "Ireki txata berriro",
"@reopenChat": {},
"commandHint_googly": "Bidali begi dibertigarri batzuk",
"@commandHint_googly": {},
"commandHint_cuddle": "Bidali besarkada goxoa",
"@commandHint_cuddle": {},
"googlyEyesContent": "{senderName}(e)k begi dibertigarri batzuk bidali dizkizu",
"@googlyEyesContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"allRooms": "Talde-txat guztiak",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"jumpToLastReadMessage": "Joan irakurritako azken mezura",
"@jumpToLastReadMessage": {},
"reportErrorDescription": "Ez! Zerbaitek huts egin du. Saiatu berriro geroago. Nahi izanez gero, eman garatzaileei errorearen berri.",
"@reportErrorDescription": {},
"cuddleContent": "{senderName}(e)k samurki besarkatu zaitu",
"@cuddleContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"readUpToHere": "Honaino irakurrita",
"@readUpToHere": {},
"discover": "Deskubritu",
"@discover": {
"type": "text",
"placeholders": {}
},
"fileHasBeenSavedAt": "Fitxategia {path}(e)n gorde da",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"jump": "Joan",
"@jump": {},
"openLinkInBrowser": "Ireki esteka nabigatzailean",
"@openLinkInBrowser": {},
"report": "eman berri",
"@report": {},
"signInWithPassword": "Hasi saioa pasahitzarekin",
"@signInWithPassword": {},
"continueWith": "Jarraitu honekin:",
"@continueWith": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Saiatu geroago edo hautatu beste zerbitzari bat.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {}
"noSearchResult": "Ez da emaitzarik aurkitu.",
"@noSearchResult": {}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Dé Céadaoin",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"warning": "Rabhadh!",
"@warning": {
"type": "text",
@ -44,17 +49,32 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Dé Máirt",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
"type": "text",
"placeholders": {}
},
"thursday": "Déardaoin",
"@thursday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "Córas",
"@systemTheme": {
"type": "text",
"placeholders": {}
},
"sunday": "Dé Domhnaigh",
"@sunday": {
"type": "text",
"placeholders": {}
},
"submit": "Cuir isteach",
"@submit": {
"type": "text",
@ -100,6 +120,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Dé Sathairn",
"@saturday": {
"type": "text",
"placeholders": {}
},
"reply": "Freagair",
"@reply": {
"type": "text",
@ -180,6 +205,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Tá cros-shíniú tosaithe",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"createNewSpace": "Spás nua",
"@createNewSpace": {
"type": "text",
@ -237,6 +267,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Dé Luain",
"@monday": {
"type": "text",
"placeholders": {}
},
"moderator": "Modhnóir",
"@moderator": {
"type": "text",
@ -317,6 +352,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Dé hAoine",
"@friday": {
"type": "text",
"placeholders": {}
},
"forward": "Seol ar aghaidh",
"@forward": {
"type": "text",
@ -344,6 +384,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Tar ar",
"@discover": {
"type": "text",
"placeholders": {}
},
"devices": "Gléasanna",
"@devices": {
"type": "text",
@ -489,6 +534,11 @@
},
"sendOnEnter": "Seol ar iontráil",
"@sendOnEnter": {},
"archivedRoom": "Seomra cartlainne",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"archive": "Cartlann",
"@archive": {
"type": "text",
@ -1262,6 +1312,8 @@
},
"scanQrCode": "Scan cód QR",
"@scanQrCode": {},
"typeInInviteLinkManually": "Clóscríobh an nasc cuiridh de láimh...",
"@typeInInviteLinkManually": {},
"inviteText": "Thug {username} cuireadh duit chuig FluffyChat.\n1. Suiteáil FluffyChat: https://fluffychat.im\n2. Cláraigh nó sínigh isteach\n3. Oscail an nasc cuiridh: {link}",
"@inviteText": {
"type": "text",
@ -1399,6 +1451,11 @@
"targetName": {}
}
},
"keysCached": "Cuirtear eochracha i dtaisce",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"joinedTheChat": "Tháinig {username} isteach sa chomhrá",
"@joinedTheChat": {
"type": "text",
@ -1683,6 +1740,11 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Tá an seomra seo curtha i gcartlann.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"theyDontMatch": "Níl siad céanna",
"@theyDontMatch": {
"type": "text",
@ -2091,6 +2153,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "D'aitheantas úsáideora:",
"@yourUserId": {},
"yourChatBackupHasBeenSetUp": "Bunaíodh do chúltaca comhrá.",
"@yourChatBackupHasBeenSetUp": {},
"openVideoCamera": "Oscail físcheamara",

View File

@ -23,7 +23,7 @@
"type": "text",
"placeholders": {}
},
"activatedEndToEndEncryption": "🔐 {username} activou a cifraxe extremo-a-extremo",
"activatedEndToEndEncryption": "🔐 {username} activou o cifrado extremo-a-extremo",
"@activatedEndToEndEncryption": {
"type": "text",
"placeholders": {
@ -82,6 +82,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Sala arquivada",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Permitir o acceso de convidadas",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -314,7 +319,7 @@
"type": "text",
"placeholders": {}
},
"channelCorruptedDecryptError": "A cifraxe está estragada",
"channelCorruptedDecryptError": "O cifrado está corrompido",
"@channelCorruptedDecryptError": {
"type": "text",
"placeholders": {}
@ -539,6 +544,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Sinatura-Cruzada activada",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Actualmente activo",
"@currentlyActive": {
"type": "text",
@ -624,6 +634,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Descubrir",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "O nome público mudou",
"@displaynameHasBeenChanged": {
"type": "text",
@ -704,12 +719,12 @@
"type": "text",
"placeholders": {}
},
"enableEncryption": "Activar cifraxe",
"enableEncryption": "Activar cifrado",
"@enableEncryption": {
"type": "text",
"placeholders": {}
},
"enableEncryptionWarning": "Non poderás desactivar a cifraxe posteriormente, tes certeza?",
"enableEncryptionWarning": "Non poderás desactivar o cifrado posteriormente, tes certeza?",
"@enableEncryptionWarning": {
"type": "text",
"placeholders": {}
@ -719,12 +734,12 @@
"type": "text",
"placeholders": {}
},
"encryption": "Cifraxe",
"encryption": "Cifrado",
"@encryption": {
"type": "text",
"placeholders": {}
},
"encryptionNotEnabled": "A cifraxe non está activada",
"encryptionNotEnabled": "O cifrado non está activado",
"@encryptionNotEnabled": {
"type": "text",
"placeholders": {}
@ -790,6 +805,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Venres",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Desde que se una",
"@fromJoining": {
"type": "text",
@ -980,6 +1000,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Chaves almacenadas",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username} expulsou a {targetName}",
"@kicked": {
"type": "text",
@ -1114,12 +1139,17 @@
"type": "text",
"placeholders": {}
},
"monday": "Luns",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Acalar chat",
"@muteChat": {
"type": "text",
"placeholders": {}
},
"needPantalaimonWarning": "Ten en conta que polo de agora precisas Pantalaimon para a cifraxe extremo-a-extremo.",
"needPantalaimonWarning": "Ten en conta que polo de agora precisas Pantalaimon para o cifrado extremo-a-extremo.",
"@needPantalaimonWarning": {
"type": "text",
"placeholders": {}
@ -1159,7 +1189,7 @@
"type": "text",
"placeholders": {}
},
"noEncryptionForPublicRooms": "Só podes activar a cifraxe tan pronto como a sala non sexa públicamente accesible.",
"noEncryptionForPublicRooms": "Só podes activar o cifrado tan pronto como a sala non sexa públicamente accesible.",
"@noEncryptionForPublicRooms": {
"type": "text",
"placeholders": {}
@ -1507,6 +1537,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Sábado",
"@saturday": {
"type": "text",
"placeholders": {}
},
"saveFile": "Gardar ficheiro",
"@saveFile": {
"type": "text",
@ -1745,6 +1780,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Domingo",
"@sunday": {
"type": "text",
"placeholders": {}
},
"synchronizingPleaseWait": "Sincronizando... Agarda.",
"@synchronizingPleaseWait": {
"type": "text",
@ -1765,6 +1805,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "A sala foi arquivada.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Xoves",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1801,6 +1851,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Martes",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Non dispoñible",
"@unavailable": {
"type": "text",
@ -1824,7 +1879,7 @@
"type": "text",
"placeholders": {}
},
"unknownEncryptionAlgorithm": "Algoritmo de cifraxe descoñecido",
"unknownEncryptionAlgorithm": "Algoritmo de cifrado descoñecido",
"@unknownEncryptionAlgorithm": {
"type": "text",
"placeholders": {}
@ -1971,6 +2026,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Mércores",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Enviamosche un email",
"@weSentYouAnEmail": {
"type": "text",
@ -2047,6 +2107,8 @@
"@addToSpace": {},
"scanQrCode": "Escanear código QR",
"@scanQrCode": {},
"typeInInviteLinkManually": "Escribe manualmente a ligazón do convite...",
"@typeInInviteLinkManually": {},
"shareYourInviteLink": "Comparte a túa ligazón de convite",
"@shareYourInviteLink": {},
"sendOnEnter": "Enter para enviar",
@ -2067,6 +2129,8 @@
"@yourChatBackupHasBeenSetUp": {},
"unverified": "Sen verificar",
"@unverified": {},
"yourUserId": "O teu ID:",
"@yourUserId": {},
"pleaseEnterValidEmail": "Escribe un enderezo de email válido.",
"@pleaseEnterValidEmail": {},
"passwordsDoNotMatch": "Os contrasinais non concordan!",
@ -2103,7 +2167,7 @@
"type": "text",
"description": "Usage hint for the command /discardsession"
},
"commandHint_create": "Crear un grupo de conversa baleiro\nUsa --no-encryption para desactivar a cifraxe",
"commandHint_create": "Crear un grupo de conversa baleiro\nUsa --no-encryption para desactivar o cifrado",
"@commandHint_create": {
"type": "text",
"description": "Usage hint for the command /create"
@ -2113,7 +2177,7 @@
"type": "text",
"description": "Usage hint for the command /clearcache"
},
"commandHint_dm": "Iniciar un chat directo\nUsa --no-encryption para desactivar a cifraxe",
"commandHint_dm": "Iniciar un chat directo\nUsa --no-encryption para desactivar o cifrado",
"@commandHint_dm": {
"type": "text",
"description": "Usage hint for the command /dm"
@ -2324,6 +2388,8 @@
"@pleaseEnterRecoveryKeyDescription": {},
"users": "Usuarias",
"@users": {},
"enableAutoBackups": "Activar copia automática",
"@enableAutoBackups": {},
"storeInSecureStorageDescription": "Gardar a chave de recuperación na almacenaxe segura deste dispositivo.",
"@storeInSecureStorageDescription": {},
"countFiles": "{count} ficheiros",
@ -2334,6 +2400,8 @@
},
"unlockOldMessages": "Desbloquear mensaxes antigas",
"@unlockOldMessages": {},
"dehydrateShare": "Esta é a copia de apoio privada de FluffyChat. Pon coidado en non perdela e mantela segura.",
"@dehydrateShare": {},
"dehydrateTorLong": "Para usuarias de TOR, é recomendable exportar a sesión antes de pechar a ventál.",
"@dehydrateTorLong": {},
"hydrateTor": "Usuarias TOR: Importar a sesión exportada",
@ -2443,13 +2511,15 @@
"senderName": {}
}
},
"noSearchResult": "Non hai resultados para a busca.",
"@noSearchResult": {},
"enterInviteLinkOrMatrixId": "Escribe a ligazón de convite ou ID Matrix...",
"@enterInviteLinkOrMatrixId": {},
"encryptThisChat": "Cifrar esta conversa",
"@encryptThisChat": {},
"endToEndEncryption": "Cifraxe de extremo a extremo",
"endToEndEncryption": "Cifrado de extremo a extremo",
"@endToEndEncryption": {},
"disableEncryptionWarning": "Por razóns de seguridade non podes desactivar a cifraxe dunha conversa onde foi activada previamente.",
"disableEncryptionWarning": "Por razóns de seguridade non podes desactivar o cifrado dunha conversa onde foi activado previamente.",
"@disableEncryptionWarning": {},
"sorryThatsNotPossible": "Lamentámolo... iso non é posible",
"@sorryThatsNotPossible": {},
@ -2475,40 +2545,5 @@
"noBackupWarning": "Aviso! Se non activas a copia de apoio do chat, perderás o acceso ás túas mensaxes cifradas. É totalmente recomendable activar a copia de apoio do chat antes de pechar a sesión.",
"@noBackupWarning": {},
"fileIsTooBigForServer": "O servidor informa de que o ficheiro é demasiado grande para envialo.",
"@fileIsTooBigForServer": {},
"fileHasBeenSavedAt": "Gardouse o ficheiro en {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"jumpToLastReadMessage": "Ir á última mensaxe lida",
"@jumpToLastReadMessage": {},
"readUpToHere": "Lin ate aquí",
"@readUpToHere": {},
"openLinkInBrowser": "Abrir ligazón no navegador",
"@openLinkInBrowser": {},
"jump": "Ir alá",
"@jump": {},
"report": "informar",
"@report": {},
"allRooms": "Todas as Conversas en grupo",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"reportErrorDescription": "Vaia! Algo fallou. Inténtao máis tarde. Se queres, podes informar do problema aos desenvolvedores.",
"@reportErrorDescription": {},
"discover": "Descubrir",
"@discover": {
"type": "text",
"placeholders": {}
},
"signInWithPassword": "Accede con contrasinal",
"@signInWithPassword": {},
"continueWith": "Continuar con:",
"@continueWith": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Inténtao máis tarde ou elixe un servidor diferente.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {}
"@fileIsTooBigForServer": {}
}

View File

@ -61,6 +61,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "חדר בארכיון",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "האם משתמשים אורחים מורשים להצטרף",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -338,6 +343,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "מזהה המשתמש שלך:",
"@yourUserId": {},
"yourChatBackupHasBeenSetUp": "גיבוי הצ'אט שלך הוגדר.",
"@yourChatBackupHasBeenSetUp": {},
"chatBackup": "גיבוי צ'אט",
@ -470,6 +477,11 @@
"username": {}
}
},
"crossSigningEnabled": "חתימה צולבת על",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "פעיל כעת",
"@currentlyActive": {
"type": "text",
@ -531,6 +543,11 @@
"type": "text",
"placeholders": {}
},
"discover": "לגלות",
"@discover": {
"type": "text",
"placeholders": {}
},
"downloadFile": "הורד קובץ",
"@downloadFile": {
"type": "text",
@ -613,6 +630,11 @@
"type": "text",
"placeholders": {}
},
"friday": "יום שישי",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "מהצטרפות",
"@fromJoining": {
"type": "text",
@ -733,6 +755,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "המפתחות נשמרים במטמון",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} בעט ב {targetName}",
"@kicked": {
"type": "text",
@ -1119,6 +1146,8 @@
},
"shareYourInviteLink": "שתף את קישור ההזמנה שלך",
"@shareYourInviteLink": {},
"typeInInviteLinkManually": "הקלד את קישור ההזמנה באופן ידני...",
"@typeInInviteLinkManually": {},
"noRoomsFound": "לא נמצאו חדרים…",
"@noRoomsFound": {
"type": "text",
@ -1283,6 +1312,11 @@
"type": "text",
"placeholders": {}
},
"monday": "יום שני",
"@monday": {
"type": "text",
"placeholders": {}
},
"noGoogleServicesWarning": "נראה שאין לך שירותי גוגל בטלפון שלך. זו החלטה טובה לפרטיות שלך! כדי לקבל התרעות ב- FluffyChat אנו ממליצים להשתמש https://microg.org/ או https://unifiedpush.org/.",
"@noGoogleServicesWarning": {
"type": "text",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -247,6 +247,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Jumat",
"@friday": {
"type": "text",
"placeholders": {}
},
"forward": "Teruskan",
"@forward": {
"type": "text",
@ -444,6 +449,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Tanda tangan silang dinyalakan",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"createNewGroup": "Buat grup baru",
"@createNewGroup": {
"type": "text",
@ -719,6 +729,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Ruangan yang Diarsipkan",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"appLock": "Kunci aplikasi",
"@appLock": {
"type": "text",
@ -870,6 +885,11 @@
"targetName": {}
}
},
"keysCached": "Kunci telah ditembolok",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"joinRoom": "Bergabung dengan ruangan",
"@joinRoom": {
"type": "text",
@ -947,6 +967,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Senin",
"@monday": {
"type": "text",
"placeholders": {}
},
"moderator": "Moderator",
"@moderator": {
"type": "text",
@ -1151,6 +1176,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Rabu",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"warning": "Peringatan!",
"@warning": {
"type": "text",
@ -1304,6 +1334,11 @@
"targetName": {}
}
},
"tuesday": "Selasa",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"tryToSendAgain": "Coba kirim lagi",
"@tryToSendAgain": {
"type": "text",
@ -1335,6 +1370,16 @@
"type": "text",
"placeholders": {}
},
"thursday": "Kamis",
"@thursday": {
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Ruangan ini telah diarsipkan.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"theyMatch": "Cocok",
"@theyMatch": {
"type": "text",
@ -1355,6 +1400,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Minggu",
"@sunday": {
"type": "text",
"placeholders": {}
},
"submit": "Kirim",
"@submit": {
"type": "text",
@ -1725,6 +1775,8 @@
},
"scanQrCode": "Pindai kode QR",
"@scanQrCode": {},
"typeInInviteLinkManually": "Masukkan tautan undangan secara manual...",
"@typeInInviteLinkManually": {},
"shareYourInviteLink": "Bagikan tautan undanganmu",
"@shareYourInviteLink": {},
"noMatrixServer": "{server1} itu bukan server Matrix, gunakan {server2} saja?",
@ -1833,6 +1885,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Temukan",
"@discover": {
"type": "text",
"placeholders": {}
},
"defaultPermissionLevel": "Level izin default",
"@defaultPermissionLevel": {
"type": "text",
@ -2011,6 +2068,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Sabtu",
"@saturday": {
"type": "text",
"placeholders": {}
},
"removeDevice": "Hapus perangkat",
"@removeDevice": {
"type": "text",
@ -2072,6 +2134,8 @@
"@link": {},
"yourChatBackupHasBeenSetUp": "Cadangan obrolanmu telah disiapkan.",
"@yourChatBackupHasBeenSetUp": {},
"yourUserId": "ID penggunamu:",
"@yourUserId": {},
"unverified": "Tidak terverifikasi",
"@unverified": {},
"pleaseEnterValidEmail": "Mohon masukkan alamat email yang valid.",
@ -2325,6 +2389,8 @@
"@recoveryKeyLost": {},
"storeInAndroidKeystore": "Simpan di Android KeyStore",
"@storeInAndroidKeystore": {},
"enableAutoBackups": "Aktifkan cadangan otomatis",
"@enableAutoBackups": {},
"storeSecurlyOnThisDevice": "Simpan secara aman di perangkat ini",
"@storeSecurlyOnThisDevice": {},
"countFiles": "{count} file",
@ -2333,6 +2399,8 @@
"count": {}
}
},
"dehydrateShare": "Ini adalah ekspor FluffyChat privat kamu. Pastikan kamu tidak menghilangkannya dan tetap rahasia.",
"@dehydrateShare": {},
"hydrate": "Pulihkan dari file cadangan",
"@hydrate": {},
"indexedDbErrorTitle": "Masalah dengan mode privat",
@ -2463,6 +2531,8 @@
"@endToEndEncryption": {},
"disableEncryptionWarning": "Demi keamanan kamu tidak bisa menonaktifkan enkripsi dalam sebuah obrolan di mana sebelumbya sudah diaktifkan.",
"@disableEncryptionWarning": {},
"noSearchResult": "Tidak ada hasil pencarian yang cocok.",
"@noSearchResult": {},
"letsStart": "Mari kita mulai",
"@letsStart": {},
"enterInviteLinkOrMatrixId": "Masukkan tautan undangan atau ID Matrix...",
@ -2474,40 +2544,5 @@
"noOtherDevicesFound": "Tidak ada perangkat lain yang ditemukan",
"@noOtherDevicesFound": {},
"fileIsTooBigForServer": "Server melaporkan bahwa file terlalu besar untuk dikirim.",
"@fileIsTooBigForServer": {},
"fileHasBeenSavedAt": "Berkas telah disimpan di {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"jumpToLastReadMessage": "Pergi ke pesan terakhir dibaca",
"@jumpToLastReadMessage": {},
"readUpToHere": "Baca sampai sini",
"@readUpToHere": {},
"jump": "Lompat",
"@jump": {},
"openLinkInBrowser": "Buka tautan dalam peramban",
"@openLinkInBrowser": {},
"discover": "Jelajahi",
"@discover": {
"type": "text",
"placeholders": {}
},
"allRooms": "Semua Percakapan Grup",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"report": "laporkan",
"@report": {},
"reportErrorDescription": "Aduh. Ada yang salah. Silakan coba lahi nanti. Jika kamu mau, kamu bisa melaporkan kutu ini kepada para pengembang.",
"@reportErrorDescription": {},
"signInWithPassword": "Masuk dengan kata sandi",
"@signInWithPassword": {},
"continueWith": "Lanjutkan dengan:",
"@continueWith": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Silakan coba lagi nanti atau pilih server yang lain.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {}
"@fileIsTooBigForServer": {}
}

View File

@ -34,6 +34,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Decovrir",
"@discover": {
"type": "text",
"placeholders": {}
},
"containsUserName": "Contene li nómine",
"@containsUserName": {
"type": "text",
@ -184,6 +189,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Claves es in cache",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"lastActiveAgo": "Ultim activité: {localizedTimeShort}",
"@lastActiveAgo": {
"type": "text",
@ -330,6 +340,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Mardí",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Índisponibil",
"@unavailable": {
"type": "text",
@ -450,6 +465,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Saturdí",
"@saturday": {
"type": "text",
"placeholders": {}
},
"dateWithYear": "{day}.{month}.{year}",
"@dateWithYear": {
"type": "text",
@ -474,6 +494,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Venerdí",
"@friday": {
"type": "text",
"placeholders": {}
},
"lightTheme": "Lucid",
"@lightTheme": {
"type": "text",
@ -504,6 +529,11 @@
"type": "text",
"placeholders": {}
},
"thursday": "Jovedí",
"@thursday": {
"type": "text",
"placeholders": {}
},
"username": "Nómine de usator",
"@username": {
"type": "text",
@ -514,6 +544,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Mercurdí",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"submit": "Inviar",
"@submit": {
"type": "text",
@ -648,6 +683,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Archivat chambre",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"changePassword": "Cambiar li contrasigne",
"@changePassword": {
"type": "text",
@ -930,6 +970,8 @@
},
"updateAvailable": "Un actualisament de FluffyChat es disponibil",
"@updateAvailable": {},
"yourUserId": "Vor ID de usator:",
"@yourUserId": {},
"editWidgets": "Modificar li widgets",
"@editWidgets": {},
"widgetEtherpad": "Textual nota",
@ -1123,6 +1165,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Lunedí",
"@monday": {
"type": "text",
"placeholders": {}
},
"newGroup": "Crear un gruppe",
"@newGroup": {},
"newSpace": "Crear un spacie",
@ -1171,6 +1218,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Soledí",
"@sunday": {
"type": "text",
"placeholders": {}
},
"unverified": "Ínverificat",
"@unverified": {},
"deviceId": "ID de aparate",

View File

@ -76,6 +76,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Stanza archiviata",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Gli utenti ospiti possono partecipare",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -427,6 +432,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Firma incrociata abilitata",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Attualmente attivo",
"@currentlyActive": {
"type": "text",
@ -512,6 +522,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Scopri",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Il nominativo è stato cambiato",
"@displaynameHasBeenChanged": {
"type": "text",
@ -664,6 +679,11 @@
"type": "text",
"placeholders": {}
},
"friday": "venerdì",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Dall'adesione",
"@fromJoining": {
"type": "text",
@ -854,6 +874,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Le chiave sono memorizzate nella cache",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} ha espulso {targetName}",
"@kicked": {
"type": "text",
@ -971,6 +996,11 @@
"type": "text",
"placeholders": {}
},
"monday": "lunedì",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Silenzia discussione",
"@muteChat": {
"type": "text",
@ -1316,6 +1346,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "sabato",
"@saturday": {
"type": "text",
"placeholders": {}
},
"search": "Cerca",
"@search": {
"type": "text",
@ -1515,6 +1550,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "domenica",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "Sistema",
"@systemTheme": {
"type": "text",
@ -1530,6 +1570,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Questa stanza è stata archiviata.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "giovedì",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1566,6 +1616,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "martedì",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Non disponibile",
"@unavailable": {
"type": "text",
@ -1736,6 +1791,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "mercoledì",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Ti abbiamo inviato un'e-mail",
"@weSentYouAnEmail": {
"type": "text",
@ -1876,6 +1936,8 @@
"@passwordsDoNotMatch": {},
"pleaseEnterValidEmail": "Inserire un indirizzo email valido.",
"@pleaseEnterValidEmail": {},
"yourUserId": "Il tuo ID utente:",
"@yourUserId": {},
"commandHint_leave": "Lascia questa stanza",
"@commandHint_leave": {
"type": "text",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -71,6 +71,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Arkivert rom",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Skal gjester tillates å ta del",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -396,6 +401,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Videreformidling av tillit på",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Aktiv nå",
"@currentlyActive": {
"type": "text",
@ -481,6 +491,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Oppdag",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Visningsnavn endret",
"@displaynameHasBeenChanged": {
"type": "text",
@ -633,6 +648,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Fredag",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Fra å ta del",
"@fromJoining": {
"type": "text",
@ -818,6 +838,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Nøkler hurtiglagret",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} kastet ut {targetName}",
"@kicked": {
"type": "text",
@ -935,6 +960,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Mandag",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Forstum sludring",
"@muteChat": {
"type": "text",
@ -1245,6 +1275,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Lørdag",
"@saturday": {
"type": "text",
"placeholders": {}
},
"search": "Søk",
"@search": {
"type": "text",
@ -1439,6 +1474,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Søndag",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "System",
"@systemTheme": {
"type": "text",
@ -1454,6 +1494,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Dette rommet har blitt arkivert.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Torsdag",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1475,6 +1525,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Tirsdag",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Utilgjengelig",
"@unavailable": {
"type": "text",
@ -1630,6 +1685,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Onsdag",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Du har fått en e-post",
"@weSentYouAnEmail": {
"type": "text",
@ -1779,5 +1839,7 @@
"@changeYourAvatar": {
"type": "text",
"placeholders": {}
}
},
"yourUserId": "Din bruker ID:",
"@yourUserId": {}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"@@locale": "pl",
"@@last_modified": "2021-08-14 12:41:09.943634",
"about": "O aplikacji",
"about": "O nas",
"@about": {
"type": "text",
"placeholders": {}
@ -67,6 +67,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Zarchiwizowane pokoje",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Czy użytkownicy-goście mogą dołączyć",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -183,7 +188,7 @@
"username": {}
}
},
"changedTheDisplaynameTo": "{username} zmienił/-a swój nick na: '{displayname}'",
"changedTheDisplaynameTo": "{username} zmienił/-a swój nick na: {displayname}",
"@changedTheDisplaynameTo": {
"type": "text",
"placeholders": {
@ -518,7 +523,7 @@
"day": {}
}
},
"dateWithYear": "{day}.{month}.{year}",
"dateWithYear": "{day}-{month}-{year}",
"@dateWithYear": {
"type": "text",
"placeholders": {
@ -527,7 +532,7 @@
"day": {}
}
},
"deactivateAccountWarning": "To zdezaktywuje twoje konto. To jest nieodwracalne! Na pewno chcesz to zrobić?",
"deactivateAccountWarning": "To dezaktywuje twoje konto. To jest nieodwracalne ! Czy jesteś pewien?",
"@deactivateAccountWarning": {
"type": "text",
"placeholders": {}
@ -699,6 +704,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Piątek",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Od dołączenia",
"@fromJoining": {
"type": "text",
@ -869,6 +879,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Klucze są załadowane",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username} wyrzucił/-a {targetName}",
"@kicked": {
"type": "text",
@ -981,6 +996,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Poniedziałek",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Wycisz czat",
"@muteChat": {
"type": "text",
@ -1209,6 +1229,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Sobota",
"@saturday": {
"type": "text",
"placeholders": {}
},
"seenByUser": "Zobaczone przez {username}",
"@seenByUser": {
"type": "text",
@ -1344,6 +1369,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Niedziela",
"@sunday": {
"type": "text",
"placeholders": {}
},
"synchronizingPleaseWait": "Synchronizacja… Proszę czekać.",
"@synchronizingPleaseWait": {
"type": "text",
@ -1354,6 +1384,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Ten pokój został przeniesiony do archiwum.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Czwartek",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1370,6 +1410,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Wtorek",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unbannedUser": "{username} odbanował/-a {targetName}",
"@unbannedUser": {
"type": "text",
@ -1495,6 +1540,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Środa",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"whoIsAllowedToJoinThisGroup": "Kto może dołączyć do tej grupy",
"@whoIsAllowedToJoinThisGroup": {
"type": "text",
@ -1698,7 +1748,7 @@
"type": "text",
"placeholders": {}
},
"whatIsGoingOn": "Co u ciebie słychać?",
"whatIsGoingOn": "Co u Ciebie słychać?",
"@whatIsGoingOn": {},
"pleaseEnterValidEmail": "Proszę podaj poprawny adres email.",
"@pleaseEnterValidEmail": {},
@ -1771,6 +1821,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "Twoja nazwa użytkownika:",
"@yourUserId": {},
"yourChatBackupHasBeenSetUp": "Twoja kopia zapasowa chatu została ustawiona.",
"@yourChatBackupHasBeenSetUp": {},
"chatHasBeenAddedToThisSpace": "Chat został dodany do tej przestrzeni",
@ -1780,6 +1832,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Odkrywaj",
"@discover": {
"type": "text",
"placeholders": {}
},
"editRoomAvatar": "Edytuj zdjęcie pokoju",
"@editRoomAvatar": {
"type": "text",
@ -1890,6 +1947,8 @@
"@scanQrCode": {},
"addToStory": "Dodaj do relacji",
"@addToStory": {},
"typeInInviteLinkManually": "Wpisz link ręcznie...",
"@typeInInviteLinkManually": {},
"createNewSpace": "Nowa przestrzeń",
"@createNewSpace": {
"type": "text",
@ -2124,8 +2183,12 @@
"@unsubscribeStories": {},
"updateNow": "Rozpocznij aktualizację w tle",
"@updateNow": {},
"dehydrateShare": "To jest twój prywatny eksport FluffyChat. Upewnij się, że nie zgubisz go i zachowaj go dla siebie.",
"@dehydrateShare": {},
"hydrateTorLong": "Czy ostatnio eksportowałeś/-aś swoją sesję na TOR? Szybko ją zaimportuj i kontynuuj rozmowy.",
"@hydrateTorLong": {},
"noSearchResult": "Brak pasujących wyników wyszukiwania.",
"@noSearchResult": {},
"dehydrateTorLong": "W przypadku użytkowników sieci TOR zaleca się eksportowanie sesji przed zamknięciem okna.",
"@dehydrateTorLong": {},
"hydrate": "Przywracanie z pliku kopii zapasowej",
@ -2204,6 +2267,11 @@
},
"commandHint_markasdm": "Oznacz jako pokój wiadomości bezpośrednich",
"@commandHint_markasdm": {},
"crossSigningEnabled": "Weryfikacja krzyżowa jest włączona",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"confirmMatrixId": "Potwierdź swój identyfikator Matrix w celu usunięcia konta.",
"@confirmMatrixId": {},
"commandHint_markasgroup": "Oznacz jako grupę",
@ -2220,268 +2288,5 @@
"dehydrateTor": "Użytkownicy TOR-a: Eksportuj sesję",
"@dehydrateTor": {},
"matrixWidgets": "Widżety Matrix",
"@matrixWidgets": {},
"unsupportedAndroidVersion": "Nieobsługiwana wersja systemu Android",
"@unsupportedAndroidVersion": {},
"widgetCustom": "Własny",
"@widgetCustom": {},
"widgetEtherpad": "Notatka",
"@widgetEtherpad": {},
"widgetJitsi": "Jitsi Meet",
"@widgetJitsi": {},
"pinMessage": "Przypnij do pokoju",
"@pinMessage": {},
"confirmEventUnpin": "Czy na pewno chcesz trwale odpiąć wydarzenie?",
"@confirmEventUnpin": {},
"youJoinedTheChat": "Dołączono do czatu",
"@youJoinedTheChat": {},
"noEmailWarning": "Wprowadź prawidłowy adres e-mail. W przeciwnym razie resetowanie hasła nie będzie możliwe. Jeśli nie chcesz, dotknij ponownie przycisku, aby kontynuować.",
"@noEmailWarning": {},
"user": "Użytkownik",
"@user": {},
"custom": "Własne",
"@custom": {},
"newGroup": "Nowa grupa",
"@newGroup": {},
"newSpace": "Nowa przestrzeń",
"@newSpace": {},
"enterInviteLinkOrMatrixId": "Wprowadź link zaproszenia lub identyfikator Matrix...",
"@enterInviteLinkOrMatrixId": {},
"fileIsTooBigForServer": "Serwer zgłasza, że plik jest zbyt duży, aby go wysłać.",
"@fileIsTooBigForServer": {},
"youBannedUser": "Zbanowałeś/-aś {user}",
"@youBannedUser": {
"placeholders": {
"user": {}
}
},
"users": "Użytkownicy",
"@users": {},
"countFiles": "{count} plików",
"@countFiles": {
"placeholders": {
"count": {}
}
},
"noOtherDevicesFound": "Nie znaleziono innych urządzeń",
"@noOtherDevicesFound": {},
"widgetUrlError": "Niepoprawny URL.",
"@widgetUrlError": {},
"widgetNameError": "Podaj nazwę wyświetlaną.",
"@widgetNameError": {},
"encryptThisChat": "Zaszyfruj ten czat",
"@encryptThisChat": {},
"endToEndEncryption": "Szyfrowanie od końca do końca",
"@endToEndEncryption": {},
"disableEncryptionWarning": "Ze względów bezpieczeństwa nie można wyłączyć szyfrowania w czacie, w którym zostało ono wcześniej włączone.",
"@disableEncryptionWarning": {},
"deviceKeys": "Klucze urządzenia:",
"@deviceKeys": {},
"emailOrUsername": "Adres e-mail lub nazwa użytkownika",
"@emailOrUsername": {},
"indexedDbErrorLong": "Przechowywanie wiadomości niestety nie jest domyślnie włączone w trybie prywatnym.\nOdwiedź\n - about:config\n - ustaw dom.indexedDB.privateBrowsing.enabled na true\nW przeciwnym razie nie jest możliwe uruchomienie FluffyChat.",
"@indexedDbErrorLong": {},
"saveKeyManuallyDescription": "Zapisz ten klucz ręcznie, uruchamiając systemowe okno dialogowe udostępniania lub schowek.",
"@saveKeyManuallyDescription": {},
"screenSharingTitle": "udostępnianie ekranu",
"@screenSharingTitle": {},
"appearOnTopDetails": "Umożliwia wyświetlanie aplikacji nad innymi (nie jest to konieczne, jeśli FluffyChat jest już ustawiony jako konto do dzwonienia)",
"@appearOnTopDetails": {},
"noKeyForThisMessage": "Może się to zdarzyć, jeśli wiadomość została wysłana przed zalogowaniem się na to konto na tym urządzeniu.\n\nMożliwe jest również, że nadawca zablokował Twoje urządzenie lub coś poszło nie tak z połączeniem internetowym.\n\nJesteś w stanie odczytać wiadomość na innej sesji? W takim razie możesz przenieść z niej wiadomość! Wejdź w Ustawienia > Urządzenia i upewnij się, że Twoje urządzenia zweryfikowały się wzajemnie. Gdy następnym razem otworzysz pokój i obie sesje będą włączone, klucze zostaną przekazane automatycznie.\n\nNie chcesz stracić kluczy podczas wylogowania lub przełączania urządzeń? Upewnij się, że w ustawieniach masz włączoną kopię zapasową czatu.",
"@noKeyForThisMessage": {},
"sorryThatsNotPossible": "Przepraszamy... to nie jest możliwe",
"@sorryThatsNotPossible": {},
"noBackupWarning": "Uwaga! Bez włączenia kopii zapasowej czatu, stracisz dostęp do swoich zaszyfrowanych wiadomości. Zaleca się włączenie kopii zapasowej czatu przed wylogowaniem.",
"@noBackupWarning": {},
"commandHint_googly": "Wyślij kręcące się oczka",
"@commandHint_googly": {},
"callingPermissions": "Uprawnienia połączeń",
"@callingPermissions": {},
"storeInAndroidKeystore": "Przechowaj w Android KeyStore",
"@storeInAndroidKeystore": {},
"commandHint_cuddle": "Wyślij przytulenie",
"@commandHint_cuddle": {},
"googlyEyesContent": "{senderName} wysyła ci kręcące się oczka",
"@googlyEyesContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"cuddleContent": "{senderName} przytula cię",
"@cuddleContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"hugContent": "{senderName} uściska cię",
"@hugContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"commandHint_hug": "Wyślij uścisk",
"@commandHint_hug": {},
"letsStart": "Zacznijmy",
"@letsStart": {},
"reactedWith": "{sender} zareagował/-a z {reaction}",
"@reactedWith": {
"type": "text",
"placeholders": {
"sender": {},
"reaction": {}
}
},
"emojis": "Emoji",
"@emojis": {},
"placeCall": "Zadzwoń",
"@placeCall": {},
"voiceCall": "Połączenie głosowe",
"@voiceCall": {},
"unsupportedAndroidVersionLong": "Ta funkcja wymaga nowszej wersji systemu Android. Sprawdź aktualizacje lub wsparcie Lineage OS.",
"@unsupportedAndroidVersionLong": {},
"videoCallsBetaWarning": "Należy pamiętać, że połączenia wideo są obecnie w fazie beta. Mogą nie działać zgodnie z oczekiwaniami lub nie działać w ogóle na wszystkich platformach.",
"@videoCallsBetaWarning": {},
"experimentalVideoCalls": "Eksperymentalne połączenia wideo",
"@experimentalVideoCalls": {},
"indexedDbErrorTitle": "Problemy związane z trybem prywatnym",
"@indexedDbErrorTitle": {},
"switchToAccount": "Przełącz na konto {number}",
"@switchToAccount": {
"type": "number",
"placeholders": {
"number": {}
}
},
"nextAccount": "Następne konto",
"@nextAccount": {},
"previousAccount": "Poprzednie konto",
"@previousAccount": {},
"editWidgets": "Edytuj widżety",
"@editWidgets": {},
"addWidget": "Dodaj widżet",
"@addWidget": {},
"widgetVideo": "Film",
"@widgetVideo": {},
"widgetName": "Nazwa",
"@widgetName": {},
"errorAddingWidget": "Błąd podczas dodawania widżetu.",
"@errorAddingWidget": {},
"youRejectedTheInvitation": "Odrzucono zaproszenie",
"@youRejectedTheInvitation": {},
"youAcceptedTheInvitation": "👍 Zaakceptowałeś/-aś zaproszenie",
"@youAcceptedTheInvitation": {},
"youHaveWithdrawnTheInvitationFor": "Wycofano zaproszenie dla {user}",
"@youHaveWithdrawnTheInvitationFor": {
"placeholders": {
"user": {}
}
},
"youInvitedBy": "📩 Zostałeś/-aś zaproszony/-a przez {user}",
"@youInvitedBy": {
"placeholders": {
"user": {}
}
},
"youInvitedUser": "📩 Zaprosiłeś/-aś {user}",
"@youInvitedUser": {
"placeholders": {
"user": {}
}
},
"youKicked": "👞 Wyrzuciłeś/-aś {user}",
"@youKicked": {
"placeholders": {
"user": {}
}
},
"youKickedAndBanned": "🙅 Wyrzuciłeś/-aś i zbanowałeś/-aś {user}",
"@youKickedAndBanned": {
"placeholders": {
"user": {}
}
},
"youUnbannedUser": "Odbanowałeś/-aś {user}",
"@youUnbannedUser": {
"placeholders": {
"user": {}
}
},
"stories": "Relacje",
"@stories": {},
"unlockOldMessages": "Odblokuj stare wiadomości",
"@unlockOldMessages": {},
"storeInSecureStorageDescription": "Przechowaj klucz odzyskiwania w bezpiecznym magazynie tego urządzenia.",
"@storeInSecureStorageDescription": {},
"storeInAppleKeyChain": "Przechowaj w pęku kluczy Apple",
"@storeInAppleKeyChain": {},
"storeSecurlyOnThisDevice": "Przechowaj bezpiecznie na tym urządzeniu",
"@storeSecurlyOnThisDevice": {},
"foregroundServiceRunning": "To powiadomienie pojawia się, gdy usługa w tle jest uruchomiona.",
"@foregroundServiceRunning": {},
"screenSharingDetail": "Udostępniasz swój ekran w FluffyChat",
"@screenSharingDetail": {},
"callingAccount": "Konto połączeń",
"@callingAccount": {},
"callingAccountDetails": "Pozwala FluffyChat używać natywnej aplikacji do wykonywania połączeń w Androidzie.",
"@callingAccountDetails": {},
"appearOnTop": "Wyświetlaj nad innymi",
"@appearOnTop": {},
"otherCallingPermissions": "Mikrofon, kamera i inne uprawnienia FluffyChat",
"@otherCallingPermissions": {},
"whyIsThisMessageEncrypted": "Dlaczego nie można odczytać tej wiadomości?",
"@whyIsThisMessageEncrypted": {},
"enterSpace": "Wejdź do przestrzeni",
"@enterSpace": {},
"enterRoom": "Wejdź do pokoju",
"@enterRoom": {},
"allSpaces": "Wszystkie przestrzenie",
"@allSpaces": {},
"numChats": "{number} czatów",
"@numChats": {
"type": "number",
"placeholders": {
"number": {}
}
},
"hideUnimportantStateEvents": "Ukryj nieistotne wydarzenia stanu",
"@hideUnimportantStateEvents": {},
"doNotShowAgain": "Nie pokazuj ponownie",
"@doNotShowAgain": {},
"wasDirectChatDisplayName": "Pusty czat (wcześniej {oldDisplayName})",
"@wasDirectChatDisplayName": {
"type": "text",
"placeholders": {
"oldDisplayName": {}
}
},
"newSpaceDescription": "Przestrzenie pozwalają na konsolidację czatów i budowanie prywatnych lub publicznych społeczności.",
"@newSpaceDescription": {},
"reopenChat": "Otwórz ponownie czat",
"@reopenChat": {},
"fileHasBeenSavedAt": "Plik został zapisany w ścieżce {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"addToBundle": "Dodaj do pakietu",
"@addToBundle": {},
"bundleName": "Nazwa pakietu",
"@bundleName": {},
"editBundlesForAccount": "Edytuj paczki dla tego konta",
"@editBundlesForAccount": {},
"jumpToLastReadMessage": "Przejdź do ostatnio przeczytanej wiadomości",
"@jumpToLastReadMessage": {},
"readUpToHere": "Czytaj do tego miejsca",
"@readUpToHere": {},
"jump": "Przejdź",
"@jump": {},
"removeFromBundle": "Usuń z tej paczki",
"@removeFromBundle": {},
"openLinkInBrowser": "Otwórz link w przeglądarce",
"@openLinkInBrowser": {}
"@matrixWidgets": {}
}

View File

@ -10,6 +10,21 @@
"type": "text",
"placeholders": {}
},
"monday": "segunda-feira",
"@monday": {
"type": "text",
"placeholders": {}
},
"saturday": "sábado",
"@saturday": {
"type": "text",
"placeholders": {}
},
"wednesday": "quarta-feira",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"about": "Sobre",
"@about": {
"type": "text",
@ -92,6 +107,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "terça-feira",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"logout": "Terminar sessão",
"@logout": {
"type": "text",
@ -102,6 +122,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "domingo",
"@sunday": {
"type": "text",
"placeholders": {}
},
"users": "Utilizadores",
"@users": {},
"close": "Fechar",
@ -116,5 +141,15 @@
"month": {},
"day": {}
}
},
"friday": "sexta-feira",
"@friday": {
"type": "text",
"placeholders": {}
},
"thursday": "quinta-feira",
"@thursday": {
"type": "text",
"placeholders": {}
}
}

View File

@ -81,6 +81,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Sala arquivada",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Usuários convidados podem participar",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -538,6 +543,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Assinatura cruzada ativada",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Ativo",
"@currentlyActive": {
"type": "text",
@ -623,6 +633,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Desvendar",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "O nome de exibição foi alterado",
"@displaynameHasBeenChanged": {
"type": "text",
@ -789,6 +804,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Sexta-feira",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Desde que entrou",
"@fromJoining": {
"type": "text",
@ -979,6 +999,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Chaves guardadas",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username} enxotou {targetName}",
"@kicked": {
"type": "text",
@ -1113,6 +1138,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Segunda-feira",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Silenciar",
"@muteChat": {
"type": "text",
@ -1506,6 +1536,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Sábado",
"@saturday": {
"type": "text",
"placeholders": {}
},
"saveFile": "Salvar arquivo",
"@saveFile": {
"type": "text",
@ -1744,6 +1779,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Domingo",
"@sunday": {
"type": "text",
"placeholders": {}
},
"synchronizingPleaseWait": "Sincronizando… Por favor, aguarde.",
"@synchronizingPleaseWait": {
"type": "text",
@ -1764,6 +1804,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Esta sala foi arquivada.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Quinta-feira",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1800,6 +1850,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Terça-feira",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Indisponível",
"@unavailable": {
"type": "text",
@ -1970,6 +2025,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Quarta-feira",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Enviamos um e-mail para você",
"@weSentYouAnEmail": {
"type": "text",
@ -2042,6 +2102,8 @@
},
"shareYourInviteLink": "Compartilhar o link do convite",
"@shareYourInviteLink": {},
"typeInInviteLinkManually": "Digitar o link do convite manualmente...",
"@typeInInviteLinkManually": {},
"oneClientLoggedOut": "Um dos seus clientes foi desvinculado",
"@oneClientLoggedOut": {},
"addAccount": "Adicionar conta",
@ -2114,6 +2176,8 @@
"@sendOnEnter": {},
"homeserver": "Servidor matriz",
"@homeserver": {},
"yourUserId": "Seu ID de usuário:",
"@yourUserId": {},
"chatHasBeenAddedToThisSpace": "A conversa foi adicionada a este espaço",
"@chatHasBeenAddedToThisSpace": {},
"commandHint_clearcache": "Limpar dados temporários",
@ -2355,6 +2419,8 @@
"@commandHint_markasdm": {},
"commandHint_markasgroup": "Marcar como grupo",
"@commandHint_markasgroup": {},
"dehydrateShare": "Este é seu extrato FluffyChat. Cuidado para não perdê-lo e o mantenha privado.",
"@dehydrateShare": {},
"hydrateTor": "Usuários TOR: Importar sessão",
"@hydrateTor": {},
"hydrateTorLong": "Você exportou sua última sessão no TOR? Importe ela rapidamente e continue conversando.",
@ -2425,6 +2491,8 @@
"@dehydrateWarning": {},
"dehydrateTorLong": "Para usuários TOR, é recomendado exportar a sessão antes de fechar a janela.",
"@dehydrateTorLong": {},
"enableAutoBackups": "Habilitar backups automáticos",
"@enableAutoBackups": {},
"whyIsThisMessageEncrypted": "Por que esta mensagem está ilegível?",
"@whyIsThisMessageEncrypted": {},
"screenSharingTitle": "Compartilhar tela",

View File

@ -90,6 +90,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Sala arquivada",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Todos os visitantes podem entrar",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -289,6 +294,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "O teu ID de utilizador:",
"@yourUserId": {},
"yourChatBackupHasBeenSetUp": "A cópia de segurança foi configurada.",
"@yourChatBackupHasBeenSetUp": {},
"chatBackup": "Cópia de segurança de conversas",
@ -508,6 +515,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Assinatura cruzada ativada",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Ativo(a) agora",
"@currentlyActive": {
"type": "text",
@ -593,6 +605,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Descobrir",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Nome de exibição alterado",
"@displaynameHasBeenChanged": {
"type": "text",
@ -761,6 +778,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Sexta-feira",
"@friday": {
"type": "text",
"placeholders": {}
},
"goToTheNewRoom": "Ir para a nova sala",
"@goToTheNewRoom": {
"type": "text",
@ -941,6 +963,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Chaves estão armazenadas em cache",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} expulsou {targetName}",
"@kicked": {
"type": "text",
@ -1070,6 +1097,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Segunda-feira",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Silenciar conversa",
"@muteChat": {
"type": "text",
@ -1135,6 +1167,8 @@
},
"shareYourInviteLink": "Partilhar a ligação de convite",
"@shareYourInviteLink": {},
"typeInInviteLinkManually": "Escrever a ligação de convite manualmente...",
"@typeInInviteLinkManually": {},
"none": "Nenhum",
"@none": {
"type": "text",
@ -1547,6 +1581,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Sábado",
"@saturday": {
"type": "text",
"placeholders": {}
},
"saveFile": "Guardar ficheiro",
"@saveFile": {
"type": "text",
@ -1670,6 +1709,8 @@
"@dehydrate": {},
"dehydrateWarning": "Esta ação não pode ser revertida. Assegura-te que guardas bem a cópia de segurança.",
"@dehydrateWarning": {},
"dehydrateShare": "Esta é a tua exportação privada do FluffyChat. Assegura-te que não a perdes e que a manténs privada.",
"@dehydrateShare": {},
"hydrateTorLong": "Exportaste a tua sessão na última vez que estiveste no TOR? Importa-a rapidamente e continua a conversar.",
"@hydrateTorLong": {},
"dehydrateTor": "Utilizadores do TOR: Exportar sessão",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -55,6 +55,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Archivovaná miestnosť",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Môžu sa pripojiť hostia",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -325,6 +330,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Vzájomné overenie je zapnuté",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Momentálne prítomní",
"@currentlyActive": {
"type": "text",
@ -470,6 +480,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Piatok",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Od pripojenia",
"@fromJoining": {
"type": "text",
@ -595,6 +610,11 @@
"username": {}
}
},
"keysCached": "Kľúče sú uložené",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} vyhodili {targetName}",
"@kicked": {
"type": "text",
@ -697,6 +717,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Pondelok",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Stlmiť chat",
"@muteChat": {
"type": "text",
@ -895,6 +920,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Sobota",
"@saturday": {
"type": "text",
"placeholders": {}
},
"seenByUser": "Videné užívateľom {username}",
"@seenByUser": {
"type": "text",
@ -1030,6 +1060,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Nedeľa",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "Systémová farba",
"@systemTheme": {
"type": "text",
@ -1045,6 +1080,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Táto miestnosť bola archivovaná.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Štvrtok",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1056,6 +1101,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Utorok",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unbannedUser": "{username} odbanovali {targetName}",
"@unbannedUser": {
"type": "text",
@ -1206,6 +1256,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Streda",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"whoIsAllowedToJoinThisGroup": "Kto môže vstúpiť do tejto skupiny",
"@whoIsAllowedToJoinThisGroup": {
"type": "text",
@ -1385,6 +1440,8 @@
},
"sendOnEnter": "Odoslať pri vstupe",
"@sendOnEnter": {},
"yourUserId": "Vaše užívateľské ID:",
"@yourUserId": {},
"ignoredUsers": "Ignorovaní užívatelia",
"@ignoredUsers": {
"type": "text",

View File

@ -71,6 +71,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Arhivirana soba",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"askSSSSSign": "Če želite podpisati drugo osebo, vnesite geslo za varno trgovino ali obnovitveni ključ.",
"@askSSSSSign": {
"type": "text",
@ -254,6 +259,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "Vaš ID uporabnika:",
"@yourUserId": {},
"yourChatBackupHasBeenSetUp": "Varnostna kopija klepeta je nastavljena.",
"@yourChatBackupHasBeenSetUp": {},
"chatBackup": "Varnostno kopiranje klepeta",
@ -551,6 +558,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Navzkrižno podpisovanje DA",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Trenutno aktiven",
"@currentlyActive": {
"type": "text",

View File

@ -76,6 +76,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Архивирана соба",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Да ли је гостима дозвољен приступ",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -499,6 +504,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Међу-потписивање укључено",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Тренутно активно",
"@currentlyActive": {
"type": "text",
@ -584,6 +594,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Истражи",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Име за приказ је измењено",
"@displaynameHasBeenChanged": {
"type": "text",
@ -741,6 +756,11 @@
"type": "text",
"placeholders": {}
},
"friday": "петак",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "од приступања",
"@fromJoining": {
"type": "text",
@ -931,6 +951,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Кључеви су кеширани",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username} избаци корисника {targetName}",
"@kicked": {
"type": "text",
@ -1055,6 +1080,11 @@
"type": "text",
"placeholders": {}
},
"monday": "понедељак",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Ућуткај ћаскање",
"@muteChat": {
"type": "text",
@ -1430,6 +1460,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "субота",
"@saturday": {
"type": "text",
"placeholders": {}
},
"search": "Претражи",
"@search": {
"type": "text",
@ -1639,6 +1674,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "недеља",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "системски",
"@systemTheme": {
"type": "text",
@ -1654,6 +1694,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Ова соба је архивирана.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "четвртак",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1690,6 +1740,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "уторак",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Недоступно",
"@unavailable": {
"type": "text",
@ -1860,6 +1915,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "среда",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Послали смо вам е-пошту",
"@weSentYouAnEmail": {
"type": "text",

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
{}
{}

View File

@ -84,6 +84,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Arşivlenmiş Oda",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Misafir kullanıcıların katılmasına izin veriliyor mu",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -543,6 +548,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Çapraz imzalama açık",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Şu anda etkin",
"@currentlyActive": {
"type": "text",
@ -628,6 +638,11 @@
"type": "text",
"placeholders": {}
},
"discover": "Keşfet",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Görünen ad değiştirildi",
"@displaynameHasBeenChanged": {
"type": "text",
@ -794,6 +809,11 @@
"type": "text",
"placeholders": {}
},
"friday": "Cuma",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "Katılmadan",
"@fromJoining": {
"type": "text",
@ -984,6 +1004,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "Anahtarlar önbelleğe alındı",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username}, {targetName} kişisini attı",
"@kicked": {
"type": "text",
@ -1118,6 +1143,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Pazartesi",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Sohbeti sessize al",
"@muteChat": {
"type": "text",
@ -1511,6 +1541,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Cumartesi",
"@saturday": {
"type": "text",
"placeholders": {}
},
"saveFile": "Dosyayı kaydet",
"@saveFile": {
"type": "text",
@ -1749,6 +1784,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Pazar",
"@sunday": {
"type": "text",
"placeholders": {}
},
"synchronizingPleaseWait": "Eşzamanlanıyor… Lütfen bekleyin.",
"@synchronizingPleaseWait": {
"type": "text",
@ -1769,6 +1809,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Bu oda arşivlendi.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Perşembe",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1805,6 +1855,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Salı",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "Yok",
"@unavailable": {
"type": "text",
@ -1975,6 +2030,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Çarşamba",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "Size bir e-posta gönderdik",
"@weSentYouAnEmail": {
"type": "text",
@ -2047,6 +2107,8 @@
},
"scanQrCode": "QR kodunu tarayın",
"@scanQrCode": {},
"typeInInviteLinkManually": "Davet bağlantısını el ile yazın...",
"@typeInInviteLinkManually": {},
"shareYourInviteLink": "Davet bağlantınızı paylaşın",
"@shareYourInviteLink": {},
"sendOnEnter": "Enter tuşu ile gönder",
@ -2075,6 +2137,8 @@
"@yourChatBackupHasBeenSetUp": {},
"unverified": "Doğrulanmadı",
"@unverified": {},
"yourUserId": "Kullanıcı kimliğiniz:",
"@yourUserId": {},
"repeatPassword": "Parolayı tekrarlayın",
"@repeatPassword": {},
"passwordsDoNotMatch": "Parolalar eşleşmiyor!",
@ -2316,6 +2380,8 @@
"@users": {},
"storeInSecureStorageDescription": "Kurtarma anahtarını bu aygıtın güvenli deposunda saklayın.",
"@storeInSecureStorageDescription": {},
"enableAutoBackups": "Otomatik yedeklemeleri etkinleştir",
"@enableAutoBackups": {},
"recoveryKey": "Kurtarma anahtarı",
"@recoveryKey": {},
"stories": "Hikayeler",
@ -2348,6 +2414,8 @@
"@indexedDbErrorTitle": {},
"dehydrateWarning": "Bu eylem geri alınamaz. Yedekleme dosyasını güvenli bir şekilde sakladığınızdan emin olun.",
"@dehydrateWarning": {},
"dehydrateShare": "Bu sizin özel FluffyChat dışa aktarımınızdır. Kaybetmediğinizden ve gizli tuttuğunuzdan emin olun.",
"@dehydrateShare": {},
"hydrateTorLong": "TOR'da en son oturumunuzu dışa aktardınız mı? Hızlıca içe aktarın ve sohbete devam edin.",
"@hydrateTorLong": {},
"indexedDbErrorLong": "Mesaj saklama özelliği ne yazık ki öntanımlı olarak gizli modda etkin değildir.\nLütfen\n - about:config sayfasına gidin ve\n - dom.indexedDB.privateBrowsing.enabled seçeneğini true olarak ayarlayın\nAksi takdirde FluffyChat çalıştırılamaz.",
@ -2375,7 +2443,7 @@
"@commandHint_markasdm": {},
"whyIsThisMessageEncrypted": "Bu mesaj neden okunamıyor?",
"@whyIsThisMessageEncrypted": {},
"noKeyForThisMessage": "Bu durum, mesaj siz bu aygıtta hesabınızda oturum açmadan önce gönderildiyse meydana gelebilir.\n\nGönderenin aygıtınızı engellemiş olması veya internet bağlantısında bir sorun olması da mümkündür.\n\nMesajı başka bir oturumda okuyabiliyor musunuz? O zaman mesajı oradan aktarabilirsiniz! Ayarlar > Aygıtlar bölümüne gidin ve aygıtlarınızın birbirini doğruladığından emin olun. Odayı bir sonraki sefer açtığınızda ve her iki oturum da ön planda olduğunda, anahtarlar otomatik olarak iletilecektir.\n\nOturumu kapatırken veya aygıt değiştirirken anahtarları kaybetmek istemiyor musunuz? Ayarlarda sohbet yedeklemesini etkinleştirdiğinizden emin olun.",
"noKeyForThisMessage": "Bu durum, mesaj siz bu aygıtta hesabınızda oturum açmadan önce gönderildiyse meydana gelebilir. \n \nGönderenin aygıtınızı engellemiş olması veya internet bağlantısında bir sorun olması da mümkündür. \n \nMesajı başka bir oturumda okuyabiliyor musunuz? O zaman mesajı oradan aktarabilirsiniz! Ayarlar > Aygıtlar bölümüne gidin ve aygıtlarınızın birbirini doğruladığından emin olun. Odayı bir sonraki sefer açtığınızda ve her iki oturum da ön planda olduğunda, anahtarlar otomatik olarak iletilecektir. \n \nOturumu kapatırken veya aygıt değiştirirken anahtarları kaybetmek istemiyor musunuz? Ayarlarda sohbet yedeklemesini etkinleştirdiğinizden emin olun.",
"@noKeyForThisMessage": {},
"screenSharingTitle": "ekran paylaşımı",
"@screenSharingTitle": {},
@ -2464,6 +2532,8 @@
"@sorryThatsNotPossible": {},
"deviceKeys": "Aygıt anahtarları:",
"@deviceKeys": {},
"noSearchResult": "Eşleşen arama sonucu yok.",
"@noSearchResult": {},
"letsStart": "Başlayalım",
"@letsStart": {},
"enterInviteLinkOrMatrixId": "Davet bağlantısını veya Matris kimliğini girin...",
@ -2475,40 +2545,5 @@
"noOtherDevicesFound": "Başka aygıt bulunamadı",
"@noOtherDevicesFound": {},
"fileIsTooBigForServer": "Sunucu, dosyanın gönderilemeyecek kadar büyük olduğunu bildiriyor.",
"@fileIsTooBigForServer": {},
"fileHasBeenSavedAt": "Dosya {path} konumuna kaydedildi",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"jumpToLastReadMessage": "Son okunan mesaja atla",
"@jumpToLastReadMessage": {},
"readUpToHere": "Buraya kadar oku",
"@readUpToHere": {},
"jump": "Atla",
"@jump": {},
"openLinkInBrowser": "Bağlantıyı tarayıcıda aç",
"@openLinkInBrowser": {},
"allRooms": "Tüm Grup Sohbetleri",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"discover": "Keşfet",
"@discover": {
"type": "text",
"placeholders": {}
},
"reportErrorDescription": "Olamaz. Bir şeyler yanlış gitti. Lütfen daha sonra tekrar deneyin. İsterseniz hatayı geliştiricilere bildirebilirsiniz.",
"@reportErrorDescription": {},
"report": "bildir",
"@report": {},
"signInWithPassword": "Parola ile oturum aç",
"@signInWithPassword": {},
"continueWith": "Devam et:",
"@continueWith": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Lütfen daha sonra tekrar deneyin veya farklı bir sunucu seçin.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {}
"@fileIsTooBigForServer": {}
}

View File

@ -62,6 +62,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Заархівована кімната",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Чи дозволено гостям приєднуватись",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -332,6 +337,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "Перехресне підписування увімкнено",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Зараз у мережі",
"@currentlyActive": {
"type": "text",
@ -484,6 +494,11 @@
"type": "text",
"placeholders": {}
},
"friday": "П'ятниця",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "З моменту приєднання",
"@fromJoining": {
"type": "text",
@ -609,6 +624,11 @@
"username": {}
}
},
"keysCached": "Ключі кешовано",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username} вилучає {targetName}",
"@kicked": {
"type": "text",
@ -711,6 +731,11 @@
"type": "text",
"placeholders": {}
},
"monday": "Понеділок",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "Вимкнути сповіщення",
"@muteChat": {
"type": "text",
@ -909,6 +934,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "Субота",
"@saturday": {
"type": "text",
"placeholders": {}
},
"seenByUser": "Переглянуто {username}",
"@seenByUser": {
"type": "text",
@ -1036,6 +1066,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "Неділя",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "Системна",
"@systemTheme": {
"type": "text",
@ -1051,6 +1086,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "Цю кімнату було заархівовано.",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "Четвер",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1062,6 +1107,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "Вівторок",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unbannedUser": "{username} розблоковує {targetName}",
"@unbannedUser": {
"type": "text",
@ -1205,6 +1255,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "Середа",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"whoIsAllowedToJoinThisGroup": "Кому дозволено приєднуватися до цієї групи",
"@whoIsAllowedToJoinThisGroup": {
"type": "text",
@ -1439,6 +1494,8 @@
},
"shareYourInviteLink": "Поділіться своїм посиланням запрошення",
"@shareYourInviteLink": {},
"typeInInviteLinkManually": "Введіть посилання запрошення власноруч...",
"@typeInInviteLinkManually": {},
"scanQrCode": "Сканувати QR-код",
"@scanQrCode": {},
"noPasswordRecoveryDescription": "Ви ще не додали спосіб відновлення пароля.",
@ -1794,6 +1851,8 @@
"type": "text",
"description": "Usage hint for the command /html"
},
"yourUserId": "Ваш ID користувача:",
"@yourUserId": {},
"commandHint_invite": "Запросіть цього користувача до цієї кімнати",
"@commandHint_invite": {
"type": "text",
@ -1931,6 +1990,11 @@
"senderName": {}
}
},
"discover": "Огляд",
"@discover": {
"type": "text",
"placeholders": {}
},
"cantOpenUri": "Не вдалося відкрити URI {uri}",
"@cantOpenUri": {
"type": "text",
@ -2320,6 +2384,8 @@
"@recoveryKey": {},
"recoveryKeyLost": "Ключ відновлення втрачено?",
"@recoveryKeyLost": {},
"enableAutoBackups": "Увімкнути автоматичне резервне копіювання",
"@enableAutoBackups": {},
"users": "Користувачі",
"@users": {},
"stories": "Історії",
@ -2346,6 +2412,8 @@
"@dehydrate": {},
"dehydrateWarning": "Цю дію не можна скасувати. Переконайтеся, що ви безпечно зберігаєте файл резервної копії.",
"@dehydrateWarning": {},
"dehydrateShare": "Це ваш приватний експорт FluffyChat. Переконайтеся, що ви не втратите його та зберігайте його приватно.",
"@dehydrateShare": {},
"dehydrateTor": "Користувачі TOR: експорт сеансу",
"@dehydrateTor": {},
"dehydrateTorLong": "Для користувачів TOR рекомендується експортувати сеанс перед закриттям вікна.",
@ -2464,6 +2532,8 @@
"@sorryThatsNotPossible": {},
"deviceKeys": "Ключі пристрою:",
"@deviceKeys": {},
"noSearchResult": "Немає відповідних результатів пошуку.",
"@noSearchResult": {},
"letsStart": "Розпочнімо",
"@letsStart": {},
"enterInviteLinkOrMatrixId": "Введіть запрошувальне посилання або Matrix ID...",
@ -2475,40 +2545,5 @@
"noBackupWarning": "Увага! Якщо ви не ввімкнете резервне копіювання бесіди, ви втратите доступ до своїх зашифрованих повідомлень. Наполегливо радимо ввімкнути резервне копіювання бесіди перед виходом.",
"@noBackupWarning": {},
"fileIsTooBigForServer": "Сервер повідомляє, що файл завеликий для надсилання.",
"@fileIsTooBigForServer": {},
"fileHasBeenSavedAt": "Файл збережено в {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"jumpToLastReadMessage": "Перейти до останнього прочитаного повідомлення",
"@jumpToLastReadMessage": {},
"readUpToHere": "Читати тут",
"@readUpToHere": {},
"jump": "Перейти",
"@jump": {},
"openLinkInBrowser": "Відкрити посилання у браузері",
"@openLinkInBrowser": {},
"allRooms": "Усі групові бесіди",
"@allRooms": {
"type": "text",
"placeholders": {}
},
"reportErrorDescription": "О, ні. Щось пішло не так. Повторіть спробу пізніше. Якщо хочете, можете повідомити про помилку розробникам.",
"@reportErrorDescription": {},
"report": "повідомити",
"@report": {},
"discover": "Огляд",
"@discover": {
"type": "text",
"placeholders": {}
},
"pleaseTryAgainLaterOrChooseDifferentServer": "Спробуйте пізніше або виберіть інший сервер.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"signInWithPassword": "Увійти за допомогою пароля",
"@signInWithPassword": {},
"continueWith": "Продовжити за допомогою:",
"@continueWith": {}
"@fileIsTooBigForServer": {}
}

View File

@ -61,6 +61,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "Phòng hội thảo đã lưu trữ",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "Khách vãng lai có được tham gia không",
"@areGuestsAllowedToJoin": {
"type": "text",

File diff suppressed because it is too large Load Diff

View File

@ -76,6 +76,11 @@
"type": "text",
"placeholders": {}
},
"archivedRoom": "已封存的對話",
"@archivedRoom": {
"type": "text",
"placeholders": {}
},
"areGuestsAllowedToJoin": "是否允許訪客加入",
"@areGuestsAllowedToJoin": {
"type": "text",
@ -422,6 +427,11 @@
"type": "text",
"placeholders": {}
},
"crossSigningEnabled": "第三方登入已啟用",
"@crossSigningEnabled": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "目前活躍",
"@currentlyActive": {
"type": "text",
@ -507,6 +517,11 @@
"type": "text",
"placeholders": {}
},
"discover": "探索",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "顯示名稱已被變更",
"@displaynameHasBeenChanged": {
"type": "text",
@ -659,6 +674,11 @@
"type": "text",
"placeholders": {}
},
"friday": "星期五",
"@friday": {
"type": "text",
"placeholders": {}
},
"fromJoining": "自加入起",
"@fromJoining": {
"type": "text",
@ -844,6 +864,11 @@
"type": "text",
"placeholders": {}
},
"keysCached": "金鑰已被快取",
"@keysCached": {
"type": "text",
"placeholders": {}
},
"kicked": "{username}踢了{targetName}",
"@kicked": {
"type": "text",
@ -961,6 +986,11 @@
"type": "text",
"placeholders": {}
},
"monday": "星期一",
"@monday": {
"type": "text",
"placeholders": {}
},
"muteChat": "將該聊天室靜音",
"@muteChat": {
"type": "text",
@ -1296,6 +1326,11 @@
"type": "text",
"placeholders": {}
},
"saturday": "星期六",
"@saturday": {
"type": "text",
"placeholders": {}
},
"search": "搜尋",
"@search": {
"type": "text",
@ -1495,6 +1530,11 @@
"type": "text",
"placeholders": {}
},
"sunday": "星期日",
"@sunday": {
"type": "text",
"placeholders": {}
},
"systemTheme": "自動",
"@systemTheme": {
"type": "text",
@ -1510,6 +1550,16 @@
"type": "text",
"placeholders": {}
},
"thisRoomHasBeenArchived": "這個聊天室已被封存。",
"@thisRoomHasBeenArchived": {
"type": "text",
"placeholders": {}
},
"thursday": "星期四",
"@thursday": {
"type": "text",
"placeholders": {}
},
"title": "FluffyChat",
"@title": {
"description": "Title for the application",
@ -1546,6 +1596,11 @@
"type": "text",
"placeholders": {}
},
"tuesday": "星期二",
"@tuesday": {
"type": "text",
"placeholders": {}
},
"unavailable": "無法取得",
"@unavailable": {
"type": "text",
@ -1716,6 +1771,11 @@
"type": "text",
"placeholders": {}
},
"wednesday": "星期三",
"@wednesday": {
"type": "text",
"placeholders": {}
},
"weSentYouAnEmail": "我們向您傳送了一封電子郵件",
"@weSentYouAnEmail": {
"type": "text",
@ -1912,6 +1972,8 @@
"type": "text",
"placeholders": {}
},
"yourUserId": "您的ID",
"@yourUserId": {},
"chatHasBeenAddedToThisSpace": "聊天室已添加到此空間",
"@chatHasBeenAddedToThisSpace": {},
"clearArchive": "清除存檔",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 212 KiB

BIN
assets/start_chat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -55,7 +55,7 @@
<div class="flex mb-8 justify-center content-center">
<a rel="me"
class="inline-block text-indigo-500 no-underline hover:text-indigo-900 hover:scale-105 transition-all text-center h-auto p-4"
rel="me" href="https://mastodon.art/@krille">
rel="me" href="https://metalhead.club/@krille">
<svg class="fill-current h-6" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
@ -116,4 +116,4 @@
</body>
</html>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -291,7 +291,6 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (

View File

@ -1,5 +1,5 @@
app_identifier("im.fluffychat.app") # The bundle identifier of your app
apple_id("christian-kussowski@posteo.de") # Your Apple email address
apple_id("christian.pauly@wtal.de") # Your Apple email address
itc_team_id("122628977") # App Store Connect Team ID
team_id("4NXF6Z997G") # Developer Portal Team ID

View File

@ -16,7 +16,7 @@ abstract class AppConfig {
static const double messageFontSize = 15.75;
static const bool allowOtherHomeservers = true;
static const bool enableRegistration = true;
static const Color primaryColor = Color(0xFF5625BA);
static const Color primaryColor = Color.fromARGB(255, 135, 103, 172);
static const Color primaryColorLight = Color(0xFFCCBDEA);
static const Color secondaryColor = Color(0xFF41a2bc);
static String _privacyUrl =
@ -33,11 +33,6 @@ abstract class AppConfig {
static const String sourceCodeUrl = 'https://gitlab.com/famedly/fluffychat';
static const String supportUrl =
'https://gitlab.com/famedly/fluffychat/issues';
static final Uri newIssueUrl = Uri(
scheme: 'https',
host: 'gitlab.com',
path: '/famedly/fluffychat/-/issues/new',
);
static const bool enableSentry = true;
static const String sentryDns =
'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143';

View File

@ -9,6 +9,7 @@ import 'package:fluffychat/pages/chat_details/chat_details.dart';
import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_permissions_settings/chat_permissions_settings.dart';
import 'package:fluffychat/pages/connect/connect_page.dart';
import 'package:fluffychat/pages/device_settings/device_settings.dart';
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker.dart';
import 'package:fluffychat/pages/invitation_selection/invitation_selection.dart';
@ -26,6 +27,7 @@ import 'package:fluffychat/pages/settings_notifications/settings_notifications.d
import 'package:fluffychat/pages/settings_security/settings_security.dart';
import 'package:fluffychat/pages/settings_stories/settings_stories.dart';
import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pages/sign_up/signup.dart';
import 'package:fluffychat/pages/story/story_page.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:fluffychat/widgets/layouts/loading_view.dart';
@ -70,7 +72,7 @@ class AppRoutes {
),
VWidget(
path: ':roomid',
widget: const ChatPage(),
widget: const Chat(),
stackedRoutes: [
VWidget(
path: 'encryption',
@ -98,7 +100,7 @@ class AppRoutes {
stackedRoutes: [
VWidget(
path: ':roomid',
widget: const ChatPage(),
widget: const Chat(),
buildTransition: _dynamicTransition,
),
],
@ -172,14 +174,14 @@ class AppRoutes {
VNester(
path: ':roomid',
widgetBuilder: (child) => SideViewLayout(
mainView: const ChatPage(),
mainView: const Chat(),
sideView: child,
),
buildTransition: _fadeTransition,
nestedRoutes: [
VWidget(
path: '',
widget: const ChatPage(),
widget: const Chat(),
buildTransition: _fadeTransition,
),
VWidget(
@ -243,7 +245,7 @@ class AppRoutes {
),
VWidget(
path: ':roomid',
widget: const ChatPage(),
widget: const Chat(),
buildTransition: _dynamicTransition,
),
],
@ -264,6 +266,23 @@ class AppRoutes {
widget: const Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'connect',
widget: const ConnectPage(),
buildTransition: _fadeTransition,
stackedRoutes: [
VWidget(
path: 'login',
widget: const Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'signup',
widget: const SignupPage(),
buildTransition: _fadeTransition,
),
],
),
VWidget(
path: 'logs',
widget: const LogViewer(),
@ -339,6 +358,23 @@ class AppRoutes {
widget: const Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'connect',
widget: const ConnectPage(),
buildTransition: _fadeTransition,
stackedRoutes: [
VWidget(
path: 'login',
widget: const Login(),
buildTransition: _fadeTransition,
),
VWidget(
path: 'signup',
widget: const SignupPage(),
buildTransition: _fadeTransition,
),
],
),
],
),
VWidget(

View File

@ -9,10 +9,7 @@ import 'app_config.dart';
abstract class FluffyThemes {
static const double columnWidth = 360.0;
static const double navRailWidth = 64.0;
static bool isColumnModeByWidth(double width) =>
width > columnWidth * 2 + navRailWidth;
static bool isColumnModeByWidth(double width) => width > columnWidth * 2 + 64;
static bool isColumnMode(BuildContext context) =>
isColumnModeByWidth(MediaQuery.of(context).size.width);
@ -41,22 +38,6 @@ abstract class FluffyThemes {
titleSmall: fallbackTextStyle,
);
static LinearGradient backgroundGradient(
BuildContext context,
int alpha,
) {
final colorScheme = Theme.of(context).colorScheme;
return LinearGradient(
begin: Alignment.topCenter,
colors: [
colorScheme.primaryContainer.withAlpha(alpha),
colorScheme.secondaryContainer.withAlpha(alpha),
colorScheme.tertiaryContainer.withAlpha(alpha),
colorScheme.primaryContainer.withAlpha(alpha),
],
);
}
static const Duration animationDuration = Duration(milliseconds: 250);
static const Curve animationCurve = Curves.easeInOut;

View File

@ -3,8 +3,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart';
import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart';
@ -69,15 +68,14 @@ class AddStoryController extends State<AddStoryPage> {
}
void importMedia() async {
final picked = await FilePicker.platform.pickFiles(
type: FileType.image,
withData: true,
final picked = await FilePickerCross.importFromStorage(
type: FileTypeCross.image,
);
final file = picked?.files.firstOrNull;
if (file == null) return;
final fileName = picked.fileName;
if (fileName == null) return;
final matrixFile = MatrixImageFile(
bytes: file.bytes!,
name: file.name,
bytes: picked.toUint8List(),
name: fileName,
);
setState(() {
image = matrixFile;

View File

@ -9,7 +9,7 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:desktop_drop/desktop_drop.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:file_picker_cross/file_picker_cross.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:image_picker/image_picker.dart';
@ -23,7 +23,6 @@ import 'package:fluffychat/pages/chat/chat_view.dart';
import 'package:fluffychat/pages/chat/event_info_dialog.dart';
import 'package:fluffychat/pages/chat/recording_dialog.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/utils/error_reporter.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/ios_badge_client_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
@ -36,56 +35,25 @@ import 'send_file_dialog.dart';
import 'send_location_dialog.dart';
import 'sticker_picker_dialog.dart';
class ChatPage extends StatelessWidget {
class Chat extends StatefulWidget {
final Widget? sideView;
const ChatPage({Key? key, this.sideView}) : super(key: key);
@override
Widget build(BuildContext context) {
final roomId = context.vRouter.pathParameters['roomid'];
final room =
roomId == null ? null : Matrix.of(context).client.getRoomById(roomId);
if (room == null) {
return Scaffold(
appBar: AppBar(title: Text(L10n.of(context)!.oopsSomethingWentWrong)),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child:
Text(L10n.of(context)!.youAreNoLongerParticipatingInThisChat),
),
),
);
}
return ChatPageWithRoom(sideView: sideView, room: room);
}
}
class ChatPageWithRoom extends StatefulWidget {
final Widget? sideView;
final Room room;
const ChatPageWithRoom({
Key? key,
required this.sideView,
required this.room,
}) : super(key: key);
const Chat({Key? key, this.sideView}) : super(key: key);
@override
ChatController createState() => ChatController();
}
class ChatController extends State<ChatPageWithRoom> {
Room get room => sendingClient.getRoomById(roomId) ?? widget.room;
class ChatController extends State<Chat> {
Room? room;
late Client sendingClient;
Client? sendingClient;
Timeline? timeline;
String? readMarkerEventId;
MatrixState? matrix;
String get roomId => widget.room.id;
String? get roomId => context.vRouter.pathParameters['roomid'];
final AutoScrollController scrollController = AutoScrollController();
@ -127,7 +95,7 @@ class ChatController extends State<ChatPageWithRoom> {
useRootNavigator: false,
builder: (c) => SendFileDialog(
files: matrixFiles,
room: room,
room: room!,
),
);
}
@ -152,10 +120,7 @@ class ChatController extends State<ChatPageWithRoom> {
Event? editEvent;
bool _scrolledUp = false;
bool get showScrollDownButton =>
_scrolledUp || timeline?.allowNewEvent == false;
bool showScrollDownButton = false;
bool get selectMode => selectedEvents.isNotEmpty;
@ -165,12 +130,16 @@ class ChatController extends State<ChatPageWithRoom> {
String pendingText = '';
bool get canLoadMore =>
timeline!.events.isEmpty ||
timeline!.events.last.type != EventTypes.RoomCreate;
bool showEmojiPicker = false;
void recreateChat() async {
final room = this.room;
final userId = room.directChatMatrixID;
if (userId == null) {
final userId = room?.directChatMatrixID;
if (room == null || userId == null) {
throw Exception(
'Try to recreate a room with is not a DM room. This should not be possible from the UI!',
);
@ -192,6 +161,12 @@ class ChatController extends State<ChatPageWithRoom> {
}
void leaveChat() async {
final room = this.room;
if (room == null) {
throw Exception(
'Leave room button clicked while room is null. This should not be possible from the UI!',
);
}
final success = await showFutureLoadingDialog(
context: context,
future: room.leave,
@ -203,40 +178,19 @@ class ChatController extends State<ChatPageWithRoom> {
EmojiPickerType emojiPickerType = EmojiPickerType.keyboard;
void requestHistory() async {
if (!timeline!.canRequestHistory) return;
Logs().v('Requesting history...');
try {
await timeline!.requestHistory(historyCount: _loadHistoryCount);
} catch (err) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
(err).toLocalizedString(context),
if (canLoadMore) {
try {
await timeline!.requestHistory(historyCount: _loadHistoryCount);
} catch (err) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
(err).toLocalizedString(context),
),
),
),
);
rethrow;
}
}
void requestFuture() async {
final timeline = this.timeline;
if (timeline == null) return;
if (!timeline.canRequestFuture) return;
Logs().v('Requesting future...');
try {
final mostRecentEventId = timeline.events.first.eventId;
await timeline.requestFuture(historyCount: _loadHistoryCount);
setReadMarker(eventId: mostRecentEventId);
} catch (err) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
(err).toLocalizedString(context),
),
),
);
rethrow;
);
rethrow;
}
}
}
@ -246,11 +200,18 @@ class ChatController extends State<ChatPageWithRoom> {
}
setReadMarker();
if (!scrollController.hasClients) return;
if (timeline?.allowNewEvent == false ||
scrollController.position.pixels > 0 && _scrolledUp == false) {
setState(() => _scrolledUp = true);
} else if (scrollController.position.pixels == 0 && _scrolledUp == true) {
setState(() => _scrolledUp = false);
if (scrollController.position.pixels ==
scrollController.position.maxScrollExtent &&
timeline!.events.isNotEmpty &&
timeline!.events[timeline!.events.length - 1].type !=
EventTypes.RoomCreate) {
requestHistory();
}
if (scrollController.position.pixels > 0 && showScrollDownButton == false) {
setState(() => showScrollDownButton = true);
} else if (scrollController.position.pixels == 0 &&
showScrollDownButton == true) {
setState(() => showScrollDownButton = false);
}
}
@ -269,12 +230,6 @@ class ChatController extends State<ChatPageWithRoom> {
inputFocus.addListener(_inputFocusListener);
_loadDraft();
super.initState();
sendingClient = Matrix.of(context).client;
readMarkerEventId = room.fullyRead;
loadTimelineFuture =
_getTimeline(eventContextId: readMarkerEventId).onError(
ErrorReporter(context, 'Unable to load timeline').onErrorCallback,
);
}
void updateView() {
@ -282,84 +237,48 @@ class ChatController extends State<ChatPageWithRoom> {
setState(() {});
}
Future<void>? loadTimelineFuture;
Future<void> _getTimeline({
String? eventContextId,
Duration timeout = const Duration(seconds: 7),
}) async {
await Matrix.of(context).client.roomsLoading;
await Matrix.of(context).client.accountDataLoading;
if (eventContextId != null &&
(!eventContextId.isValidMatrixId || eventContextId.sigil != '\$')) {
eventContextId = null;
}
try {
timeline = await room
.getTimeline(
onUpdate: updateView,
eventContextId: eventContextId,
)
.timeout(timeout);
} catch (e, s) {
Logs().w('Unable to load timeline on event ID $eventContextId', e, s);
if (!mounted) return;
timeline = await room.getTimeline(onUpdate: updateView);
if (!mounted) return;
if (e is TimeoutException || e is IOException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(L10n.of(context)!.jumpToLastReadMessage),
action: SnackBarAction(
label: L10n.of(context)!.jump,
onPressed: () => scrollToEventId(eventContextId!),
),
),
);
Future<bool> getTimeline() async {
if (timeline == null) {
await Matrix.of(context).client.roomsLoading;
await Matrix.of(context).client.accountDataLoading;
timeline = await room!.getTimeline(onUpdate: updateView);
if (timeline!.events.isNotEmpty) {
if (room!.markedUnread) room!.markUnread(false);
setReadMarker();
}
// when the scroll controller is attached we want to scroll to an event id, if specified
// and update the scroll controller...which will trigger a request history, if the
// "load more" button is visible on the screen
SchedulerBinding.instance.addPostFrameCallback((_) async {
if (mounted) {
final event = VRouter.of(context).queryParameters['event'];
if (event != null) {
scrollToEventId(event);
}
_updateScrollController();
}
});
}
timeline!.requestKeys(onlineKeyBackupOnly: false);
if (timeline!.events.isNotEmpty) {
if (room.markedUnread) room.markUnread(false);
setReadMarker();
}
// when the scroll controller is attached we want to scroll to an event id, if specified
// and update the scroll controller...which will trigger a request history, if the
// "load more" button is visible on the screen
SchedulerBinding.instance.addPostFrameCallback((_) async {
if (mounted) {
final event = VRouter.of(context).queryParameters['event'];
if (event != null) {
scrollToEventId(event);
}
}
});
return;
return true;
}
Future<void>? _setReadMarkerFuture;
void setReadMarker({String? eventId}) {
if (_setReadMarkerFuture != null) return;
if (eventId == null &&
!room.hasNewMessages &&
room.notificationCount == 0) {
return;
void setReadMarker([_]) {
if (_setReadMarkerFuture == null &&
(room!.hasNewMessages || room!.notificationCount > 0) &&
timeline != null &&
timeline!.events.isNotEmpty &&
Matrix.of(context).webHasFocus) {
Logs().v('Set read marker...');
// ignore: unawaited_futures
_setReadMarkerFuture = timeline!.setReadMarker().then((_) {
_setReadMarkerFuture = null;
});
room!.client.updateIosBadge();
}
if (!Matrix.of(context).webHasFocus) return;
final timeline = this.timeline;
if (timeline == null || timeline.events.isEmpty) return;
eventId ??= timeline.events.first.eventId;
Logs().v('Set read marker...', eventId);
// ignore: unawaited_futures
_setReadMarkerFuture = timeline.setReadMarker(eventId: eventId).then((_) {
_setReadMarkerFuture = null;
});
room.client.updateIosBadge();
}
@override
@ -372,24 +291,15 @@ class ChatController extends State<ChatPageWithRoom> {
TextEditingController sendController = TextEditingController();
void setSendingClient(Client c) {
// first cancel typing with the old sending client
void setSendingClient(Client? c) {
// first cancle 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);
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);
}
@ -407,7 +317,7 @@ class ChatController extends State<ChatPageWithRoom> {
final commandMatch = RegExp(r'^\/(\w+)').firstMatch(sendController.text);
if (commandMatch != null &&
!sendingClient.commands.keys.contains(commandMatch[1]!.toLowerCase())) {
!room!.client.commands.keys.contains(commandMatch[1]!.toLowerCase())) {
final l10n = L10n.of(context)!;
final dialogResult = await showOkCancelAlertDialog(
context: context,
@ -422,7 +332,7 @@ class ChatController extends State<ChatPageWithRoom> {
}
// ignore: unawaited_futures
room.sendTextEvent(
room!.sendTextEvent(
sendController.text,
inReplyTo: replyEvent,
editEventId: editEvent?.eventId,
@ -442,49 +352,46 @@ class ChatController extends State<ChatPageWithRoom> {
}
void sendFileAction() async {
final result = await FilePicker.platform.pickFiles(
allowMultiple: true,
withData: true,
final result = await FilePickerCross.importMultipleFromStorage(
type: FileTypeCross.any,
);
if (result == null || result.files.isEmpty) return;
if (result.isEmpty) return;
await showDialog(
context: context,
useRootNavigator: false,
builder: (c) => SendFileDialog(
files: result.files
files: result
.map(
(xfile) => MatrixFile(
bytes: xfile.bytes!,
name: xfile.name,
bytes: xfile.toUint8List(),
name: xfile.fileName!,
).detectFileType,
)
.toList(),
room: room,
room: room!,
),
);
}
void sendImageAction() async {
final result = await FilePicker.platform.pickFiles(
type: FileType.image,
withData: true,
allowMultiple: true,
final result = await FilePickerCross.importMultipleFromStorage(
type: FileTypeCross.image,
);
if (result == null || result.files.isEmpty) return;
if (result.isEmpty) return;
await showDialog(
context: context,
useRootNavigator: false,
builder: (c) => SendFileDialog(
files: result.files
files: result
.map(
(xfile) => MatrixFile(
bytes: xfile.bytes!,
name: xfile.name,
bytes: xfile.toUint8List(),
name: xfile.fileName!,
).detectFileType,
)
.toList(),
room: room,
room: room!,
),
);
}
@ -505,7 +412,7 @@ class ChatController extends State<ChatPageWithRoom> {
name: file.path,
)
],
room: room,
room: room!,
),
);
}
@ -526,7 +433,7 @@ class ChatController extends State<ChatPageWithRoom> {
name: file.path,
)
],
room: room,
room: room!,
),
);
}
@ -534,7 +441,7 @@ class ChatController extends State<ChatPageWithRoom> {
void sendStickerAction() async {
final sticker = await showAdaptiveBottomSheet<ImagePackImageContent>(
context: context,
builder: (c) => StickerPickerDialog(room: room),
builder: (c) => StickerPickerDialog(room: room!),
);
if (sticker == null) return;
final eventContent = <String, dynamic>{
@ -543,7 +450,7 @@ class ChatController extends State<ChatPageWithRoom> {
'url': sticker.url.toString(),
};
// send the sticker
await room.sendEvent(
await room!.sendEvent(
eventContent,
type: EventTypes.Sticker,
);
@ -577,7 +484,7 @@ class ChatController extends State<ChatPageWithRoom> {
bytes: audioFile.readAsBytesSync(),
name: audioFile.path,
);
await room.sendFileEvent(
await room!.sendFileEvent(
file,
inReplyTo: replyEvent,
extraContent: {
@ -627,7 +534,7 @@ class ChatController extends State<ChatPageWithRoom> {
await showDialog(
context: context,
useRootNavigator: false,
builder: (c) => SendLocationDialog(room: room),
builder: (c) => SendLocationDialog(room: room!),
);
}
@ -733,7 +640,7 @@ class ChatController extends State<ChatPageWithRoom> {
if (client == null) {
return;
}
final room = client.getRoomById(roomId)!;
final room = client.getRoomById(roomId!)!;
await Event.fromJson(event.toJson(), room).redactEvent();
}
} else {
@ -749,14 +656,14 @@ class ChatController extends State<ChatPageWithRoom> {
}
List<Client?> get currentRoomBundle {
final clients = Matrix.of(context).currentBundle!;
clients.removeWhere((c) => c!.getRoomById(roomId) == null);
final clients = matrix!.currentBundle!;
clients.removeWhere((c) => c!.getRoomById(roomId!) == null);
return clients;
}
bool get canRedactSelectedEvents {
if (isArchived) return false;
final clients = Matrix.of(context).currentBundle;
final clients = matrix!.currentBundle;
for (final event in selectedEvents) {
if (event.canRedact == false &&
!(clients!.any((cl) => event.senderId == cl!.userID))) return false;
@ -811,23 +718,49 @@ class ChatController extends State<ChatPageWithRoom> {
}
void scrollToEventId(String eventId) async {
final eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
var eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
if (eventIndex == -1) {
setState(() {
timeline = null;
_scrolledUp = false;
loadTimelineFuture = _getTimeline(
eventContextId: eventId,
timeout: const Duration(seconds: 30),
).onError(
ErrorReporter(context, 'Unable to load timeline after scroll to ID')
.onErrorCallback,
);
});
await loadTimelineFuture;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
scrollToEventId(eventId);
});
// event id not found...maybe we can fetch it?
// the try...finally is here to start and close the loading dialog reliably
await showFutureLoadingDialog(
context: context,
future: () async {
// okay, we first have to fetch if the event is in the room
try {
final event = await timeline!.getEventById(eventId);
if (event == null) {
// event is null...meaning something is off
return;
}
} catch (err) {
if (err is MatrixException && err.errcode == 'M_NOT_FOUND') {
// event wasn't found, as the server gave a 404 or something
return;
}
rethrow;
}
// okay, we know that the event *is* in the room
while (eventIndex == -1) {
if (!canLoadMore) {
// we can't load any more events but still haven't found ours yet...better stop here
return;
}
try {
await timeline!.requestHistory(historyCount: _loadHistoryCount);
} catch (err) {
if (err is TimeoutException) {
// loading the history timed out...so let's do nothing
return;
}
rethrow;
}
eventIndex =
timeline!.events.indexWhere((e) => e.eventId == eventId);
}
},
);
}
if (!mounted) {
return;
}
await scrollController.scrollToIndex(
@ -837,21 +770,7 @@ class ChatController extends State<ChatPageWithRoom> {
_updateScrollController();
}
void scrollDown() async {
if (!timeline!.allowNewEvent) {
setState(() {
timeline = null;
_scrolledUp = false;
loadTimelineFuture = _getTimeline().onError(
ErrorReporter(context, 'Unable to load timeline after scroll down')
.onErrorCallback,
);
});
await loadTimelineFuture;
setReadMarker(eventId: timeline!.events.first.eventId);
}
scrollController.jumpTo(0);
}
void scrollDown() => scrollController.jumpTo(0);
void onEmojiSelected(_, Emoji? emoji) {
switch (emojiPickerType) {
@ -869,18 +788,15 @@ class ChatController extends State<ChatPageWithRoom> {
setState(() => showEmojiPicker = false);
if (emoji == null) return;
// make sure we don't send the same emoji twice
if (_allReactionEvents.any(
(e) => e.content.tryGetMap('m.relates_to')?['key'] == emoji.emoji,
)) {
return;
}
if (_allReactionEvents
.any((e) => e.content['m.relates_to']['key'] == emoji.emoji)) return;
return sendEmojiAction(emoji.emoji);
}
void forgetRoom() async {
final result = await showFutureLoadingDialog(
context: context,
future: room.forget,
future: room!.forget,
);
if (result.error != null) return;
VRouter.of(context).to('/archive');
@ -929,7 +845,7 @@ class ChatController extends State<ChatPageWithRoom> {
final events = List<Event>.from(selectedEvents);
setState(() => selectedEvents.clear());
for (final event in events) {
await room.sendReaction(
await room!.sendReaction(
event.eventId,
emoji!,
);
@ -976,7 +892,7 @@ class ChatController extends State<ChatPageWithRoom> {
useRootNavigator: false,
context: context,
title: L10n.of(context)!.goToTheNewRoom,
message: room
message: room!
.getState(EventTypes.RoomTombstone)!
.parsedTombstoneContent
.body,
@ -987,8 +903,8 @@ class ChatController extends State<ChatPageWithRoom> {
}
final result = await showFutureLoadingDialog(
context: context,
future: () => room.client.joinRoom(
room
future: () => room!.client.joinRoom(
room!
.getState(EventTypes.RoomTombstone)!
.parsedTombstoneContent
.replacementRoom,
@ -996,7 +912,7 @@ class ChatController extends State<ChatPageWithRoom> {
);
await showFutureLoadingDialog(
context: context,
future: room.leave,
future: room!.leave,
);
if (result.error == null) {
VRouter.of(context).toSegments(['rooms', result.result!]);
@ -1073,16 +989,18 @@ class ChatController extends State<ChatPageWithRoom> {
cancelLabel: L10n.of(context)!.cancel,
);
if (response == OkCancelResult.ok) {
final events = room.pinnedEventIds
final events = room!.pinnedEventIds
..removeWhere((oldEvent) => oldEvent == eventId);
showFutureLoadingDialog(
context: context,
future: () => room.setPinnedEvents(events),
future: () => room!.setPinnedEvents(events),
);
}
}
void pinEvent() {
final room = this.room;
if (room == null) return;
final pinnedEventIds = room.pinnedEventIds;
final selectedEventIds = selectedEvents.map((e) => e.eventId).toSet();
final unpin = selectedEventIds.length == 1 &&
@ -1108,7 +1026,7 @@ class ChatController extends State<ChatPageWithRoom> {
await prefs.setString('draft_$roomId', text);
});
setReadMarker();
if (text.endsWith(' ') && Matrix.of(context).hasComplexBundles) {
if (text.endsWith(' ') && matrix!.hasComplexBundles) {
final clients = currentRoomBundle;
for (final client in clients) {
final prefix = client!.sendPrefix;
@ -1127,7 +1045,7 @@ class ChatController extends State<ChatPageWithRoom> {
typingCoolDown = Timer(const Duration(seconds: 2), () {
typingCoolDown = null;
currentlyTyping = false;
room.setTyping(false);
room!.setTyping(false);
});
typingTimeout ??= Timer(const Duration(seconds: 30), () {
typingTimeout = null;
@ -1135,13 +1053,14 @@ class ChatController extends State<ChatPageWithRoom> {
});
if (!currentlyTyping) {
currentlyTyping = true;
room.setTyping(true, timeout: const Duration(seconds: 30).inMilliseconds);
room!
.setTyping(true, timeout: const Duration(seconds: 30).inMilliseconds);
}
setState(() => inputText = text);
}
bool get isArchived =>
{Membership.leave, Membership.ban}.contains(room.membership);
{Membership.leave, Membership.ban}.contains(room?.membership);
void showEventInfo([Event? event]) =>
(event ?? selectedEvents.single).showInfoDialog(context);
@ -1189,7 +1108,7 @@ class ChatController extends State<ChatPageWithRoom> {
if (success.result != null) {
final voipPlugin = Matrix.of(context).voipPlugin;
try {
await voipPlugin!.voip.inviteToCall(room.id, callType);
await voipPlugin!.voip.inviteToCall(room!.id, callType);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toLocalizedString(context))),

View File

@ -16,6 +16,9 @@ class ChatAppBarTitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
final room = controller.room;
if (room == null) {
return Container();
}
if (controller.selectedEvents.isNotEmpty) {
return Text(controller.selectedEvents.length.toString());
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
@ -47,26 +48,6 @@ class ChatEventList extends StatelessWidget {
(BuildContext context, int i) {
// Footer to display typing indicator and read receipts:
if (i == 0) {
if (controller.timeline!.isRequestingFuture) {
return const Center(
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
);
}
if (controller.timeline!.canRequestFuture) {
return Builder(
builder: (context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => controller.requestFuture(),
);
return Center(
child: IconButton(
onPressed: controller.requestFuture,
icon: const Icon(Icons.refresh_outlined),
),
);
},
);
}
return Column(
mainAxisSize: MainAxisSize.min,
children: [
@ -83,22 +64,18 @@ class ChatEventList extends StatelessWidget {
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
);
}
if (controller.timeline!.canRequestHistory) {
return Builder(
builder: (context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => controller.requestHistory(),
);
return Center(
child: IconButton(
onPressed: controller.requestHistory,
icon: const Icon(Icons.refresh_outlined),
),
);
},
if (controller.canLoadMore) {
Center(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
),
onPressed: controller.requestHistory,
child: Text(L10n.of(context)!.loadMore),
),
);
}
return const SizedBox.shrink();
return Container();
}
// The message at this index:
@ -130,14 +107,11 @@ class ChatEventList extends StatelessWidget {
selected: controller.selectedEvents
.any((e) => e.eventId == event.eventId),
timeline: controller.timeline!,
displayReadMarker:
controller.readMarkerEventId == event.eventId &&
controller.timeline?.allowNewEvent == false,
nextEvent: i < controller.timeline!.events.length
? controller.timeline!.events[i]
: null,
)
: const SizedBox.shrink(),
: Container(),
);
},
childCount: controller.timeline!.events.length + 2,

View File

@ -23,7 +23,7 @@ class ChatInputRow extends StatelessWidget {
Widget build(BuildContext context) {
if (controller.showEmojiPicker &&
controller.emojiPickerType == EmojiPickerType.reaction) {
return const SizedBox.shrink();
return Container();
}
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
@ -72,7 +72,7 @@ class ChatInputRow extends StatelessWidget {
),
),
)
: const SizedBox.shrink(),
: Container(),
]
: <Widget>[
KeyBoardShortcuts(
@ -146,7 +146,7 @@ class ChatInputRow extends StatelessWidget {
contentPadding: const EdgeInsets.all(0),
),
),
if (controller.room
if (controller.room!
.getImagePacks(ImagePackUsage.sticker)
.isNotEmpty)
PopupMenuItem<String>(
@ -215,9 +215,9 @@ class ChatInputRow extends StatelessWidget {
),
),
),
if (Matrix.of(context).isMultiAccount &&
Matrix.of(context).hasComplexBundles &&
Matrix.of(context).currentBundle!.length > 1)
if (controller.matrix!.isMultiAccount &&
controller.matrix!.hasComplexBundles &&
controller.matrix!.currentBundle!.length > 1)
Container(
height: 56,
alignment: Alignment.center,
@ -227,7 +227,7 @@ class ChatInputRow extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: InputBar(
room: controller.room,
room: controller.room!,
minLines: 1,
maxLines: 8,
autofocus: !PlatformInfos.isMobile,
@ -279,9 +279,8 @@ class _ChatAccountPicker extends StatelessWidget {
const _ChatAccountPicker(this.controller, {Key? key}) : super(key: key);
void _popupMenuButtonSelected(String mxid, BuildContext context) {
final client = Matrix.of(context)
.currentBundle!
void _popupMenuButtonSelected(String mxid) {
final client = controller.matrix!.currentBundle!
.firstWhere((cl) => cl!.userID == mxid, orElse: () => null);
if (client == null) {
Logs().w('Attempted to switch to a non-existing client $mxid');
@ -292,13 +291,14 @@ class _ChatAccountPicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
controller.matrix ??= Matrix.of(context);
final clients = controller.currentRoomBundle;
return Padding(
padding: const EdgeInsets.all(8.0),
child: FutureBuilder<Profile>(
future: controller.sendingClient.fetchOwnProfile(),
future: controller.sendingClient!.fetchOwnProfile(),
builder: (context, snapshot) => PopupMenuButton<String>(
onSelected: (mxid) => _popupMenuButtonSelected(mxid, context),
onSelected: _popupMenuButtonSelected,
itemBuilder: (BuildContext context) => clients
.map(
(client) => PopupMenuItem<String>(
@ -322,7 +322,7 @@ class _ChatAccountPicker extends StatelessWidget {
child: Avatar(
mxContent: snapshot.data?.avatarUrl,
name: snapshot.data?.displayName ??
Matrix.of(context).client.userID!.localpart,
controller.matrix!.client.userID!.localpart,
size: 20,
),
),

View File

@ -125,27 +125,43 @@ class ChatView extends StatelessWidget {
} else {
return [
if (Matrix.of(context).voipPlugin != null &&
controller.room.isDirectChat)
controller.room!.isDirectChat)
IconButton(
onPressed: controller.onPhoneButtonTap,
icon: const Icon(Icons.call_outlined),
tooltip: L10n.of(context)!.placeCall,
),
EncryptionButton(controller.room),
ChatSettingsPopupMenu(controller.room, !controller.room.isDirectChat),
EncryptionButton(controller.room!),
ChatSettingsPopupMenu(controller.room!, !controller.room!.isDirectChat),
];
}
}
@override
Widget build(BuildContext context) {
if (controller.room.membership == Membership.invite) {
controller.matrix ??= Matrix.of(context);
final client = controller.matrix!.client;
controller.sendingClient ??= client;
controller.room = controller.sendingClient!.getRoomById(controller.roomId!);
if (controller.room == null) {
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context)!.oopsSomethingWentWrong),
),
body: Center(
child: Text(L10n.of(context)!.youAreNoLongerParticipatingInThisChat),
),
);
}
if (controller.room!.membership == Membership.invite) {
showFutureLoadingDialog(
context: context,
future: () => controller.room.join(),
future: () => controller.room!.join(),
);
}
final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0;
final colorScheme = Theme.of(context).colorScheme;
return VWidgetGuard(
onSystemPop: (redirector) async {
@ -158,13 +174,13 @@ class ChatView extends StatelessWidget {
}
},
child: GestureDetector(
onTapDown: (_) => controller.setReadMarker(),
onTapDown: controller.setReadMarker,
behavior: HitTestBehavior.opaque,
child: StreamBuilder(
stream: controller.room.onUpdate.stream
stream: controller.room!.onUpdate.stream
.rateLimit(const Duration(seconds: 1)),
builder: (context, snapshot) => FutureBuilder(
future: controller.loadTimelineFuture,
builder: (context, snapshot) => FutureBuilder<bool>(
future: controller.getTimeline(),
builder: (BuildContext context, snapshot) {
return Scaffold(
appBar: AppBar(
@ -182,7 +198,7 @@ class ChatView extends StatelessWidget {
color: Theme.of(context).colorScheme.primary,
)
: UnreadRoomsBadge(
filter: (r) => r.id != controller.roomId,
filter: (r) => r.id != controller.roomId!,
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()),
),
@ -196,7 +212,6 @@ class ChatView extends StatelessWidget {
padding: const EdgeInsets.only(bottom: 56.0),
child: FloatingActionButton(
onPressed: controller.scrollDown,
heroTag: null,
mini: true,
child: const Icon(Icons.arrow_downward_outlined),
),
@ -219,9 +234,14 @@ class ChatView extends StatelessWidget {
else
Container(
decoration: BoxDecoration(
gradient: FluffyThemes.backgroundGradient(
context,
64,
gradient: LinearGradient(
begin: Alignment.topCenter,
colors: [
colorScheme.primaryContainer.withAlpha(64),
colorScheme.secondaryContainer.withAlpha(64),
colorScheme.tertiaryContainer.withAlpha(64),
colorScheme.primaryContainer.withAlpha(64),
],
),
),
),
@ -251,8 +271,8 @@ class ChatView extends StatelessWidget {
),
),
),
if (controller.room.canSendDefaultMessages &&
controller.room.membership == Membership.join)
if (controller.room!.canSendDefaultMessages &&
controller.room!.membership == Membership.join)
Container(
margin: EdgeInsets.only(
bottom: bottomSheetPadding,
@ -277,7 +297,7 @@ class ChatView extends StatelessWidget {
Brightness.light
? Colors.white
: Colors.black,
child: controller.room.isAbandonedDMRoom ==
child: controller.room?.isAbandonedDMRoom ==
true
? Row(
mainAxisAlignment:
@ -306,7 +326,7 @@ class ChatView extends StatelessWidget {
const EdgeInsets.all(16),
),
icon: const Icon(
Icons.forum_outlined,
Icons.chat_outlined,
),
onPressed:
controller.recreateChat,

View File

@ -20,9 +20,7 @@ class EncryptionButton extends StatelessWidget {
.where((s) => s.deviceLists != null),
builder: (context, snapshot) {
return FutureBuilder<EncryptionHealthState>(
future: room.encrypted
? room.calcEncryptionHealthState()
: Future.value(EncryptionHealthState.allVerified),
future: room.calcEncryptionHealthState(),
builder: (BuildContext context, snapshot) => IconButton(
tooltip: room.encrypted
? L10n.of(context)!.encrypted

View File

@ -4,12 +4,12 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:just_audio/just_audio.dart';
import 'package:matrix/matrix.dart';
import 'package:path_provider/path_provider.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/error_reporter.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import '../../../utils/matrix_sdk_extensions/event_extension.dart';
@ -132,10 +132,14 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
} else {
await audioPlayer.setAudioSource(MatrixFileAudioSource(matrixFile!));
}
audioPlayer.play().onError(
ErrorReporter(context, 'Unable to play audio message')
.onErrorCallback,
);
audioPlayer.play().catchError((e, s) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(L10n.of(context)!.oopsSomethingWentWrong),
),
);
Logs().w('Error while playing audio', e, s);
});
}
static const double buttonSize = 36;

View File

@ -36,16 +36,19 @@ class _CuteContentState extends State<CuteContent> {
return GestureDetector(
onTap: addOverlay,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.event.text,
style: const TextStyle(fontSize: 150),
),
if (label != null) Text(label)
],
child: SizedBox.square(
dimension: 300,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.event.text,
style: const TextStyle(fontSize: 150),
),
if (label != null) Text(label)
],
),
),
);
},
@ -141,26 +144,24 @@ class _CuteEventOverlayState extends State<CuteEventOverlay>
return SizedBox(
height: constraints.maxHeight,
width: constraints.maxWidth,
child: OverflowBox(
child: Stack(
alignment: Alignment.bottomLeft,
fit: StackFit.expand,
children: items
.map(
(position) => Positioned(
left: position.width * width,
bottom: (height *
.25 *
position.height *
(controller?.value ?? 0)) -
_CuteOverlayContent.size,
child: _CuteOverlayContent(
emoji: widget.emoji,
),
child: Stack(
alignment: Alignment.bottomLeft,
fit: StackFit.expand,
children: items
.map(
(position) => Positioned(
left: position.width * width,
bottom: (height *
.25 *
position.height *
(controller?.value ?? 0)) -
_CuteOverlayContent.size,
child: _CuteOverlayContent(
emoji: widget.emoji,
),
)
.toList(),
),
),
)
.toList(),
),
);
},

View File

@ -1,29 +1,32 @@
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_html/flutter_html.dart';
import 'package:flutter_html_table/flutter_html_table.dart';
import 'package:flutter_math_fork/flutter_math.dart';
import 'package:linkify/linkify.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_matrix_html/flutter_html.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../../config/app_config.dart';
import '../../../config/setting_keys.dart';
import '../../../pages/image_viewer/image_viewer.dart';
import '../../../utils/matrix_sdk_extensions/matrix_locals.dart';
import '../../../utils/url_launcher.dart';
class HtmlMessage extends StatelessWidget {
final String html;
final int? maxLines;
final Room room;
final Color textColor;
final TextStyle? defaultTextStyle;
final TextStyle? linkStyle;
final double? emoteSize;
const HtmlMessage({
Key? key,
required this.html,
this.maxLines,
required this.room,
this.textColor = Colors.black,
this.defaultTextStyle,
this.linkStyle,
this.emoteSize,
}) : super(key: key);
@override
@ -44,461 +47,118 @@ class HtmlMessage extends StatelessWidget {
'',
);
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
final linkifiedRenderHtml = linkify(
renderHtml,
options: const LinkifyOptions(humanize: false),
)
.map(
(element) {
if (element is! UrlElement ||
element.text.contains('<') ||
element.text.contains('>') ||
element.text.contains('"')) {
return element.text;
}
return '<a href="${element.url}">${element.text}</a>';
},
)
.join('')
.replaceAll('\n', '');
final linkColor = textColor.withAlpha(150);
// there is no need to pre-validate the html, as we validate it while rendering
final matrix = Matrix.of(context);
final themeData = Theme.of(context);
return Html(
data: linkifiedRenderHtml,
style: {
'*': Style(
color: textColor,
margin: Margins.all(0),
fontSize: FontSize(fontSize),
),
'a': Style(color: linkColor, textDecorationColor: linkColor),
'h1': Style(
fontSize: FontSize(fontSize * 2),
lineHeight: LineHeight.number(1.5),
fontWeight: FontWeight.w600,
),
'h2': Style(
fontSize: FontSize(fontSize * 1.75),
lineHeight: LineHeight.number(1.5),
fontWeight: FontWeight.w500,
),
'h3': Style(
fontSize: FontSize(fontSize * 1.5),
lineHeight: LineHeight.number(1.5),
),
'h4': Style(
fontSize: FontSize(fontSize * 1.25),
lineHeight: LineHeight.number(1.5),
),
'h5': Style(
fontSize: FontSize(fontSize * 1.25),
lineHeight: LineHeight.number(1.5),
),
'h6': Style(
fontSize: FontSize(fontSize),
lineHeight: LineHeight.number(1.5),
),
'blockquote': Style(
border: Border(
left: BorderSide(
width: 3,
color: textColor,
),
data: renderHtml,
defaultTextStyle: defaultTextStyle,
emoteSize: emoteSize,
linkStyle: linkStyle ??
themeData.textTheme.bodyMedium!.copyWith(
color: themeData.colorScheme.secondary,
decoration: TextDecoration.underline,
decorationColor: themeData.colorScheme.secondary,
),
padding: HtmlPaddings.only(left: 6, bottom: 0),
),
'hr': Style(
border: Border.all(color: textColor, width: 0.5),
),
'table': Style(
border: Border.all(color: textColor, width: 0.5),
),
'tr': Style(
border: Border.all(color: textColor, width: 0.5),
),
'td': Style(
border: Border.all(color: textColor, width: 0.5),
padding: HtmlPaddings.all(2),
),
'th': Style(
border: Border.all(color: textColor, width: 0.5),
),
shrinkToFit: true,
maxLines: maxLines,
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
onPillTap: (url) => UrlLauncher(context, url).launchUrl(),
getMxcUrl: (
String mxc,
double? width,
double? height, {
bool? animated = false,
}) {
final ratio = MediaQuery.of(context).devicePixelRatio;
return Uri.parse(mxc)
.getThumbnail(
matrix.client,
width: (width ?? 800) * ratio,
height: (height ?? 800) * ratio,
method: ThumbnailMethod.scale,
animated: AppConfig.autoplayImages ? animated : false,
)
.toString();
},
extensions: [
RoomPillExtension(context, room),
CodeExtension(fontSize: fontSize),
MatrixMathExtension(
style: TextStyle(fontSize: fontSize, color: textColor),
onImageTap: (String mxc) => showDialog(
context: Matrix.of(context).navigatorContext,
useRootNavigator: false,
builder: (_) => ImageViewer(
Event(
type: EventTypes.Message,
content: <String, dynamic>{
'body': mxc,
'url': mxc,
'msgtype': MessageTypes.Image,
},
senderId: room.client.userID!,
originServerTs: DateTime.now(),
eventId: 'fake_event',
room: room,
),
),
const TableHtmlExtension(),
SpoilerExtension(textColor: textColor),
const ImageExtension(),
FontColorExtension(),
],
onLinkTap: (url, _, __) => UrlLauncher(context, url).launchUrl(),
onlyRenderTheseTags: const {
...allowedHtmlTags,
// Needed to make it work properly
'body',
'html',
),
setCodeLanguage: (String key, String value) async {
await matrix.store.setItem('${SettingKeys.codeLanguage}.$key', value);
},
getCodeLanguage: (String key) async {
return await matrix.store.getItem('${SettingKeys.codeLanguage}.$key');
},
getPillInfo: (String url) async {
final identityParts = url.parseIdentifierIntoParts();
final identifier = identityParts?.primaryIdentifier;
if (identifier == null) {
return {};
}
if (identifier.sigil == '@') {
// we have a user pill
final user = room.getState('m.room.member', identifier);
if (user != null) {
return user.content;
}
// there might still be a profile...
final profile = await room.client.getProfileFromUserId(identifier);
return {
'displayname': profile.displayName,
'avatar_url': profile.avatarUrl.toString(),
};
}
if (identifier.sigil == '#') {
// we have an alias pill
for (final r in room.client.rooms) {
final state = r.getState('m.room.canonical_alias');
if (state != null &&
((state.content['alias'] is String &&
state.content['alias'] == identifier) ||
(state.content['alt_aliases'] is List &&
state.content['alt_aliases'].contains(identifier)))) {
// we have a room!
return {
'displayname':
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
'avatar_url': r.getState('m.room.avatar')?.content['url'],
};
}
}
return {};
}
if (identifier.sigil == '!') {
// we have a room ID pill
final r = room.client.getRoomById(identifier);
if (r == null) {
return {};
}
return {
'displayname':
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
'avatar_url': r.getState('m.room.avatar')?.content['url'],
};
}
return {};
},
shrinkWrap: true,
);
}
/// Keep in sync with: https://spec.matrix.org/v1.6/client-server-api/#mroommessage-msgtypes
static const Set<String> allowedHtmlTags = {
'font',
'del',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'blockquote',
'p',
'a',
'ul',
'ol',
'sup',
'sub',
'li',
'b',
'i',
'u',
'strong',
'em',
'strike',
'code',
'hr',
'br',
'div',
'table',
'thead',
'tbody',
'tr',
'th',
'td',
'caption',
'pre',
'span',
'img',
'details',
'summary',
// Not in the allowlist of the matrix spec yet but should be harmless:
'ruby',
'rp',
'rt',
};
}
class FontColorExtension extends HtmlExtension {
static const String colorAttribute = 'color';
static const String mxColorAttribute = 'data-mx-color';
static const String bgColorAttribute = 'data-mx-bg-color';
@override
Set<String> get supportedTags => {'font', 'span'};
@override
bool matches(ExtensionContext context) {
if (!supportedTags.contains(context.elementName)) return false;
return context.element?.attributes.keys.any(
{
colorAttribute,
mxColorAttribute,
bgColorAttribute,
}.contains,
) ??
false;
}
Color? hexToColor(String? hexCode) {
if (hexCode == null) return null;
if (hexCode.startsWith('#')) hexCode = hexCode.substring(1);
if (hexCode.length == 6) hexCode = 'FF$hexCode';
final colorValue = int.tryParse(hexCode, radix: 16);
return colorValue == null ? null : Color(colorValue);
}
@override
InlineSpan build(
ExtensionContext context,
) {
final colorText = context.element?.attributes[colorAttribute] ??
context.element?.attributes[mxColorAttribute];
final bgColor = context.element?.attributes[bgColorAttribute];
return TextSpan(
style: TextStyle(
color: hexToColor(colorText),
backgroundColor: hexToColor(bgColor),
),
text: context.innerHtml,
);
}
}
class ImageExtension extends HtmlExtension {
final double defaultDimension;
const ImageExtension({this.defaultDimension = 64});
@override
Set<String> get supportedTags => {'img'};
@override
InlineSpan build(ExtensionContext context) {
final mxcUrl = Uri.tryParse(context.attributes['src'] ?? '');
if (mxcUrl == null || mxcUrl.scheme != 'mxc') {
return TextSpan(text: context.attributes['alt']);
}
final width = double.tryParse(context.attributes['width'] ?? '');
final height = double.tryParse(context.attributes['height'] ?? '');
return WidgetSpan(
child: SizedBox(
width: width ?? height ?? defaultDimension,
height: height ?? width ?? defaultDimension,
child: MxcImage(
uri: mxcUrl,
width: width ?? height ?? defaultDimension,
height: height ?? width ?? defaultDimension,
cacheKey: mxcUrl.toString(),
),
),
);
}
}
class SpoilerExtension extends HtmlExtension {
final Color textColor;
const SpoilerExtension({required this.textColor});
@override
Set<String> get supportedTags => {'span'};
static const String customDataAttribute = 'data-mx-spoiler';
@override
bool matches(ExtensionContext context) {
if (context.elementName != 'span') return false;
return context.element?.attributes.containsKey(customDataAttribute) ??
false;
}
@override
InlineSpan build(ExtensionContext context) {
var obscure = true;
final children = context.inlineSpanChildren;
return WidgetSpan(
child: StatefulBuilder(
builder: (context, setState) {
return InkWell(
onTap: () => setState(() {
obscure = !obscure;
}),
child: RichText(
text: TextSpan(
style: obscure ? TextStyle(backgroundColor: textColor) : null,
children: children,
),
),
);
},
),
);
}
}
class MatrixMathExtension extends HtmlExtension {
final TextStyle? style;
MatrixMathExtension({this.style});
@override
Set<String> get supportedTags => {'div'};
@override
bool matches(ExtensionContext context) {
if (context.elementName != 'div') return false;
final mathData = context.element?.attributes['data-mx-maths'];
return mathData != null;
}
@override
InlineSpan build(ExtensionContext context) {
final data = context.element?.attributes['data-mx-maths'] ?? '';
return WidgetSpan(
child: Math.tex(
data,
textStyle: style,
onErrorFallback: (e) {
Logs().d('Flutter math parse error', e);
return Text(
data,
style: style,
);
},
),
);
}
}
class CodeExtension extends HtmlExtension {
final double fontSize;
CodeExtension({required this.fontSize});
@override
Set<String> get supportedTags => {'code'};
@override
InlineSpan build(ExtensionContext context) => WidgetSpan(
child: Material(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(4),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: HighlightView(
context.element?.text ?? '',
language: context.element?.className
.split(' ')
.singleWhereOrNull(
(className) => className.startsWith('language-'),
)
?.split('language-')
.last ??
'md',
theme: shadesOfPurpleTheme,
padding: EdgeInsets.symmetric(
horizontal: 6,
vertical: context.element?.parent?.localName == 'pre' ? 6 : 0,
),
textStyle: TextStyle(fontSize: fontSize),
),
),
),
);
}
class RoomPillExtension extends HtmlExtension {
final Room room;
final BuildContext context;
RoomPillExtension(this.context, this.room);
@override
Set<String> get supportedTags => {'a'};
@override
bool matches(ExtensionContext context) {
if (context.elementName != 'a') return false;
final userId = context.element?.attributes['href']
?.parseIdentifierIntoParts()
?.primaryIdentifier;
return userId != null;
}
static final _cachedUsers = <String, User?>{};
Future<User?> _fetchUser(String matrixId) async =>
_cachedUsers[room.id + matrixId] ??= await room.requestUser(matrixId);
@override
InlineSpan build(ExtensionContext context) {
final href = context.element?.attributes['href'];
final matrixId = href?.parseIdentifierIntoParts()?.primaryIdentifier;
if (href == null || matrixId == null) {
return TextSpan(text: context.innerHtml);
}
if (matrixId.sigil == '@') {
return WidgetSpan(
child: FutureBuilder<User?>(
future: _fetchUser(matrixId),
builder: (context, snapshot) => MatrixPill(
key: Key('user_pill_$matrixId'),
name: _cachedUsers[room.id + matrixId]?.calcDisplayname() ??
matrixId.localpart ??
matrixId,
avatar: _cachedUsers[room.id + matrixId]?.avatarUrl,
uri: href,
outerContext: this.context,
),
),
);
}
if (matrixId.sigil == '#' || matrixId.sigil == '!') {
final room = matrixId.sigil == '!'
? this.room.client.getRoomById(matrixId)
: this.room.client.getRoomByAlias(matrixId);
if (room != null) {
return WidgetSpan(
child: MatrixPill(
name: room.getLocalizedDisplayname(),
avatar: room.avatar,
uri: href,
outerContext: this.context,
),
);
}
}
return TextSpan(text: context.innerHtml);
}
}
class MatrixPill extends StatelessWidget {
final String name;
final BuildContext outerContext;
final Uri? avatar;
final String uri;
const MatrixPill({
super.key,
required this.name,
required this.outerContext,
this.avatar,
required this.uri,
});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: UrlLauncher(outerContext, uri).launchUrl,
child: Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
side: BorderSide(
color: Theme.of(outerContext).colorScheme.onPrimaryContainer,
width: 0.5,
),
),
color: Theme.of(outerContext).colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Avatar(
mxContent: avatar,
name: name,
size: 16,
),
const SizedBox(width: 6),
Text(
name,
style: TextStyle(
color: Theme.of(outerContext).colorScheme.onPrimaryContainer,
),
),
],
),
),
),
);
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart';
@ -19,7 +18,6 @@ import 'verification_request_content.dart';
class Message extends StatelessWidget {
final Event event;
final Event? nextEvent;
final bool displayReadMarker;
final void Function(Event)? onSelect;
final void Function(Event)? onAvatarTab;
final void Function(Event)? onInfoTab;
@ -32,7 +30,6 @@ class Message extends StatelessWidget {
const Message(
this.event, {
this.nextEvent,
this.displayReadMarker = false,
this.longPressSelect = false,
this.onSelect,
this.onInfoTab,
@ -57,7 +54,7 @@ class Message extends StatelessWidget {
EventTypes.CallInvite
}.contains(event.type)) {
if (event.type.startsWith('m.call.')) {
return const SizedBox.shrink();
return Container();
}
return StateMessage(event);
}
@ -306,8 +303,7 @@ class Message extends StatelessWidget {
Widget container;
if (event.hasAggregatedEvents(timeline, RelationshipTypes.reaction) ||
displayTime ||
selected ||
displayReadMarker) {
selected) {
container = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
@ -351,35 +347,6 @@ class Message extends StatelessWidget {
),
child: MessageReactions(event, timeline),
),
if (displayReadMarker)
Row(
children: [
Expanded(
child: Divider(color: Theme.of(context).colorScheme.primary),
),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).colorScheme.primary,
),
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(4),
),
margin: const EdgeInsets.all(8.0),
padding: const EdgeInsets.symmetric(
horizontal: 8,
),
child: Text(
L10n.of(context)!.readUpToHere,
style:
TextStyle(color: Theme.of(context).colorScheme.primary),
),
),
Expanded(
child: Divider(color: Theme.of(context).colorScheme.primary),
),
],
),
],
);
} else {

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix_link_text/link_text.dart';
import 'package:fluffychat/pages/chat/events/video_player.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
@ -150,10 +150,23 @@ class MessageContent extends StatelessWidget {
if (event.messageType == MessageTypes.Emote) {
html = '* $html';
}
final bigEmotes = event.onlyEmotes &&
event.numberEmotes > 0 &&
event.numberEmotes <= 10;
return HtmlMessage(
html: html,
textColor: textColor,
defaultTextStyle: TextStyle(
color: textColor,
fontSize: bigEmotes ? fontSize * 3 : fontSize,
),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
decorationColor: textColor.withAlpha(150),
),
room: event.room,
emoteSize: bigEmotes ? fontSize * 3 : fontSize * 1.5,
);
}
// else we fall through to the normal message rendering
@ -229,26 +242,25 @@ class MessageContent extends StatelessWidget {
hideReply: true,
),
builder: (context, snapshot) {
return Linkify(
return LinkText(
text: snapshot.data ??
event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
),
style: TextStyle(
textStyle: TextStyle(
color: textColor,
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration:
event.redacted ? TextDecoration.lineThrough : null,
),
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
decorationColor: textColor.withAlpha(150),
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
);
},
);

View File

@ -37,17 +37,14 @@ class MessageDownloadContent extends StatelessWidget {
color: textColor,
),
const SizedBox(width: 16),
Flexible(
child: Text(
filename,
maxLines: 1,
style: TextStyle(
color: textColor,
fontWeight: FontWeight.bold,
),
overflow: TextOverflow.ellipsis,
Text(
filename,
maxLines: 1,
style: TextStyle(
color: textColor,
fontWeight: FontWeight.bold,
),
)
),
],
),
),

View File

@ -61,7 +61,7 @@ class MessageReactions extends StatelessWidget {
final evt = allReactionEvents.firstWhereOrNull(
(e) =>
e.senderId == e.room.client.userID &&
e.content.tryGetMap('m.relates_to')?['key'] == r.key,
e.content['m.relates_to']['key'] == r.key,
);
if (evt != null) {
showFutureLoadingDialog(

View File

@ -5,6 +5,7 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import '../../../config/app_config.dart';
import 'html_message.dart';
class ReplyContent extends StatelessWidget {
final Event replyEvent;
@ -25,23 +26,47 @@ class ReplyContent extends StatelessWidget {
final displayEvent =
timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent;
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
replyBody = Text(
displayEvent.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: false,
hideReply: true,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: ownMessage
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).colorScheme.onBackground,
fontSize: fontSize,
),
);
if (AppConfig.renderHtml &&
[EventTypes.Message, EventTypes.Encrypted]
.contains(displayEvent.type) &&
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote]
.contains(displayEvent.messageType) &&
!displayEvent.redacted &&
displayEvent.content['format'] == 'org.matrix.custom.html' &&
displayEvent.content['formatted_body'] is String) {
String? html = displayEvent.content['formatted_body'];
if (displayEvent.messageType == MessageTypes.Emote) {
html = '* $html';
}
replyBody = HtmlMessage(
html: html!,
defaultTextStyle: TextStyle(
color: ownMessage
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).colorScheme.onBackground,
fontSize: fontSize,
),
maxLines: 1,
room: displayEvent.room,
emoteSize: fontSize * 1.5,
);
} else {
replyBody = Text(
displayEvent.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: false,
hideReply: true,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: ownMessage
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).colorScheme.onBackground,
fontSize: fontSize,
),
);
}
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[

View File

@ -14,7 +14,6 @@ import 'package:video_player/video_player.dart';
import 'package:fluffychat/pages/chat/events/image_bubble.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import '../../../utils/error_reporter.dart';
class EventVideoPlayer extends StatefulWidget {
final Event event;
@ -52,8 +51,7 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
final networkUri = _networkUri;
if (kIsWeb && networkUri != null && _chewieManager == null) {
_chewieManager ??= ChewieController(
videoPlayerController:
VideoPlayerController.networkUrl(Uri.parse(networkUri)),
videoPlayerController: VideoPlayerController.network(networkUri),
autoPlay: true,
autoInitialize: true,
);
@ -71,7 +69,12 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
),
);
} catch (e, s) {
ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.toLocalizedString(context)),
),
);
Logs().w('Error while playing video', e, s);
} finally {
// Workaround for Chewie needs time to get the aspectRatio
await Future.delayed(const Duration(milliseconds: 100));

View File

@ -183,13 +183,12 @@ class InputBar extends StatelessWidget {
final state = r.getState(EventTypes.RoomCanonicalAlias);
if ((state != null &&
((state.content['alias'] is String &&
state.content
.tryGet<String>('alias')!
state.content['alias']
.split(':')[0]
.toLowerCase()
.contains(roomSearch)) ||
(state.content['alt_aliases'] is List &&
(state.content['alt_aliases'] as List).any(
state.content['alt_aliases'].any(
(l) =>
l is String &&
l
@ -264,8 +263,6 @@ class InputBar extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
MxcImage(
// ensure proper ordering ...
key: ValueKey(suggestion['name']),
uri: suggestion['mxc'] is String
? Uri.parse(suggestion['mxc'] ?? '')
: null,
@ -316,7 +313,7 @@ class InputBar extends StatelessWidget {
),
);
}
return const SizedBox.shrink();
return Container();
}
void insertSuggestion(_, Map<String, String?> suggestion) {
@ -461,12 +458,11 @@ class InputBar extends StatelessWidget {
buildSuggestion(c, s, Matrix.of(context).client),
onSuggestionSelected: (Map<String, String?> suggestion) =>
insertSuggestion(context, suggestion),
errorBuilder: (BuildContext context, Object? error) =>
const SizedBox.shrink(),
loadingBuilder: (BuildContext context) => const SizedBox.shrink(),
errorBuilder: (BuildContext context, Object? error) => Container(),
loadingBuilder: (BuildContext context) => Container(),
// fix loading briefly flickering a dark box
noItemsFoundBuilder: (BuildContext context) => const SizedBox
.shrink(), // fix loading briefly showing no suggestions
noItemsFoundBuilder: (BuildContext context) =>
Container(), // fix loading briefly showing no suggestions
),
),
);

View File

@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix_link_text/link_text.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat/chat.dart';
@ -46,14 +46,14 @@ class PinnedEvents extends StatelessWidget {
@override
Widget build(BuildContext context) {
final pinnedEventIds = controller.room.pinnedEventIds;
final pinnedEventIds = controller.room!.pinnedEventIds;
if (pinnedEventIds.isEmpty) {
return const SizedBox.shrink();
return Container();
}
final completers = pinnedEventIds.map<Completer<Event?>>((e) {
final completer = Completer<Event?>();
controller.room
controller.room!
.getEventById(e)
.then((value) => completer.complete(value));
return completer;
@ -86,10 +86,11 @@ class PinnedEvents extends StatelessWidget {
color: Theme.of(context).colorScheme.onSurfaceVariant,
icon: const Icon(Icons.push_pin),
tooltip: L10n.of(context)!.unpin,
onPressed:
controller.room.canSendEvent(EventTypes.RoomPinnedEvents)
? () => controller.unpinEvent(event.eventId)
: null,
onPressed: controller.room
?.canSendEvent(EventTypes.RoomPinnedEvents) ??
false
? () => controller.unpinEvent(event.eventId)
: null,
),
Expanded(
child: Padding(
@ -101,16 +102,15 @@ class PinnedEvents extends StatelessWidget {
hideReply: true,
),
builder: (context, snapshot) {
return Linkify(
return LinkText(
text: snapshot.data ??
event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: true,
hideReply: true,
),
options: const LinkifyOptions(humanize: false),
maxLines: 2,
style: TextStyle(
textStyle: TextStyle(
color:
Theme.of(context).colorScheme.onSurfaceVariant,
overflow: TextOverflow.ellipsis,
@ -127,8 +127,8 @@ class PinnedEvents extends StatelessWidget {
decorationColor:
Theme.of(context).colorScheme.onSurfaceVariant,
),
onOpen: (url) =>
UrlLauncher(context, url.url).launchUrl(),
onLinkTap: (url) =>
UrlLauncher(context, url).launchUrl(),
);
},
),

View File

@ -15,10 +15,10 @@ class ReactionsPicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (controller.showEmojiPicker) return const SizedBox.shrink();
if (controller.showEmojiPicker) return Container();
final display = controller.editEvent == null &&
controller.replyEvent == null &&
controller.room.canSendDefaultMessages &&
controller.room!.canSendDefaultMessages &&
controller.selectedEvents.isNotEmpty;
return AnimatedContainer(
duration: FluffyThemes.animationDuration,
@ -29,7 +29,7 @@ class ReactionsPicker extends StatelessWidget {
child: Builder(
builder: (context) {
if (!display) {
return const SizedBox.shrink();
return Container();
}
final proposals = proposeEmojis(
controller.selectedEvents.first.plaintextBody,
@ -52,7 +52,7 @@ class ReactionsPicker extends StatelessWidget {
for (final event in allReactionEvents) {
try {
emojis.remove(event.content.tryGetMap('m.relates_to')!['key']);
emojis.remove(event.content['m.relates_to']['key']);
} catch (_) {}
}
return Row(

View File

@ -58,7 +58,7 @@ class _EditContent extends StatelessWidget {
Widget build(BuildContext context) {
final event = this.event;
if (event == null) {
return const SizedBox.shrink();
return Container();
}
return Row(
children: <Widget>[

View File

@ -12,7 +12,7 @@ class SeenByRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final seenByUsers = controller.room.getSeenByUsers(controller.timeline!);
final seenByUsers = controller.room!.getSeenByUsers(controller.timeline!);
const maxAvatars = 7;
return Container(
width: double.infinity,

View File

@ -39,7 +39,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
final imageKeys =
filteredImagePackImageEntried.map((e) => e.key).toList();
if (imageKeys.isEmpty) {
return const SizedBox.shrink();
return Container();
}
final packName = pack.pack.displayName ?? packSlugs[packIndex];
return Column(

View File

@ -11,8 +11,8 @@ class TombstoneDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (controller.room.getState(EventTypes.RoomTombstone) == null) {
return const SizedBox.shrink();
if (controller.room!.getState(EventTypes.RoomTombstone) == null) {
return Container();
}
return SizedBox(
height: 72,
@ -26,7 +26,7 @@ class TombstoneDisplay extends StatelessWidget {
child: const Icon(Icons.upgrade_outlined),
),
title: Text(
controller.room
controller.room!
.getState(EventTypes.RoomTombstone)!
.parsedTombstoneContent
.body,

View File

@ -12,7 +12,7 @@ class TypingIndicators extends StatelessWidget {
@override
Widget build(BuildContext context) {
final typingUsers = controller.room.typingUsers
final typingUsers = controller.room!.typingUsers
..removeWhere((u) => u.stateKey == Matrix.of(context).client.userID);
const topPadding = 20.0;
const bottomPadding = 4.0;

View File

@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart';
import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:image_picker/image_picker.dart';
@ -83,9 +82,7 @@ class ChatDetailsController extends State<ChatDetails> {
RequestType.GET,
'/client/unstable/org.matrix.msc2432/rooms/${Uri.encodeComponent(room.id)}/aliases',
)
.then(
(response) => List<String>.from(response['aliases'] as Iterable),
),
.then((response) => List<String>.from(response['aliases'])),
);
// Switch to the stable api once it is implemented.
@ -315,15 +312,12 @@ class ChatDetailsController extends State<ChatDetails> {
name: result.path,
);
} else {
final picked = await FilePicker.platform.pickFiles(
type: FileType.image,
withData: true,
);
final pickedFile = picked?.files.firstOrNull;
if (pickedFile == null) return;
final result =
await FilePickerCross.importFromStorage(type: FileTypeCross.image);
if (result.fileName == null) return;
file = MatrixFile(
bytes: pickedFile.bytes!,
name: pickedFile.name,
bytes: result.toUint8List(),
name: result.fileName!,
);
}
await showFutureLoadingDialog(

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix_link_text/link_text.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.dart';
@ -125,14 +125,13 @@ class ChatDetailsView extends StatelessWidget {
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
),
child: Linkify(
child: LinkText(
text: room.topic.isEmpty
? L10n.of(context)!.addGroupDescription
: room.topic,
options: const LinkifyOptions(humanize: false),
linkStyle:
const TextStyle(color: Colors.blueAccent),
style: TextStyle(
textStyle: TextStyle(
fontSize: 14,
color: Theme.of(context)
.textTheme
@ -143,8 +142,8 @@ class ChatDetailsView extends StatelessWidget {
.bodyMedium!
.color,
),
onOpen: (url) =>
UrlLauncher(context, url.url).launchUrl(),
onLinkTap: (url) =>
UrlLauncher(context, url).launchUrl(),
),
),
const SizedBox(height: 8),
@ -410,7 +409,7 @@ class ChatDetailsView extends StatelessWidget {
),
onTap: () => VRouter.of(context).to('invite'),
)
: const SizedBox.shrink(),
: Container(),
],
)
: i < controller.members!.length + 1

View File

@ -62,7 +62,7 @@ class ParticipantListItem extends StatelessWidget {
),
),
membershipBatch[user.membership]!.isEmpty
? const SizedBox.shrink()
? Container()
: Container(
padding: const EdgeInsets.all(4),
margin: const EdgeInsets.symmetric(horizontal: 8),

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