fluffy merge and testing updates

This commit is contained in:
ggurdin 2024-01-22 11:43:30 -05:00
commit d28ff4e997
178 changed files with 100003 additions and 111581 deletions

1
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1 @@
* @krille-chan

View file

@ -1,5 +1,5 @@
blank_issues_enabled: false
blank_issues_enabled: true
contact_links:
- name: 👬 FluffyChat Community
url: https://matrix.to/#/#fluffychat:matrix.org
about: Please ask and answer questions here.
about: Please ask and answer questions here.

View file

@ -9,3 +9,12 @@ updates:
commit-message:
prefix: "build: "
include: "scope"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-name: "*"
commit-message:
prefix: "build: "
include: "scope"

View file

@ -8,7 +8,7 @@ jobs:
code_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:
@ -24,11 +24,13 @@ jobs:
run: git apply ./scripts/enable-android-google-services.patch
- run: flutter analyze
- run: flutter test
- name: Check for unused localization strings
run: flutter pub run translations_cleaner list-unused-terms -a
build_debug_apk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: actions/setup-java@v1
with:
@ -43,7 +45,7 @@ jobs:
build_debug_web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:
@ -57,7 +59,7 @@ jobs:
build_debug_linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:
@ -71,7 +73,7 @@ jobs:
build_debug_ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:

View file

@ -10,17 +10,18 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
- uses: actions/stale@v9
with:
days-before-issue-stale: 30
days-before-issue-stale: 120
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
stale-issue-message: "This issue is stale because it has been open for 120 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
stale-pr-message: "This pull request is stale because it has been open for 30 days with no activity."
stale-pr-message: "This pull request is stale because it has been open for 120 days with no activity."
close-pr-message: "This pull request was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: 30
days-before-pr-stale: 120
days-before-pr-close: 14
exempt-milestones: true
exempt-assignees: krille-chan
repo-token: ${{ secrets.GITHUB_TOKEN }}
operations-per-run: 500
repo-token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -4,6 +4,7 @@ on:
push:
branches:
- main
workflow_dispatch:
env:
WEB_APP_ENV: ${{ vars.WEB_APP_ENV }}
@ -13,14 +14,11 @@ jobs:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install nodejs -y
- run: flutter pub get
- name: Prepare web
run: ./scripts/prepare-web.sh

View file

@ -11,7 +11,7 @@ jobs:
name: Create Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Get Changelog Entry
id: changelog_reader
uses: mindsers/changelog-reader-action@v2

View file

@ -13,7 +13,7 @@ jobs:
build_web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:
@ -25,11 +25,11 @@ jobs:
- name: Prepare web
run: ./scripts/prepare-web.sh
- name: Build Release Web
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps --base-href "/web/"
- name: Create archive
run: tar -czf pangeachat-web.tar.gz build/web/
- name: Upload Web Build
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Web Build
path: pangeachat-web.tar.gz
@ -42,11 +42,26 @@ jobs:
asset_path: pangeachat-web.tar.gz
asset_name: pangeachat-web.tar.gz
asset_content_type: application/gzip
- name: Build Website
run: |
cd docs && npx tailwindcss -o ./tailwind.css --minify && cd ..
mv docs public
mv repo public || true
mv build/web/ public/web
cp public/web -r public/nightly
- name: Deploy to GitHub Pages
if: startsWith(github.ref, 'refs/tags/v')
uses: peaceiris/actions-gh-pages@v3
with:
personal_token: ${{ secrets.PAGES_DEPLOY_TOKEN }}
publish_dir: ./public
publish_branch: gh-pages
cname: fluffychat.im
build_apk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: actions/setup-java@v1
with:
@ -82,7 +97,7 @@ jobs:
build_linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:
@ -107,7 +122,7 @@ jobs:
deploy_playstore:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: actions/setup-java@v1
with:
@ -154,3 +169,25 @@ jobs:
bundle exec fastlane deploy_release
fi
cd ..
promote_snapcraft:
runs-on: ubuntu-latest
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v2
- name: Get Tag Name
id: tag_name
run: echo "::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})"
- name: Promote Snap
env: # Workaround for https://github.com/snapcore/snapcraft/issues/4439
SNAPCRAFT_HAS_TTY: "true"
run: |
if [[ $GITHUB_REF_NAME == rc* ]]; then
yes | snapcraft promote fluffychat --from-channel edge --to-channel candidate
else
yes | snapcraft promote fluffychat --from-channel edge --to-channel stable
fi

View file

@ -1,3 +1,233 @@
## v1.17.1
Minor bugfix release.
- build: Update matrix sdk 0.25.5 (Krille)
- build: Update to flutter 3.16.7 (Krille)
- chore: Remove vibration on iOS for long press (Krille)
- design: Better paddings in tablet mode (krille-chan)
- docs: Fix typo in readme (Krille)
- Fix dependency. missing yq when invoking setup-web. also ensure updated config.json copied in (Isaac Johnson)
- fix: text nodes with multiple links crash the timeline (Krille)
- fix: URL too long when reporting bug (Krille)
- fix: Wait for user device keys before start verification (Krille)
## v1.17.0
FluffyChat v1.17.0 refreshes the overall user experience, changes some design and fixes a lot of bugs. It also replaces the stories feature with matrix presences, introduces a new kind of database to store the messages locally and improves the performance and app stability.
- change: Remove wallpaper feature (krille-chan)
- design: Adjust login page design (krille-chan)
- design: Adjust new chat page design (Krille)
- design: Adjust reply design (krille-chan)
- design: New design for login page (krille-chan)
- feat: Add registration buttons for servers with public registration url (krille-chan)
- feat: Animate in new events in timeline (krille-chan)
- feat: Block users who sent invites (krille-chan)
- feat: Display migration notification (Krille)
- feat: Hovermenu for messages for mouse (krille-chan)
- feat: New change password page with server capabilities check (krille-chan)
- feat: Search for public spaces (krille-chan)
- feat: Try out FluffyBox 2 database (Krille)
- fix: Add 3pid email for password reset (krille-chan)
- fix: Audiomessage break app (Krille)
- fix: Cannot change send on enter on desktop (krille-chan)
- fix: Darktheme contrast fixes with primary color (krille-chan)
- fix: Join public rooms (krille-chan)
- fix: Make user admin (krille-chan)
- fix: New json url for homeserver list (krille-chan)
- fix: Open notification for invite crashes app (krille-chan)
- fix: Remove web background (Krille)
- fix: Some links not clickable in messages (Krille)
- fix: Update manual endpoints (Krille)
- fix: Web SSO (Krille)
- refactor: More stable scroll to event (krille-chan)
- refactor: Reinvite other part instead of reopen dm (Krille)
- refactor: Remove todo list feature (krille-chan)
- refactor: Remove unnecessary setState in ChatPage for better performance (krille-chan)
- refactor: Remove unused code (krille-chan)
- refactor: Remove unused localization strings and add ci check (krille-chan)
- refactor: Replace stories feature with presence status msg (Krille)
- refactor: Spaces UX improvements (krille-chan)
- Translated using Weblate (Arabic) (Rex_sa)
- Translated using Weblate (Basque) (xabirequejo)
- Translated using Weblate (Chinese (Simplified)) (Eric)
- Translated using Weblate (Chinese (Simplified)) (Poesty Li)
- Translated using Weblate (Croatian) (Milo Ivir)
- Translated using Weblate (Estonian) (Priit Jõerüüt)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (German) (Christian)
- Translated using Weblate (German) (nautilusx)
- Translated using Weblate (Hindi) (immodded)
- Translated using Weblate (Italian) (Claudio Maradonna)
- Translated using Weblate (Italian) (Timothy Redaelli)
- Translated using Weblate (Portuguese (Brazil)) (Hermógenes Oliveira)
- Translated using Weblate (Russian) (v1s7)
- Translated using Weblate (Spanish) (José Muñoz)
- Translated using Weblate (Turkish) (Oğuz Ersen)
- Translated using Weblate (Ukrainian) (Ihor Hordiichuk)
## v1.16.1
Test candidate for the new database.
## v1.16.0
- build: Set olm to 1.3.2 to fix android build (krille-chan)
- build: Update dependencies (krille-chan)
- build: Update flutter_olm (Krille)
- build: Update matrix dart sdk to 0.23.0 (Krille)
- build: Update Matrix Dart SDK to 0.24.0 (Krille)
- build: Update openssl crypto (Krille)
- build: Update to flutter 3.16.2 (krille-chan)
- build: Workaround for broken flutter secure storage on linux (krille-chan)
- chore: Add error report for incorrect recovery key (Krille)
- chore: Always show notification popup on android (krille-chan)
- chore: Do not ship unused emoji font for android and iOS (krille-chan)
- chore: Fetch cached presence (Krille)
- chore: Update pubspec.lock (Krille)
- chore: upgrade flutter to 3.16.0 (lauren n. liberda)
- docs: Fix links to GitHub (Jérémie Roquet)
- feat: Display presences in the app (krille-chan)
- feat: Enable experimental suport for dehydrated devices (Krille)
- feat: Improved UX design for new chat page (krille-chan)
- feat: New UX design for create group chat (krille-chan)
- fix: Block users (krille-chan)
- fix: Blurhash crashes on height 0 (krille-chan)
- fix: Do not hide push if app romm in foreground but is in background (krille-chan)
- fix: Do not scroll up on enter chat (Krille)
- fix: emoji import from ZIP file (The one with the braid)
- fix: Encryption dialog crashes in column mode (krille-chan)
- fix: Error widget spamming with dialogs (Krille)
- fix: fcm patch (lauren n. liberda)
- fix: Glitch in event info dialog (krille-chan)
- fix: message bubble position on desktop devices (The one with the braid)
- fix: navigating back from full screen video (Aryan Arora)
- fix: Only load first pinned event (krille-chan)
- fix: Userbottomsheet crash on some edge cases (krille-chan)
- fix: whatever happens with android native libraries since flutter 3.16 (lauren n. liberda)
- refactor: Check if app is in foreground on pushhelper (krille-chan)
- refactor: Event list (krille-chan)
- refactor: Migrate for Flutter 3.16.0 (Krille)
- refactor: Remove copy dialog before opening links (krille-chan)
- Translated using Weblate (Arabic) (Rex_sa)
- Translated using Weblate (Basque) (xabirequejo)
- Translated using Weblate (Bengali) (Allan Nordhøy)
- Translated using Weblate (Bengali) (Anonymous)
- Translated using Weblate (Bengali) (Graeme Power)
- Translated using Weblate (Bengali) (Joaquim Homrighausen)
- Translated using Weblate (Bengali) (Raatty)
- Translated using Weblate (Bengali) (Sorunome)
- Translated using Weblate (Catalan) (Adolfo Jayme Barrientos)
- Translated using Weblate (Catalan) (Anonymous)
- Translated using Weblate (Catalan) (Auri B.P)
- Translated using Weblate (Catalan) (Joaquim Homrighausen)
- Translated using Weblate (Catalan) (Raatty)
- Translated using Weblate (Chinese (Simplified)) (Anonymous)
- Translated using Weblate (Chinese (Simplified)) (Eric)
- Translated using Weblate (Chinese (Traditional)) (Anonymous)
- Translated using Weblate (Chinese (Traditional)) (Joaquim Homrighausen)
- Translated using Weblate (Chinese (Traditional)) (Raatty)
- Translated using Weblate (Chinese (Traditional)) (SuperSonic)
- Translated using Weblate (Croatian) (Anonymous)
- Translated using Weblate (Czech) (Anonymous)
- Translated using Weblate (Czech) (Tomkoid)
- Translated using Weblate (Esperanto) (Anonymous)
- Translated using Weblate (Esperanto) (Joaquim Homrighausen)
- Translated using Weblate (Esperanto) (Raatty)
- Translated using Weblate (Esperanto) (Tirifto)
- Translated using Weblate (Finnish) (Anonymous)
- Translated using Weblate (French) (Anonymous)
- Translated using Weblate (French) (Mæve Rey)
- Translated using Weblate (Galician) (Anonymous)
- Translated using Weblate (Galician) (josé m)
- Translated using Weblate (German) (Bella)
- Translated using Weblate (German) (Christian)
- Translated using Weblate (Greek) (Anonymous)
- Translated using Weblate (Hebrew) (Anonymous)
- Translated using Weblate (Hebrew) (Joaquim Homrighausen)
- Translated using Weblate (Hebrew) (Raatty)
- Translated using Weblate (Hebrew) (Sorunome)
- Translated using Weblate (Hebrew) (y batvinik)
- Translated using Weblate (Hindi) (Anonymous)
- Translated using Weblate (Hungarian) (Anonymous)
- Translated using Weblate (Hungarian) (Joaquim Homrighausen)
- Translated using Weblate (Hungarian) (notramo)
- Translated using Weblate (Hungarian) (Raatty)
- Translated using Weblate (Indonesian) (Anonymous)
- Translated using Weblate (Irish) (Anonymous)
- Translated using Weblate (Irish) (Graeme Power)
- Translated using Weblate (Irish) (Joaquim Homrighausen)
- Translated using Weblate (Irish) (Raatty)
- Translated using Weblate (Italian) (Anonymous)
- Translated using Weblate (Italian) (J. Lavoie)
- Translated using Weblate (Italian) (Joaquim Homrighausen)
- Translated using Weblate (Italian) (Raatty)
- Translated using Weblate (Japanese) (Anonymous)
- Translated using Weblate (Japanese) (cPidx)
- Translated using Weblate (Korean) (Anonymous)
- Translated using Weblate (Korean) (Kim Tae Kyeong)
- Translated using Weblate (Korean) (Raatty)
- Translated using Weblate (Latvian) (Anonymous)
- Translated using Weblate (Lithuanian) (Anonymous)
- Translated using Weblate (Lithuanian) (Mind)
- Translated using Weblate (Norwegian Bokmål) (Allan Nordhøy)
- Translated using Weblate (Norwegian Bokmål) (Anonymous)
- Translated using Weblate (Norwegian Bokmål) (Joaquim Homrighausen)
- Translated using Weblate (Norwegian Bokmål) (Raatty)
- Translated using Weblate (Occidental) (Anonymous)
- Translated using Weblate (Occidental) (OIS)
- Translated using Weblate (Persian) (Anonymous)
- Translated using Weblate (Polish) (Anonymous)
- Translated using Weblate (Portuguese (Brazil)) (Anonymous)
- Translated using Weblate (Portuguese (Portugal)) (Anonymous)
- Translated using Weblate (Portuguese (Portugal)) (Joaquim Homrighausen)
- Translated using Weblate (Portuguese (Portugal)) (Raatty)
- Translated using Weblate (Portuguese (Portugal)) (Tmpod)
- Translated using Weblate (Portuguese) (Allan Nordhøy)
- Translated using Weblate (Portuguese) (Anonymous)
- Translated using Weblate (Portuguese) (Christian)
- Translated using Weblate (Portuguese) (Graeme Power)
- Translated using Weblate (Portuguese) (Joaquim Homrighausen)
- Translated using Weblate (Portuguese) (Raatty)
- Translated using Weblate (Portuguese) (Sorunome)
- Translated using Weblate (Romanian) (Anonymous)
- Translated using Weblate (Russian) (Anonymous)
- Translated using Weblate (Serbian) (Anonymous)
- Translated using Weblate (Serbian) (Joaquim Homrighausen)
- Translated using Weblate (Serbian) (Raatty)
- Translated using Weblate (Serbian) (Слободан Симић(Slobodan Simić))
- Translated using Weblate (Slovak) (Allan Nordhøy)
- Translated using Weblate (Slovak) (Anonymous)
- Translated using Weblate (Slovak) (Graeme Power)
- Translated using Weblate (Slovak) (Joaquim Homrighausen)
- Translated using Weblate (Slovak) (Raatty)
- Translated using Weblate (Slovenian) (Anonymous)
- Translated using Weblate (Slovenian) (Joaquim Homrighausen)
- Translated using Weblate (Slovenian) (Raatty)
- Translated using Weblate (Spanish) (Anonymous)
- Translated using Weblate (Spanish) (Joaquim Homrighausen)
- Translated using Weblate (Spanish) (José Muñoz)
- Translated using Weblate (Spanish) (Mæve Rey)
- Translated using Weblate (Spanish) (programmerpony)
- Translated using Weblate (Spanish) (Raatty)
- Translated using Weblate (Swedish) (Anonymous)
- Translated using Weblate (Swedish) (Dennis)
- Translated using Weblate (Swedish) (Fredrik Lindqvist)
- Translated using Weblate (Swedish) (paintwithblue)
- Translated using Weblate (Tamil) (Anonymous)
- Translated using Weblate (Tamil) (Graeme Power)
- Translated using Weblate (Tamil) (Joaquim Homrighausen)
- Translated using Weblate (Tamil) (Raatty)
- Translated using Weblate (Tamil) (Sorunome)
- Translated using Weblate (Thai) (Anonymous)
- Translated using Weblate (Tibetan) (Anonymous)
- Translated using Weblate (Turkish) (Anonymous)
- Translated using Weblate (Turkish) (Yourredyknowwhoitisss)
- Translated using Weblate (Vietnamese) (Allan Nordhøy)
- Translated using Weblate (Vietnamese) (Anonymous)
- Translated using Weblate (Vietnamese) (Christian)
- Translated using Weblate (Vietnamese) (Graeme Power)
- Translated using Weblate (Vietnamese) (Joaquim Homrighausen)
- Translated using Weblate (Vietnamese) (Raatty)
- Translated using Weblate (Vietnamese) (Sorunome)
## v1.15.1
- feat: Make all text in chat selectable on desktop (krille-chan)
- chore: Add border to images in timeline (krille-chan)

View file

@ -1,8 +1,15 @@
FROM ghcr.io/cirruslabs/flutter as builder
RUN sudo apt update && sudo apt install curl jq -y
RUN sudo apt update && sudo apt install curl wget jq -y
WORKDIR /tmp
RUN wget https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64.tar.gz
RUN tar -xzvf ./yq_linux_amd64.tar.gz
RUN mv yq_linux_amd64 /usr/bin/yq
COPY . /app
WORKDIR /app
RUN ./scripts/prepare-web.sh
COPY config.* /app/
RUN flutter pub get
RUN flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps

View file

@ -54,7 +54,7 @@ Please visit the [Wiki](https://github.com/krille-chan/fluffychat/wiki) for buil
# Special thanks
* <a href="https://github.com/fabiyamada">Fabiyamada</a> is a graphics designer from Brasil and has made the fluffychat logo and the banner. Big thanks for her great designs.
* <a href="https://github.com/fabiyamada">Fabiyamada</a> is a graphics designer and has made the fluffychat logo and the banner. Big thanks for her great designs.
* <a href="https://github.com/advocatux">Advocatux</a> has made the Spanish translation with great love and care. He always stands by my side and supports my work with great commitment.

View file

@ -45,7 +45,7 @@ android {
defaultConfig {
applicationId "com.talktolearn.chat"
minSdkVersion 19
minSdkVersion 21
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

1
android/app/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1 @@
-keep class net.sqlcipher.** { *; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View file

@ -1,185 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="210mm" height="297mm" viewBox="0 0 210 297" version="1.1" id="svg8" inkscape:version="0.92.3 (d244b95, 2018-08-02)" sodipodi:docname="1.svg">
<defs id="defs2"/>
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.5" inkscape:cx="675" inkscape:cy="548.18772" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="640" inkscape:window-height="748" inkscape:window-x="640" inkscape:window-y="24" inkscape:window-maximized="0"/>
<metadata id="metadata5">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1">
<g id="g3735" transform="matrix(1.3863783,0,0,1.3863783,53.572287,59.067903)">
<path id="path3715" d="m 12.7,30.7 c 4,5.3 1.9,-0.3 7.3,-0.3 5.4,0 8,-5.1 7.9,-10.4 C 27.8,14.7 16.2,7.7 11.1,9.4 c -3.2,1.2 -1.6,17 1.6,21.3 z" class="st0" inkscape:connector-curvature="0" style="fill:#878787"/>
<path id="path3717" d="m 14.6,25.1 c 2.3,3 9.3,-1.9 9.3,-5 0,-3.1 -6.7,-7.1 -9.6,-6 -1.9,0.6 -1.6,8.5 0.3,11 z" class="st1" inkscape:connector-curvature="0" style="fill:#ffffff"/>
<path id="path3719" d="m 61,30.6 c -3.9,5.3 -1.9,-0.2 -7.3,-0.2 -5.4,0 -8,-5 -8,-10.4 0,-5.4 11.7,-12.4 16.7,-10.7 3.3,1.1 1.8,17 -1.4,21.3 z" class="st0" inkscape:connector-curvature="0" style="fill:#878787"/>
<path id="path3721" d="m 59.1,25 c -2.3,3.1 -9.3,-1.8 -9.3,-4.9 0,-3.1 6.7,-7.1 9.6,-6.1 1.8,0.6 1.5,8.5 -0.3,11 z" class="st1" inkscape:connector-curvature="0" style="fill:#ffffff"/>
<g id="g3725">
<path id="path3723" d="m 30.7,60.8 c 0.2,2.1 2.6,3.6 4.9,2.1 0.5,-0.3 0.8,-0.9 0.9,-1.4 0.1,-0.3 0.4,-0.6 0.8,-0.6 0.4,0 0.7,0.2 0.8,0.6 0.1,0.5 0.3,0.9 0.7,1.2 2.3,1.8 4.9,0.3 5.1,-1.9 0,-0.3 0.2,-0.6 0.5,-0.7 0.3,-0.1 0.6,0 0.8,0.2 0.6,0.6 1.3,1 2.1,1 0,0 0,0 0,0 1,0 2,-0.5 2.6,-1.5 0.4,-0.6 0.5,-1.2 0.4,-2 0,-0.3 0.1,-0.6 0.3,-0.8 0.2,-0.2 0.6,-0.2 0.8,0 0.6,0.3 1.3,0.5 2,0.3 1.1,-0.2 2,-1.1 2.3,-2.1 0.2,-0.7 0.1,-1.4 -0.2,-2.1 -0.1,-0.3 -0.1,-0.6 0.1,-0.8 0.2,-0.2 0.5,-0.3 0.8,-0.3 0.7,0.1 1.3,0.1 1.9,-0.2 0.9,-0.4 1.5,-1.2 1.7,-2.2 0.2,-1 -0.1,-1.9 -0.8,-2.6 -0.2,-0.2 -0.3,-0.5 -0.2,-0.8 0.1,-0.3 0.4,-0.5 0.7,-0.5 0.7,0 1.5,-0.2 2,-0.8 1.7,-1.8 0.9,-4.2 -1,-5 -0.3,-0.1 -0.5,-0.4 -0.5,-0.8 0,-0.3 0.3,-0.6 0.6,-0.7 0.6,-0.1 1.1,-0.4 1.5,-0.8 1.6,-2.1 0.3,-4.7 -1.8,-5 -0.3,0 -0.6,-0.3 -0.6,-0.6 -0.1,-0.3 0,-0.6 0.3,-0.8 0.8,-0.6 1.2,-1.4 1.2,-2.3 0,-0.3 -0.1,-0.7 -0.3,-1 -1.3,-2 -2.1,-2.2 -3,-2.1 -0.3,0 -0.6,-0.1 -0.7,-0.4 -0.2,-0.3 -0.1,-0.6 0,-0.8 0.3,-0.5 0.5,-1 0.5,-1.6 0,-0.7 -0.2,-1.4 -0.6,-1.9 -0.6,-0.7 -1.4,-1.1 -2.3,-1.2 -0.4,0 -0.8,0.1 -1.2,0.2 -0.3,0.1 -0.6,0.1 -0.8,-0.2 -0.2,-0.2 -0.3,-0.5 -0.2,-0.8 0.2,-0.6 0.2,-1.2 0,-1.8 -0.4,-1.1 -1.3,-1.9 -2.5,-2.1 -0.8,-0.1 -1.6,0.1 -2.3,0.7 -0.2,0.2 -0.6,0.2 -0.8,0.1 -0.3,-0.1 -0.4,-0.4 -0.4,-0.7 0.1,-1.2 -0.5,-2.2 -1.4,-2.7 -1.8,-0.9 -3.5,0 -4.2,1.3 -0.2,0.3 -0.5,0.5 -0.8,0.4 -0.3,0 -0.6,-0.3 -0.6,-0.6 -0.2,-1 -0.8,-1.8 -1.7,-2.2 -1.9,-0.8 -3.9,0.3 -4.2,2.1 -0.1,0.3 -0.3,0.6 -0.6,0.6 C 33,14.3 32.7,14.1 32.5,13.8 32,13.1 31,12.5 30,12.5 c -1.6,0 -3,1.3 -3.1,2.9 0,0 0,0.1 0,0.1 v 0.1 c 0,0.3 -0.2,0.6 -0.4,0.7 -0.3,0.1 -0.6,0.1 -0.8,-0.1 -0.6,-0.5 -1.4,-0.7 -2.3,-0.6 -1.1,0.1 -2.1,1 -2.4,2.1 -0.2,0.6 -0.2,1.2 0,1.8 0.1,0.3 0,0.6 -0.2,0.8 -0.2,0.2 -0.5,0.3 -0.8,0.2 -0.6,-0.2 -1.2,-0.3 -1.8,-0.1 -1.1,0.3 -2,1.2 -2.2,2.3 -0.2,0.8 0,1.6 0.5,2.3 0.2,0.2 0.2,0.6 0,0.8 -0.2,0.3 -0.4,0.4 -0.7,0.4 h -0.1 c 0,0 -0.1,0 -0.1,0 -1.6,0 -3,1.3 -3.1,2.9 0,1 0.5,2 1.3,2.6 0.3,0.2 0.4,0.5 0.3,0.8 -0.1,0.3 -0.4,0.6 -0.7,0.6 -0.5,0 -1,0.2 -1.4,0.5 -2.1,2 -0.9,4.8 1.2,5.3 0.3,0.1 0.6,0.4 0.6,0.7 0,0.3 -0.2,0.7 -0.5,0.8 -0.4,0.2 -0.8,0.4 -1.1,0.8 -1.6,2.3 0,4.9 2.2,4.9 0.3,0 0.6,0.2 0.7,0.5 0.1,0.3 0.1,0.6 -0.2,0.8 -0.6,0.6 -0.9,1.4 -0.8,2.2 0.1,1.1 0.8,2.1 1.9,2.6 0.6,0.3 1.2,0.3 1.8,0.2 0.3,-0.1 0.6,0 0.8,0.3 0.2,0.2 0.2,0.6 0.1,0.8 -0.3,0.6 -0.4,1.3 -0.2,2 0.3,1.1 1.1,1.9 2.2,2.2 1,0.2 1.7,-0.1 2.2,-0.3 0.2,-0.1 -1.3,3.6 -2.4,6.1 -0.3,0.8 0.5,1.7 1.4,1.3 3.2,-1.7 8.8,-4.2 8.8,-4 z" class="st0" inkscape:connector-curvature="0" style="fill:#878787"/>
</g>
<g id="g3733">
<circle id="circle3727" r="3.7" cy="38.299999" cx="24.9" class="st1" style="fill:#ffffff"/>
<path id="path3729" d="m 40.7,38.3 c 0,2.1 -1.7,3.7 -3.7,3.7 -2.1,0 -3.7,-1.7 -3.7,-3.7 0,-2 7.4,-2.1 7.4,0 z" class="st1" inkscape:connector-curvature="0" style="fill:#ffffff"/>
<circle id="circle3731" r="3.7" cy="38.299999" cx="49" class="st1" style="fill:#ffffff"/>
</g>
</g>
<g transform="matrix(0.20394939,0,0,0.20394939,53.147489,17.386415)" id="layer1-3">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
<g transform="matrix(0.14331597,0,0,0.14331597,115.93632,6.4245667)" id="layer1-7" inkscape:label="Layer 1">
<g style="display:inline" id="g4845-5" transform="matrix(0,-1,-1,0,373.50506,516.50504)">
<g inkscape:label="Layer 1" id="g4778-3" transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" inkscape:export-filename="next01.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90">
<g style="display:inline" id="g4780-5" transform="matrix(-1,0,0,1,575.99999,611)">
<rect transform="scale(-1,1)" y="345.36221" x="-438.00244" height="96" width="96.037987" id="rect4782-6" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"/>
<path inkscape:connector-curvature="0" id="path4170" d="m 429.99929,393.33773 c 0,0.004 -9.5e-4,0.007 -0.007,0.009 -0.001,0.007 -0.006,0.007 -0.009,0.009 -0.003,0.006 -0.008,0.008 -0.011,0.009 -0.003,0.006 -0.003,0.006 -0.007,0.009 -10.86428,5.06033 -21.9364,8.97057 -28.72087,11.19382 -0.006,7.31383 -0.28348,19.36 -1.77427,30.84126 -0.004,10e-4 -0.006,0.002 -0.009,-0.002 -0.007,10e-4 -0.01,-0.004 -0.0129,-0.007 -0.007,-10e-4 -0.009,-0.004 -0.011,-0.007 -0.007,-10e-4 -0.006,-0.002 -0.009,-0.004 -8.17196,-8.76466 -15.31373,-18.08322 -19.52571,-23.8462 -6.9601,2.25521 -18.50812,5.71446 -29.89293,7.84517 -0.002,-0.003 -0.003,-0.004 0,-0.009 -0.003,-0.006 10e-4,-0.01 0.002,-0.0129 -0.001,-0.007 10e-4,-0.011 0.002,-0.0129 -0.001,-0.007 -0.001,-0.008 0,-0.011 5.81401,-10.47791 12.47128,-20.14765 16.65275,-25.93264 -4.29657,-5.92015 -11.1555,-15.83137 -16.70025,-25.99521 0.002,-0.003 0.006,-0.003 0.0111,-0.002 0.004,-0.004 0.01,-0.002 0.0129,-0.002 0.006,-0.003 0.011,-0.002 0.0129,-0.002 0.006,-0.003 0.007,-0.004 0.0111,-0.004 11.76574,2.28943 23.02338,5.63169 29.81955,7.81927 4.30503,-5.9142 11.61263,-15.49511 19.56888,-23.90661 0.003,10e-4 0.006,0.003 0.007,0.009 0.006,0.003 0.006,0.008 0.007,0.011 0.004,0.004 0.006,0.0112 0.007,0.0129 0.004,0.004 0.007,0.004 0.009,0.009 1.4575,11.89244 1.75546,23.62651 1.77428,30.76358 6.95749,2.26527 18.3316,6.25356 28.7921,11.21756 z" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"/>
</g>
</g>
</g>
</g>
<g transform="matrix(-0.05275813,-0.13325181,0.13325181,-0.05275813,6.9661783,108.94066)" id="layer1-7-2" inkscape:label="Layer 1">
<g style="display:inline" id="g4845-5-9" transform="matrix(0,-1,-1,0,373.50506,516.50504)">
<g inkscape:label="Layer 1" id="g4778-3-1" transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" inkscape:export-filename="next01.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90">
<g style="display:inline" id="g4780-5-2" transform="matrix(-1,0,0,1,575.99999,611)">
<rect transform="scale(-1,1)" y="345.36221" x="-438.00244" height="96" width="96.037987" id="rect4782-6-7" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"/>
<path inkscape:connector-curvature="0" id="path4170-0" d="m 429.99929,393.33773 c 0,0.004 -9.5e-4,0.007 -0.007,0.009 -0.001,0.007 -0.006,0.007 -0.009,0.009 -0.003,0.006 -0.008,0.008 -0.011,0.009 -0.003,0.006 -0.003,0.006 -0.007,0.009 -10.86428,5.06033 -21.9364,8.97057 -28.72087,11.19382 -0.006,7.31383 -0.28348,19.36 -1.77427,30.84126 -0.004,10e-4 -0.006,0.002 -0.009,-0.002 -0.007,10e-4 -0.01,-0.004 -0.0129,-0.007 -0.007,-10e-4 -0.009,-0.004 -0.011,-0.007 -0.007,-10e-4 -0.006,-0.002 -0.009,-0.004 -8.17196,-8.76466 -15.31373,-18.08322 -19.52571,-23.8462 -6.9601,2.25521 -18.50812,5.71446 -29.89293,7.84517 -0.002,-0.003 -0.003,-0.004 0,-0.009 -0.003,-0.006 10e-4,-0.01 0.002,-0.0129 -0.001,-0.007 10e-4,-0.011 0.002,-0.0129 -0.001,-0.007 -0.001,-0.008 0,-0.011 5.81401,-10.47791 12.47128,-20.14765 16.65275,-25.93264 -4.29657,-5.92015 -11.1555,-15.83137 -16.70025,-25.99521 0.002,-0.003 0.006,-0.003 0.0111,-0.002 0.004,-0.004 0.01,-0.002 0.0129,-0.002 0.006,-0.003 0.011,-0.002 0.0129,-0.002 0.006,-0.003 0.007,-0.004 0.0111,-0.004 11.76574,2.28943 23.02338,5.63169 29.81955,7.81927 4.30503,-5.9142 11.61263,-15.49511 19.56888,-23.90661 0.003,10e-4 0.006,0.003 0.007,0.009 0.006,0.003 0.006,0.008 0.007,0.011 0.004,0.004 0.006,0.0112 0.007,0.0129 0.004,0.004 0.007,0.004 0.009,0.009 1.4575,11.89244 1.75546,23.62651 1.77428,30.76358 6.95749,2.26527 18.3316,6.25356 28.7921,11.21756 z" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"/>
</g>
</g>
</g>
</g>
<g transform="matrix(0.11335991,-0.05676641,0.05676641,0.11335991,131.32884,182.57734)" id="layer1-3-9">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845-3">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778-6">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780-0">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782-62"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849-6" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
<g transform="matrix(-0.08526503,-0.09382307,0.09382307,-0.08526503,162.91459,100.60822)" id="layer1-3-1">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845-8">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778-7">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780-9">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782-2"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849-0" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
<g transform="matrix(0.10698289,0.06802614,-0.06802614,0.10698289,41.950743,171.57375)" id="layer1-3-2">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845-37">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778-5">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780-92">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782-28"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849-9" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
<g transform="matrix(0.21497396,0,0,0.21497395,186.37278,111.09076)" id="layer1-7-7" inkscape:label="Layer 1">
<g style="display:inline" id="g4845-5-3" transform="matrix(0,-1,-1,0,373.50506,516.50504)">
<g inkscape:label="Layer 1" id="g4778-3-6" transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" inkscape:export-filename="next01.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90">
<g style="display:inline" id="g4780-5-1" transform="matrix(-1,0,0,1,575.99999,611)">
<rect transform="scale(-1,1)" y="345.36221" x="-438.00244" height="96" width="96.037987" id="rect4782-6-2" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"/>
<path inkscape:connector-curvature="0" id="path4170-9" d="m 429.99929,393.33773 c 0,0.004 -9.5e-4,0.007 -0.007,0.009 -0.001,0.007 -0.006,0.007 -0.009,0.009 -0.003,0.006 -0.008,0.008 -0.011,0.009 -0.003,0.006 -0.003,0.006 -0.007,0.009 -10.86428,5.06033 -21.9364,8.97057 -28.72087,11.19382 -0.006,7.31383 -0.28348,19.36 -1.77427,30.84126 -0.004,10e-4 -0.006,0.002 -0.009,-0.002 -0.007,10e-4 -0.01,-0.004 -0.0129,-0.007 -0.007,-10e-4 -0.009,-0.004 -0.011,-0.007 -0.007,-10e-4 -0.006,-0.002 -0.009,-0.004 -8.17196,-8.76466 -15.31373,-18.08322 -19.52571,-23.8462 -6.9601,2.25521 -18.50812,5.71446 -29.89293,7.84517 -0.002,-0.003 -0.003,-0.004 0,-0.009 -0.003,-0.006 10e-4,-0.01 0.002,-0.0129 -0.001,-0.007 10e-4,-0.011 0.002,-0.0129 -0.001,-0.007 -0.001,-0.008 0,-0.011 5.81401,-10.47791 12.47128,-20.14765 16.65275,-25.93264 -4.29657,-5.92015 -11.1555,-15.83137 -16.70025,-25.99521 0.002,-0.003 0.006,-0.003 0.0111,-0.002 0.004,-0.004 0.01,-0.002 0.0129,-0.002 0.006,-0.003 0.011,-0.002 0.0129,-0.002 0.006,-0.003 0.007,-0.004 0.0111,-0.004 11.76574,2.28943 23.02338,5.63169 29.81955,7.81927 4.30503,-5.9142 11.61263,-15.49511 19.56888,-23.90661 0.003,10e-4 0.006,0.003 0.007,0.009 0.006,0.003 0.006,0.008 0.007,0.011 0.004,0.004 0.006,0.0112 0.007,0.0129 0.004,0.004 0.007,0.004 0.009,0.009 1.4575,11.89244 1.75546,23.62651 1.77428,30.76358 6.95749,2.26527 18.3316,6.25356 28.7921,11.21756 z" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"/>
</g>
</g>
</g>
</g>
<g transform="matrix(-0.08041856,0.09800893,-0.09800893,-0.08041856,139.91992,284.24876)" id="layer1-3-1-3">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845-8-1">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778-7-9">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780-9-4">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782-2-7"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849-0-8" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
<g transform="matrix(0.01456664,-0.07599603,0.07599603,0.01456664,80.480787,182.36304)" id="layer1-7-2-4" inkscape:label="Layer 1">
<g style="display:inline" id="g4845-5-9-5" transform="matrix(0,-1,-1,0,373.50506,516.50504)">
<g inkscape:label="Layer 1" id="g4778-3-1-0" transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" inkscape:export-filename="next01.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90">
<g style="display:inline" id="g4780-5-2-3" transform="matrix(-1,0,0,1,575.99999,611)">
<rect transform="scale(-1,1)" y="345.36221" x="-438.00244" height="96" width="96.037987" id="rect4782-6-7-6" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"/>
<path inkscape:connector-curvature="0" id="path4170-0-1" d="m 429.99929,393.33773 c 0,0.004 -9.5e-4,0.007 -0.007,0.009 -0.001,0.007 -0.006,0.007 -0.009,0.009 -0.003,0.006 -0.008,0.008 -0.011,0.009 -0.003,0.006 -0.003,0.006 -0.007,0.009 -10.86428,5.06033 -21.9364,8.97057 -28.72087,11.19382 -0.006,7.31383 -0.28348,19.36 -1.77427,30.84126 -0.004,10e-4 -0.006,0.002 -0.009,-0.002 -0.007,10e-4 -0.01,-0.004 -0.0129,-0.007 -0.007,-10e-4 -0.009,-0.004 -0.011,-0.007 -0.007,-10e-4 -0.006,-0.002 -0.009,-0.004 -8.17196,-8.76466 -15.31373,-18.08322 -19.52571,-23.8462 -6.9601,2.25521 -18.50812,5.71446 -29.89293,7.84517 -0.002,-0.003 -0.003,-0.004 0,-0.009 -0.003,-0.006 10e-4,-0.01 0.002,-0.0129 -0.001,-0.007 10e-4,-0.011 0.002,-0.0129 -0.001,-0.007 -0.001,-0.008 0,-0.011 5.81401,-10.47791 12.47128,-20.14765 16.65275,-25.93264 -4.29657,-5.92015 -11.1555,-15.83137 -16.70025,-25.99521 0.002,-0.003 0.006,-0.003 0.0111,-0.002 0.004,-0.004 0.01,-0.002 0.0129,-0.002 0.006,-0.003 0.011,-0.002 0.0129,-0.002 0.006,-0.003 0.007,-0.004 0.0111,-0.004 11.76574,2.28943 23.02338,5.63169 29.81955,7.81927 4.30503,-5.9142 11.61263,-15.49511 19.56888,-23.90661 0.003,10e-4 0.006,0.003 0.007,0.009 0.006,0.003 0.006,0.008 0.007,0.011 0.004,0.004 0.006,0.0112 0.007,0.0129 0.004,0.004 0.007,0.004 0.009,0.009 1.4575,11.89244 1.75546,23.62651 1.77428,30.76358 6.95749,2.26527 18.3316,6.25356 28.7921,11.21756 z" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"/>
</g>
</g>
</g>
</g>
<g transform="matrix(-0.12765378,0.07360069,-0.07360069,-0.12765378,81.653111,258.78327)" id="layer1-7-2-0" inkscape:label="Layer 1">
<g style="display:inline" id="g4845-5-9-6" transform="matrix(0,-1,-1,0,373.50506,516.50504)">
<g inkscape:label="Layer 1" id="g4778-3-1-3" transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" inkscape:export-filename="next01.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90">
<g style="display:inline" id="g4780-5-2-2" transform="matrix(-1,0,0,1,575.99999,611)">
<rect transform="scale(-1,1)" y="345.36221" x="-438.00244" height="96" width="96.037987" id="rect4782-6-7-0" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"/>
<path inkscape:connector-curvature="0" id="path4170-0-6" d="m 429.99929,393.33773 c 0,0.004 -9.5e-4,0.007 -0.007,0.009 -0.001,0.007 -0.006,0.007 -0.009,0.009 -0.003,0.006 -0.008,0.008 -0.011,0.009 -0.003,0.006 -0.003,0.006 -0.007,0.009 -10.86428,5.06033 -21.9364,8.97057 -28.72087,11.19382 -0.006,7.31383 -0.28348,19.36 -1.77427,30.84126 -0.004,10e-4 -0.006,0.002 -0.009,-0.002 -0.007,10e-4 -0.01,-0.004 -0.0129,-0.007 -0.007,-10e-4 -0.009,-0.004 -0.011,-0.007 -0.007,-10e-4 -0.006,-0.002 -0.009,-0.004 -8.17196,-8.76466 -15.31373,-18.08322 -19.52571,-23.8462 -6.9601,2.25521 -18.50812,5.71446 -29.89293,7.84517 -0.002,-0.003 -0.003,-0.004 0,-0.009 -0.003,-0.006 10e-4,-0.01 0.002,-0.0129 -0.001,-0.007 10e-4,-0.011 0.002,-0.0129 -0.001,-0.007 -0.001,-0.008 0,-0.011 5.81401,-10.47791 12.47128,-20.14765 16.65275,-25.93264 -4.29657,-5.92015 -11.1555,-15.83137 -16.70025,-25.99521 0.002,-0.003 0.006,-0.003 0.0111,-0.002 0.004,-0.004 0.01,-0.002 0.0129,-0.002 0.006,-0.003 0.011,-0.002 0.0129,-0.002 0.006,-0.003 0.007,-0.004 0.0111,-0.004 11.76574,2.28943 23.02338,5.63169 29.81955,7.81927 4.30503,-5.9142 11.61263,-15.49511 19.56888,-23.90661 0.003,10e-4 0.006,0.003 0.007,0.009 0.006,0.003 0.006,0.008 0.007,0.011 0.004,0.004 0.006,0.0112 0.007,0.0129 0.004,0.004 0.007,0.004 0.009,0.009 1.4575,11.89244 1.75546,23.62651 1.77428,30.76358 6.95749,2.26527 18.3316,6.25356 28.7921,11.21756 z" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"/>
</g>
</g>
</g>
</g>
<g transform="matrix(-0.12725507,-0.14002765,0.14002765,-0.12725507,1.4536,290.74091)" id="layer1-3-1-1">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845-8-5">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778-7-5">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780-9-47">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782-2-6"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849-0-5" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
<g transform="matrix(-0.04494106,-0.11350812,0.11350812,-0.04494106,164.40006,247.77949)" id="layer1-7-2-6" inkscape:label="Layer 1">
<g style="display:inline" id="g4845-5-9-9" transform="matrix(0,-1,-1,0,373.50506,516.50504)">
<g inkscape:label="Layer 1" id="g4778-3-1-37" transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)" inkscape:export-filename="next01.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90">
<g style="display:inline" id="g4780-5-2-4" transform="matrix(-1,0,0,1,575.99999,611)">
<rect transform="scale(-1,1)" y="345.36221" x="-438.00244" height="96" width="96.037987" id="rect4782-6-7-5" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"/>
<path inkscape:connector-curvature="0" id="path4170-0-2" d="m 429.99929,393.33773 c 0,0.004 -9.5e-4,0.007 -0.007,0.009 -0.001,0.007 -0.006,0.007 -0.009,0.009 -0.003,0.006 -0.008,0.008 -0.011,0.009 -0.003,0.006 -0.003,0.006 -0.007,0.009 -10.86428,5.06033 -21.9364,8.97057 -28.72087,11.19382 -0.006,7.31383 -0.28348,19.36 -1.77427,30.84126 -0.004,10e-4 -0.006,0.002 -0.009,-0.002 -0.007,10e-4 -0.01,-0.004 -0.0129,-0.007 -0.007,-10e-4 -0.009,-0.004 -0.011,-0.007 -0.007,-10e-4 -0.006,-0.002 -0.009,-0.004 -8.17196,-8.76466 -15.31373,-18.08322 -19.52571,-23.8462 -6.9601,2.25521 -18.50812,5.71446 -29.89293,7.84517 -0.002,-0.003 -0.003,-0.004 0,-0.009 -0.003,-0.006 10e-4,-0.01 0.002,-0.0129 -0.001,-0.007 10e-4,-0.011 0.002,-0.0129 -0.001,-0.007 -0.001,-0.008 0,-0.011 5.81401,-10.47791 12.47128,-20.14765 16.65275,-25.93264 -4.29657,-5.92015 -11.1555,-15.83137 -16.70025,-25.99521 0.002,-0.003 0.006,-0.003 0.0111,-0.002 0.004,-0.004 0.01,-0.002 0.0129,-0.002 0.006,-0.003 0.011,-0.002 0.0129,-0.002 0.006,-0.003 0.007,-0.004 0.0111,-0.004 11.76574,2.28943 23.02338,5.63169 29.81955,7.81927 4.30503,-5.9142 11.61263,-15.49511 19.56888,-23.90661 0.003,10e-4 0.006,0.003 0.007,0.009 0.006,0.003 0.006,0.008 0.007,0.011 0.004,0.004 0.006,0.0112 0.007,0.0129 0.004,0.004 0.007,0.004 0.009,0.009 1.4575,11.89244 1.75546,23.62651 1.77428,30.76358 6.95749,2.26527 18.3316,6.25356 28.7921,11.21756 z" style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"/>
</g>
</g>
</g>
</g>
<g transform="matrix(0.11335991,-0.05676641,0.05676641,0.11335991,164.93091,31.23567)" id="layer1-3-9-5">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845-3-4">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778-6-7">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780-0-4">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782-62-4"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849-6-3" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
<g transform="matrix(0.07328708,0.04660033,-0.04660033,0.07328708,202.61287,11.89896)" id="layer1-3-2-0">
<g transform="matrix(0,-1,-1,0,373.51,516.51)" id="g4845-37-7">
<g transform="matrix(-0.9996,0,0,1,575.94,-611)" id="g4778-5-8">
<g transform="matrix(-1,0,0,1,576,611)" id="g4780-92-6">
<rect x="-438" y="345.35999" width="96.038002" height="96" transform="scale(-1,1)" style="color:#000000;fill:none" id="rect4782-28-8"/>
<path d="m 430,411.93 c 0,11.675 -9.3424,21.174 -20.961,21.429 -5.3915,0 -9.5995,-1.413 -13.487,-2.8571 -16.066,-6.7837 -29.485,-20 -45.584,-37.143 16.098,-17.143 29.518,-30.359 45.584,-37.143 3.888,-1.4441 8.096,-2.8571 13.487,-2.8571 11.618,0.25447 20.961,9.7538 20.961,21.429 0,7.9422 -4.3266,14.87 -10.748,18.571 6.4217,3.7014 10.748,10.629 10.748,18.571 z" style="color:#000000;fill:#808080" id="path4849-9-8" inkscape:connector-curvature="0"/>
</g>
</g>
</g>
</g>
</g>
<style id="style3713" type="text/css">
.st0{fill:#878787;}
.st1{fill:#FFFFFF;}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

View file

@ -1,168 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
x="0px"
y="0px"
viewBox="0 0 640 640"
xml:space="preserve"
id="svg24"
sodipodi:docname="info-logo.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14, custom)"
width="640"
height="640"
inkscape:export-filename="info-logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29" /><sodipodi:namedview
id="namedview26"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
inkscape:zoom="1.4142136"
inkscape:cx="235.113"
inkscape:cy="375.4737"
inkscape:window-width="1920"
inkscape:window-height="1012"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg24" />
<style
type="text/css"
id="style2">
.st0{fill:url(#SVGID_1_);}
.st1{fill:#F094BE;}
.st2{fill:#4D3F92;}
.st3{fill:#FFFFFF;}
</style>
<text
xml:space="preserve"
style="font-size:26.6667px;line-height:1.25;font-family:'Noto Sans Linear B';-inkscape-font-specification:'Noto Sans Linear B';display:none;fill:#ff0000;fill-opacity:0.993491"
x="94.643097"
y="633.18518"
id="text455"
inkscape:label="Note: I do not know the original font used...."
sodipodi:insensitive="true"><tspan
sodipodi:role="line"
id="tspan453"
x="0"
y="0"
style="font-size:26.6667px;fill:#ff0000;fill-opacity:0.993491">Note: I do not know the original font used....</tspan></text><g
id="Capa_2"
transform="matrix(1.6168892,0,0,1.6168895,172.88889,94.00966)"
style="image-rendering:auto">
<g
id="g21"
transform="translate(9.4535881e-5,0.46581846)">
<path
class="st2"
d="m 151.6,95.1 c 1.5,-0.3 2.8,-1 3.8,-2 4,-5.3 0.8,-11.8 -4.5,-12.6 -0.8,0 -1.5,-0.8 -1.5,-1.5 0,-0.3 0,-0.5 0,-0.5 0.8,-0.8 1.5,-1.8 2.5,-3.3 8.1,-10.8 11.8,-50.6 3.8,-53.7 -9.8,-3.3 -29.7,6.3 -38.3,17.4 -0.5,-0.3 -1,-1 -1,-1.8 0.3,-3 -1.3,-5.5 -3.5,-6.8 -4.5,-2.3 -8.8,0 -10.6,3.3 -0.5,0.8 -1.3,1.3 -2,1 -0.8,0 -1.5,-0.8 -1.5,-1.5 -0.5,-2.5 -2,-4.5 -4.3,-5.5 -4.8,-2 -9.8,0.8 -10.6,5.3 -0.3,0.8 -0.8,1.5 -1.5,1.5 -0.8,0.3 -1.5,-0.3 -2,-1 -1.5,-2.3 -4,-3.8 -6.5,-3.8 -4,0 -7.6,3.3 -7.8,7.3 v 0.3 0.3 c 0,0.8 -0.5,1.5 -1,1.8 H 64.8 C 56.5,28.5 36.3,18.6 26.3,21.9 c -8.1,2.8 -4.3,42.6 4,53.4 1.5,2 2.8,3.5 3.8,4.5 -0.3,0.8 -1,1.5 -1.8,1.5 -1.3,0 -2.5,0.5 -3.5,1.3 -5.3,5 -2.3,12.1 3,13.4 0.8,0.3 1.5,1 1.5,1.8 0,0.8 -0.5,1.8 -1.3,2 -1,0.5 -2,1 -2.8,2 -4,5.8 0,12.3 5.5,12.3 0.8,0 1.5,0.5 1.8,1.3 0.3,0.8 0.3,1.5 -0.5,2 -1.5,1.5 -2.3,3.5 -2,5.5 0.3,2.8 2,5.3 4.8,6.5 1.5,0.8 3,0.8 4.5,0.5 0.8,-0.3 1.5,0 2,0.8 0.5,0.5 0.5,1.5 0.3,2 -0.8,1.5 -1,3.3 -0.5,5 0.8,2.8 2.8,4.8 5.5,5.5 2.5,0.5 4.3,-0.3 5.5,-0.8 0.5,-0.3 -3.3,9.1 -6,15.4 -0.8,2 1.3,4.3 3.5,3.3 8.3,-3.8 22.2,-10.3 22.2,-9.8 0.5,5.3 6.5,9.1 12.3,5.3 1.3,-0.8 2,-2.3 2.3,-3.5 0.3,-0.8 1,-1.5 2,-1.5 1,0 1.8,0.5 2,1.5 0.3,1.3 0.8,2.3 1.8,3 5.8,4.5 12.3,0.8 12.8,-4.8 0,-0.8 0.5,-1.5 1.3,-1.8 0.8,-0.3 1.5,0 2,0.5 1.5,1.5 3.3,2.5 5.3,2.5 v 0 c 2.5,0 5,-1.3 6.5,-3.8 1,-1.5 1.3,-3 1,-5 0,-0.8 0.3,-1.5 0.8,-2 0.5,-0.5 1.5,-0.5 2,0 1.5,0.8 3.3,1.3 5,0.8 2.8,-0.5 5,-2.8 5.8,-5.3 0.5,-1.8 0.3,-3.5 -0.5,-5.3 -0.3,-0.8 -0.3,-1.5 0.3,-2 0.6,-0.5 1.3,-0.8 2,-0.8 1.8,0.3 3.3,0.3 4.8,-0.5 2.3,-1 3.8,-3 4.3,-5.5 0.5,-2.5 -0.3,-4.8 -2,-6.5 -0.5,-0.5 -0.8,-1.3 -0.5,-2 0.3,-0.7 1,-1.3 1.8,-1.3 1.8,0 3.8,-0.5 5,-2 4.3,-4.5 2.3,-10.6 -2.5,-12.6 -0.8,-0.3 -1.3,-1 -1.3,-2 0,-0.9 0.7,-1.6 1.5,-1.6 z"
id="path7"
style="stroke:#ffffff;stroke-width:4.32930058;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill" />
<path
class="st3"
d="m 131.4,42.2 c 0.5,1.5 0.5,3 0,4.5 -0.3,0.8 0,1.5 0.5,2 0.5,0.5 1.3,0.8 2,0.5 1,-0.5 2,-0.5 3,-0.5 2.3,0 4.3,1 5.8,3 1,1.3 1.8,3 1.5,4.8 0,1.5 -0.5,2.8 -1.3,4 -0.5,0.5 -0.5,1.5 0,2 0.3,0.3 0.5,0.8 1,0.8 1,-0.3 2,-1 2.8,-2 4.5,-6.3 5.3,-26.2 0.8,-27.7 -4.5,-1.5 -12.3,1.5 -17.9,6 1.1,0.5 1.6,1.3 1.8,2.6 z"
id="path9" />
<path
class="st3"
d="m 39,63.6 c 0.3,-0.3 0.5,-0.5 0.8,-0.8 0.5,-0.8 0.3,-1.5 0,-2 C 38.5,59 38.2,57 38.5,55 39,52.2 41.3,50 44,49.2 c 1.5,-0.5 3,-0.3 4.5,0.3 0.8,0.3 1.5,0 2,-0.5 0.5,-0.5 0.8,-1.3 0.5,-2 -0.5,-1.5 -0.5,-3 0,-4.5 0.3,-1 0.8,-2 1.5,-2.8 -5.5,-4.5 -13.9,-7.8 -18.4,-6.3 -4.5,1.5 -3.7,21.4 0.9,27.7 1,1.5 2.2,2.2 4,2.5 z"
id="path11" />
<g
id="g19">
<circle
class="st3"
cx="60.900002"
cy="94.599998"
r="9.3000002"
id="circle13" />
<path
class="st3"
d="m 100.7,94.6 c 0,5.3 -4.3,9.3 -9.3,9.3 -5.3,0 -9.3,-4.3 -9.3,-9.3 0,-5 18.6,-5.3 18.6,0 z"
id="path15" />
<circle
class="st3"
cx="121.6"
cy="94.599998"
r="9.3000002"
id="circle17" />
</g>
</g>
</g><g
id="g2720"
inkscape:label="fluffychat"
style="display:inline;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
transform="matrix(0.92025338,0,0,0.92025338,-6.3220161,-4.7055202)"><g
id="g14535"
inkscape:label="chat"
style="fill:#00a1bc;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"><path
style="color:#000000;fill:#00a1bc;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 562.92197,448.4866 c -2.60917,-0.30486 -5.17384,1.64339 -5.69858,4.21245 -0.55728,2.72842 -0.24323,7.91189 -0.24323,7.91189 -2.74579,-0.0686 -5.55869,-0.59559 -7.95219,1.32171 -2.65609,2.12765 -2.58626,7.00643 0,8.95134 2.34163,1.76095 7.95219,1.25707 7.95219,1.25707 0,10.01284 3.8e-4,19.77116 0,29.784 0.49843,4.92696 4.90461,8.87339 9.77862,9.22679 3.37489,0.31796 6.92487,0.1972 10.13895,-0.94458 2.75868,-1.13315 4.655,-4.36744 3.42462,-7.26639 -0.79803,-2.84864 -4.22476,-3.70476 -6.73165,-2.71773 -2.98388,1.72425 -4.84172,-1.67013 -4.47089,-4.25546 -0.0315,-8.02703 0.014,-16.05409 0.003,-24.08114 2.70412,-0.0577 5.42586,0.12654 8.11662,-0.11491 4.83581,-0.90643 5.26253,-8.45876 0.98226,-10.6606 -1.74161,-0.89591 -5.46982,-0.20326 -9.09888,-0.20326 0,0 0.006,-8.17372 -0.31554,-8.93716 -0.95528,-2.26675 -3.55257,-3.48402 -5.8856,-3.48402 z"
id="path14479"
sodipodi:nodetypes="sscssscccsscccscsss"
inkscape:label="t" /><path
style="color:#000000;fill:#00a1bc;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 533.83203,458.7646 c -3.15141,-0.34296 -5.3587,2.78921 -5.64507,5.62849 -4.36454,-3.26144 -9.61516,-5.8459 -15.20756,-5.3963 -6.1831,0.04 -12.19324,2.65617 -16.52963,7.04459 -4.78443,4.13385 -6.10787,10.65878 -6.45154,16.67045 -0.74696,9.79875 2.9723,20.42469 11.3567,26.07195 4.72285,3.21814 10.72707,3.94029 16.29823,3.34602 3.96818,-0.47405 7.23684,-2.99092 10.0626,-5.63903 0.94876,3.35182 4.62364,5.93313 8.09234,4.68427 2.67575,-0.73754 4.24888,-3.52866 3.90033,-6.20541 -0.023,-13.74703 0.0461,-27.49708 -0.0348,-41.24221 -0.22956,-2.88413 -2.96011,-5.18257 -5.84164,-4.96282 z m -5.64507,23.94723 c 1.07828,6.5283 -1.60114,14.12719 -7.86545,17.06103 -4.79574,2.30687 -11.28382,1.71783 -14.76373,-2.60852 -4.1706,-4.99711 -5.43623,-12.34122 -2.74467,-18.33861 1.20342,-3.17686 3.3771,-6.03449 6.51321,-7.48705 5.19868,-2.91069 11.95962,-0.98785 15.64553,3.45474 1.89114,2.17769 3.29829,4.97591 3.21511,7.91841 z"
id="path12957"
sodipodi:nodetypes="ccsssscccscccscc"
inkscape:label="a" /><path
style="color:#000000;fill:#00a1bc;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 443.06641,439.94141 c -3.26433,-0.0465 -6.38452,2.89404 -5.90305,6.25001 0.0229,19.9673 -0.0459,39.93753 0.0344,59.903 0.62565,4.36445 6.57701,6.42025 9.82803,3.46417 2.47654,-1.86092 1.63462,-5.09581 1.78481,-7.74763 0.014,-6.16519 -0.16754,-10.96992 0,-17.13031 0.10617,-3.90402 0.32092,-9.9351 3.79481,-12.41759 3.66729,-2.85819 10.09275,-3.39125 13.27607,-0.31533 3.28466,3.17383 3.05437,10.54267 3.05369,11.74336 -0.005,8.0496 -0.0355,16.10183 0.0353,24.14882 0.80701,3.40737 5.32389,3.39114 8.08366,2.86381 3.60962,-0.47168 4.05352,-4.29795 3.6902,-7.21157 -0.059,-9.29553 0.25875,-18.60594 -0.25343,-27.89111 -0.49072,-5.78736 -2.03801,-10.89373 -6.23092,-13.58338 -5.57775,-3.57799 -14.24081,-3.55982 -19.88013,0 l -5.56919,3.51556 c -0.0217,-6.95923 0.0434,-13.9211 -0.0327,-20.87868 -0.34574,-2.71871 -3.05656,-4.71379 -5.71157,-4.71313 z"
id="path12939"
sodipodi:nodetypes="sscssscssssscascccs"
inkscape:label="h" /><path
style="color:#000000;fill:#00a1bc;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 391.91477,465.80108 c -7.57016,7.75562 -8.50788,19.94372 -5.30926,29.86701 3.38934,9.70476 13.24407,16.98333 23.65486,16.44674 6.17898,0.0435 12.93496,-1.24727 17.5401,-5.62829 2.78827,-3.46147 -0.34158,-10.49924 -5.13073,-8.5498 -3.84026,1.83312 -7.88892,3.96797 -12.31147,3.2528 -7.29184,-0.63102 -13.62087,-6.9993 -13.71248,-14.4019 -0.47868,-6.96567 3.61497,-14.86735 10.88009,-16.32616 4.0948,-0.89565 8.1361,0.43964 11.84821,2.0877 2.74473,1.20129 6.92733,2.38997 8.54085,-1.1092 2.05726,-3.93759 -1.06922,-8.39249 -4.75218,-9.94292 -7.03297,-3.7132 -15.61905,-3.33367 -23.00158,-0.92478 -3.03694,1.2244 -5.80603,3.05639 -8.24641,5.2288 z"
id="path12912"
sodipodi:nodetypes="ssssscccssssssccccss"
inkscape:label="c" /></g><g
id="g14529"
inkscape:label="fluffy"
style="fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"><path
style="color:#000000;fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 364.1013,507.69269 c -0.28866,4.09909 -1.36926,8.95002 -5.67816,10.59919 -4.72351,1.73746 -10.372,1.86184 -14.85523,-0.654 -3.7123,-2.44424 -9.3586,0.29378 -8.95093,4.93619 0.18108,4.54251 4.91635,6.57172 8.83075,6.98673 8.42045,1.69401 18.29318,1.4336 25.17485,-4.32187 4.05818,-3.36451 7.25406,-8.26315 6.89092,-13.72643 0.55108,-15.0706 0.12618,-30.15744 0.2469,-45.233 0,-5.07272 -4.90962,-8.01177 -8.99688,-5.70693 -3.44378,2.45795 -2.39927,9.5583 -2.66313,14.77473 -0.0893,6.25566 0.19851,12.54437 -0.16132,18.7791 -1.9182,7.73184 -13.50136,9.71442 -18.10525,3.34532 -2.59461,-4.37765 -1.10335,-9.66951 -1.51487,-14.47456 -0.1096,-6.36844 0.2575,-12.78073 -0.2057,-19.12097 -1.55752,-5.57454 -11.54277,-5.31263 -11.57028,0.97725 -0.0947,10.73576 -0.20144,21.49831 0.0772,32.23681 0.40229,5.90824 3.9844,11.55506 9.57033,13.81024 7.19201,3.59272 15.68253,1.22319 21.91078,-3.2078 z"
id="path8185"
sodipodi:nodetypes="sasssccccccszssssss"
inkscape:label="y" /><use
x="0"
y="0"
xlink:href="#path8140"
id="use8146-8"
transform="translate(160.70024)"
inkscape:label="f"
style="fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" /><use
x="0"
y="0"
xlink:href="#path8140"
id="use8146"
transform="translate(120.79317)"
inkscape:label="f"
style="fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" /><path
style="color:#000000;fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 203.09961,459.81323 c -3.4821,0.005 -6.38987,3.50283 -5.7894,6.9306 0.0673,9.26978 -0.15327,18.54633 0.13261,27.81164 0.35452,5.09017 2.57678,10.21523 6.82972,13.22644 5.55736,4.38774 13.10608,4.7518 19.86263,3.9702 6.13229,-0.79837 11.54541,-4.96558 14.38597,-10.38016 2.82972,-5.25148 2.04801,-11.37621 2.1897,-17.10493 -0.10216,-6.59361 0.13071,-13.20167 -0.0996,-19.78723 -0.72775,-3.82018 -5.84877,-6.11481 -8.99816,-3.58329 -2.51578,1.5391 -2.9212,4.6479 -2.62583,7.32275 -0.008,7.68886 0.16168,15.38634 -0.0559,23.06981 -0.48698,3.93482 -2.69157,8.39569 -6.95923,9.17239 -3.94192,0.90809 -9.00029,0.30019 -11.2182,-3.51487 -2.44119,-4.33438 -1.38965,-9.47747 -1.63329,-14.21621 -0.005,-5.96309 0.0606,-11.92636 -0.006,-17.88931 -0.48316,-2.81114 -3.21405,-4.945 -6.01521,-5.02783 z"
id="path1096"
inkscape:label="u"
sodipodi:nodetypes="sssssssssssasss" /><path
style="color:#000000;fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 178.07031,440.24903 c -2.94273,-0.099 -6.12137,2.26392 -5.66929,5.43981 0.006,20.3714 -0.0127,40.74317 0.01,61.11433 0.23114,3.95816 5.38988,5.36394 8.48062,3.72849 2.17789,-0.83721 3.06822,-3.17125 2.82068,-5.34227 0,-20.07299 0,-40.14599 0,-60.21898 0.0267,-2.89989 -3.02815,-4.71312 -5.64156,-4.72138 z"
id="path1226"
inkscape:label="l"
sodipodi:nodetypes="sssssss" /><path
style="color:#000000;fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:7.6066007;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 139.73461,448.98277 c -1.14826,3.54622 -0.82212,7.38974 -0.94471,11.07629 -3.09968,0.1617 -6.7129,-0.69772 -9.15473,1.75098 -2.7547,2.92023 -1.92459,9.01668 2.44338,9.87866 2.20538,0.45339 4.47409,0.19142 6.71135,0.26152 0.0152,11.5529 -0.0304,23.10713 0.0229,34.65919 -0.10894,3.56452 3.75176,4.97291 6.73777,4.56755 3.00221,0.16258 5.93165,-2.21911 5.51966,-5.39349 0,-11.27775 0,-22.5555 0,-33.83325 3.22434,-0.0869 6.4758,0.20412 9.67787,-0.21167 4.22512,-0.5039 5.63076,-6.354 2.95775,-9.24029 -2.71637,-2.28723 -6.44022,-1.38284 -9.70112,-1.5715 -0.97817,0 -1.95633,0 -2.9345,0 -0.005,-2.94202 -0.4927,-6.38548 1.50629,-8.82582 2.27205,-1.97081 5.68412,-1.89124 8.14223,-0.31788 3.12241,1.35659 5.95844,-2.03832 6.28128,-4.89774 0.55889,-3.56544 -2.59547,-6.6544 -6.01774,-6.93035 -7.34549,-1.08714 -15.56841,1.21331 -20.11043,7.37767 -0.40614,0.53074 -0.78524,1.08209 -1.13728,1.65013 z"
id="path8140"
inkscape:label="f"
sodipodi:nodetypes="scszscszscszscsszcs" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 15 KiB

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -35,11 +35,6 @@
"type": "text",
"placeholders": {}
},
"addGroupDescription": "Eine Beschreibung für die Gruppe hinzufügen",
"@addGroupDescription": {
"type": "text",
"placeholders": {}
},
"admin": "Admin",
"@admin": {
"type": "text",
@ -299,11 +294,6 @@
"type": "text",
"placeholders": {}
},
"changeWallpaper": "Hintergrund ändern",
"@changeWallpaper": {
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "Deinen Avatar ändern",
"@changeYourAvatar": {
"type": "text",
@ -344,11 +334,6 @@
"type": "text",
"placeholders": {}
},
"chooseAUsername": "Wähle einen Benutzernamen",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"clearArchive": "Archiv leeren",
"@clearArchive": {},
"close": "Schließen",
@ -356,7 +341,7 @@
"type": "text",
"placeholders": {}
},
"commandHint_ban": "Verbanne den übergebenen Benutzer aus diesen Raum",
"commandHint_ban": "Banne ausgewählten Benutzer aus diesen Raum",
"@commandHint_ban": {
"type": "text",
"description": "Usage hint for the command /ban"
@ -371,7 +356,7 @@
"type": "text",
"description": "Usage hint for the command /invite"
},
"commandHint_join": "Betrete den übergebenen Raum",
"commandHint_join": "Betrete den ausgewählten Raum",
"@commandHint_join": {
"type": "text",
"description": "Usage hint for the command /join"
@ -524,11 +509,6 @@
"username": {}
}
},
"createNewGroup": "Neue Gruppe erstellen",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
"createNewSpace": "Neuer Space",
"@createNewSpace": {
"type": "text",
@ -594,11 +574,6 @@
"type": "text",
"placeholders": {}
},
"deny": "Ablehnen",
"@deny": {
"type": "text",
"placeholders": {}
},
"device": "Gerät",
"@device": {
"type": "text",
@ -639,11 +614,6 @@
"type": "text",
"placeholders": {}
},
"editChatPermissions": "Chatberechtigungen bearbeiten",
"@editChatPermissions": {
"type": "text",
"placeholders": {}
},
"editDisplayname": "Anzeigename ändern",
"@editDisplayname": {
"type": "text",
@ -731,18 +701,11 @@
"senderName": {}
}
},
"enterAGroupName": "Gib einen Gruppennamen ein",
"@enterAGroupName": {
"type": "text",
"placeholders": {}
},
"enterAnEmailAddress": "Gib eine E-Mail-Adresse ein",
"@enterAnEmailAddress": {
"type": "text",
"placeholders": {}
},
"enterASpacepName": "Namen für den Space eingeben",
"@enterASpacepName": {},
"enterYourHomeserver": "Gib Deinen Homeserver ein",
"@enterYourHomeserver": {
"type": "text",
@ -805,16 +768,6 @@
"type": "text",
"placeholders": {}
},
"groupDescription": "Gruppenbeschreibung",
"@groupDescription": {
"type": "text",
"placeholders": {}
},
"groupDescriptionHasBeenChanged": "Gruppenbeschreibung wurde geändert",
"@groupDescriptionHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"groupIsPublic": "Öffentliche Gruppe",
"@groupIsPublic": {
"type": "text",
@ -890,16 +843,6 @@
"type": "text",
"placeholders": {}
},
"ignoreListDescription": "Du kannst störende Personen ignorieren. Du bist dann nicht mehr in der Lage, Nachrichten oder Raumeinladungen von diesen zu erhalten.",
"@ignoreListDescription": {
"type": "text",
"placeholders": {}
},
"ignoreUsername": "Ignoriere Benutzername",
"@ignoreUsername": {
"type": "text",
"placeholders": {}
},
"iHaveClickedOnLink": "Ich habe den Link angeklickt",
"@iHaveClickedOnLink": {
"type": "text",
@ -1003,11 +946,6 @@
"localizedTimeShort": {}
}
},
"lastSeenLongTimeAgo": "Vor sehr langer Zeit gesehen",
"@lastSeenLongTimeAgo": {
"type": "text",
"placeholders": {}
},
"leave": "Verlassen",
"@leave": {
"type": "text",
@ -1067,23 +1005,11 @@
"homeserver": {}
}
},
"loginWith": "Anmelden mit",
"@loginWith": {
"type": "text",
"placeholders": {
"brand": {}
}
},
"logout": "Abmelden",
"@logout": {
"type": "text",
"placeholders": {}
},
"makeSureTheIdentifierIsValid": "Gib bitte einen richtigen Benutzernamen ein",
"@makeSureTheIdentifierIsValid": {
"type": "text",
"placeholders": {}
},
"memberChanges": "Änderungen der Mitglieder",
"@memberChanges": {
"type": "text",
@ -1099,11 +1025,6 @@
"type": "text",
"placeholders": {}
},
"messageWillBeRemovedWarning": "Nachricht wird für alle Mitglieder entfernt",
"@messageWillBeRemovedWarning": {
"type": "text",
"placeholders": {}
},
"moderator": "Moderator",
"@moderator": {
"type": "text",
@ -1256,11 +1177,6 @@
"type": "text",
"placeholders": {}
},
"optionalGroupName": "(Optional) Gruppenname",
"@optionalGroupName": {
"type": "text",
"placeholders": {}
},
"or": "Oder",
"@or": {
"type": "text",
@ -1328,11 +1244,6 @@
"type": "text",
"placeholders": {}
},
"pleaseChooseAUsername": "Bitte wähle einen Benutzernamen",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"pleaseClickOnLink": "Bitte auf den Link in der E-Mail klicken und dann fortfahren.",
"@pleaseClickOnLink": {
"type": "text",
@ -1343,11 +1254,6 @@
"type": "text",
"placeholders": {}
},
"pleaseEnterAMatrixIdentifier": "Bitte eine Matrix-ID eingeben.",
"@pleaseEnterAMatrixIdentifier": {
"type": "text",
"placeholders": {}
},
"pleaseEnterYourPassword": "Bitte dein Passwort eingeben",
"@pleaseEnterYourPassword": {
"type": "text",
@ -1516,22 +1422,6 @@
"username": {}
}
},
"seenByUserAndCountOthers": "{count, plural, other{Gelesen von {username} und {count} anderen}}",
"@seenByUserAndCountOthers": {
"type": "text",
"placeholders": {
"username": {},
"count": {}
}
},
"seenByUserAndUser": "Gelesen von {username} und {username2}",
"@seenByUserAndUser": {
"type": "text",
"placeholders": {
"username": {},
"username2": {}
}
},
"send": "Senden",
"@send": {
"type": "text",
@ -1633,11 +1523,6 @@
"type": "text",
"placeholders": {}
},
"setGroupDescription": "Gruppenbeschreibung festlegen",
"@setGroupDescription": {
"type": "text",
"placeholders": {}
},
"setInvitationLink": "Einladungslink festlegen",
"@setInvitationLink": {
"type": "text",
@ -1680,11 +1565,6 @@
"type": "text",
"placeholders": {}
},
"signUp": "Registrieren",
"@signUp": {
"type": "text",
"placeholders": {}
},
"singlesignon": "Einmalige Anmeldung",
"@singlesignon": {
"type": "text",
@ -2003,21 +1883,11 @@
"type": "text",
"placeholders": {}
},
"youAreInvitedToThisChat": "Du wurdest in diesen Chat eingeladen",
"@youAreInvitedToThisChat": {
"type": "text",
"placeholders": {}
},
"youAreNoLongerParticipatingInThisChat": "Du bist kein Mitglied mehr in diesem Chat",
"@youAreNoLongerParticipatingInThisChat": {
"type": "text",
"placeholders": {}
},
"youCannotInviteYourself": "Du kannst dich nicht selbst einladen",
"@youCannotInviteYourself": {
"type": "text",
"placeholders": {}
},
"youHaveBeenBannedFromThisChat": "Du wurdest aus dem Chat verbannt",
"@youHaveBeenBannedFromThisChat": {
"type": "text",
@ -2038,8 +1908,6 @@
},
"scanQrCode": "QR-Code scannen",
"@scanQrCode": {},
"shareYourInviteLink": "Teile deinen Einladungslink",
"@shareYourInviteLink": {},
"chatHasBeenAddedToThisSpace": "Chat wurde zum Space hinzugefügt",
"@chatHasBeenAddedToThisSpace": {},
"autoplayImages": "Animierte Sticker und Emotes automatisch abspielen",
@ -2089,23 +1957,10 @@
"@removeFromSpace": {},
"addToSpaceDescription": "Wähle einen Space aus, um diesen Chat hinzuzufügen.",
"@addToSpaceDescription": {},
"loginWithOneClick": "Anmelden mit einem Klick",
"@loginWithOneClick": {},
"start": "Start",
"@start": {},
"passwordsDoNotMatch": "Passwörter stimmen nicht überein!",
"@passwordsDoNotMatch": {},
"pleaseEnterValidEmail": "Bitte gib eine gültige E-Mail-Adresse ein.",
"@pleaseEnterValidEmail": {},
"repeatPassword": "Passwort wiederholen",
"@repeatPassword": {},
"pleaseChooseAtLeastChars": "Bitte wähle mindestens {min} Zeichen.",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"commandHint_dm": "Starte einen direkten Chat\nBenutze --no-encryption um die Verschlüsselung auszuschalten",
"@commandHint_dm": {
"type": "text",
@ -2126,18 +1981,6 @@
"type": "text",
"description": "Usage hint for the command /create"
},
"yourStory": "Deine Story",
"@yourStory": {},
"replyHasBeenSent": "Antwort wurde gesendet",
"@replyHasBeenSent": {},
"storyFrom": "Story von {date}: \n{body}",
"@storyFrom": {
"type": "text",
"placeholders": {
"date": {},
"body": {}
}
},
"openVideoCamera": "Video aufnehmen",
"@openVideoCamera": {
"type": "text",
@ -2150,31 +1993,8 @@
"size": {}
}
},
"whoCanSeeMyStoriesDesc": "Bitte beachte, dass sich Leute in deiner Story sehen und kontaktieren können.",
"@whoCanSeeMyStoriesDesc": {},
"addToStory": "Story hinzufügen",
"@addToStory": {},
"publish": "Veröffentlichen",
"@publish": {},
"whoCanSeeMyStories": "Wer kann meine Storys sehen?",
"@whoCanSeeMyStories": {},
"unsubscribeStories": "Story deabbonieren",
"@unsubscribeStories": {},
"thisUserHasNotPostedAnythingYet": "Dieses Mitglied hat noch keine Story gepostet",
"@thisUserHasNotPostedAnythingYet": {},
"whatIsGoingOn": "Was gibt es neues?",
"@whatIsGoingOn": {},
"addDescription": "Beschreibung hinzufügen",
"@addDescription": {},
"storyPrivacyWarning": "Bitte beachte, dass sich die Leute in deiner Story sehen und kontaktieren können. Ihre Stories sind 24 Stunden lang sichtbar, aber es gibt keine Garantie dafür, dass sie von allen Geräten und Servern gelöscht werden.",
"@storyPrivacyWarning": {},
"iUnderstand": "Ich habe verstanden",
"@iUnderstand": {},
"bubbleSize": "Sprechblasengröße",
"@bubbleSize": {
"type": "text",
"placeholders": {}
},
"pinMessage": "An Raum anheften",
"@pinMessage": {},
"emojis": "Emojis",
@ -2193,8 +2013,6 @@
"@unsupportedAndroidVersionLong": {},
"experimentalVideoCalls": "Experimentelle Videoanrufe",
"@experimentalVideoCalls": {},
"matrixWidgets": "Matrix-Widgets",
"@matrixWidgets": {},
"reactedWith": "{sender} reagierte mit {reaction}",
"@reactedWith": {
"type": "text",
@ -2222,8 +2040,6 @@
},
"nextAccount": "Nächstes Konto",
"@nextAccount": {},
"editWidgets": "Widgets bearbeiten",
"@editWidgets": {},
"widgetJitsi": "Jitsi Meet",
"@widgetJitsi": {},
"widgetCustom": "Angepasst",
@ -2247,11 +2063,6 @@
"type": "text",
"placeholders": {}
},
"showDirectChatsInSpaces": "Zugehörige Direkt-Chats in Spaces anzeigen",
"@showDirectChatsInSpaces": {
"type": "text",
"placeholders": {}
},
"widgetNameError": "Bitte gib einen Anzeigenamen an.",
"@widgetNameError": {},
"youKicked": "👞 Du hast {user} rausgeworfen",
@ -2302,12 +2113,6 @@
"user": {}
}
},
"noEmailWarning": "Bitte gib eine gültige E-Mail-Adresse ein. Andernfalls kannst du dein Passwort nicht zurücksetzen. Wenn du das nicht möchtest, tippe erneut auf die Schaltfläche, um fortzufahren.",
"@noEmailWarning": {},
"updateAvailable": "FluffyChat-Update verfügbar",
"@updateAvailable": {},
"updateNow": "Update im Hintergrund starten",
"@updateNow": {},
"recoveryKey": "Wiederherstellungs-Schlüssel",
"@recoveryKey": {},
"recoveryKeyLost": "Wiederherstellungsschlüssel verloren?",
@ -2358,8 +2163,6 @@
"@storeInAppleKeyChain": {},
"indexedDbErrorLong": "Die Nachrichtenspeicherung ist im privaten Modus standardmäßig leider nicht aktiviert.\nBitte besuche\n- about:config\n- Setze dom.indexedDB.privateBrowsing.enabled auf true\nAndernfalls ist es nicht möglich, FluffyChat auszuführen.",
"@indexedDbErrorLong": {},
"stories": "Status",
"@stories": {},
"confirmMatrixId": "Bitte bestätigen deine Matrix-ID, um dein Konto zu löschen.",
"@confirmMatrixId": {},
"supposedMxid": "das sollte sein {mxid}",
@ -2451,8 +2254,6 @@
},
"sorryThatsNotPossible": "Sorry ... das ist nicht möglich",
"@sorryThatsNotPossible": {},
"letsStart": "Los geht's",
"@letsStart": {},
"hugContent": "{senderName} umarmt dich",
"@hugContent": {
"type": "text",
@ -2462,12 +2263,8 @@
},
"commandHint_googly": "Googly Eyes senden",
"@commandHint_googly": {},
"endToEndEncryption": "Ende-zu-Ende-Verschlüsselung",
"@endToEndEncryption": {},
"disableEncryptionWarning": "Aus Sicherheitsgründen können Sie die Verschlüsselung in einem Chat nicht deaktivieren, wo sie zuvor aktiviert wurde.",
"@disableEncryptionWarning": {},
"enterInviteLinkOrMatrixId": "Einladungslink oder Matrix-ID eingeben …",
"@enterInviteLinkOrMatrixId": {},
"reopenChat": "Chat wieder eröffnen",
"@reopenChat": {},
"fileIsTooBigForServer": "Der Server meldet, dass die Datei zu groß ist für eine Übermittlung ist.",
@ -2492,25 +2289,18 @@
"@jumpToLastReadMessage": {},
"readUpToHere": "Bis hier gelesen",
"@readUpToHere": {},
"discover": "Entdecken",
"@discover": {
"type": "text",
"placeholders": {}
},
"pleaseTryAgainLaterOrChooseDifferentServer": "Bitte versuche es später noch einmal oder wähle einen anderen Server.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"jump": "Springen",
"@jump": {},
"openLinkInBrowser": "Link im Browser öffnen",
"@openLinkInBrowser": {},
"reportErrorDescription": "Hoppla. Etwas ist schief gelaufen. Bitte versuche es später noch einmal. Wenn du möchtest, kannst du den Fehler bei den Entwicklern melden.",
"reportErrorDescription": "😭 Oh nein. Etwas ist schief gelaufen. Wenn du möchtest, kannst du den Bug bei den Entwicklern melden.",
"@reportErrorDescription": {},
"report": "Melden",
"@report": {},
"signInWithPassword": "Anmelden mit Passwort",
"@signInWithPassword": {},
"continueWith": "Fortfahren mit:",
"@continueWith": {},
"signInWith": "Anmelden mit {provider}",
"@signInWith": {
"type": "text",
@ -2524,17 +2314,8 @@
"@importEmojis": {},
"importFromZipFile": "Aus ZIP-Datei importieren",
"@importFromZipFile": {},
"importZipFile": "ZIP-Datei importieren",
"@importZipFile": {},
"exportEmotePack": "Emote-Paket als ZIP-Datei exportieren",
"@exportEmotePack": {},
"savedEmotePack": "Emote-Paket wurde unter {path} gespeichert!",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"notAnImage": "Keine Bilddatei.",
"@notAnImage": {},
"replace": "Ersetzen",
@ -2551,10 +2332,6 @@
"@inviteContactToGroupQuestion": {},
"tryAgain": "Neuer Versuch",
"@tryAgain": {},
"anyoneCanKnock": "Jeder kann anklopfen",
"@anyoneCanKnock": {},
"noOneCanJoin": "Niemand darf beitreten",
"@noOneCanJoin": {},
"redactMessageDescription": "Die Nachricht wird für alle Teilnehmer dieses Gesprächs gelöscht. Dies kann nicht rückgängig gemacht werden.",
"@redactMessageDescription": {},
"redactedBy": "Gelöscht von {username}",
@ -2582,8 +2359,6 @@
"@optionalRedactReason": {},
"messagesStyle": "Nachrichten:",
"@messagesStyle": {},
"noGroupDescriptionYet": "Noch keine Gruppenbeschreibung erstellt.",
"@noGroupDescriptionYet": {},
"chatPermissions": "Chatberechtigungen",
"@chatPermissions": {},
"chatDescription": "Chatbeschreibung",
@ -2596,12 +2371,10 @@
"@invalidServerName": {},
"directChat": "Privater Chat",
"@directChat": {},
"addChatDescription": "Chatbeschreibung hinzufügen",
"addChatDescription": "Chatbeschreibung hinzufügen ...",
"@addChatDescription": {},
"setChatDescription": "Chatbeschreibung festlegen",
"@setChatDescription": {},
"requests": "Anfragen",
"@requests": {},
"inviteGroupChat": "📨 Einladungen zum Gruppenchat",
"@inviteGroupChat": {},
"invitePrivateChat": "📨 Einladungen zum privaten Chat",
@ -2634,30 +2407,83 @@
"@removeDevicesDescription": {},
"unbanUserDescription": "Der Benutzer kann den Chat dann wieder betreten, wenn er es versucht.",
"@unbanUserDescription": {},
"todoLists": "(Beta) Aufgabenlisten",
"@todoLists": {},
"editTodo": "Aufgabe bearbeiten",
"@editTodo": {},
"pushNotificationsNotAvailable": "Push-Benachrichtigungen nicht verfügbar",
"@pushNotificationsNotAvailable": {},
"pleaseAddATitle": "Bitte einen Titel hinzufügen",
"@pleaseAddATitle": {},
"makeAdminDescription": "Sobald du diesen Benutzer zum Administrator gemacht hast, kannst du das möglicherweise nicht mehr rückgängig machen, da er dann über dieselben Berechtigungen wie du verfügt.",
"@makeAdminDescription": {},
"noTodosYet": "Zu diesem Chat wurden noch keine Aufgaben hinzugefügt. Erstellen die erste Aufgabe und fange an, mit anderen zusammenzuarbeiten. 📝",
"@noTodosYet": {},
"archiveRoomDescription": "Der Chat wird in das Archiv verschoben. Andere Benutzer können sehen, dass du den Chat verlassen hast.",
"@archiveRoomDescription": {},
"newTodo": "Neue Aufgabe",
"@newTodo": {},
"learnMore": "Erfahre mehr",
"@learnMore": {},
"todoListChangedError": "Hoppla ... Die Aufgabenliste wurde geändert, während Sie sie bearbeitet haben.",
"@todoListChangedError": {},
"roomUpgradeDescription": "Der Chat wird dann mit der neuen Raumversion neu erstellt. Alle Teilnehmer werden benachrichtigt, dass sie zum neuen Chat wechseln müssen. Mehr über Raumversionen erfährst du unter https://spec.matrix.org/latest/rooms/",
"@roomUpgradeDescription": {},
"kickUserDescription": "Der Benutzer wird aus dem Chat geworfen, aber nicht gebannt. In öffentlichen Chats kann der Benutzer jederzeit wieder beitreten.",
"@kickUserDescription": {},
"todosUnencrypted": "Bitte beachte, dass Todos für jeden im Chat sichtbar und nicht Ende zu Ende verschlüsselt sind.",
"@todosUnencrypted": {}
"blockListDescription": "Du kannst Benutzer blockieren, die dich stören. Von Benutzern auf deiner persönlichen Blocklierliste kannst du keine Nachrichten oder Raumeinladungen mehr erhalten.",
"@blockListDescription": {},
"createGroupAndInviteUsers": "Gruppe erstellen und Nutzer einladen",
"@createGroupAndInviteUsers": {},
"startConversation": "Unterhaltung starten",
"@startConversation": {},
"blockedUsers": "Blockierte Benutzer",
"@blockedUsers": {},
"groupCanBeFoundViaSearch": "Gruppe kann über die Suche gefunden werden",
"@groupCanBeFoundViaSearch": {},
"noUsersFoundWithQuery": "Leider konnte mit \"{query}\" kein Benutzer gefunden werden. Bitte schau nach, ob dir ein Tippfehler unterlaufen ist.",
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
}
},
"block": "Blockieren",
"@block": {},
"yourGlobalUserIdIs": "Deine globale Benutzer-ID ist: ",
"@yourGlobalUserIdIs": {},
"commandHint_sendraw": "Rohes JSON senden",
"@commandHint_sendraw": {},
"wrongRecoveryKey": "Entschuldigung ... das scheint nicht der richtige Wiederherstellungsschlüssel zu sein.",
"@wrongRecoveryKey": {},
"blockUsername": "Blockiere Benutzername",
"@blockUsername": {},
"groupName": "Gruppenname",
"@groupName": {},
"searchChatsRooms": "Suche nach #Chats, @Nutzer ...",
"@searchChatsRooms": {},
"databaseMigrationTitle": "Datenbank wird optimiert",
"@databaseMigrationTitle": {},
"databaseMigrationBody": "Bitte warten. Dies kann einen Moment dauern.",
"@databaseMigrationBody": {},
"thisDevice": "Dieses Gerät:",
"@thisDevice": {},
"publicSpaces": "Öffentliche Spaces",
"@publicSpaces": {},
"passwordIsWrong": "Dein eingegebenes Passwort ist falsch",
"@passwordIsWrong": {},
"pleaseEnterYourCurrentPassword": "Bitte dein aktuelles Passwort eingeben",
"@pleaseEnterYourCurrentPassword": {},
"publicLink": "Öffentlicher Link",
"@publicLink": {},
"nothingFound": "Nichts gefunden ...",
"@nothingFound": {},
"decline": "Ablehnen",
"@decline": {},
"newPassword": "Neues Passwort",
"@newPassword": {},
"passwordsDoNotMatch": "Passwörter stimmen nicht überein",
"@passwordsDoNotMatch": {},
"subspace": "Sub-Space",
"@subspace": {},
"select": "Auswählen",
"@select": {},
"pleaseChooseAStrongPassword": "Bitte wähle ein starkes Passwort",
"@pleaseChooseAStrongPassword": {},
"addChatOrSubSpace": "Chat oder Sub-Space hinzufügen",
"@addChatOrSubSpace": {},
"leaveEmptyToClearStatus": "Leer lassen, um den Status zu löschen.",
"@leaveEmptyToClearStatus": {},
"joinSpace": "Space beitreten",
"@joinSpace": {},
"searchForUsers": "Suche nach @benutzer ...",
"@searchForUsers": {}
}

File diff suppressed because it is too large Load diff

View file

@ -1,43 +1,20 @@
{
"@@locale": "en",
"@@last_modified": "2021-08-14 12:38:37.885451",
"passwordsDoNotMatch": "Passwords do not match!",
"@passwordsDoNotMatch": {},
"pleaseEnterValidEmail": "Please enter a valid email address.",
"@pleaseEnterValidEmail": {},
"repeatPassword": "Repeat password",
"@repeatPassword": {},
"pleaseChooseAtLeastChars": "Please choose at least {min} characters.",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"notAnImage": "Not an image file.",
"remove": "Replace",
"remove": "Remove",
"importNow": "Import now",
"importEmojis": "Import Emojis",
"importFromZipFile": "Import from .zip file",
"importZipFile": "Import .zip file",
"exportEmotePack": "Export Emote pack as .zip",
"replace": "Replace",
"savedEmotePack": "Saved emote pack to {path}!",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"about": "About",
"@about": {
"type": "text",
"placeholders": {}
},
"updateAvailable": "FluffyChat update available",
"@updateAvailable": {},
"updateNow": "Start update in background",
"@updateNow": {},
"accept": "Accept",
"@accept": {
"type": "text",
@ -249,11 +226,6 @@
"type": "text",
"placeholders": {}
},
"bubbleSize": "Bubble size",
"@bubbleSize": {
"type": "text",
"placeholders": {}
},
"cancel": "Cancel",
"@cancel": {
"type": "text",
@ -395,11 +367,6 @@
"type": "text",
"placeholders": {}
},
"changeWallpaper": "Change wallpaper",
"@changeWallpaper": {
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "Change your avatar",
"@changeYourAvatar": {
"type": "text",
@ -445,11 +412,6 @@
"type": "text",
"placeholders": {}
},
"chooseAUsername": "Choose a username",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"clearArchive": "Clear archive",
"@clearArchive": {},
"close": "Close",
@ -720,11 +682,6 @@
"type": "text",
"placeholders": {}
},
"deny": "Deny",
"@deny": {
"type": "text",
"placeholders": {}
},
"device": "Device",
"@device": {
"type": "text",
@ -750,11 +707,6 @@
"type": "text",
"placeholders": {}
},
"discover": "Discover",
"@discover": {
"type": "text",
"placeholders": {}
},
"displaynameHasBeenChanged": "Displayname has been changed",
"@displaynameHasBeenChanged": {
"type": "text",
@ -1046,6 +998,10 @@
"type": "text",
"placeholders": {}
},
"block": "block",
"blockedUsers": "Blocked users",
"blockListDescription": "You can block users who are disturbing you. You won't be able to receive any messages or room invites from the users on your personal block list.",
"blockUsername": "Ignore username",
"iHaveClickedOnLink": "I have clicked on the link",
"@iHaveClickedOnLink": {
"type": "text",
@ -1070,14 +1026,11 @@
"@inviteContactToGroup": {
"type": "text",
"placeholders": {
"contact": {},
"groupName": {}
}
},
"inviteContactToGroup": "Invite contact to {groupName}",
"noChatDescriptionYet": "No chat description created yet.",
"anyoneCanKnock": "Anyone can knock",
"noOneCanJoin": "No one can join",
"tryAgain": "Try again",
"invalidServerName": "Invalid server name",
"invited": "Invited",
@ -1158,11 +1111,6 @@
"localizedTimeShort": {}
}
},
"lastSeenLongTimeAgo": "Seen a long time ago",
"@lastSeenLongTimeAgo": {
"type": "text",
"placeholders": {}
},
"leave": "Leave",
"@leave": {
"type": "text",
@ -1236,18 +1184,11 @@
"homeserver": {}
}
},
"loginWithOneClick": "Sign in with one click",
"@loginWithOneClick": {},
"logout": "Logout",
"@logout": {
"type": "text",
"placeholders": {}
},
"makeSureTheIdentifierIsValid": "Make sure the identifier is valid",
"@makeSureTheIdentifierIsValid": {
"type": "text",
"placeholders": {}
},
"memberChanges": "Member changes",
"@memberChanges": {
"type": "text",
@ -1264,11 +1205,6 @@
"type": "text",
"placeholders": {}
},
"messageWillBeRemovedWarning": "Message will be removed for all participants",
"@messageWillBeRemovedWarning": {
"type": "text",
"placeholders": {}
},
"moderator": "Moderator",
"@moderator": {
"type": "text",
@ -1527,11 +1463,6 @@
"type": "text",
"placeholders": {}
},
"pleaseChooseAUsername": "Please choose a username",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"pleaseClickOnLink": "Please click on the link in the email and then proceed.",
"@pleaseClickOnLink": {
"type": "text",
@ -1542,11 +1473,6 @@
"type": "text",
"placeholders": {}
},
"pleaseEnterAMatrixIdentifier": "Please enter a Matrix ID.",
"@pleaseEnterAMatrixIdentifier": {
"type": "text",
"placeholders": {}
},
"pleaseEnterRecoveryKey": "Please enter your recovery key:",
"@pleaseEnterRecoveryKey": {},
"pleaseEnterYourPassword": "Please enter your password",
@ -1644,7 +1570,6 @@
"type": "text",
"placeholders": {}
},
"remove": "Remove",
"@remove": {
"type": "text",
"placeholders": {}
@ -1737,22 +1662,6 @@
"username": {}
}
},
"seenByUserAndCountOthers": "{count, plural, other{Seen by {username} and {count} others}}",
"@seenByUserAndCountOthers": {
"type": "text",
"placeholders": {
"username": {},
"count": {}
}
},
"seenByUserAndUser": "Seen by {username} and {username2}",
"@seenByUserAndUser": {
"type": "text",
"placeholders": {
"username": {},
"username2": {}
}
},
"send": "Send",
"@send": {
"type": "text",
@ -1897,21 +1806,11 @@
"type": "text",
"placeholders": {}
},
"showDirectChatsInSpaces": "Show related Direct Chats in Spaces",
"@showDirectChatsInSpaces": {
"type": "text",
"placeholders": {}
},
"showPassword": "Show password",
"@showPassword": {
"type": "text",
"placeholders": {}
},
"signUp": "Sign up",
"@signUp": {
"type": "text",
"placeholders": {}
},
"singlesignon": "Single Sign on",
"@singlesignon": {
"type": "text",
@ -2233,21 +2132,11 @@
"type": "text",
"placeholders": {}
},
"youAreInvitedToThisChat": "You are invited to this chat",
"@youAreInvitedToThisChat": {
"type": "text",
"placeholders": {}
},
"youAreNoLongerParticipatingInThisChat": "You are no longer participating in this chat",
"@youAreNoLongerParticipatingInThisChat": {
"type": "text",
"placeholders": {}
},
"youCannotInviteYourself": "You cannot invite yourself",
"@youCannotInviteYourself": {
"type": "text",
"placeholders": {}
},
"youHaveBeenBannedFromThisChat": "You have been banned from this chat",
"@youHaveBeenBannedFromThisChat": {
"type": "text",
@ -2276,20 +2165,8 @@
"@start": {},
"pleaseEnterRecoveryKeyDescription": "To unlock your old messages, please enter your recovery key that has been generated in a previous session. Your recovery key is NOT your password.",
"@pleaseEnterRecoveryKeyDescription": {},
"addToStory": "Add to story",
"@addToStory": {},
"publish": "Publish",
"@publish": {},
"whoCanSeeMyStories": "Who can see my stories?",
"@whoCanSeeMyStories": {},
"unsubscribeStories": "Unsubscribe stories",
"@unsubscribeStories": {},
"thisUserHasNotPostedAnythingYet": "This user has not posted anything in their story yet",
"@thisUserHasNotPostedAnythingYet": {},
"yourStory": "Your story",
"@yourStory": {},
"replyHasBeenSent": "Reply has been sent",
"@replyHasBeenSent": {},
"videoWithSize": "Video ({size})",
"@videoWithSize": {
"type": "text",
@ -2297,24 +2174,6 @@
"size": {}
}
},
"storyFrom": "Story from {date}: \n{body}",
"@storyFrom": {
"type": "text",
"placeholders": {
"date": {},
"body": {}
}
},
"whoCanSeeMyStoriesDesc": "Please note that people can see and contact each other in your story.",
"@whoCanSeeMyStoriesDesc": {},
"whatIsGoingOn": "What is going on?",
"@whatIsGoingOn": {},
"addDescription": "Add description",
"@addDescription": {},
"storyPrivacyWarning": "Please note that people can see and contact each other in your story. Your stories will be visible for 24 hours but there is no guarantee that they will be deleted from all devices and servers.",
"@storyPrivacyWarning": {},
"iUnderstand": "I understand",
"@iUnderstand": {},
"openChat": "Open Chat",
"@openChat": {},
"markAsRead": "Mark as read",
@ -2323,8 +2182,6 @@
"@reportUser": {},
"dismiss": "Dismiss",
"@dismiss": {},
"matrixWidgets": "Matrix Widgets",
"@matrixWidgets": {},
"reactedWith": "{sender} reacted with {reaction}",
"@reactedWith": {
"type": "text",
@ -2368,8 +2225,6 @@
"@nextAccount": {},
"previousAccount": "Previous account",
"@previousAccount": {},
"editWidgets": "Edit widgets",
"@editWidgets": {},
"addWidget": "Add widget",
"@addWidget": {},
"widgetVideo": "Video",
@ -2442,10 +2297,6 @@
"user": {}
}
},
"noEmailWarning": "Please enter a valid email address. Otherwise you won't be able to reset your password. If you don't want to, tap again on the button to continue.",
"@noEmailWarning": {},
"stories": "Stories",
"@stories": {},
"users": "Users",
"@users": {},
"unlockOldMessages": "Unlock old messages",
@ -2520,12 +2371,9 @@
},
"newSpaceDescription": "Spaces allows you to consolidate your chats and build private or public communities.",
"encryptThisChat": "Encrypt this chat",
"endToEndEncryption": "End to end encryption",
"disableEncryptionWarning": "For security reasons you can not disable encryption in a chat, where it has been enabled before.",
"sorryThatsNotPossible": "Sorry... that is not possible",
"deviceKeys": "Device keys:",
"letsStart": "Let's start",
"enterInviteLinkOrMatrixId": "Enter invite link or Matrix ID...",
"reopenChat": "Reopen chat",
"noBackupWarning": "Don't forget your password!",
"noOtherDevicesFound": "No other devices found",
@ -2541,10 +2389,9 @@
"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.",
"reportErrorDescription": "😭 Oh no. Something went wrong. If you want, you can report this bug to the developers.",
"report": "report",
"signInWithPassword": "Sign in with password",
"continueWith": "Continue with:",
"pleaseTryAgainLaterOrChooseDifferentServer": "Please try again later or choose a different server.",
"signInWith": "Sign in with {provider}",
"@signInWith": {
@ -3920,15 +3767,6 @@
"makeAdminDescription": "Once you make this user admin, you may not be able to undo this as they will then have the same permissions as you.",
"pushNotificationsNotAvailable": "Push notifications not available",
"learnMore": "Learn more",
"todoLists": "(Beta) Todolists",
"newTodo": "New todo",
"noTodosYet": "No todos have been added to this chat yet. Create your first todo and start cooperating with others. 📝",
"editTodo": "Edit todo",
"pleaseAddATitle": "Please add a title",
"todoListChangedError": "Oops... The todo list has been changed while you edited it.",
"todosUnencrypted": "Please notice that todos are visible by everyone in the chat and are not end to end encrypted.",
"noAddToSpacePermissions": "You can't add a chat to this space",
"alreadyInSpace": "The chat is already in this space",
"yourGlobalUserIdIs": "Your global user-ID is: ",
"noUsersFoundWithQuery": "Unfortunately no user could be found with \"{query}\". Please check whether you made a typo.",
"@noUsersFoundWithQuery": {
@ -3956,5 +3794,83 @@
"inNoSpaces": "You are not a member of any classes or exchanges",
"successfullySubscribed": "You have successfully subscribed!",
"clickToManageSubscription": "Click here to manage your subscription.",
"emptyInviteWarning": "Add this chat to a class or exchange to invite other users."
"emptyInviteWarning": "Add this chat to a class or exchange to invite other users.",
"nothingFound": "Nothing found...",
"groupName": "Group name",
"createGroupAndInviteUsers": "Create a group and invite users",
"groupCanBeFoundViaSearch": "Group can be found via search",
"wrongRecoveryKey": "Sorry... this does not seem to be the correct recovery key.",
"startConversation": "Start conversation",
"commandHint_sendraw": "Send raw json",
"databaseMigrationTitle": "Database is optimized",
"databaseMigrationBody": "Please wait. This may take a moment.",
"leaveEmptyToClearStatus": "Leave empty to clear your status.",
"select": "Select",
"searchForUsers": "Search for @users...",
"pleaseEnterYourCurrentPassword": "Please enter your current password",
"newPassword": "New password",
"pleaseChooseAStrongPassword": "Please choose a strong password",
"passwordsDoNotMatch": "Passwords do not match",
"passwordIsWrong": "Your entered password is wrong",
"publicLink": "Public link",
"joinSpace": "Join space",
"publicSpaces": "Public spaces",
"addChatOrSubSpace": "Add chat or sub space",
"subspace": "Subspace",
"decline": "Decline",
"thisDevice": "This device:",
"initAppError": "An error occured while init the app",
"databaseBuildErrorBody": "Unable to build the SQlite database. The app tries to use the legacy database for now. Please report this error to the developers at {url}. The error message is: {error}",
"@databaseBuildErrorBody": {
"type": "text",
"placeholders": {
"url": {},
"error": {}
}
},
"sessionLostBody": "Your session is lost. Please report this error to the developers at {url}. The error message is: {error}",
"@sessionLostBody": {
"type": "text",
"placeholders": {
"url": {},
"error": {}
}
},
"restoreSessionBody": "The app now tries to restore your session from the backup. Please report this error to the developers at {url}. The error message is: {error}",
"@restoreSessionBody": {
"type": "text",
"placeholders": {
"url": {},
"error": {}
}
},
"forwardMessageTo": "Forward message to {roomName}?",
"@forwardMessageTo": {
"type": "text",
"placeholders": {
"roomName": {}
}
},
"signUp": "Sign up",
"pleaseChooseAtLeastChars": "Please choose at least {min} characters.",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"noEmailWarning": "Please enter a valid email address. Otherwise you won't be able to reset your password. If you don't want to, tap again on the button to continue.",
"pleaseEnterValidEmail": "Please enter a valid email address.",
"noAddToSpacePermissions": "You can't add a chat to this space",
"alreadyInSpace": "The chat is already in this space",
"pleaseChooseAUsername": "Please choose a username",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"chooseAUsername": "Choose a username",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
}
}

File diff suppressed because it is too large Load diff

View file

@ -4428,28 +4428,28 @@
"exportEmotePack": "Exportar Emote pack como .zip",
"savedEmotePack": "¡Paquete de emoticonos guardado en {path}!",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"type": "text",
"placeholders": {
"path": {}
}
},
"sendTypingNotifications": "Enviar notificaciones de mecanografía",
"reportToTeacher": "¿A quién desea informar de este mensaje?",
"reportMessageTitle": "{reportingUserId} ha informado de un mensaje de {reportedUserId} en el chat {roomName}.",
"@reportMessageTitle": {
"placeholders": {
"reportingUserId": {},
"reportedUserId": {},
"roomName": {}
}
},
"placeholders": {
"reportingUserId": {},
"reportedUserId": {},
"roomName": {}
}
},
"reportMessageBody": "Mensaje: {reportedMessage}\nMotivo: {reason}",
"@reportMessageBody": {
"placeholders": {
"reportedMessage": {},
"reason": {}
}
},
"placeholders": {
"reportedMessage": {},
"reason": {}
}
},
"noTeachersFound": "No se han encontrado profesores a los que informar",
"noAddToSpacePermissions": "No puedes añadir un chat a este espacio",
"alreadyInSpace": "El chat ya está en este espacio",
@ -4458,7 +4458,7 @@
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
"query": {}
}
},
"searchChatsRooms": "Busca #chats, @users...",
@ -4471,13 +4471,68 @@
"viewArchive": "Ver archivo",
"trialExpiration": "Su prueba gratuita caduca el {expiration}.",
"@trialExpiration": {
"placeholders": {
"expiration": {}
}
},
"placeholders": {
"expiration": {}
}
},
"freeTrialDesc": "Los nuevos usuarios reciben una semana de prueba gratuita de Pangea Chat",
"activateTrial": "Activar prueba",
"successfullySubscribed": "Se ha suscrito correctamente.",
"clickToManageSubscription": "Haga clic aquí para gestionar su suscripción.",
"emptyInviteWarning": "Añade este chat a una clase o intercambio para invitar a otros usuarios."
}
"emptyInviteWarning": "Añade este chat a una clase o intercambio para invitar a otros usuarios.",
"block": "bloque",
"blockedUsers": "Usuarios bloqueados",
"blockListDescription": "Puedes bloquear a los usuarios que te molesten. No podrás recibir mensajes ni invitaciones a salas de los usuarios de tu lista personal de bloqueados.",
"blockUsername": "Ignorar nombre de usuario",
"nothingFound": "No se ha encontrado nada...",
"wrongRecoveryKey": "Lo siento... esta no parece ser la clave de recuperación correcta.",
"startConversation": "Iniciar conversación",
"commandHint_sendraw": "Enviar json sin procesar",
"databaseMigrationTitle": "La base de datos está optimizada",
"databaseMigrationBody": "Por favor, espere. Esto puede tardar un momento.",
"leaveEmptyToClearStatus": "Déjelo vacío para borrar su estado.",
"select": "Seleccione",
"searchForUsers": "Buscar @usuarios...",
"pleaseEnterYourCurrentPassword": "Introduzca su contraseña actual",
"newPassword": "Nueva contraseña",
"pleaseChooseAStrongPassword": "Elija una contraseña segura",
"passwordIsWrong": "La contraseña introducida es incorrecta",
"publicLink": "Enlace público",
"joinSpace": "Unirse al espacio",
"publicSpaces": "Espacios públicos",
"addChatOrSubSpace": "Añadir chat o subespacio",
"subspace": "Subespacio",
"thisDevice": "Este aparato:",
"initAppError": "Se ha producido un error al iniciar la aplicación",
"databaseBuildErrorBody": "No se puede construir la base de datos SQlite. La aplicación intenta utilizar la base de datos heredada por ahora. Por favor, informe de este error a los desarrolladores en {url}. El mensaje de error es: {error}",
"@databaseBuildErrorBody": {
"type": "text",
"placeholders": {
"url": {},
"error": {}
}
},
"sessionLostBody": "Su sesión se ha perdido. Por favor, informe de este error a los desarrolladores en {url}. El mensaje de error es: {error}",
"@sessionLostBody": {
"type": "text",
"placeholders": {
"url": {},
"error": {}
}
},
"restoreSessionBody": "La aplicación intenta ahora restaurar tu sesión desde la copia de seguridad. Por favor, informa de este error a los desarrolladores en {url}. El mensaje de error es: {error}",
"@restoreSessionBody": {
"type": "text",
"placeholders": {
"url": {},
"error": {}
}
},
"forwardMessageTo": "¿Reenviar mensaje a {roomName}?",
"@forwardMessageTo": {
"type": "text",
"placeholders": {
"roomName": {}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -30,11 +30,6 @@
"username": {}
}
},
"addGroupDescription": "Gehitu taldearen deskribapena",
"@addGroupDescription": {
"type": "text",
"placeholders": {}
},
"admin": "Administratzailea",
"@admin": {
"type": "text",
@ -231,11 +226,6 @@
"type": "text",
"placeholders": {}
},
"changeWallpaper": "Aldatu horma-irudia",
"@changeWallpaper": {
"type": "text",
"placeholders": {}
},
"channelCorruptedDecryptError": "Zifraketa hondatu egin da",
"@channelCorruptedDecryptError": {
"type": "text",
@ -256,11 +246,6 @@
"type": "text",
"placeholders": {}
},
"chooseAUsername": "Aukeratu erabiltzaile-izen bat",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"close": "Itxi",
"@close": {
"type": "text",
@ -327,11 +312,6 @@
"username": {}
}
},
"createNewGroup": "Sortu talde berria",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "Une honetan aktibo",
"@currentlyActive": {
"type": "text",
@ -377,11 +357,6 @@
"type": "text",
"placeholders": {}
},
"deny": "Ukatu",
"@deny": {
"type": "text",
"placeholders": {}
},
"device": "Gailua",
"@device": {
"type": "text",
@ -459,11 +434,6 @@
"senderName": {}
}
},
"enterAGroupName": "Sartu talderako izena",
"@enterAGroupName": {
"type": "text",
"placeholders": {}
},
"enterYourHomeserver": "Sartu zure zerbitzaria",
"@enterYourHomeserver": {
"type": "text",
@ -499,16 +469,6 @@
"type": "text",
"placeholders": {}
},
"groupDescription": "Taldearen deskribapena",
"@groupDescription": {
"type": "text",
"placeholders": {}
},
"groupDescriptionHasBeenChanged": "Taldearen deskribapena aldatu da",
"@groupDescriptionHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"groupIsPublic": "Taldea publikoa da",
"@groupIsPublic": {
"type": "text",
@ -642,11 +602,6 @@
"localizedTimeShort": {}
}
},
"lastSeenLongTimeAgo": "Luze da ikusi zenetik",
"@lastSeenLongTimeAgo": {
"type": "text",
"placeholders": {}
},
"leave": "Irten",
"@leave": {
"type": "text",
@ -701,16 +656,6 @@
"type": "text",
"placeholders": {}
},
"makeSureTheIdentifierIsValid": "Egiaztatu identifikazioa baliozkoa dela",
"@makeSureTheIdentifierIsValid": {
"type": "text",
"placeholders": {}
},
"messageWillBeRemovedWarning": "Mezua partaide guztientzat ezabatuko da",
"@messageWillBeRemovedWarning": {
"type": "text",
"placeholders": {}
},
"moderator": "Moderatzailea",
"@moderator": {
"type": "text",
@ -791,11 +736,6 @@
"type": "text",
"placeholders": {}
},
"optionalGroupName": "(Hautazkoa) Taldearen izena",
"@optionalGroupName": {
"type": "text",
"placeholders": {}
},
"passphraseOrKey": "pasaesaldia edo berreskuratze-gakoa",
"@passphraseOrKey": {
"type": "text",
@ -823,16 +763,6 @@
"fileName": {}
}
},
"pleaseChooseAUsername": "Aukeratu erabiltzaile-izen bat",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"pleaseEnterAMatrixIdentifier": "Sartu Matrix ID bat.",
"@pleaseEnterAMatrixIdentifier": {
"type": "text",
"placeholders": {}
},
"pleaseEnterYourPassword": "Sartu zure pasahitza",
"@pleaseEnterYourPassword": {
"type": "text",
@ -931,14 +861,6 @@
"username": {}
}
},
"seenByUserAndUser": "{username}(e)k eta {username2}(e)k ikusi dute",
"@seenByUserAndUser": {
"type": "text",
"placeholders": {
"username": {},
"username2": {}
}
},
"send": "Bidali",
"@send": {
"type": "text",
@ -1016,11 +938,6 @@
"senderName": {}
}
},
"setGroupDescription": "Ezarri taldeko deskribapena",
"@setGroupDescription": {
"type": "text",
"placeholders": {}
},
"setInvitationLink": "Gonbidapen-esteka ezarri",
"@setInvitationLink": {
"type": "text",
@ -1048,11 +965,6 @@
"username": {}
}
},
"signUp": "Eman izena",
"@signUp": {
"type": "text",
"placeholders": {}
},
"skip": "Saltatu",
"@skip": {
"type": "text",
@ -1274,21 +1186,11 @@
"type": "text",
"placeholders": {}
},
"youAreInvitedToThisChat": "Txat honetara gonbidatu zaituzte",
"@youAreInvitedToThisChat": {
"type": "text",
"placeholders": {}
},
"youAreNoLongerParticipatingInThisChat": "Ez duzu txat honetan parte hartzen honezkero",
"@youAreNoLongerParticipatingInThisChat": {
"type": "text",
"placeholders": {}
},
"youCannotInviteYourself": "Ezin duzu zure burua gonbidatu",
"@youCannotInviteYourself": {
"type": "text",
"placeholders": {}
},
"youHaveBeenBannedFromThisChat": "Txat honetan debekua ezarri dizute",
"@youHaveBeenBannedFromThisChat": {
"type": "text",
@ -1359,11 +1261,6 @@
"type": "text",
"placeholders": {}
},
"editChatPermissions": "Editatu berriketa-baimenak",
"@editChatPermissions": {
"type": "text",
"placeholders": {}
},
"editRoomAvatar": "Gelaren abatarra editatu",
"@editRoomAvatar": {
"type": "text",
@ -1379,8 +1276,6 @@
"type": "text",
"placeholders": {}
},
"enterASpacepName": "Sartu gunerako izena",
"@enterASpacepName": {},
"homeserver": "Zerbitzaria",
"@homeserver": {},
"errorObtainingLocation": "Errorea kokapena lortzerakoan: {error}",
@ -1425,8 +1320,6 @@
"type": "text",
"placeholders": {}
},
"shareYourInviteLink": "Partekatu gonbidapen esteka",
"@shareYourInviteLink": {},
"notificationsEnabledForThisAccount": "Gaitu kontu honentzako jakinarazpenak",
"@notificationsEnabledForThisAccount": {
"type": "text",
@ -1477,8 +1370,6 @@
"unreadCount": {}
}
},
"iUnderstand": "Ulertzen dut",
"@iUnderstand": {},
"videoCallsBetaWarning": "Kontuan izan bideo-deiak beta fasean daudela. Litekeena da behar bezala erabili ezin izatea —erabili ahal badira—.",
"@videoCallsBetaWarning": {},
"toggleMuted": "Ikusi / Ezkutatu mutututakoak",
@ -1486,32 +1377,16 @@
"type": "text",
"placeholders": {}
},
"yourStory": "Zure storya",
"@yourStory": {},
"replyHasBeenSent": "Erantzuna bidali da",
"@replyHasBeenSent": {},
"all": "Guztia",
"@all": {
"type": "text",
"placeholders": {}
},
"loginWithOneClick": "Hasi saioa klik bakarrarekin",
"@loginWithOneClick": {},
"next": "Hurrengoa",
"@next": {
"type": "text",
"placeholders": {}
},
"storyFrom": "{date}ko storya:\n{body}",
"@storyFrom": {
"type": "text",
"placeholders": {
"date": {},
"body": {}
}
},
"whoCanSeeMyStoriesDesc": "Kontuan izan jendeak bata bestea ikusi eta harremanetan jar daitekeela zure storyan.",
"@whoCanSeeMyStoriesDesc": {},
"experimentalVideoCalls": "Bideo-dei esperimentalak",
"@experimentalVideoCalls": {},
"emailOrUsername": "ePosta edo erabiltzaile-izena",
@ -1580,8 +1455,6 @@
"@time": {},
"dismiss": "Baztertu",
"@dismiss": {},
"matrixWidgets": "Matrixen widgetak",
"@matrixWidgets": {},
"switchToAccount": "Aldatu {number} kontura",
"@switchToAccount": {
"type": "number",
@ -1601,23 +1474,8 @@
"type": "text",
"placeholders": {}
},
"whatIsGoingOn": "Zertan zabiltza?",
"@whatIsGoingOn": {},
"addDescription": "Gehitu deskribapena",
"@addDescription": {},
"passwordsDoNotMatch": "Pasahitzak ez datoz bat!",
"@passwordsDoNotMatch": {},
"pleaseEnterValidEmail": "Sartu baliozko ePosta helbide bat.",
"@pleaseEnterValidEmail": {},
"repeatPassword": "Idatzi berriro pasahitza",
"@repeatPassword": {},
"pleaseChooseAtLeastChars": "Aukeratu gutxienez {min} karaktere.",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"addEmail": "Gehitu ePosta",
"@addEmail": {
"type": "text",
@ -1742,11 +1600,6 @@
"supportedVersions": {}
}
},
"bubbleSize": "Puxiken tamaina",
"@bubbleSize": {
"type": "text",
"placeholders": {}
},
"commandHint_dm": "Hasi banakako txat bat\nErabili --no-encyption zifratzea desgaitzeko",
"@commandHint_dm": {
"type": "text",
@ -1801,11 +1654,6 @@
"type": "text",
"placeholders": {}
},
"ignoreUsername": "Ezikusi erabiltzaile-izena",
"@ignoreUsername": {
"type": "text",
"placeholders": {}
},
"iHaveClickedOnLink": "Estekan sakatu dut",
"@iHaveClickedOnLink": {
"type": "text",
@ -1860,8 +1708,6 @@
},
"openGallery": "Ireki bilduma",
"@openGallery": {},
"storyPrivacyWarning": "Kontuan izan jendeak bata bestea ikus dezakeela eta bata bestearekin harremanetan jar daitekeela. Zure storya 24 orduz egongo da ikusgai baina ezin da ziurtatu gailu eta zerbitzari guztietatik ezabatuko denik denbora pasatakoan.",
"@storyPrivacyWarning": {},
"pinMessage": "Finkatu gelan",
"@pinMessage": {},
"reactedWith": "{sender}(e)k {reaction}(r)ekin erreakzionatu du",
@ -1970,11 +1816,6 @@
"type": "text",
"placeholders": {}
},
"ignoreListDescription": "Gogaitzen zaituzten erabiltzaileak ezikusi ditzakezu. Ez duzu ezikusitako pertsonen zerrendan daudenen mezurik edota gonbidapenik jasoko.",
"@ignoreListDescription": {
"type": "text",
"placeholders": {}
},
"inoffensive": "Ez da iraingarria",
"@inoffensive": {
"type": "text",
@ -2089,14 +1930,6 @@
"type": "text",
"placeholders": {}
},
"seenByUserAndCountOthers": "{count, plural, other{{username}(e)k eta beste {count}(e)k ikusi dute}}",
"@seenByUserAndCountOthers": {
"type": "text",
"placeholders": {
"username": {},
"count": {}
}
},
"sendAsText": "Bidali testu bezala",
"@sendAsText": {
"type": "text"
@ -2203,29 +2036,14 @@
"@addToSpaceDescription": {},
"start": "Hasi",
"@start": {},
"addToStory": "Gehitu storyra",
"@addToStory": {},
"publish": "Argitaratu",
"@publish": {},
"whoCanSeeMyStories": "Nork ikus ditzazke nire storyak?",
"@whoCanSeeMyStories": {},
"unsubscribeStories": "Storyak jasotzeari utzi",
"@unsubscribeStories": {},
"thisUserHasNotPostedAnythingYet": "Erabiltzaile honek oraindik ez du ezer argitaratu bere storyetan",
"@thisUserHasNotPostedAnythingYet": {},
"reportUser": "Salatu erabiltzailea",
"@reportUser": {},
"openChat": "Ireki txata",
"@openChat": {},
"addWidget": "Gehitu widgeta",
"@addWidget": {},
"showDirectChatsInSpaces": "Erakutsi zerikusia duten banakako mezuak guneetan",
"@showDirectChatsInSpaces": {
"type": "text",
"placeholders": {}
},
"editWidgets": "Editatu widgetak",
"@editWidgets": {},
"widgetVideo": "Bideoa",
"@widgetVideo": {},
"widgetEtherpad": "Testu-oharra",
@ -2327,10 +2145,6 @@
"@user": {},
"custom": "Neurrira egindakoa",
"@custom": {},
"updateAvailable": "FluffyChaten eguneraketa bat dago eskuragai",
"@updateAvailable": {},
"updateNow": "Abiarazi eguneraketa atzeko planoan",
"@updateNow": {},
"storeInSecureStorageDescription": "Gorde berreskuratze-gakoa gailu honetako biltegiratze seguruan.",
"@storeInSecureStorageDescription": {},
"storeInAppleKeyChain": "Gorde Apple KeyChain-en",
@ -2341,14 +2155,10 @@
"@dehydrateTorLong": {},
"hydrateTorLong": "Esportatu al zenuen zure saioa TOR erabili zenuen azken aldian? Inportatu segidan eta jarraitu txateatzen.",
"@hydrateTorLong": {},
"noEmailWarning": "Sartu baliozko ePosta helbide bat, bestela ezingo duzu pasahitza berrezarri. Hala ere nahi ez baduzu, sakatu berriro botoia aurrera egiteko.",
"@noEmailWarning": {},
"dehydrateTor": "TOR Erabiltzaileak: Esportatu saioa",
"@dehydrateTor": {},
"hydrateTor": "TOR Erabiltzaileak: Inportatu esportatutako saioa",
"@hydrateTor": {},
"stories": "Storyak",
"@stories": {},
"saveKeyManuallyDescription": "Gorde eskuz gako hau gailuko partekatze-menua edo arbela erabiliz.",
"@saveKeyManuallyDescription": {},
"indexedDbErrorTitle": "Arazoak modu pribatuan",
@ -2420,8 +2230,6 @@
"@noBackupWarning": {},
"doNotShowAgain": "Ez erakutsi berriro",
"@doNotShowAgain": {},
"enterInviteLinkOrMatrixId": "Sartu gonbidapen-esteka edo Matrix IDa…",
"@enterInviteLinkOrMatrixId": {},
"fileIsTooBigForServer": "Zerbitzariak dio fitxategia handiegia dela bidali ahal izateko.",
"@fileIsTooBigForServer": {},
"noOtherDevicesFound": "Ez da beste gailurik aurkitu",
@ -2430,16 +2238,12 @@
"@startFirstChat": {},
"newSpaceDescription": "Guneek txatak taldekatzea ahalbidetzen dute eta komunitate pribatu edo publikoak osatzea.",
"@newSpaceDescription": {},
"endToEndEncryption": "Ertzetik ertzerako zifraketa",
"@endToEndEncryption": {},
"disableEncryptionWarning": "Segurtasun arrazoiak direla-eta, ezin duzu lehendik zifratuta zegoen txat bateko zifraketa ezgaitu.",
"@disableEncryptionWarning": {},
"encryptThisChat": "Zifratu txata",
"@encryptThisChat": {},
"commandHint_hug": "Bidali besarkada",
"@commandHint_hug": {},
"letsStart": "Has gaitezen",
"@letsStart": {},
"hugContent": "{senderName}(e)k besarkatu zaitu",
"@hugContent": {
"type": "text",
@ -2480,11 +2284,6 @@
},
"readUpToHere": "Honaino irakurrita",
"@readUpToHere": {},
"discover": "Arakatu",
"@discover": {
"type": "text",
"placeholders": {}
},
"fileHasBeenSavedAt": "Fitxategia {path}(e)n gorde da",
"@fileHasBeenSavedAt": {
"type": "text",
@ -2500,8 +2299,6 @@
"@report": {},
"signInWithPassword": "Hasi saioa pasahitzarekin",
"@signInWithPassword": {},
"continueWith": "Jarraitu honekin:",
"@continueWith": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Saiatu geroago edo aukeratu beste zerbitzari bat.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"signInWith": "Hasi saioa {provider}(r)ekin",
@ -2519,25 +2316,14 @@
"@importEmojis": {},
"importFromZipFile": "Inportatu .zip fitxategi batetik",
"@importFromZipFile": {},
"importZipFile": "Inportatu .zip fitxategia",
"@importZipFile": {},
"exportEmotePack": "Esportatu emote-sorta .zip gisa",
"@exportEmotePack": {},
"replace": "Ordezkatu",
"@replace": {},
"savedEmotePack": "Emote-sorta {path}(e)n gorde da!",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"sendTypingNotifications": "Jakinarazi idazten nagoela",
"@sendTypingNotifications": {},
"setColorTheme": "Ezarri kolore-gaia:",
"@setColorTheme": {},
"requests": "Eskaerak",
"@requests": {},
"tryAgain": "Saiatu berriro",
"@tryAgain": {},
"messagesStyle": "Mezuak:",
@ -2569,16 +2355,12 @@
"reason": {}
}
},
"anyoneCanKnock": "Edonork eska dezake batzeko baimena",
"@anyoneCanKnock": {},
"redactMessageDescription": "Mezua elkarrizketa honetako partaide guztientzat botako da atzera. Ezin da desegin.",
"@redactMessageDescription": {},
"addChatDescription": "Gehitu txat honen deskribapena",
"addChatDescription": "Gehitu txat honen deskribapena",
"@addChatDescription": {},
"directChat": "Banakako txata",
"@directChat": {},
"noOneCanJoin": "Ezin da inor batu",
"@noOneCanJoin": {},
"wrongPinEntered": "PIN okerra! Saiatu berriro {seconds} segundu barru…",
"@wrongPinEntered": {
"type": "text",
@ -2617,18 +2399,10 @@
"@removeDevicesDescription": {},
"unbanUserDescription": "Erabiltzailea txatera berriro sartu ahal izango da berak nahi izanez gero.",
"@unbanUserDescription": {},
"todoLists": "(Beta) Zeregin-zerrendak",
"@todoLists": {},
"editTodo": "Editatu zeregina",
"@editTodo": {},
"pushNotificationsNotAvailable": "Push jakinarazpenak ez daude erabilgarri",
"@pushNotificationsNotAvailable": {},
"pleaseAddATitle": "Gehitu izenburua",
"@pleaseAddATitle": {},
"makeAdminDescription": "Behin erabiltzaile hau administratzaile eginda, litekeena da desegin ezin izatea zuk dituzun baimenak izango dituelako.",
"@makeAdminDescription": {},
"noTodosYet": "Oraindik ez da zereginik gehitu txat honetara. Sortu lehen zeregina eta hasi elkarlanean besteekin. 📝",
"@noTodosYet": {},
"archiveRoomDescription": "Txata artxibategira mugituko da. Beste erabiltzaileek txatetik alde egin duzula ikusi ahal izango dute.",
"@archiveRoomDescription": {},
"hasKnocked": "{user}(e)k baimena eskatu du",
@ -2637,18 +2411,79 @@
"user": {}
}
},
"newTodo": "Zeregin berria",
"@newTodo": {},
"learnMore": "Gehiago irakurri",
"@learnMore": {},
"todoListChangedError": "Hara… zeregin-zerrenda aldatu da editatzen ari zinen bitartean.",
"@todoListChangedError": {},
"roomUpgradeDescription": "Gela bertsio berri gisa birsortuko da txata. Partaide guztiei jakinaraziko zaie txat berrira aldatu behar direla. Gehiago irakur dezakezu gela bertsioei buruz ondorengo estekan: https://spec.matrix.org/latest/rooms/",
"@roomUpgradeDescription": {},
"pleaseEnterANumber": "Sartu 0 baino zenbaki handiago bat",
"@pleaseEnterANumber": {},
"kickUserDescription": "Erabiltzailea txatetik kanporatu da baina ez zaio debekua ezarri. Txat publikoen kasuan, edozein momentutan batu daiteke berriro.",
"@kickUserDescription": {},
"todosUnencrypted": "Kontuan izan zereginak txateko guztientzat daudela ikusgai, eta ez daudela ertzetik ertzera zifratuta.",
"@todosUnencrypted": {}
"createGroupAndInviteUsers": "Sortu talde bat eta gonbidatu partaideak",
"@createGroupAndInviteUsers": {},
"startConversation": "Hasi elkarrizketa",
"@startConversation": {},
"groupCanBeFoundViaSearch": "Bilaketa erabiliz aurkitu daiteke taldea",
"@groupCanBeFoundViaSearch": {},
"noUsersFoundWithQuery": "Zoritxarrez ez da \"{query}\" duen erabiltzailerik aurkitu. Egiaztatu zuzen idatzi duzula.",
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
}
},
"yourGlobalUserIdIs": "Zure erabiltzaile-ID orokorra: ",
"@yourGlobalUserIdIs": {},
"commandHint_sendraw": "Bidali json gordina",
"@commandHint_sendraw": {},
"wrongRecoveryKey": "Barka baina ez dirudi berreskuratze-gako zuzena denik.",
"@wrongRecoveryKey": {},
"groupName": "Taldearen izena",
"@groupName": {},
"searchChatsRooms": "Bilatu #txatak, @erabiltzaileak…",
"@searchChatsRooms": {},
"blockListDescription": "Gogaitzen zaituzten erabiltzaileak blokeatu ditzakezu. Ez duzu blokeatutakoen zerrendan dituzun erabiltzaileen mezurik edo gelara batzeko gonbidapenik jasoko.",
"@blockListDescription": {},
"blockedUsers": "Blokeatutako erabiltzaileak",
"@blockedUsers": {},
"block": "blokeatu",
"@block": {},
"blockUsername": "Ezikusi erabiltzaile-izena",
"@blockUsername": {},
"databaseMigrationTitle": "Datu-basea optimizatu da",
"@databaseMigrationTitle": {},
"databaseMigrationBody": "Itxaron, litekeena da tarte bat behar izatea.",
"@databaseMigrationBody": {},
"publicSpaces": "Gune publikoak",
"@publicSpaces": {},
"passwordIsWrong": "Sartu duzun pasahitza okerra da",
"@passwordIsWrong": {},
"pleaseEnterYourCurrentPassword": "Sartu oraingo pasahitza",
"@pleaseEnterYourCurrentPassword": {},
"publicLink": "Esteka publikoa",
"@publicLink": {},
"nothingFound": "Ez da ezer aurkitu…",
"@nothingFound": {},
"newPassword": "Pasahitz berria",
"@newPassword": {},
"passwordsDoNotMatch": "Pasahitzak ez datoz bat",
"@passwordsDoNotMatch": {},
"subspace": "Azpi-gunea",
"@subspace": {},
"select": "Hautatu",
"@select": {},
"pleaseChooseAStrongPassword": "Aukeratu pasahitz sendo bat",
"@pleaseChooseAStrongPassword": {},
"addChatOrSubSpace": "Gehitu txata edo azpi-gunea",
"@addChatOrSubSpace": {},
"leaveEmptyToClearStatus": "Utzi hutsik zure egoera garbitzeko.",
"@leaveEmptyToClearStatus": {},
"joinSpace": "Batu gunera",
"@joinSpace": {},
"searchForUsers": "Bilatu @erabiltzaileak…",
"@searchForUsers": {},
"thisDevice": "Gailu hau:",
"@thisDevice": {},
"decline": "Baztertu",
"@decline": {}
}

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

File diff suppressed because it is too large Load diff

View file

@ -35,11 +35,6 @@
"type": "text",
"placeholders": {}
},
"addGroupDescription": "Engade a descrición do grupo",
"@addGroupDescription": {
"type": "text",
"placeholders": {}
},
"admin": "Admin",
"@admin": {
"type": "text",
@ -304,11 +299,6 @@
"type": "text",
"placeholders": {}
},
"changeWallpaper": "Cambiar imaxe de fondo",
"@changeWallpaper": {
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "Cambia o avatar",
"@changeYourAvatar": {
"type": "text",
@ -349,11 +339,6 @@
"type": "text",
"placeholders": {}
},
"chooseAUsername": "Elixe un identificador",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"clearArchive": "Baleirar arquivo",
"@clearArchive": {},
"close": "Pechar",
@ -529,11 +514,6 @@
"username": {}
}
},
"createNewGroup": "Crear novo grupo",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
"createNewSpace": "Novo espazo",
"@createNewSpace": {
"type": "text",
@ -599,11 +579,6 @@
"type": "text",
"placeholders": {}
},
"deny": "Denegar",
"@deny": {
"type": "text",
"placeholders": {}
},
"device": "Dispositivo",
"@device": {
"type": "text",
@ -644,11 +619,6 @@
"type": "text",
"placeholders": {}
},
"editChatPermissions": "Editar permisos da conversa",
"@editChatPermissions": {
"type": "text",
"placeholders": {}
},
"editDisplayname": "Editar nome público",
"@editDisplayname": {
"type": "text",
@ -736,18 +706,11 @@
"senderName": {}
}
},
"enterAGroupName": "Escribe un nome para o grupo",
"@enterAGroupName": {
"type": "text",
"placeholders": {}
},
"enterAnEmailAddress": "Escribe un enderezo de email",
"@enterAnEmailAddress": {
"type": "text",
"placeholders": {}
},
"enterASpacepName": "Escribe o nome para o espazo",
"@enterASpacepName": {},
"enterYourHomeserver": "Escribe o teu servidor de inicio",
"@enterYourHomeserver": {
"type": "text",
@ -810,16 +773,6 @@
"type": "text",
"placeholders": {}
},
"groupDescription": "Descrición do grupo",
"@groupDescription": {
"type": "text",
"placeholders": {}
},
"groupDescriptionHasBeenChanged": "Cambiouse a descrición do grupo",
"@groupDescriptionHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"groupIsPublic": "O grupo é público",
"@groupIsPublic": {
"type": "text",
@ -895,16 +848,6 @@
"type": "text",
"placeholders": {}
},
"ignoreListDescription": "Podes ignorar usuarias molestas. Non recibirás ningunha mensaxe nin convites a salas da túa lista personal de usuarias ignoradas.",
"@ignoreListDescription": {
"type": "text",
"placeholders": {}
},
"ignoreUsername": "Ignorar nome de usuaria",
"@ignoreUsername": {
"type": "text",
"placeholders": {}
},
"iHaveClickedOnLink": "Premín na ligazón",
"@iHaveClickedOnLink": {
"type": "text",
@ -1008,11 +951,6 @@
"localizedTimeShort": {}
}
},
"lastSeenLongTimeAgo": "Hai moito que non aparece",
"@lastSeenLongTimeAgo": {
"type": "text",
"placeholders": {}
},
"leave": "Saír",
"@leave": {
"type": "text",
@ -1072,23 +1010,11 @@
"homeserver": {}
}
},
"loginWith": "Conecta con {brand}",
"@loginWith": {
"type": "text",
"placeholders": {
"brand": {}
}
},
"logout": "Pechar sesión",
"@logout": {
"type": "text",
"placeholders": {}
},
"makeSureTheIdentifierIsValid": "Asegúrate de que o identificador é válido",
"@makeSureTheIdentifierIsValid": {
"type": "text",
"placeholders": {}
},
"memberChanges": "Cambios de participantes",
"@memberChanges": {
"type": "text",
@ -1104,11 +1030,6 @@
"type": "text",
"placeholders": {}
},
"messageWillBeRemovedWarning": "Vai ser eliminada a mensaxe para todas as participantes",
"@messageWillBeRemovedWarning": {
"type": "text",
"placeholders": {}
},
"moderator": "Moderadora",
"@moderator": {
"type": "text",
@ -1164,7 +1085,7 @@
"type": "text",
"placeholders": {}
},
"noGoogleServicesWarning": "Semella que non tes Firebase Cloud Messaging dispoñible no teu dispositivo. Para recibir notificacións push recomendamos que instales MicroG ou Unified Push.",
"noGoogleServicesWarning": "Semella que non tes Firebase Cloud Messaging dispoñible no teu dispositivo. Para recibir notificacións push recomendamos que instales ntfy. Con ntfy ou outro provedor Unified Push podes recibir notificacións push de xeito seguro. Podes descargar ntfy desde a PlayStore ou F-Droid.",
"@noGoogleServicesWarning": {
"type": "text",
"placeholders": {}
@ -1269,11 +1190,6 @@
"type": "text",
"placeholders": {}
},
"optionalGroupName": "(Optativo) Nome do grupo",
"@optionalGroupName": {
"type": "text",
"placeholders": {}
},
"or": "Ou",
"@or": {
"type": "text",
@ -1341,11 +1257,6 @@
"type": "text",
"placeholders": {}
},
"pleaseChooseAUsername": "Elixe un identificador",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"pleaseClickOnLink": "Preme na ligazón do email e segue as instrucións.",
"@pleaseClickOnLink": {
"type": "text",
@ -1356,11 +1267,6 @@
"type": "text",
"placeholders": {}
},
"pleaseEnterAMatrixIdentifier": "Escribe un ID Matrix.",
"@pleaseEnterAMatrixIdentifier": {
"type": "text",
"placeholders": {}
},
"pleaseEnterYourPassword": "Escribe o teu contrasinal",
"@pleaseEnterYourPassword": {
"type": "text",
@ -1529,22 +1435,6 @@
"username": {}
}
},
"seenByUserAndCountOthers": "{count, plural, other{Visto por {username} e {count} outras}}",
"@seenByUserAndCountOthers": {
"type": "text",
"placeholders": {
"username": {},
"count": {}
}
},
"seenByUserAndUser": "Visto por {username} e {username2}",
"@seenByUserAndUser": {
"type": "text",
"placeholders": {
"username": {},
"username2": {}
}
},
"send": "Enviar",
"@send": {
"type": "text",
@ -1646,11 +1536,6 @@
"type": "text",
"placeholders": {}
},
"setGroupDescription": "Establecer descrición do grupo",
"@setGroupDescription": {
"type": "text",
"placeholders": {}
},
"setInvitationLink": "Establecer ligazón do convite",
"@setInvitationLink": {
"type": "text",
@ -1693,11 +1578,6 @@
"type": "text",
"placeholders": {}
},
"signUp": "Rexistro",
"@signUp": {
"type": "text",
"placeholders": {}
},
"singlesignon": "Conexión Unificada SSO",
"@singlesignon": {
"type": "text",
@ -2016,21 +1896,11 @@
"type": "text",
"placeholders": {}
},
"youAreInvitedToThisChat": "Convidáronte a esta conversa",
"@youAreInvitedToThisChat": {
"type": "text",
"placeholders": {}
},
"youAreNoLongerParticipatingInThisChat": "Xa non participas desta conversa",
"@youAreNoLongerParticipatingInThisChat": {
"type": "text",
"placeholders": {}
},
"youCannotInviteYourself": "Non podes autoconvidarte",
"@youCannotInviteYourself": {
"type": "text",
"placeholders": {}
},
"youHaveBeenBannedFromThisChat": "Foches vetada nesta conversa",
"@youHaveBeenBannedFromThisChat": {
"type": "text",
@ -2047,8 +1917,6 @@
"@addToSpace": {},
"scanQrCode": "Escanear código QR",
"@scanQrCode": {},
"shareYourInviteLink": "Comparte a túa ligazón de convite",
"@shareYourInviteLink": {},
"sendOnEnter": "Enter para enviar",
"@sendOnEnter": {},
"homeserver": "Servidor de inicio",
@ -2067,19 +1935,8 @@
"@yourChatBackupHasBeenSetUp": {},
"unverified": "Sen verificar",
"@unverified": {},
"pleaseEnterValidEmail": "Escribe un enderezo de email válido.",
"@pleaseEnterValidEmail": {},
"passwordsDoNotMatch": "Os contrasinais non concordan!",
"@passwordsDoNotMatch": {},
"repeatPassword": "Repite o contrasinal",
"@repeatPassword": {},
"pleaseChooseAtLeastChars": "Escribe polo menos {min} caracteres.",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"messageInfo": "Info da mensaxe",
"@messageInfo": {},
"time": "Hora",
@ -2090,8 +1947,6 @@
"@openGallery": {},
"addToSpaceDescription": "Elixe un espazo ao que engadir esta conversa.",
"@addToSpaceDescription": {},
"loginWithOneClick": "Conéctate cun click",
"@loginWithOneClick": {},
"messageType": "Tipo de mensaxe",
"@messageType": {},
"removeFromSpace": "Retirar do espazo",
@ -2123,10 +1978,6 @@
"type": "text",
"placeholders": {}
},
"yourStory": "A túa Historia",
"@yourStory": {},
"replyHasBeenSent": "Enviouse a resposta",
"@replyHasBeenSent": {},
"videoWithSize": "Vídeo ({size})",
"@videoWithSize": {
"type": "text",
@ -2134,39 +1985,8 @@
"size": {}
}
},
"storyFrom": "Historia do {date}:\n{body}",
"@storyFrom": {
"type": "text",
"placeholders": {
"date": {},
"body": {}
}
},
"whoCanSeeMyStoriesDesc": "Ten en conta que as usuarias poden verse e contactar unhas coas outras na túa historia.",
"@whoCanSeeMyStoriesDesc": {},
"addToStory": "Engadir a historia",
"@addToStory": {},
"publish": "Publicar",
"@publish": {},
"whoCanSeeMyStories": "Quen pode ver as miñas historias?",
"@whoCanSeeMyStories": {},
"unsubscribeStories": "Retirar subscrición das historias",
"@unsubscribeStories": {},
"thisUserHasNotPostedAnythingYet": "A usuaria non publicou aínda ningunha historia",
"@thisUserHasNotPostedAnythingYet": {},
"bubbleSize": "Tamaño da burbulla",
"@bubbleSize": {
"type": "text",
"placeholders": {}
},
"whatIsGoingOn": "Que acontece?",
"@whatIsGoingOn": {},
"addDescription": "Engadir descrición",
"@addDescription": {},
"storyPrivacyWarning": "Ten en conta que outras persoas poden verse en contactar entre elas na túa historia. As historias son visibles durante 24 horas pero non hai garantía de que sexan eliminadas en tódolos dispositivos e servidores.",
"@storyPrivacyWarning": {},
"iUnderstand": "Comprendo",
"@iUnderstand": {},
"dismiss": "Desbotar",
"@dismiss": {},
"markAsRead": "Marcar como lido",
@ -2187,8 +2007,6 @@
"@videoCallsBetaWarning": {},
"unsupportedAndroidVersion": "Version de Android non soportada",
"@unsupportedAndroidVersion": {},
"matrixWidgets": "Widgets de Matrix",
"@matrixWidgets": {},
"reactedWith": "{sender} reaccionou con {reaction}",
"@reactedWith": {
"type": "text",
@ -2236,8 +2054,6 @@
"@widgetEtherpad": {},
"errorAddingWidget": "Erro ao engadir o widget.",
"@errorAddingWidget": {},
"editWidgets": "Editar widgets",
"@editWidgets": {},
"editBundlesForAccount": "Editar os feixes desta conta",
"@editBundlesForAccount": {},
"addToBundle": "Engadir ao feixe",
@ -2249,11 +2065,6 @@
"type": "text",
"placeholders": {}
},
"showDirectChatsInSpaces": "Mostrar Conversas Directas relacionadas nos Espazos",
"@showDirectChatsInSpaces": {
"type": "text",
"placeholders": {}
},
"youRejectedTheInvitation": "Rexeitaches o convite",
"@youRejectedTheInvitation": {},
"youBannedUser": "Vetaches a {user}",
@ -2302,10 +2113,6 @@
"user": {}
}
},
"noEmailWarning": "Escribe un enderezo de email válido. Doutro xeito non poderás restablecer o contrasinal. Se non queres, toca outra vez no botón para continuar.",
"@noEmailWarning": {},
"stories": "Historias",
"@stories": {},
"saveKeyManuallyDescription": "Garda esta chave manualmente usando o sistema para compartir do dispositivo ou portapapeis.",
"@saveKeyManuallyDescription": {},
"storeInAndroidKeystore": "Gardar en Android KeyStore",
@ -2356,10 +2163,6 @@
"@user": {},
"custom": "Personal",
"@custom": {},
"updateAvailable": "Actualización dispoñible para FluffyChat",
"@updateAvailable": {},
"updateNow": "Iniciar actualización en segundo plano",
"@updateNow": {},
"confirmMatrixId": "Confirma o teu ID Matrix para poder eliminar a conta.",
"@confirmMatrixId": {},
"supposedMxid": "Debería ser {mxid}",
@ -2443,12 +2246,8 @@
"senderName": {}
}
},
"enterInviteLinkOrMatrixId": "Escribe a ligazón de convite ou ID Matrix...",
"@enterInviteLinkOrMatrixId": {},
"encryptThisChat": "Cifrar esta conversa",
"@encryptThisChat": {},
"endToEndEncryption": "Cifraxe de extremo a extremo",
"@endToEndEncryption": {},
"disableEncryptionWarning": "Por razóns de seguridade non podes desactivar a cifraxe dunha conversa onde foi activada previamente.",
"@disableEncryptionWarning": {},
"sorryThatsNotPossible": "Lamentámolo... iso non é posible",
@ -2457,8 +2256,6 @@
"@deviceKeys": {},
"newSpaceDescription": "Os Espazos permítenche consolidar as túas conversas e construir comunidades públicas ou privadas.",
"@newSpaceDescription": {},
"letsStart": "Imos alá",
"@letsStart": {},
"startFirstChat": "Abre a túa primeira conversa",
"@startFirstChat": {},
"wasDirectChatDisplayName": "Conversa baleira (era {oldDisplayName})",
@ -2498,17 +2295,10 @@
"type": "text",
"placeholders": {}
},
"reportErrorDescription": "Vaia! Algo fallou. Inténtao máis tarde. Se queres, podes informar do problema aos desenvolvedores.",
"reportErrorDescription": "😭 Vaia! Algo fallou. Se queres, podes informar do problema ás persoas desenvolvedoras.",
"@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": {},
"signInWith": "Accede con {provider}",
@ -2526,19 +2316,10 @@
"@importEmojis": {},
"importFromZipFile": "Importar desde ficheiro .zip",
"@importFromZipFile": {},
"importZipFile": "Importar ficheiro .zip",
"@importZipFile": {},
"exportEmotePack": "Exportar paquete Emote como .zip",
"@exportEmotePack": {},
"replace": "Substituír",
"@replace": {},
"savedEmotePack": "Paquete emote gardado en {path}!",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"sendTypingNotifications": "Enviar notificación de escritura",
"@sendTypingNotifications": {},
"createGroup": "Crear grupo",
@ -2555,12 +2336,6 @@
"@setTheme": {},
"inviteContactToGroupQuestion": "Queres convidar a {contact} para que se una á conversa \"{groupName}\"?",
"@inviteContactToGroupQuestion": {},
"noGroupDescriptionYet": "Aínda non hai unha descrición do grupo.",
"@noGroupDescriptionYet": {},
"anyoneCanKnock": "Calquera pode chamar",
"@anyoneCanKnock": {},
"noOneCanJoin": "Ninguén pode unirse",
"@noOneCanJoin": {},
"tryAgain": "Intentar outra vez",
"@tryAgain": {},
"optionalRedactReason": "(Optativo) Razón para editar a mensaxe...",
@ -2584,7 +2359,7 @@
"@redactMessageDescription": {},
"invite": "Convidar",
"@invite": {},
"addChatDescription": "Engadir descrición da conversa",
"addChatDescription": "Engadir descrición da conversa...",
"@addChatDescription": {},
"chatPermissions": "Permisos da conversa",
"@chatPermissions": {},
@ -2600,8 +2375,6 @@
"@directChat": {},
"setChatDescription": "Escribir descrición da conversa",
"@setChatDescription": {},
"requests": "Solicitudes",
"@requests": {},
"inviteGroupChat": "📨 Convidar a conversa en grupo",
"@inviteGroupChat": {},
"invitePrivateChat": "📨 Convidar a conversa privada",
@ -2617,18 +2390,10 @@
"@removeDevicesDescription": {},
"unbanUserDescription": "A usuaria vai poder entrar outra vez na conversa se quere.",
"@unbanUserDescription": {},
"todoLists": "(Beta) Lista de tarefas",
"@todoLists": {},
"editTodo": "Editar tarefa",
"@editTodo": {},
"pushNotificationsNotAvailable": "Non están dispoñibles as notificacións push",
"@pushNotificationsNotAvailable": {},
"pleaseAddATitle": "Engade un título",
"@pleaseAddATitle": {},
"makeAdminDescription": "Cando convirtas a esta usuaria en admin non poderás desfacer a acción xa que terá os mesmos permisos ca ti.",
"@makeAdminDescription": {},
"noTodosYet": "Non se engadiron tarefas aínda a este chat. Crea primeiro a túa lista de tarefas e comeza a colaborar con outras. 📝",
"@noTodosYet": {},
"archiveRoomDescription": "Vaise mover o chat ao arquivo. Outras usuarias poderán ver que saíches da conversa.",
"@archiveRoomDescription": {},
"invalidInput": "Contido non válido!",
@ -2646,18 +2411,79 @@
"seconds": {}
}
},
"newTodo": "Nova tarefa",
"@newTodo": {},
"learnMore": "Saber máis",
"@learnMore": {},
"todoListChangedError": "Ooi... A lista cambiou mentras ti a editabas.",
"@todoListChangedError": {},
"roomUpgradeDescription": "Vaise recrear o chat coa nova versión da sala. Todas as participantes recibirán unha notificación para que cambien ao novo chat. Podes ler máis información acerca das versións das salas en https://spec.matrix.org/latest/rooms/",
"@roomUpgradeDescription": {},
"pleaseEnterANumber": "Escribe un número maior de cero",
"@pleaseEnterANumber": {},
"kickUserDescription": "A usuaria foi expulsada pero non vetada. En conversas públicas a usuaria pode volver cando queira.",
"@kickUserDescription": {},
"todosUnencrypted": "",
"@todosUnencrypted": {}
"createGroupAndInviteUsers": "Crear un grupo e convidar usuarias",
"@createGroupAndInviteUsers": {},
"groupCanBeFoundViaSearch": "O grupo pódese atopar ao buscar",
"@groupCanBeFoundViaSearch": {},
"noUsersFoundWithQuery": "Lamentamos non atopar ningunha usuaria con \"{query}\". Comproba se está ben escrito.",
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
}
},
"yourGlobalUserIdIs": "O teu ID-usuaria global é: ",
"@yourGlobalUserIdIs": {},
"groupName": "Nome do grupo",
"@groupName": {},
"searchChatsRooms": "Buscar #conversas, @usuarias...",
"@searchChatsRooms": {},
"startConversation": "Iniciar conversa",
"@startConversation": {},
"commandHint_sendraw": "Enviar json sen editar",
"@commandHint_sendraw": {},
"wrongRecoveryKey": "Desculpa... non semella ser o xeito correcto de recuperar a chave.",
"@wrongRecoveryKey": {},
"blockListDescription": "Podes bloquear usuarias que che molesten. Non recibirás mensaxes nin convites para salas procedentes das usuarias da túa lista persoal de bloqueo.",
"@blockListDescription": {},
"blockedUsers": "Usuarias bloqueadas",
"@blockedUsers": {},
"block": "bloquear",
"@block": {},
"blockUsername": "Ignorar identificador",
"@blockUsername": {},
"thisDevice": "Este dispositivo:",
"@thisDevice": {},
"publicSpaces": "Espazos públicos",
"@publicSpaces": {},
"passwordIsWrong": "O contrasinal escrito non é correcto",
"@passwordIsWrong": {},
"pleaseEnterYourCurrentPassword": "Escribe o contrasinal actual",
"@pleaseEnterYourCurrentPassword": {},
"publicLink": "Ligazón pública",
"@publicLink": {},
"nothingFound": "Non atopamos nada...",
"@nothingFound": {},
"decline": "Desbotar",
"@decline": {},
"newPassword": "Novo contrasinal",
"@newPassword": {},
"passwordsDoNotMatch": "Os contrasinais non concordan",
"@passwordsDoNotMatch": {},
"subspace": "Subespazo",
"@subspace": {},
"select": "Escolle",
"@select": {},
"pleaseChooseAStrongPassword": "Elixe un contrasinal forte",
"@pleaseChooseAStrongPassword": {},
"addChatOrSubSpace": "Engadir chat ou sub espazo",
"@addChatOrSubSpace": {},
"leaveEmptyToClearStatus": "Deixa baleiro para limpar o teu estado.",
"@leaveEmptyToClearStatus": {},
"joinSpace": "Únete ao espazo",
"@joinSpace": {},
"searchForUsers": "Buscar @persoas...",
"@searchForUsers": {},
"databaseMigrationTitle": "Base de datos optimizada",
"@databaseMigrationTitle": {},
"databaseMigrationBody": "Agarda, podería levarnos un pouco.",
"@databaseMigrationBody": {}
}

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

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

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

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

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

@ -34,11 +34,6 @@
"type": "text",
"placeholders": {}
},
"addGroupDescription": "Adicionar uma descrição para o grupo",
"@addGroupDescription": {
"type": "text",
"placeholders": {}
},
"admin": "Admin",
"@admin": {
"type": "text",
@ -303,11 +298,6 @@
"type": "text",
"placeholders": {}
},
"changeWallpaper": "Alterar o pano de fundo",
"@changeWallpaper": {
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "Alterar seu avatar",
"@changeYourAvatar": {
"type": "text",
@ -348,11 +338,6 @@
"type": "text",
"placeholders": {}
},
"chooseAUsername": "Escolha um nome de usuário",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"clearArchive": "Limpar arquivo",
"@clearArchive": {},
"close": "Fechar",
@ -528,11 +513,6 @@
"username": {}
}
},
"createNewGroup": "Novo grupo",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
"createNewSpace": "Novo espaço",
"@createNewSpace": {
"type": "text",
@ -598,11 +578,6 @@
"type": "text",
"placeholders": {}
},
"deny": "Rejeitar",
"@deny": {
"type": "text",
"placeholders": {}
},
"device": "Dispositivo",
"@device": {
"type": "text",
@ -643,11 +618,6 @@
"type": "text",
"placeholders": {}
},
"editChatPermissions": "Editar permissões da conversa",
"@editChatPermissions": {
"type": "text",
"placeholders": {}
},
"editDisplayname": "Editar nome de exibição",
"@editDisplayname": {
"type": "text",
@ -735,18 +705,11 @@
"senderName": {}
}
},
"enterAGroupName": "Insira um nome de grupo",
"@enterAGroupName": {
"type": "text",
"placeholders": {}
},
"enterAnEmailAddress": "Inserir endereço de e-mail",
"@enterAnEmailAddress": {
"type": "text",
"placeholders": {}
},
"enterASpacepName": "Insira um nome pro espaço",
"@enterASpacepName": {},
"enterYourHomeserver": "Insira um servidor matriz",
"@enterYourHomeserver": {
"type": "text",
@ -809,16 +772,6 @@
"type": "text",
"placeholders": {}
},
"groupDescription": "Descrição do grupo",
"@groupDescription": {
"type": "text",
"placeholders": {}
},
"groupDescriptionHasBeenChanged": "Descrição do grupo alterada",
"@groupDescriptionHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"groupIsPublic": "Grupo público",
"@groupIsPublic": {
"type": "text",
@ -894,16 +847,6 @@
"type": "text",
"placeholders": {}
},
"ignoreListDescription": "Você pode ignorar usuários que estão lhe pertubando. Não será possível receber mensagens ou convites de usuários na sua lista pessoal de ignorados.",
"@ignoreListDescription": {
"type": "text",
"placeholders": {}
},
"ignoreUsername": "Ignorar usuário",
"@ignoreUsername": {
"type": "text",
"placeholders": {}
},
"iHaveClickedOnLink": "Eu cliquei no link",
"@iHaveClickedOnLink": {
"type": "text",
@ -954,7 +897,7 @@
"type": "text",
"placeholders": {}
},
"inviteText": "{username} convidou você para o FluffyChat. \n1. Instale o FluffyChat: https://fluffychat.im \n2. Entre ou crie uma conta \n3. Abra o link do convite: {link}",
"inviteText": "{username} convidou você para o FluffyChat. \n1. Visite fluffychat.im e instale o aplicativo\n2. Entre ou crie uma conta \n3. Abra o link do convite:\n {link}",
"@inviteText": {
"type": "text",
"placeholders": {
@ -1007,11 +950,6 @@
"localizedTimeShort": {}
}
},
"lastSeenLongTimeAgo": "Visto há muito tempo atrás",
"@lastSeenLongTimeAgo": {
"type": "text",
"placeholders": {}
},
"leave": "Sair",
"@leave": {
"type": "text",
@ -1071,23 +1009,11 @@
"homeserver": {}
}
},
"loginWith": "Entrar com {brand}",
"@loginWith": {
"type": "text",
"placeholders": {
"brand": {}
}
},
"logout": "Encerrar sessão",
"@logout": {
"type": "text",
"placeholders": {}
},
"makeSureTheIdentifierIsValid": "Certifique-se de que a identificação é válida",
"@makeSureTheIdentifierIsValid": {
"type": "text",
"placeholders": {}
},
"memberChanges": "Alterações de membros",
"@memberChanges": {
"type": "text",
@ -1103,11 +1029,6 @@
"type": "text",
"placeholders": {}
},
"messageWillBeRemovedWarning": "Mensagem será removida para todos os participantes",
"@messageWillBeRemovedWarning": {
"type": "text",
"placeholders": {}
},
"moderator": "Moderador",
"@moderator": {
"type": "text",
@ -1163,7 +1084,7 @@
"type": "text",
"placeholders": {}
},
"noGoogleServicesWarning": "Aparentemente você não tem serviços Google no seu celular. Boa decisão para a sua privacidade! Para receber notificações no FluffyChat, recomendamos usar https://microg.org/ ou https://unifiedpush.org.",
"noGoogleServicesWarning": "Aparentemente você não tem serviços Google no seu celular. Para receber notificações no FluffyChat, recomendamos instalar ntfy.",
"@noGoogleServicesWarning": {
"type": "text",
"placeholders": {}
@ -1268,11 +1189,6 @@
"type": "text",
"placeholders": {}
},
"optionalGroupName": "(Opcional) Nome do Grupo",
"@optionalGroupName": {
"type": "text",
"placeholders": {}
},
"or": "Ou",
"@or": {
"type": "text",
@ -1340,11 +1256,6 @@
"type": "text",
"placeholders": {}
},
"pleaseChooseAUsername": "Por favor, escolha um nome de usuário",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"pleaseClickOnLink": "Por favor, clique a ligação no e-mail para prosseguir.",
"@pleaseClickOnLink": {
"type": "text",
@ -1355,11 +1266,6 @@
"type": "text",
"placeholders": {}
},
"pleaseEnterAMatrixIdentifier": "Por favor, insira o ID Matrix.",
"@pleaseEnterAMatrixIdentifier": {
"type": "text",
"placeholders": {}
},
"pleaseEnterYourPassword": "Por favor, insira sua senha",
"@pleaseEnterYourPassword": {
"type": "text",
@ -1528,22 +1434,6 @@
"username": {}
}
},
"seenByUserAndCountOthers": "{count, plural, other{Visto por {username} e mais {count} pessoas}}",
"@seenByUserAndCountOthers": {
"type": "text",
"placeholders": {
"username": {},
"count": {}
}
},
"seenByUserAndUser": "Visto por {username} e {username2}",
"@seenByUserAndUser": {
"type": "text",
"placeholders": {
"username": {},
"username2": {}
}
},
"send": "Enviar",
"@send": {
"type": "text",
@ -1645,11 +1535,6 @@
"type": "text",
"placeholders": {}
},
"setGroupDescription": "Fixar uma descrição do grupo",
"@setGroupDescription": {
"type": "text",
"placeholders": {}
},
"setInvitationLink": "Enviar link de convite",
"@setInvitationLink": {
"type": "text",
@ -1692,11 +1577,6 @@
"type": "text",
"placeholders": {}
},
"signUp": "Registrar",
"@signUp": {
"type": "text",
"placeholders": {}
},
"singlesignon": "Identidade Única",
"@singlesignon": {
"type": "text",
@ -1960,7 +1840,7 @@
"type": "text",
"placeholders": {}
},
"wallpaper": "Pano de fundo",
"wallpaper": "Pano de fundo:",
"@wallpaper": {
"type": "text",
"placeholders": {}
@ -2015,21 +1895,11 @@
"type": "text",
"placeholders": {}
},
"youAreInvitedToThisChat": "Você foi convidada(o) a esta conversa",
"@youAreInvitedToThisChat": {
"type": "text",
"placeholders": {}
},
"youAreNoLongerParticipatingInThisChat": "Você não está mais participando desta conversa",
"@youAreNoLongerParticipatingInThisChat": {
"type": "text",
"placeholders": {}
},
"youCannotInviteYourself": "Você não pode se autoconvidar",
"@youCannotInviteYourself": {
"type": "text",
"placeholders": {}
},
"youHaveBeenBannedFromThisChat": "Você foi banido desta conversa",
"@youHaveBeenBannedFromThisChat": {
"type": "text",
@ -2040,19 +1910,12 @@
"type": "text",
"placeholders": {}
},
"shareYourInviteLink": "Compartilhar o link do convite",
"@shareYourInviteLink": {},
"oneClientLoggedOut": "Um dos seus clientes foi desvinculado",
"@oneClientLoggedOut": {},
"addAccount": "Adicionar conta",
"@addAccount": {},
"unverified": "Não verificado",
"@unverified": {},
"bubbleSize": "Tamanho do balão",
"@bubbleSize": {
"type": "text",
"placeholders": {}
},
"yourChatBackupHasBeenSetUp": "Seu backup de conversas foi configurado.",
"@yourChatBackupHasBeenSetUp": {},
"editBundlesForAccount": "Editar coleções para esta conta",
@ -2065,49 +1928,14 @@
"@sender": {},
"publish": "Publicar",
"@publish": {},
"storyPrivacyWarning": "Por favor, note que pessoas podem ver e contactar umas às outras no seu painel. Ele ficará visível por apenas 24 horas, mas não há garantias de que será apagado por todos dispositivos e servidores.",
"@storyPrivacyWarning": {},
"iUnderstand": "Eu compreendo",
"@iUnderstand": {},
"yourStory": "Seu painel",
"@yourStory": {},
"replyHasBeenSent": "Resposta enviada",
"@replyHasBeenSent": {},
"removeFromSpace": "Remover do espaço",
"@removeFromSpace": {},
"loginWithOneClick": "Entrar com um clique",
"@loginWithOneClick": {},
"storyFrom": "Painel de {date}:\n{body}",
"@storyFrom": {
"type": "text",
"placeholders": {
"date": {},
"body": {}
}
},
"whoCanSeeMyStoriesDesc": "Por favor, note que pessoas podem ver e contactar umas às outras no seu painel.",
"@whoCanSeeMyStoriesDesc": {},
"link": "Link",
"@link": {},
"start": "Começar",
"@start": {},
"whatIsGoingOn": "O que está acontecendo?",
"@whatIsGoingOn": {},
"addDescription": "Adicionar descrição",
"@addDescription": {},
"passwordsDoNotMatch": "Senhas diferentes!",
"@passwordsDoNotMatch": {},
"pleaseEnterValidEmail": "Por favor, insira um email válido.",
"@pleaseEnterValidEmail": {},
"repeatPassword": "Repita a senha",
"@repeatPassword": {},
"pleaseChooseAtLeastChars": "Por favor, use ao menos {min} caracteres.",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"addToSpace": "Adicionar ao espaço",
"@addToSpace": {},
"sendOnEnter": "Enviar ao pressionar enter",
@ -2159,14 +1987,6 @@
"@openGallery": {},
"addToSpaceDescription": "Selecione um espaço para adicionar esta conversa.",
"@addToSpaceDescription": {},
"addToStory": "Adicionar ao painel",
"@addToStory": {},
"whoCanSeeMyStories": "Quem pode ver meu painel?",
"@whoCanSeeMyStories": {},
"unsubscribeStories": "Desinscrever de painéis",
"@unsubscribeStories": {},
"thisUserHasNotPostedAnythingYet": "Este(a) usuário(a) ainda não postou no seu painel",
"@thisUserHasNotPostedAnythingYet": {},
"videoWithSize": "Vídeo ({size})",
"@videoWithSize": {
"type": "text",
@ -2178,8 +1998,6 @@
"@markAsRead": {},
"dismiss": "Descartar",
"@dismiss": {},
"matrixWidgets": "Ferramentas Matrix",
"@matrixWidgets": {},
"separateChatTypes": "Separar Conversas Diretas e Grupos",
"@separateChatTypes": {
"type": "text",
@ -2246,13 +2064,6 @@
"@nextAccount": {},
"previousAccount": "Conta anterior",
"@previousAccount": {},
"editWidgets": "Editar ferramentas",
"@editWidgets": {},
"showDirectChatsInSpaces": "Mostrar Conversas Diretas relacionadas nos Espaços",
"@showDirectChatsInSpaces": {
"type": "text",
"placeholders": {}
},
"youRejectedTheInvitation": "Você rejeitou o convite",
"@youRejectedTheInvitation": {},
"youBannedUser": "Você baniu {user}",
@ -2314,8 +2125,6 @@
"@indexedDbErrorLong": {},
"users": "Usuários",
"@users": {},
"stories": "Stories",
"@stories": {},
"confirmMatrixId": "Por favor, confirme seu ID Matrix para apagar sua conta.",
"@confirmMatrixId": {},
"supposedMxid": "Isto deveria ser {mxid}",
@ -2351,7 +2160,7 @@
"senderName": {}
}
},
"commandHint_markasdm": "Marcar como conversa direta",
"commandHint_markasdm": "Marcar como conversa direta para o ID Matrix dado",
"@commandHint_markasdm": {},
"commandHint_markasgroup": "Marcar como grupo",
"@commandHint_markasgroup": {},
@ -2413,10 +2222,6 @@
},
"doNotShowAgain": "Não mostrar novamente",
"@doNotShowAgain": {},
"updateAvailable": "Atualização do FluffyChat disponível",
"@updateAvailable": {},
"updateNow": "Iniciar atualização nos bastidores",
"@updateNow": {},
"unlockOldMessages": "Destrancar mensagens antigas",
"@unlockOldMessages": {},
"dehydrate": "Exportar sessão e limpar dispositivo",
@ -2438,9 +2243,7 @@
"number": {}
}
},
"noEmailWarning": "Por favor, insira um e-mail válido. De outro modo, você não conseguirá recuperar sua senha. Caso prefira assim, toque novamente no botão para continuar.",
"@noEmailWarning": {},
"noKeyForThisMessage": "Isto pode ocorrer caso a mensagem tenha sido enviada antes da entrada na sua conta com este dispositivo.\n\nTambém é possível que o remetente tenha bloqueado o seu dispositivo ou ocorreu algum problema com a conexão.\n\nVocê consegue ler as mensagens em outra sessão? Então, pode transferir as mensagens de lá! Vá em Configurações > Dispositivos e confira se os dispositivos verificaram um ao outro. Quando abrir a conversa da próxima vez e ambas as sessões estiverem abertas, as chaves serão transmitidas automaticamente.\n\nNão gostaria de perder suas chaves quando sair ou trocar de dispositivos? Certifique-se que o backup de conversas esteja habilitado nas configurações.",
"noKeyForThisMessage": "Isto pode ocorrer caso a mensagem tenha sido enviada antes da entrada na sua conta com este dispositivo.\n\nTambém é possível que o remetente tenha bloqueado o seu dispositivo ou ocorreu algum problema com a conexão.\n\nVocê consegue ler as mensagens em outra sessão? Então, pode transferir as mensagens de lá! Vá em Configurações > Dispositivos e confira se os dispositivos verificaram um ao outro. Quando abrir a sala da próxima vez e ambas as sessões estiverem abertas, as chaves serão transmitidas automaticamente.\n\nNão gostaria de perder suas chaves quando sair ou trocar de dispositivos? Certifique-se que o backup de conversas esteja habilitado nas configurações.",
"@noKeyForThisMessage": {},
"allRooms": "Todos os Chats em Grupo",
"@allRooms": {
@ -2457,101 +2260,75 @@
"@importFromZipFile": {},
"sendTypingNotifications": "Enviar notificações de digitação",
"@sendTypingNotifications": {},
"discover": "Descobrir",
"@discover": {
"type": "text",
"placeholders": {}
},
"startFirstChat": "Comece seu primeiro chat",
"@startFirstChat": {},
"savedEmotePack": "Pacote de emote salvo em {path}!",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"importZipFile": "Importar arquivo .zip",
"@importZipFile": {},
"exportEmotePack": "Exportar pacote de Emotes como .zip",
"@exportEmotePack": {},
"replace": "Substituir",
"@replace": {},
"jumpToLastReadMessage": "",
"jumpToLastReadMessage": "Pular para a última mensagem lida",
"@jumpToLastReadMessage": {},
"reportErrorDescription": "",
"reportErrorDescription": "😭 Ah, não. Algo deu errado. Se quiser, pode relatar isto aos desenvolvedores.",
"@reportErrorDescription": {},
"setColorTheme": "",
"setColorTheme": "Aplicar paleta de cor:",
"@setColorTheme": {},
"banUserDescription": "",
"banUserDescription": "O usuário será banido da conversa e não poderá participar novamente até que isto seja revogado.",
"@banUserDescription": {},
"requests": "",
"@requests": {},
"removeDevicesDescription": "",
"removeDevicesDescription": "Você encerrará a sessão neste dispositivo e não poderá mais receber mensagens.",
"@removeDevicesDescription": {},
"tryAgain": "",
"tryAgain": "Tente novamente",
"@tryAgain": {},
"unbanUserDescription": "",
"unbanUserDescription": "O usuário poderá ingressar novamente na conversa, caso tente.",
"@unbanUserDescription": {},
"todoLists": "",
"@todoLists": {},
"messagesStyle": "",
"messagesStyle": "Mensagens:",
"@messagesStyle": {},
"newSpaceDescription": "",
"newSpaceDescription": "Espaços permitem que você consolide suas conversas e construa comunidades públicas ou privadas.",
"@newSpaceDescription": {},
"chatDescription": "",
"chatDescription": "Descrição da conversa",
"@chatDescription": {},
"encryptThisChat": "",
"encryptThisChat": "Encriptar esta conversa",
"@encryptThisChat": {},
"reopenChat": "",
"reopenChat": "Reabrir conversa",
"@reopenChat": {},
"editTodo": "",
"@editTodo": {},
"pushNotificationsNotAvailable": "",
"pushNotificationsNotAvailable": "Notificações não estão disponíveis",
"@pushNotificationsNotAvailable": {},
"invalidServerName": "",
"invalidServerName": "Nome de usuário inválido",
"@invalidServerName": {},
"chatPermissions": "",
"chatPermissions": "Permissões da conversa",
"@chatPermissions": {},
"signInWithPassword": "",
"signInWithPassword": "Autenticar com senha",
"@signInWithPassword": {},
"pleaseAddATitle": "",
"@pleaseAddATitle": {},
"makeAdminDescription": "",
"makeAdminDescription": "Assim que promover este usuário a administrador, não poderá desfazê-lo e ele terá as mesmas permissões que você.",
"@makeAdminDescription": {},
"setChatDescription": "",
"setChatDescription": "Inserir descrição da conversa",
"@setChatDescription": {},
"noOtherDevicesFound": "",
"noOtherDevicesFound": "Nenhum outro dispositivo encontrado",
"@noOtherDevicesFound": {},
"redactedBy": "",
"redactedBy": "Removido por {username}",
"@redactedBy": {
"type": "text",
"placeholders": {
"username": {}
}
},
"signInWith": "",
"signInWith": "Autenticar com {provider}",
"@signInWith": {
"type": "text",
"placeholders": {
"provider": {}
}
},
"fileIsTooBigForServer": "",
"fileIsTooBigForServer": "O servidor avisa que o arquivo é grande demais para ser enviado.",
"@fileIsTooBigForServer": {},
"noTodosYet": "",
"@noTodosYet": {},
"readUpToHere": "",
"readUpToHere": "Marcar como lido até aqui",
"@readUpToHere": {},
"optionalRedactReason": "",
"optionalRedactReason": "(Opcional) Motivo para remover esta mensagem.",
"@optionalRedactReason": {},
"archiveRoomDescription": "",
"archiveRoomDescription": "A conversa será movida para o arquivo. Outros usuários verão que você deixou a conversa.",
"@archiveRoomDescription": {},
"letsStart": "",
"@letsStart": {},
"inviteContactToGroupQuestion": "",
"inviteContactToGroupQuestion": "Você quer convidar {contact} para a conversa \"{groupName}\"?",
"@inviteContactToGroupQuestion": {},
"redactedByBecause": "",
"redactedByBecause": "Removido por {username}, pois: \"{reason}\"",
"@redactedByBecause": {
"type": "text",
"placeholders": {
@ -2559,102 +2336,153 @@
"reason": {}
}
},
"fileHasBeenSavedAt": "",
"fileHasBeenSavedAt": "Arquivo salvo em {path}",
"@fileHasBeenSavedAt": {
"type": "text",
"placeholders": {
"path": {}
}
},
"anyoneCanKnock": "",
"@anyoneCanKnock": {},
"redactMessageDescription": "",
"redactMessageDescription": "A mensagem será removida para todos participantes desta conversa. Isto não poderá ser desfeito.",
"@redactMessageDescription": {},
"invalidInput": "",
"invalidInput": "Inserção inválida!",
"@invalidInput": {},
"todosUnencrypted": "",
"@todosUnencrypted": {},
"report": "",
"report": "Relatar",
"@report": {},
"addChatDescription": "",
"addChatDescription": "Inserir descrição da conversa...",
"@addChatDescription": {},
"hasKnocked": "",
"hasKnocked": "{user} bateu na porta",
"@hasKnocked": {
"placeholders": {
"user": {}
}
},
"openLinkInBrowser": "",
"openLinkInBrowser": "Abrir no navegador",
"@openLinkInBrowser": {},
"disableEncryptionWarning": "",
"disableEncryptionWarning": "Por razões de segurança, não possível desabilitar a encriptação uma vez habilitada.",
"@disableEncryptionWarning": {},
"directChat": "",
"directChat": "Conversa direta",
"@directChat": {},
"noOneCanJoin": "",
"@noOneCanJoin": {},
"wrongPinEntered": "",
"wrongPinEntered": "PIN incorreto! Tente novamente em {seconds} segundos...",
"@wrongPinEntered": {
"type": "text",
"placeholders": {
"seconds": {}
}
},
"inviteGroupChat": "",
"inviteGroupChat": "📨 Convidar para o grupo",
"@inviteGroupChat": {},
"invitePrivateChat": "",
"invitePrivateChat": "📨 Convidar para uma conversa privada",
"@invitePrivateChat": {},
"wasDirectChatDisplayName": "",
"wasDirectChatDisplayName": "Conversa vazia (era {oldDisplayName})",
"@wasDirectChatDisplayName": {
"type": "text",
"placeholders": {
"oldDisplayName": {}
}
},
"noChatDescriptionYet": "",
"noChatDescriptionYet": "Nenhuma descrição da conversa disponível.",
"@noChatDescriptionYet": {},
"newTodo": "",
"@newTodo": {},
"learnMore": "",
"learnMore": "Saiba mais",
"@learnMore": {},
"chatDescriptionHasBeenChanged": "",
"chatDescriptionHasBeenChanged": "Descrição da conversa alterada",
"@chatDescriptionHasBeenChanged": {},
"todoListChangedError": "",
"@todoListChangedError": {},
"enterInviteLinkOrMatrixId": "",
"@enterInviteLinkOrMatrixId": {},
"roomUpgradeDescription": "",
"roomUpgradeDescription": "A conversa será recriada com a nova versão de sala. Todos participantes será notificados e terão que migrar para a nova sala. Você pode encontrar mais informações sobre versões de sala em https://spec.matrix.org/latest/room/",
"@roomUpgradeDescription": {},
"pleaseEnterANumber": "",
"pleaseEnterANumber": "Por favor, insira um número maior que 0",
"@pleaseEnterANumber": {},
"profileNotFound": "",
"profileNotFound": "O usuário não foi encontrado neste servidor. Talvez um problema de conexão ou o usuário não existe.",
"@profileNotFound": {},
"jump": "",
"jump": "Pular",
"@jump": {},
"sorryThatsNotPossible": "",
"sorryThatsNotPossible": "Desculpe... isto não é possível",
"@sorryThatsNotPossible": {},
"shareInviteLink": "",
"shareInviteLink": "Compartilhar convite",
"@shareInviteLink": {},
"deviceKeys": "",
"deviceKeys": "Chaves de dispositivo:",
"@deviceKeys": {},
"emoteKeyboardNoRecents": "",
"emoteKeyboardNoRecents": "Emotes recentes aparecem aqui...",
"@emoteKeyboardNoRecents": {
"type": "text",
"placeholders": {}
},
"endToEndEncryption": "",
"@endToEndEncryption": {},
"setTheme": "",
"setTheme": "Aplicar tema:",
"@setTheme": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "",
"pleaseTryAgainLaterOrChooseDifferentServer": "Por favor, tente novamente mais tarde ou escolha um servidor diferente.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"createGroup": "",
"createGroup": "Criar grupo",
"@createGroup": {},
"noBackupWarning": "",
"noBackupWarning": "Atenção! Sem habilitar o backup de conversa, você perderá acesso a suas mensagens encriptadas. É altamente recomendável habilitar o backup antes de encerrar a sessão.",
"@noBackupWarning": {},
"kickUserDescription": "",
"kickUserDescription": "O usuário foi enxotado da conversa, mas não banido. Em conversas públicas, o usuário pode reingressar a qualquer momento.",
"@kickUserDescription": {},
"invite": "",
"invite": "Convidar",
"@invite": {},
"continueWith": "",
"@continueWith": {}
"blockListDescription": "Você pode bloquear usuários que estejam perturbando. Você não receberá mensagens ou convites de usuários na sua lista pessoal de bloqueios.",
"@blockListDescription": {},
"createGroupAndInviteUsers": "Criar um grupo e convidar pessoas",
"@createGroupAndInviteUsers": {},
"thisDevice": "Este dispositivo:",
"@thisDevice": {},
"startConversation": "Começar uma conversa",
"@startConversation": {},
"publicSpaces": "Espaços públicos",
"@publicSpaces": {},
"blockedUsers": "Usuários bloqueados",
"@blockedUsers": {},
"passwordIsWrong": "A senha inserida está incorreta",
"@passwordIsWrong": {},
"pleaseEnterYourCurrentPassword": "Por favor, insira sua senha atual",
"@pleaseEnterYourCurrentPassword": {},
"groupCanBeFoundViaSearch": "Grupos podem ser encontrados via busca",
"@groupCanBeFoundViaSearch": {},
"publicLink": "Link público",
"@publicLink": {},
"noUsersFoundWithQuery": "Infelizmente, não foi encontrado usuário via \"{query}\". Por favor, verifique se digitou corretamente.",
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
}
},
"block": "Bloquear",
"@block": {},
"nothingFound": "Nada foi encontrado...",
"@nothingFound": {},
"yourGlobalUserIdIs": "Seu ID global é: ",
"@yourGlobalUserIdIs": {},
"decline": "Rejeitar",
"@decline": {},
"newPassword": "Nova senha",
"@newPassword": {},
"passwordsDoNotMatch": "Senhas não batem",
"@passwordsDoNotMatch": {},
"commandHint_sendraw": "Enviar JSON puro",
"@commandHint_sendraw": {},
"wrongRecoveryKey": "Desculpe... esta não parece ser a chave de recuperação correta.",
"@wrongRecoveryKey": {},
"subspace": "Subespaço",
"@subspace": {},
"select": "Selecionar",
"@select": {},
"pleaseChooseAStrongPassword": "Por favor, escolha uma senha forte",
"@pleaseChooseAStrongPassword": {},
"blockUsername": "Ignore usuário",
"@blockUsername": {},
"addChatOrSubSpace": "Adicionar conversa ou subespaço",
"@addChatOrSubSpace": {},
"groupName": "Nome do grupo",
"@groupName": {},
"leaveEmptyToClearStatus": "Deixe em branco para limpar seu Status.",
"@leaveEmptyToClearStatus": {},
"joinSpace": "Ingressar no espaço",
"@joinSpace": {},
"searchForUsers": "Buscar por @usuários...",
"@searchForUsers": {},
"databaseMigrationTitle": "Banco de dados otimizado",
"@databaseMigrationTitle": {},
"searchChatsRooms": "Buscar por #conversas, @usuários...",
"@searchChatsRooms": {},
"databaseMigrationBody": "Por favor, espere. Isto pode demorar um pouco.",
"@databaseMigrationBody": {}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@
"type": "text",
"placeholders": {}
},
"acceptedTheInvitation": "👍 {username} принял(а) приглашение войти в чат",
"acceptedTheInvitation": "👍 {username} принял(а) приглашение",
"@acceptedTheInvitation": {
"type": "text",
"placeholders": {
@ -35,11 +35,6 @@
"type": "text",
"placeholders": {}
},
"addGroupDescription": "Добавить описание группы",
"@addGroupDescription": {
"type": "text",
"placeholders": {}
},
"admin": "Администратор",
"@admin": {
"type": "text",
@ -304,11 +299,6 @@
"type": "text",
"placeholders": {}
},
"changeWallpaper": "Изменить фон чатов",
"@changeWallpaper": {
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "Изменить свой аватар",
"@changeYourAvatar": {
"type": "text",
@ -349,11 +339,6 @@
"type": "text",
"placeholders": {}
},
"chooseAUsername": "Выберите имя пользователя",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"clearArchive": "Очистить архив",
"@clearArchive": {},
"close": "Закрыть",
@ -529,11 +514,6 @@
"username": {}
}
},
"createNewGroup": "Новая группа",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
"createNewSpace": "Новое пространство",
"@createNewSpace": {
"type": "text",
@ -599,11 +579,6 @@
"type": "text",
"placeholders": {}
},
"deny": "Запретить",
"@deny": {
"type": "text",
"placeholders": {}
},
"device": "Устройство",
"@device": {
"type": "text",
@ -644,11 +619,6 @@
"type": "text",
"placeholders": {}
},
"editChatPermissions": "Изменить разрешения чата",
"@editChatPermissions": {
"type": "text",
"placeholders": {}
},
"editDisplayname": "Отображаемое имя",
"@editDisplayname": {
"type": "text",
@ -736,18 +706,11 @@
"senderName": {}
}
},
"enterAGroupName": "Введите название группы",
"@enterAGroupName": {
"type": "text",
"placeholders": {}
},
"enterAnEmailAddress": "Введите адрес электронной почты",
"@enterAnEmailAddress": {
"type": "text",
"placeholders": {}
},
"enterASpacepName": "Введите название пространства",
"@enterASpacepName": {},
"enterYourHomeserver": "Введите адрес вашего сервера Matrix",
"@enterYourHomeserver": {
"type": "text",
@ -810,16 +773,6 @@
"type": "text",
"placeholders": {}
},
"groupDescription": "Описание группы",
"@groupDescription": {
"type": "text",
"placeholders": {}
},
"groupDescriptionHasBeenChanged": "Описание группы изменено",
"@groupDescriptionHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"groupIsPublic": "Публичная группа",
"@groupIsPublic": {
"type": "text",
@ -895,16 +848,6 @@
"type": "text",
"placeholders": {}
},
"ignoreListDescription": "Вы можете игнорировать пользователей, которые вам мешают. Вы не сможете получать сообщения или приглашения в комнату от пользователей из вашего личного списка игнорирования.",
"@ignoreListDescription": {
"type": "text",
"placeholders": {}
},
"ignoreUsername": "Игнорировать имя пользователя",
"@ignoreUsername": {
"type": "text",
"placeholders": {}
},
"iHaveClickedOnLink": "Я перешёл по ссылке",
"@iHaveClickedOnLink": {
"type": "text",
@ -980,7 +923,7 @@
"type": "text",
"placeholders": {}
},
"kicked": "👞 {username} исключил(а) {targetName}",
"kicked": "👞 {username} выгнал(а) {targetName}",
"@kicked": {
"type": "text",
"placeholders": {
@ -988,7 +931,7 @@
"targetName": {}
}
},
"kickedAndBanned": "🙅 {username} заблокировал(а) {targetName}",
"kickedAndBanned": "🙅 {username} выгнал(а) и заблокировал(а) {targetName}",
"@kickedAndBanned": {
"type": "text",
"placeholders": {
@ -996,7 +939,7 @@
"targetName": {}
}
},
"kickFromChat": "Исключить из чата",
"kickFromChat": "Выгнать из чата",
"@kickFromChat": {
"type": "text",
"placeholders": {}
@ -1008,11 +951,6 @@
"localizedTimeShort": {}
}
},
"lastSeenLongTimeAgo": "был(а) в сети давно",
"@lastSeenLongTimeAgo": {
"type": "text",
"placeholders": {}
},
"leave": "Покинуть",
"@leave": {
"type": "text",
@ -1072,23 +1010,11 @@
"homeserver": {}
}
},
"loginWith": "Войти через {brand}",
"@loginWith": {
"type": "text",
"placeholders": {
"brand": {}
}
},
"logout": "Выйти",
"@logout": {
"type": "text",
"placeholders": {}
},
"makeSureTheIdentifierIsValid": "Убедитесь, что идентификатор действителен",
"@makeSureTheIdentifierIsValid": {
"type": "text",
"placeholders": {}
},
"memberChanges": "Изменения участников",
"@memberChanges": {
"type": "text",
@ -1104,11 +1030,6 @@
"type": "text",
"placeholders": {}
},
"messageWillBeRemovedWarning": "Сообщение будет удалено для всех участников",
"@messageWillBeRemovedWarning": {
"type": "text",
"placeholders": {}
},
"moderator": "Модератор",
"@moderator": {
"type": "text",
@ -1164,7 +1085,7 @@
"type": "text",
"placeholders": {}
},
"noGoogleServicesWarning": "Похоже, у вас нет служб Google на вашем телефоне. Это хорошее решение для вашей конфиденциальности! Для получения push-уведомлений во FluffyChat мы рекомендуем использовать https://microg.org/ или https://unifiedpush.org/.",
"noGoogleServicesWarning": "Похоже, у вас нет служб Google на вашем телефоне. Это хорошее решение для вашей конфиденциальности! Для получения push-уведомлений во FluffyChat мы рекомендуем использовать ntfy. С ntfy или другим провайдером единых уведомлений вы можете получать push-уведомления безопасным способом передачи данных. Скачать ntfy можно из F-Droid или из Play Маркета.",
"@noGoogleServicesWarning": {
"type": "text",
"placeholders": {}
@ -1249,7 +1170,7 @@
"type": "text",
"placeholders": {}
},
"oopsSomethingWentWrong": "Упс, что-то пошло не так…",
"oopsSomethingWentWrong": "Ой, что-то пошло не так…",
"@oopsSomethingWentWrong": {
"type": "text",
"placeholders": {}
@ -1269,11 +1190,6 @@
"type": "text",
"placeholders": {}
},
"optionalGroupName": "(необязательно) Название группы",
"@optionalGroupName": {
"type": "text",
"placeholders": {}
},
"or": "Или",
"@or": {
"type": "text",
@ -1341,11 +1257,6 @@
"type": "text",
"placeholders": {}
},
"pleaseChooseAUsername": "Пожалуйста, выберите имя пользователя",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"pleaseClickOnLink": "Пожалуйста, нажмите на ссылку в электронной почте, для того чтобы продолжить.",
"@pleaseClickOnLink": {
"type": "text",
@ -1356,11 +1267,6 @@
"type": "text",
"placeholders": {}
},
"pleaseEnterAMatrixIdentifier": "Пожалуйста, введите Matrix ID.",
"@pleaseEnterAMatrixIdentifier": {
"type": "text",
"placeholders": {}
},
"pleaseEnterYourPassword": "Пожалуйста, введите ваш пароль",
"@pleaseEnterYourPassword": {
"type": "text",
@ -1529,22 +1435,6 @@
"username": {}
}
},
"seenByUserAndCountOthers": "{count, plural, other{Просмотрено пользователями {username} и {count} другими}}",
"@seenByUserAndCountOthers": {
"type": "text",
"placeholders": {
"username": {},
"count": {}
}
},
"seenByUserAndUser": "Просмотрено пользователями {username} и {username2}",
"@seenByUserAndUser": {
"type": "text",
"placeholders": {
"username": {},
"username2": {}
}
},
"send": "Прислать",
"@send": {
"type": "text",
@ -1646,11 +1536,6 @@
"type": "text",
"placeholders": {}
},
"setGroupDescription": "Задать описание группы",
"@setGroupDescription": {
"type": "text",
"placeholders": {}
},
"setInvitationLink": "Установить ссылку для приглашения",
"@setInvitationLink": {
"type": "text",
@ -1693,11 +1578,6 @@
"type": "text",
"placeholders": {}
},
"signUp": "Зарегистрироваться",
"@signUp": {
"type": "text",
"placeholders": {}
},
"singlesignon": "Единая точка входа",
"@singlesignon": {
"type": "text",
@ -2016,21 +1896,11 @@
"type": "text",
"placeholders": {}
},
"youAreInvitedToThisChat": "Вы приглашены в этот чат",
"@youAreInvitedToThisChat": {
"type": "text",
"placeholders": {}
},
"youAreNoLongerParticipatingInThisChat": "Вы больше не участвуете в этом чате",
"@youAreNoLongerParticipatingInThisChat": {
"type": "text",
"placeholders": {}
},
"youCannotInviteYourself": "Вы не можете пригласить себя",
"@youCannotInviteYourself": {
"type": "text",
"placeholders": {}
},
"youHaveBeenBannedFromThisChat": "Вы были заблокированы в этом чате",
"@youHaveBeenBannedFromThisChat": {
"type": "text",
@ -2047,8 +1917,6 @@
"@addToSpace": {},
"scanQrCode": "Сканировать QR-код",
"@scanQrCode": {},
"shareYourInviteLink": "Поделиться ссылкой приглашения",
"@shareYourInviteLink": {},
"sendOnEnter": "Отправлять по Enter",
"@sendOnEnter": {},
"homeserver": "Сервер Matrix",
@ -2075,8 +1943,6 @@
"@yourChatBackupHasBeenSetUp": {},
"unverified": "Не проверено",
"@unverified": {},
"passwordsDoNotMatch": "Пароли не совпадают!",
"@passwordsDoNotMatch": {},
"commandHint_clearcache": "Очистить кэш",
"@commandHint_clearcache": {
"type": "text",
@ -2098,17 +1964,6 @@
"@openGallery": {},
"removeFromSpace": "Удалить из пространства",
"@removeFromSpace": {},
"loginWithOneClick": "Вход одним нажатием",
"@loginWithOneClick": {},
"pleaseEnterValidEmail": "Пожалуйста, введите действительный адрес электронной почты.",
"@pleaseEnterValidEmail": {},
"pleaseChooseAtLeastChars": "Пожалуйста, выберите не менее {min} символов.",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"commandHint_create": "Создайте пустой групповой чат\nИспользуйте --no-encryption, чтобы отключить шифрование",
"@commandHint_create": {
"type": "text",
@ -2131,10 +1986,6 @@
"type": "text",
"placeholders": {}
},
"yourStory": "Ваша история",
"@yourStory": {},
"replyHasBeenSent": "Ответ отправлен",
"@replyHasBeenSent": {},
"videoWithSize": "Видео ({size})",
"@videoWithSize": {
"type": "text",
@ -2142,39 +1993,8 @@
"size": {}
}
},
"storyFrom": "История за {date}:\n{body}",
"@storyFrom": {
"type": "text",
"placeholders": {
"date": {},
"body": {}
}
},
"addToStory": "Добавить в историю",
"@addToStory": {},
"publish": "Опубликовать",
"@publish": {},
"whoCanSeeMyStories": "Кто может видеть мои истории?",
"@whoCanSeeMyStories": {},
"unsubscribeStories": "Отписаться от историй",
"@unsubscribeStories": {},
"thisUserHasNotPostedAnythingYet": "Этот пользователь еще ничего не опубликовал в своей истории",
"@thisUserHasNotPostedAnythingYet": {},
"bubbleSize": "Размер пузыря",
"@bubbleSize": {
"type": "text",
"placeholders": {}
},
"whoCanSeeMyStoriesDesc": "Обратите внимание, что люди могут видеть и связываться друг с другом в вашей истории.",
"@whoCanSeeMyStoriesDesc": {},
"whatIsGoingOn": "Что происходит?",
"@whatIsGoingOn": {},
"addDescription": "Добавить описание",
"@addDescription": {},
"storyPrivacyWarning": "Обратите внимание, что люди могут видеть и связываться друг с другом в вашей истории. Ваши истории будут видны в течение 24 часов, но нет гарантии, что они будут удалены со всех устройств и серверов.",
"@storyPrivacyWarning": {},
"iUnderstand": "Я понимаю",
"@iUnderstand": {},
"dismiss": "Отклонить",
"@dismiss": {},
"markAsRead": "Отметить как прочитанное",
@ -2183,8 +2003,6 @@
"@reportUser": {},
"openChat": "Открыть чат",
"@openChat": {},
"matrixWidgets": "Виджеты Matrix",
"@matrixWidgets": {},
"reactedWith": "{sender} реагирует с {reaction}",
"@reactedWith": {
"type": "text",
@ -2224,8 +2042,6 @@
"@nextAccount": {},
"previousAccount": "Предыдущая учётная запись",
"@previousAccount": {},
"editWidgets": "Редактировать виджеты",
"@editWidgets": {},
"addWidget": "Добавить виджет",
"@addWidget": {},
"widgetVideo": "Видео",
@ -2244,11 +2060,6 @@
"@widgetNameError": {},
"errorAddingWidget": "Ошибка при добавлении виджета.",
"@errorAddingWidget": {},
"showDirectChatsInSpaces": "Показывать связанные Личные чаты в Пространствах",
"@showDirectChatsInSpaces": {
"type": "text",
"placeholders": {}
},
"separateChatTypes": "Разделять личные чаты и группы",
"@separateChatTypes": {
"type": "text",
@ -2266,7 +2077,7 @@
},
"youJoinedTheChat": "Вы присоединились к чату",
"@youJoinedTheChat": {},
"youKickedAndBanned": "🙅 Вы заблокировали {user}",
"youKickedAndBanned": "🙅 Вы выгнали и заблокировали {user}",
"@youKickedAndBanned": {
"placeholders": {
"user": {}
@ -2302,12 +2113,8 @@
"user": {}
}
},
"noEmailWarning": "Пожалуйста, введите действительный адрес электронной почты. В противном случае вы не сможете сбросить пароль. Если вы не хотите этого делать, нажмите еще раз на кнопку, чтобы продолжить.",
"@noEmailWarning": {},
"recoveryKeyLost": "Ключ восстановления утерян?",
"@recoveryKeyLost": {},
"stories": "Истории",
"@stories": {},
"users": "Пользователи",
"@users": {},
"unlockOldMessages": "Разблокировать старые сообщения",
@ -2351,10 +2158,6 @@
"@indexedDbErrorLong": {},
"custom": "Пользовательское",
"@custom": {},
"updateAvailable": "Доступно обновление для FluffyChat",
"@updateAvailable": {},
"updateNow": "Обновить в фоновом режиме",
"@updateNow": {},
"hydrate": "Восстановить из файла резервной копии",
"@hydrate": {},
"hydrateTor": "Пользователи TOR: Импорт экспорта сессии",
@ -2412,17 +2215,10 @@
"number": {}
}
},
"discover": "Обзор",
"@discover": {
"type": "text",
"placeholders": {}
},
"fileIsTooBigForServer": "Файл слишком большой.",
"@fileIsTooBigForServer": {},
"hideUnimportantStateEvents": "Скрыть необязательные события статуса",
"@hideUnimportantStateEvents": {},
"endToEndEncryption": "Сквозное шифрование",
"@endToEndEncryption": {},
"sorryThatsNotPossible": "Извините... это невозможно",
"@sorryThatsNotPossible": {},
"openLinkInBrowser": "Открыть ссылку в браузере",
@ -2434,20 +2230,20 @@
"path": {}
}
},
"commandHint_cuddle": "Отправить объятия",
"commandHint_cuddle": "Отправить улыбку",
"@commandHint_cuddle": {},
"readUpToHere": "Дочитать до сюда",
"@readUpToHere": {},
"commandHint_hug": "Отправить обнимашки",
"@commandHint_hug": {},
"cuddleContent": "{senderName} обнял Вас",
"cuddleContent": "{senderName} улыбнулся(-ась) Вам",
"@cuddleContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"hugContent": "{senderName} обнял Вас",
"hugContent": "{senderName} обнял(а) Вас",
"@hugContent": {
"type": "text",
"placeholders": {
@ -2464,15 +2260,11 @@
"@disableEncryptionWarning": {},
"deviceKeys": "Ключи устройств:",
"@deviceKeys": {},
"letsStart": "Давайте начнём",
"@letsStart": {},
"enterInviteLinkOrMatrixId": "Введите ссылку приглашения или Matrix ID...",
"@enterInviteLinkOrMatrixId": {},
"noBackupWarning": "Внимание! Без резервных копий, Вы потеряете доступ к своим зашифрованным сообщениям. Крайне рекомендуется включить резервные копии перед выходом.",
"@noBackupWarning": {},
"noOtherDevicesFound": "Другие устройства не найдены",
"@noOtherDevicesFound": {},
"reportErrorDescription": "О, нет. Что-то пошло не так. Пожалуйста, повторите попытку позже. При желании вы можете сообщить об ошибке разработчикам.",
"reportErrorDescription": "😭 О, нет. Что-то пошло не так. При желании вы можете сообщить об этой ошибке разработчикам.",
"@reportErrorDescription": {},
"report": "пожаловаться",
"@report": {},
@ -2511,21 +2303,12 @@
"@importNow": {},
"importEmojis": "Импортировать эмодзи",
"@importEmojis": {},
"importFromZipFile": "Импортировать из ZIP файла",
"importFromZipFile": "Импортировать из ZIP-файла",
"@importFromZipFile": {},
"importZipFile": "Импортировать ZIP файл",
"@importZipFile": {},
"exportEmotePack": "Экспортировать набор эмодзи как ZIP",
"@exportEmotePack": {},
"replace": "Заменить",
"@replace": {},
"savedEmotePack": "Набор эмодзи сохранены в {path}!",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"googlyEyesContent": "{senderName} выпучил глаза",
"@googlyEyesContent": {
"type": "text",
@ -2535,8 +2318,6 @@
},
"signInWithPassword": "Войти с помощью пароля",
"@signInWithPassword": {},
"continueWith": "Продолжить с:",
"@continueWith": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "Повторите попытку позже или выберите другой сервер.",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"sendTypingNotifications": "Отправлять уведомления о наборе текста",
@ -2545,13 +2326,9 @@
"@createGroup": {},
"inviteContactToGroupQuestion": "Вы хотите пригласить {contact} в чат \"{groupName}\"?",
"@inviteContactToGroupQuestion": {},
"anyoneCanKnock": "Вход по разрешению",
"@anyoneCanKnock": {},
"noOneCanJoin": "Вход воспрещён",
"@noOneCanJoin": {},
"tryAgain": "Повторите попытку",
"@tryAgain": {},
"addChatDescription": "Добавить описание чата",
"addChatDescription": "Добавить описание чата...",
"@addChatDescription": {},
"chatPermissions": "Права в чате",
"@chatPermissions": {},
@ -2598,8 +2375,6 @@
"@setColorTheme": {},
"invite": "Пригласить",
"@invite": {},
"requests": "Запросы",
"@requests": {},
"invitePrivateChat": "📨 Пригласить в приватный чат",
"@invitePrivateChat": {},
"inviteGroupChat": "📨 Пригласить в групповой чат",
@ -2618,44 +2393,89 @@
"type": "text",
"placeholders": {}
},
"banUserDescription": "",
"banUserDescription": "Заблокированные в чате пользователи не смогут перезайти в чат, пока они не будут разблокированны.",
"@banUserDescription": {},
"removeDevicesDescription": "",
"removeDevicesDescription": "Вы выйдете с этого устройства и больше не будете получать сообщения.",
"@removeDevicesDescription": {},
"unbanUserDescription": "",
"unbanUserDescription": "Пользователь сможет при желании зайти в чат снова.",
"@unbanUserDescription": {},
"todoLists": "",
"@todoLists": {},
"editTodo": "",
"@editTodo": {},
"pushNotificationsNotAvailable": "",
"pushNotificationsNotAvailable": "Push-уведомления недоступны",
"@pushNotificationsNotAvailable": {},
"pleaseAddATitle": "",
"@pleaseAddATitle": {},
"makeAdminDescription": "",
"makeAdminDescription": "Как только вы назначите этого пользователя администратором, вы не сможете этого отменить, так как их права доступа и ваши будут одинаковы.",
"@makeAdminDescription": {},
"noTodosYet": "",
"@noTodosYet": {},
"archiveRoomDescription": "",
"archiveRoomDescription": "Чат переместится в архив. Другим пользователям будет видно, что вы вышли из чата.",
"@archiveRoomDescription": {},
"todosUnencrypted": "",
"@todosUnencrypted": {},
"hasKnocked": "",
"hasKnocked": "{user} постучался",
"@hasKnocked": {
"placeholders": {
"user": {}
}
},
"newTodo": "",
"@newTodo": {},
"learnMore": "",
"learnMore": "Узнать больше",
"@learnMore": {},
"todoListChangedError": "",
"@todoListChangedError": {},
"roomUpgradeDescription": "",
"roomUpgradeDescription": "Затем чат будет воссоздан с новой версией комнаты. Все участники будут уведомлены о необходимости перейти в новый чат. Вы можете узнать больше о версиях комнат на https://spec.matrix.org/latest/rooms/",
"@roomUpgradeDescription": {},
"pleaseEnterANumber": "",
"pleaseEnterANumber": "Пожалуйста введите число больше 0",
"@pleaseEnterANumber": {},
"kickUserDescription": "",
"@kickUserDescription": {}
"kickUserDescription": "Пользователь будет изгнан из чата, но не будет заблокирован. В публичных чатах пользователь может перезайти когда угодно.",
"@kickUserDescription": {},
"blockListDescription": "Вы можете заглушить тревожащих вас пользователей. Вы не будете получать сообщения или приглашения в комнату от пользователей из вашего личного чёрного списка.",
"@blockListDescription": {},
"blockedUsers": "Заглушённые пользователи",
"@blockedUsers": {},
"block": "заглушить",
"@block": {},
"blockUsername": "Игнорировать имя пользователя",
"@blockUsername": {},
"createGroupAndInviteUsers": "Создать и начать приглашать",
"@createGroupAndInviteUsers": {},
"startConversation": "Начать общение",
"@startConversation": {},
"groupCanBeFoundViaSearch": "Группа может быть найдена поиском",
"@groupCanBeFoundViaSearch": {},
"noUsersFoundWithQuery": "К сожалению пользователей с именем \"{query}\" не найдено. Убедитесь, что вы не совершили опечатку.",
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
}
},
"yourGlobalUserIdIs": "Ваш глобальный идентификатор - ",
"@yourGlobalUserIdIs": {},
"commandHint_sendraw": "Отправить сырой json",
"@commandHint_sendraw": {},
"wrongRecoveryKey": "Простите... судя по всему это неверный ключ восстановления.",
"@wrongRecoveryKey": {},
"groupName": "Название группы",
"@groupName": {},
"databaseMigrationTitle": "База данных оптимизированна",
"@databaseMigrationTitle": {},
"searchChatsRooms": "Поиск #чатов, @людей...",
"@searchChatsRooms": {},
"databaseMigrationBody": "Пожалуйста, подождите. Это может занять некоторое время.",
"@databaseMigrationBody": {},
"publicSpaces": "Публичные пространства",
"@publicSpaces": {},
"passwordIsWrong": "Вы ввели неверный пароль",
"@passwordIsWrong": {},
"pleaseEnterYourCurrentPassword": "Пожалуйста, введите свой текущий пароль",
"@pleaseEnterYourCurrentPassword": {},
"publicLink": "Публичная ссылка",
"@publicLink": {},
"nothingFound": "Ничего не найдено...",
"@nothingFound": {},
"newPassword": "Новый пароль",
"@newPassword": {},
"passwordsDoNotMatch": "Пароли не совпадают",
"@passwordsDoNotMatch": {},
"select": "Выбрать",
"@select": {},
"pleaseChooseAStrongPassword": "Пожалуйста, подберите сильный пароль",
"@pleaseChooseAStrongPassword": {},
"leaveEmptyToClearStatus": "Чтобы очистить статус, оставьте поле пустым.",
"@leaveEmptyToClearStatus": {},
"joinSpace": "Присоединиться к пространству",
"@joinSpace": {},
"searchForUsers": "Поиск @пользователей...",
"@searchForUsers": {}
}

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

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

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

@ -35,11 +35,6 @@
"type": "text",
"placeholders": {}
},
"addGroupDescription": "添加群组描述",
"@addGroupDescription": {
"type": "text",
"placeholders": {}
},
"admin": "管理员",
"@admin": {
"type": "text",
@ -287,11 +282,6 @@
"type": "text",
"placeholders": {}
},
"changeWallpaper": "更改壁纸",
"@changeWallpaper": {
"type": "text",
"placeholders": {}
},
"changeYourAvatar": "更改你的头像",
"@changeYourAvatar": {
"type": "text",
@ -332,11 +322,6 @@
"type": "text",
"placeholders": {}
},
"chooseAUsername": "输入一个用户名",
"@chooseAUsername": {
"type": "text",
"placeholders": {}
},
"clearArchive": "清除存档",
"@clearArchive": {},
"close": "关闭",
@ -512,11 +497,6 @@
"username": {}
}
},
"createNewGroup": "创建新群组",
"@createNewGroup": {
"type": "text",
"placeholders": {}
},
"currentlyActive": "目前活跃",
"@currentlyActive": {
"type": "text",
@ -577,11 +557,6 @@
"type": "text",
"placeholders": {}
},
"deny": "否认",
"@deny": {
"type": "text",
"placeholders": {}
},
"device": "设备",
"@device": {
"type": "text",
@ -622,11 +597,6 @@
"type": "text",
"placeholders": {}
},
"editChatPermissions": "编辑聊天权限",
"@editChatPermissions": {
"type": "text",
"placeholders": {}
},
"editDisplayname": "编辑昵称",
"@editDisplayname": {
"type": "text",
@ -714,11 +684,6 @@
"senderName": {}
}
},
"enterAGroupName": "输入群组名称",
"@enterAGroupName": {
"type": "text",
"placeholders": {}
},
"enterAnEmailAddress": "输入一个电子邮件地址",
"@enterAnEmailAddress": {
"type": "text",
@ -779,16 +744,6 @@
"type": "text",
"placeholders": {}
},
"groupDescription": "群组描述",
"@groupDescription": {
"type": "text",
"placeholders": {}
},
"groupDescriptionHasBeenChanged": "群组描述已被更改",
"@groupDescriptionHasBeenChanged": {
"type": "text",
"placeholders": {}
},
"groupIsPublic": "群组是公开的",
"@groupIsPublic": {
"type": "text",
@ -864,16 +819,6 @@
"type": "text",
"placeholders": {}
},
"ignoreListDescription": "你可以忽略打扰你的用户。你将不会收到来自忽略列表中用户的任何消息或聊天室邀请。",
"@ignoreListDescription": {
"type": "text",
"placeholders": {}
},
"ignoreUsername": "忽略用户名",
"@ignoreUsername": {
"type": "text",
"placeholders": {}
},
"iHaveClickedOnLink": "我已经点击了链接",
"@iHaveClickedOnLink": {
"type": "text",
@ -977,11 +922,6 @@
"localizedTimeShort": {}
}
},
"lastSeenLongTimeAgo": "很长时间未上线",
"@lastSeenLongTimeAgo": {
"type": "text",
"placeholders": {}
},
"leave": "离开",
"@leave": {
"type": "text",
@ -1031,23 +971,11 @@
"homeserver": {}
}
},
"loginWith": "使用 {brand} 登录",
"@loginWith": {
"type": "text",
"placeholders": {
"brand": {}
}
},
"logout": "注销",
"@logout": {
"type": "text",
"placeholders": {}
},
"makeSureTheIdentifierIsValid": "确保标识符正确",
"@makeSureTheIdentifierIsValid": {
"type": "text",
"placeholders": {}
},
"memberChanges": "成员变更",
"@memberChanges": {
"type": "text",
@ -1063,11 +991,6 @@
"type": "text",
"placeholders": {}
},
"messageWillBeRemovedWarning": "消息将对所有参与者移除",
"@messageWillBeRemovedWarning": {
"type": "text",
"placeholders": {}
},
"moderator": "协管员",
"@moderator": {
"type": "text",
@ -1210,11 +1133,6 @@
"type": "text",
"placeholders": {}
},
"optionalGroupName": "(可选) 群组名称",
"@optionalGroupName": {
"type": "text",
"placeholders": {}
},
"or": "或",
"@or": {
"type": "text",
@ -1282,11 +1200,6 @@
"type": "text",
"placeholders": {}
},
"pleaseChooseAUsername": "请选择用户名",
"@pleaseChooseAUsername": {
"type": "text",
"placeholders": {}
},
"pleaseClickOnLink": "请点击电子邮件中的链接,然后继续。",
"@pleaseClickOnLink": {
"type": "text",
@ -1297,11 +1210,6 @@
"type": "text",
"placeholders": {}
},
"pleaseEnterAMatrixIdentifier": "请输入 Matrix ID。",
"@pleaseEnterAMatrixIdentifier": {
"type": "text",
"placeholders": {}
},
"pleaseEnterYourPassword": "请输入你的密码",
"@pleaseEnterYourPassword": {
"type": "text",
@ -1470,22 +1378,6 @@
"username": {}
}
},
"seenByUserAndCountOthers": "{count, plural, other{被 {username} 和 {count} 个其他人看见}}",
"@seenByUserAndCountOthers": {
"type": "text",
"placeholders": {
"username": {},
"count": {}
}
},
"seenByUserAndUser": "被 {username} 和 {username2} 看见",
"@seenByUserAndUser": {
"type": "text",
"placeholders": {
"username": {},
"username2": {}
}
},
"send": "发送",
"@send": {
"type": "text",
@ -1587,11 +1479,6 @@
"type": "text",
"placeholders": {}
},
"setGroupDescription": "设置群组描述",
"@setGroupDescription": {
"type": "text",
"placeholders": {}
},
"setInvitationLink": "设置邀请链接",
"@setInvitationLink": {
"type": "text",
@ -1629,11 +1516,6 @@
"type": "text",
"placeholders": {}
},
"signUp": "注册",
"@signUp": {
"type": "text",
"placeholders": {}
},
"singlesignon": "单点登录",
"@singlesignon": {
"type": "text",
@ -1942,21 +1824,11 @@
"type": "text",
"placeholders": {}
},
"youAreInvitedToThisChat": "你被邀请到此聊天",
"@youAreInvitedToThisChat": {
"type": "text",
"placeholders": {}
},
"youAreNoLongerParticipatingInThisChat": "你已不再参与此聊天",
"@youAreNoLongerParticipatingInThisChat": {
"type": "text",
"placeholders": {}
},
"youCannotInviteYourself": "你不能邀请你自己",
"@youCannotInviteYourself": {
"type": "text",
"placeholders": {}
},
"youHaveBeenBannedFromThisChat": "你已被此聊天封禁",
"@youHaveBeenBannedFromThisChat": {
"type": "text",
@ -2004,8 +1876,6 @@
"error": {}
}
},
"enterASpacepName": "输入空间名称",
"@enterASpacepName": {},
"createNewSpace": "创建新空间",
"@createNewSpace": {
"type": "text",
@ -2045,15 +1915,6 @@
},
"sendOnEnter": "按 Enter 键发送",
"@sendOnEnter": {},
"passwordsDoNotMatch": "密码不匹配!",
"@passwordsDoNotMatch": {},
"pleaseChooseAtLeastChars": "请至少输入 {min} 个字符。",
"@pleaseChooseAtLeastChars": {
"type": "text",
"placeholders": {
"min": {}
}
},
"yourChatBackupHasBeenSetUp": "你的聊天记录备份已设置。",
"@yourChatBackupHasBeenSetUp": {},
"scanQrCode": "扫描二维码",
@ -2068,12 +1929,8 @@
"@removeFromBundle": {},
"unverified": "未经验证",
"@unverified": {},
"pleaseEnterValidEmail": "请输入一个有效的电子邮件地址。",
"@pleaseEnterValidEmail": {},
"repeatPassword": "重复输入密码",
"@repeatPassword": {},
"shareYourInviteLink": "分享你的邀请链接",
"@shareYourInviteLink": {},
"addAccount": "添加账户",
"@addAccount": {},
"editBundlesForAccount": "编辑此账户的集合",
@ -2098,8 +1955,6 @@
"@messageInfo": {},
"time": "时间",
"@time": {},
"loginWithOneClick": "一键登录",
"@loginWithOneClick": {},
"addToSpaceDescription": "选择一个空间以添加此聊天。",
"@addToSpaceDescription": {},
"removeFromSpace": "从此空间中移除",
@ -2131,20 +1986,8 @@
"type": "text",
"placeholders": {}
},
"addToStory": "添加到 Story",
"@addToStory": {},
"publish": "发布",
"@publish": {},
"whoCanSeeMyStories": "谁能看到我的 Story",
"@whoCanSeeMyStories": {},
"unsubscribeStories": "取消 Story 订阅",
"@unsubscribeStories": {},
"thisUserHasNotPostedAnythingYet": "此用户尚未在 Story 发布任何内容",
"@thisUserHasNotPostedAnythingYet": {},
"yourStory": "你的 Story",
"@yourStory": {},
"replyHasBeenSent": "回复已发送",
"@replyHasBeenSent": {},
"videoWithSize": "视频 ({size})",
"@videoWithSize": {
"type": "text",
@ -2152,29 +1995,6 @@
"size": {}
}
},
"storyFrom": "自 {date} 起的 Story: \n{body}",
"@storyFrom": {
"type": "text",
"placeholders": {
"date": {},
"body": {}
}
},
"whoCanSeeMyStoriesDesc": "请注意,人们可以在你的 Story 中看到彼此并相互联系。",
"@whoCanSeeMyStoriesDesc": {},
"whatIsGoingOn": "发生什么事了?",
"@whatIsGoingOn": {},
"addDescription": "添加描述",
"@addDescription": {},
"bubbleSize": "气泡大小",
"@bubbleSize": {
"type": "text",
"placeholders": {}
},
"storyPrivacyWarning": "请注意,人们可以在你的 Story 中看到和联系彼此。你的故事在 24 小时内可见,但不能保证它们将从所有设备和服务器上删除。",
"@storyPrivacyWarning": {},
"iUnderstand": "我了解",
"@iUnderstand": {},
"dismiss": "忽略",
"@dismiss": {},
"markAsRead": "标为已读",
@ -2183,8 +2003,6 @@
"@reportUser": {},
"openChat": "打开聊天",
"@openChat": {},
"matrixWidgets": "Matrix 小部件",
"@matrixWidgets": {},
"reactedWith": "{sender} 回应了 {reaction}",
"@reactedWith": {
"type": "text",
@ -2224,8 +2042,6 @@
"@nextAccount": {},
"previousAccount": "上个账户",
"@previousAccount": {},
"editWidgets": "编辑小部件",
"@editWidgets": {},
"widgetVideo": "视频",
"@widgetVideo": {},
"widgetJitsi": "Jitsi Meet",
@ -2249,11 +2065,6 @@
"type": "text",
"placeholders": {}
},
"showDirectChatsInSpaces": "在空间中显示相关私聊",
"@showDirectChatsInSpaces": {
"type": "text",
"placeholders": {}
},
"youRejectedTheInvitation": "你拒绝了邀请",
"@youRejectedTheInvitation": {},
"youJoinedTheChat": "你加入了聊天",
@ -2302,8 +2113,6 @@
"user": {}
}
},
"noEmailWarning": "请输入有效电子邮件地址。否则你将无法重置密码。如果你不想输入邮件地址,再次轻点按钮以继续。",
"@noEmailWarning": {},
"storeInSecureStorageDescription": "将恢复密钥存储在此设备的安全存储中。",
"@storeInSecureStorageDescription": {},
"storeInAppleKeyChain": "存储在 Apple KeyChain 中",
@ -2326,8 +2135,6 @@
"@storeSecurlyOnThisDevice": {},
"users": "用户",
"@users": {},
"stories": "故事",
"@stories": {},
"countFiles": "{count} 个文件",
"@countFiles": {
"placeholders": {
@ -2356,10 +2163,6 @@
"@user": {},
"custom": "自定义",
"@custom": {},
"updateAvailable": "FluffyChat 更新可用",
"@updateAvailable": {},
"updateNow": "开始后台更新",
"@updateNow": {},
"confirmMatrixId": "请确认你的 Matrix ID 以删除账户。",
"@confirmMatrixId": {},
"supposedMxid": "应为 {mxid}",
@ -2456,8 +2259,6 @@
"@newSpaceDescription": {},
"encryptThisChat": "加密此聊天",
"@encryptThisChat": {},
"endToEndEncryption": "端对端加密",
"@endToEndEncryption": {},
"disableEncryptionWarning": "出于安全考虑 ,你不能在之前已启用加密的聊天中禁用加密。",
"@disableEncryptionWarning": {},
"sorryThatsNotPossible": "非常抱歉……这是做不到的",
@ -2466,13 +2267,6 @@
"@deviceKeys": {},
"report": "举报",
"@report": {},
"discover": "探索",
"@discover": {
"type": "text",
"placeholders": {}
},
"enterInviteLinkOrMatrixId": "输入邀请链接或 Matrix ID...",
"@enterInviteLinkOrMatrixId": {},
"fileIsTooBigForServer": "服务器报告文件过大,无法发送。",
"@fileIsTooBigForServer": {},
"noOtherDevicesFound": "未找到其它设备",
@ -2497,8 +2291,6 @@
"type": "text",
"placeholders": {}
},
"letsStart": "让我们开始吧",
"@letsStart": {},
"fileHasBeenSavedAt": "文件已保存在 {path}",
"@fileHasBeenSavedAt": {
"type": "text",
@ -2506,27 +2298,18 @@
"path": {}
}
},
"reportErrorDescription": "哦不。出了点差错。请稍后重试。如果你愿意,可以向开发人员报告此错误。",
"reportErrorDescription": "😭哦不。出了点差错。如果你愿意,可以向开发人员报告此错误。",
"@reportErrorDescription": {},
"noBackupWarning": "警告!如果不启用聊天备份,你将无法访问加密消息。强烈建议在注销前先启用聊天备份。",
"@noBackupWarning": {},
"signInWithPassword": "使用密码登录",
"@signInWithPassword": {},
"continueWith": "继续:",
"@continueWith": {},
"pleaseTryAgainLaterOrChooseDifferentServer": "请稍后再试或选择其它服务器。",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"reopenChat": "重新打开聊天",
"@reopenChat": {},
"importEmojis": "导入表情包",
"@importEmojis": {},
"savedEmotePack": "已将表情包保存至 {path}",
"@savedEmotePack": {
"type": "text",
"placeholders": {
"path": {}
}
},
"notAnImage": "不是图像文件。",
"@notAnImage": {},
"importNow": "立即导入",
@ -2535,8 +2318,6 @@
"@importFromZipFile": {},
"replace": "替换",
"@replace": {},
"importZipFile": "导入 .zip 文件",
"@importZipFile": {},
"exportEmotePack": "以 .zip 格式导出表情包",
"@exportEmotePack": {},
"sendTypingNotifications": "发送正在输入通知",
@ -2549,13 +2330,9 @@
"@profileNotFound": {},
"inviteContactToGroupQuestion": "你是否要邀请 {contact} 参与聊天 \"{groupName}\"",
"@inviteContactToGroupQuestion": {},
"anyoneCanKnock": "任何人都可以请求加入",
"@anyoneCanKnock": {},
"noOneCanJoin": "没有人可以加入",
"@noOneCanJoin": {},
"tryAgain": "重试",
"@tryAgain": {},
"addChatDescription": "添加聊天说明",
"addChatDescription": "添加聊天说明…",
"@addChatDescription": {},
"chatPermissions": "聊天权限",
"@chatPermissions": {},
@ -2598,8 +2375,6 @@
"reason": {}
}
},
"requests": "请求",
"@requests": {},
"inviteGroupChat": "📨 邀请至群聊",
"@inviteGroupChat": {},
"invitePrivateChat": "📨 邀请至私聊",
@ -2626,36 +2401,57 @@
},
"pleaseEnterANumber": "请输入大于 0 的数",
"@pleaseEnterANumber": {},
"banUserDescription": "",
"banUserDescription": "该用户将被禁止进入聊天室,在解除封禁之前将不能再进入聊天室。",
"@banUserDescription": {},
"removeDevicesDescription": "",
"removeDevicesDescription": "你将注销此设备,无法再接收消息。",
"@removeDevicesDescription": {},
"unbanUserDescription": "",
"unbanUserDescription": "如果用户尝试加入则可以再次进入聊天。",
"@unbanUserDescription": {},
"todoLists": "",
"@todoLists": {},
"editTodo": "",
"@editTodo": {},
"pushNotificationsNotAvailable": "",
"pushNotificationsNotAvailable": "推送通知不可用",
"@pushNotificationsNotAvailable": {},
"pleaseAddATitle": "",
"@pleaseAddATitle": {},
"makeAdminDescription": "",
"makeAdminDescription": "一旦你将该用户设为管理员,你可能无法撤销,因为他们将拥有与你相同的权限。",
"@makeAdminDescription": {},
"noTodosYet": "",
"@noTodosYet": {},
"archiveRoomDescription": "",
"archiveRoomDescription": "聊天将被移至存档。其他用户将能看到你已离开聊天。",
"@archiveRoomDescription": {},
"todosUnencrypted": "",
"@todosUnencrypted": {},
"newTodo": "",
"@newTodo": {},
"learnMore": "",
"learnMore": "了解更多",
"@learnMore": {},
"todoListChangedError": "",
"@todoListChangedError": {},
"roomUpgradeDescription": "",
"roomUpgradeDescription": "聊天之后将以新的聊天室版本重新创建。所有参与者都会收到通知以切换到新的聊天室。有关聊天室版本的更多信息,请访问 https://spec.matrix.org/latest/rooms/",
"@roomUpgradeDescription": {},
"kickUserDescription": "",
"@kickUserDescription": {}
"kickUserDescription": "该用户会被踢出聊天但未被封禁。在公开聊天中,该用户可以随时重新加入。",
"@kickUserDescription": {},
"blockListDescription": "你可以屏蔽打扰你的用户。你将不会收到来自屏蔽列表中用户的任何消息或聊天室邀请。",
"@blockListDescription": {},
"createGroupAndInviteUsers": "创建群组并邀请用户",
"@createGroupAndInviteUsers": {},
"startConversation": "开始对话",
"@startConversation": {},
"blockedUsers": "已屏蔽的用户",
"@blockedUsers": {},
"groupCanBeFoundViaSearch": "可通过搜索找到该群组",
"@groupCanBeFoundViaSearch": {},
"noUsersFoundWithQuery": "很遗憾,没有找到有关\"{query}\"的用户。请检查是否有错别字。",
"@noUsersFoundWithQuery": {
"type": "text",
"placeholders": {
"query": {}
}
},
"block": "屏蔽",
"@block": {},
"yourGlobalUserIdIs": "你的全局用户 ID 是: ",
"@yourGlobalUserIdIs": {},
"commandHint_sendraw": "发送原始 json",
"@commandHint_sendraw": {},
"wrongRecoveryKey": "抱歉…这似乎不是正确的恢复密钥。",
"@wrongRecoveryKey": {},
"blockUsername": "忽略用户名",
"@blockUsername": {},
"groupName": "群组名称",
"@groupName": {},
"databaseMigrationTitle": "数据库已优化",
"@databaseMigrationTitle": {},
"searchChatsRooms": "搜索 #聊天,@用户…",
"@searchChatsRooms": {},
"databaseMigrationBody": "请稍候。可能需要稍等片刻。",
"@databaseMigrationBody": {}
}

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

View file

@ -1,8 +1,7 @@
import 'dart:ui';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/config/environment.dart';
import 'package:matrix/matrix.dart';
abstract class AppConfig {
// #Pangea
@ -107,6 +106,11 @@ abstract class AppConfig {
'https://github.com/googlefonts/noto-emoji/';
static const double borderRadius = 16.0;
static const double columnWidth = 360.0;
static final Uri homeserverList = Uri(
scheme: 'https',
host: 'servers.joinmatrix.org',
path: 'servers.json',
);
// #Pangea
static String googlePlayMangementUrl =

View file

@ -21,8 +21,8 @@ import 'package:fluffychat/pages/settings_emotes/settings_emotes.dart';
import 'package:fluffychat/pages/settings_ignore_list/settings_ignore_list.dart';
import 'package:fluffychat/pages/settings_multiple_emotes/settings_multiple_emotes.dart';
import 'package:fluffychat/pages/settings_notifications/settings_notifications.dart';
import 'package:fluffychat/pages/settings_password/settings_password.dart';
import 'package:fluffychat/pages/settings_security/settings_security.dart';
import 'package:fluffychat/pages/settings_stories/settings_stories.dart';
import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pangea/guard/p_vguard.dart';
import 'package:fluffychat/pangea/pages/analytics/student_analytics/student_analytics.dart';
@ -195,32 +195,6 @@ abstract class AppRoutes {
),
],
),
// GoRoute(
// path: 'stories/create',
// pageBuilder: (context, state) => defaultPageBuilder(
// context,
// const AddStoryPage(),
// ),
// redirect: loggedOutRedirect,
// ),
// GoRoute(
// path: 'stories/:roomid',
// pageBuilder: (context, state) => defaultPageBuilder(
// context,
// const StoryPage(),
// ),
// redirect: loggedOutRedirect,
// routes: [
// GoRoute(
// path: 'share',
// pageBuilder: (context, state) => defaultPageBuilder(
// context,
// const AddStoryPage(),
// ),
// redirect: loggedOutRedirect,
// ),
// ],
// ),
// Pangea#
GoRoute(
path: 'archive',
@ -396,19 +370,25 @@ abstract class AppRoutes {
),
routes: [
GoRoute(
path: 'stories',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const SettingsStories(),
),
path: 'password',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
const SettingsPassword(),
);
},
redirect: loggedOutRedirect,
),
GoRoute(
path: 'ignorelist',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const SettingsIgnoreList(),
),
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
SettingsIgnoreList(
initialUserId: state.extra?.toString(),
),
);
},
redirect: loggedOutRedirect,
),
GoRoute(
@ -448,7 +428,10 @@ abstract class AppRoutes {
path: ':roomid',
pageBuilder: (context, state) => defaultPageBuilder(
context,
ChatPage(roomId: state.pathParameters['roomid']!),
ChatPage(
roomId: state.pathParameters['roomid']!,
shareText: state.uri.queryParameters['body'],
),
),
redirect: loggedOutRedirect,
routes: [
@ -546,19 +529,6 @@ abstract class AppRoutes {
],
redirect: loggedOutRedirect,
),
// #Pangea
// GoRoute(
// path: 'tasks',
// pageBuilder: (context, state) => defaultPageBuilder(
// context,
// TasksPage(
// room: Matrix.of(context)
// .client
// .getRoomById(state.pathParameters['roomid']!)!,
// ),
// ),
// ),
// Pangea#
],
),
],

View file

@ -1,5 +1,4 @@
abstract class SettingKeys {
static const String wallpaper = 'chat.fluffy.wallpaper';
static const String renderHtml = 'chat.fluffy.renderHtml';
static const String hideRedactedEvents = 'chat.fluffy.hideRedactedEvents';
static const String hideUnknownEvents = 'chat.fluffy.hideUnknownEvents';

View file

@ -1,314 +0,0 @@
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart';
import 'package:video_player/video_player.dart';
import 'package:fluffychat/pages/add_story/add_story_view.dart';
import 'package:fluffychat/pages/add_story/invite_story_page.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart';
import 'package:fluffychat/utils/resize_image.dart';
import 'package:fluffychat/utils/story_theme_data.dart';
import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/app_lock.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/matrix_sdk_extensions/client_stories_extension.dart';
class AddStoryPage extends StatefulWidget {
const AddStoryPage({super.key});
@override
AddStoryController createState() => AddStoryController();
}
class AddStoryController extends State<AddStoryPage> {
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
late Color backgroundColor;
late Color backgroundColorDark;
MatrixImageFile? image;
MatrixVideoFile? video;
VideoPlayerController? videoPlayerController;
bool get hasMedia => image != null || video != null;
bool hasText = false;
bool textFieldHasFocus = false;
BoxFit fit = BoxFit.contain;
int alignmentX = 0;
int alignmentY = 0;
void toggleBoxFit() {
if (fit == BoxFit.contain) {
setState(() {
fit = BoxFit.cover;
});
} else {
setState(() {
fit = BoxFit.contain;
});
}
}
void updateHasText(String text) {
if (hasText != text.isNotEmpty) {
setState(() {
hasText = text.isNotEmpty;
});
}
}
void importMedia() async {
final picked = await AppLock.of(context).pauseWhile(
FilePicker.platform.pickFiles(
type: FileType.image,
withData: true,
),
);
final file = picked?.files.firstOrNull;
if (file == null) return;
final matrixFile = MatrixImageFile(
bytes: file.bytes!,
name: file.name,
);
setState(() {
image = matrixFile;
});
}
void capturePhoto() async {
final picked = await ImagePicker().pickImage(
source: ImageSource.camera,
);
if (picked == null) return;
final matrixFile = await showFutureLoadingDialog(
context: context,
future: () async {
final bytes = await picked.readAsBytes();
return MatrixImageFile(
bytes: bytes,
name: picked.name,
);
},
);
setState(() {
image = matrixFile.result;
});
}
void updateColor() {
final rand = Random().nextInt(1000).toString();
setState(() {
backgroundColor = rand.color;
backgroundColorDark = rand.darkColor;
});
}
void captureVideo() async {
final picked = await ImagePicker().pickVideo(
source: ImageSource.camera,
);
if (picked == null) return;
final bytes = await picked.readAsBytes();
setState(() {
video = MatrixVideoFile(bytes: bytes, name: picked.name);
videoPlayerController = VideoPlayerController.file(File(picked.path))
..setLooping(true);
});
}
void reset() => setState(() {
image = video = null;
alignmentX = alignmentY = 0;
controller.clear();
});
void postStory() async {
if (video == null && image == null && controller.text.isEmpty) return;
final client = Matrix.of(context).client;
var storiesRoom = await client.getStoriesRoom(context);
// Invite contacts if necessary
final undecided = await showFutureLoadingDialog(
context: context,
future: () => client.getUndecidedContactsForStories(storiesRoom),
);
final result = undecided.result;
if (result == null) return;
if (result.isNotEmpty) {
final created = await showDialog<bool>(
context: context,
useRootNavigator: false,
builder: (context) => InviteStoryPage(storiesRoom: storiesRoom),
);
if (created != true) return;
storiesRoom ??= await client.getStoriesRoom(context);
}
// Post story
final postResult = await showFutureLoadingDialog(
context: context,
future: () async {
if (storiesRoom == null) throw ('Stories room is null');
var video = this.video?.detectFileType;
if (video != null) {
video = await video.resizeVideo();
final thumbnail = await video.getVideoThumbnail();
await storiesRoom.sendFileEvent(
video,
extraContent: {
'body': controller.text,
StoryThemeData.contentKey: StoryThemeData(
fit: fit,
alignmentX: alignmentX,
alignmentY: alignmentY,
).toJson(),
},
thumbnail: thumbnail,
);
return;
}
final image = this.image;
if (image != null) {
await storiesRoom.sendFileEvent(
image,
extraContent: {
'body': controller.text,
StoryThemeData.contentKey: StoryThemeData(
fit: fit,
alignmentX: alignmentX,
alignmentY: alignmentY,
).toJson(),
},
);
return;
}
await storiesRoom.sendEvent(<String, dynamic>{
'msgtype': MessageTypes.Text,
'body': controller.text,
StoryThemeData.contentKey: StoryThemeData(
color1: backgroundColor,
color2: backgroundColorDark,
fit: fit,
alignmentX: alignmentX,
alignmentY: alignmentY,
).toJson(),
});
},
);
if (postResult.error == null) {
context.pop();
}
}
void onVerticalDragUpdate(DragUpdateDetails details) {
final delta = details.primaryDelta;
if (delta == null) return;
if (delta > 0 && alignmentY < 100) {
setState(() {
alignmentY += 1;
});
} else if (delta < 0 && alignmentY > -100) {
setState(() {
alignmentY -= 1;
});
}
}
void onHorizontalDragUpdate(DragUpdateDetails details) {
final delta = details.primaryDelta;
if (delta == null) return;
if (delta > 0 && alignmentX < 100) {
setState(() {
alignmentX += 1;
});
} else if (delta < 0 && alignmentX > -100) {
setState(() {
alignmentX -= 1;
});
}
}
@override
void initState() {
super.initState();
final rand = Random().nextInt(1000).toString();
backgroundColor = rand.color;
backgroundColorDark = rand.darkColor;
focusNode.addListener(() {
if (textFieldHasFocus != focusNode.hasFocus) {
setState(() {
textFieldHasFocus = focusNode.hasFocus;
});
}
});
final shareContent = Matrix.of(context).shareContent;
if (shareContent != null) {
controller.text = shareContent.tryGet<String>('body') ?? '';
final shareFile = shareContent.tryGet<MatrixFile>('file')?.detectFileType;
if (shareFile is MatrixImageFile) {
setState(() {
image = shareFile;
});
} else if (shareFile is MatrixVideoFile) {
setState(() {
video = shareFile;
});
}
final msgType = shareContent.tryGet<String>('msgtype');
if (msgType == MessageTypes.Image) {
Event(
content: shareContent,
type: EventTypes.Message,
room: Room(id: '!tmproom', client: Matrix.of(context).client),
eventId: 'tmpevent',
senderId: '@tmpsender:example',
originServerTs: DateTime.now(),
).downloadAndDecryptAttachment().then((file) {
setState(() {
image = file.detectFileType as MatrixImageFile;
});
});
} else if (msgType == MessageTypes.Video) {
Event(
content: shareContent,
type: EventTypes.Message,
room: Room(id: '!tmproom', client: Matrix.of(context).client),
eventId: 'tmpevent',
senderId: '@tmpsender:example',
originServerTs: DateTime.now(),
).downloadAndDecryptAttachment().then((file) {
setState(() {
video = file.detectFileType as MatrixVideoFile;
});
});
}
Matrix.of(context).shareContent = null;
}
}
@override
void dispose() {
videoPlayerController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => AddStoryView(this);
}

View file

@ -1,200 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:video_player/video_player.dart';
import 'add_story.dart';
class AddStoryView extends StatelessWidget {
final AddStoryController controller;
const AddStoryView(this.controller, {super.key});
@override
Widget build(BuildContext context) {
final video = controller.videoPlayerController;
return Scaffold(
backgroundColor: Colors.blueGrey.shade900,
appBar: AppBar(
leading: const BackButton(color: Colors.white),
systemOverlayStyle: SystemUiOverlayStyle.light,
backgroundColor: Colors.transparent,
elevation: 0,
iconTheme: const IconThemeData(color: Colors.white),
title: Text(
L10n.of(context)!.addToStory,
style: const TextStyle(
color: Colors.white,
shadows: [
Shadow(
color: Colors.black,
offset: Offset(0, 0),
blurRadius: 5,
),
],
),
),
actions: [
if (controller.hasMedia)
IconButton(
icon: const Icon(Icons.fullscreen_outlined),
color: Colors.white,
onPressed: controller.toggleBoxFit,
),
if (!controller.hasMedia)
IconButton(
icon: const Icon(Icons.color_lens_outlined),
color: Colors.white,
onPressed: controller.updateColor,
),
IconButton(
icon: const Icon(Icons.delete_outlined),
color: Colors.white,
onPressed: controller.reset,
),
],
),
extendBodyBehindAppBar: true,
body: GestureDetector(
onVerticalDragUpdate: controller.onVerticalDragUpdate,
onHorizontalDragUpdate: controller.onHorizontalDragUpdate,
child: Stack(
children: [
if (video != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 80.0),
child: FutureBuilder(
future: video.initialize().then((_) => video.play()),
builder: (_, __) => Center(child: VideoPlayer(video)),
),
),
AnimatedContainer(
duration: const Duration(seconds: 1),
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 80.0,
),
decoration: BoxDecoration(
image: controller.image == null
? null
: DecorationImage(
image: MemoryImage(controller.image!.bytes),
fit: controller.fit,
opacity: 0.75,
),
gradient: controller.hasMedia
? null
: LinearGradient(
colors: [
controller.backgroundColorDark,
controller.backgroundColor,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Align(
alignment: Alignment(
controller.alignmentX / 100,
controller.alignmentY / 100,
),
child: IntrinsicWidth(
child: TextField(
controller: controller.controller,
focusNode: controller.focusNode,
minLines: 1,
maxLines: 15,
autofocus: false,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 24,
color: Colors.white,
shadows: controller.hasMedia
? const [
Shadow(
color: Colors.black,
offset: Offset(5, 5),
blurRadius: 20,
),
Shadow(
color: Colors.black,
offset: Offset(5, 5),
blurRadius: 20,
),
Shadow(
color: Colors.black,
offset: Offset(-5, -5),
blurRadius: 20,
),
Shadow(
color: Colors.black,
offset: Offset(-5, -5),
blurRadius: 20,
),
]
: null,
),
onChanged: controller.updateHasText,
decoration: InputDecoration(
border: InputBorder.none,
hintText: controller.hasMedia
? L10n.of(context)!.addDescription
: L10n.of(context)!.whatIsGoingOn,
filled: false,
hintStyle: TextStyle(
color: Colors.white.withOpacity(0.5),
backgroundColor: Colors.transparent,
),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
),
),
),
),
],
),
),
floatingActionButton: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (!controller.hasMedia) ...[
FloatingActionButton(
onPressed: controller.importMedia,
backgroundColor: controller.backgroundColorDark,
foregroundColor: Colors.white,
heroTag: null,
child: const Icon(Icons.photo_outlined),
),
const SizedBox(width: 16),
FloatingActionButton(
onPressed: controller.capturePhoto,
backgroundColor: controller.backgroundColorDark,
foregroundColor: Colors.white,
heroTag: null,
child: const Icon(Icons.camera_alt_outlined),
),
const SizedBox(width: 16),
FloatingActionButton(
onPressed: controller.captureVideo,
backgroundColor: controller.backgroundColorDark,
foregroundColor: Colors.white,
heroTag: null,
child: const Icon(Icons.video_camera_front_outlined),
),
],
if (controller.hasMedia || controller.hasText) ...[
const SizedBox(width: 16),
FloatingActionButton(
onPressed: controller.postStory,
backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: Theme.of(context).colorScheme.onSurface,
child: const Icon(Icons.send_rounded),
),
],
],
),
);
}
}

View file

@ -1,140 +0,0 @@
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
class InviteStoryPage extends StatefulWidget {
final Room? storiesRoom;
const InviteStoryPage({
required this.storiesRoom,
super.key,
});
@override
InviteStoryPageState createState() => InviteStoryPageState();
}
class InviteStoryPageState extends State<InviteStoryPage> {
Set<String> _undecided = {};
final Set<String> _invite = {};
void _inviteAction() async {
final confirmed = await showOkCancelAlertDialog(
context: context,
message: L10n.of(context)!.storyPrivacyWarning,
okLabel: L10n.of(context)!.iUnderstand,
cancelLabel: L10n.of(context)!.cancel,
);
if (confirmed != OkCancelResult.ok) return;
final result = await showFutureLoadingDialog(
context: context,
future: () async {
final client = Matrix.of(context).client;
var room = await client.getStoriesRoom(context);
final inviteList = _invite.toList();
if (room == null) {
room = await client.createStoriesRoom(inviteList.take(10).toList());
if (inviteList.length > 10) {
inviteList.removeRange(0, 10);
} else {
inviteList.clear();
}
}
for (final userId in inviteList) {
room.invite(userId);
}
_undecided.removeAll(_invite);
_undecided.addAll(client.storiesBlockList);
await client.setStoriesBlockList(_undecided.toList());
},
);
if (result.error != null) return;
Navigator.of(context).pop<bool>(true);
}
Future<List<User>>? loadContacts;
@override
Widget build(BuildContext context) {
loadContacts ??= Matrix.of(context)
.client
.getUndecidedContactsForStories(widget.storiesRoom)
.then((contacts) {
return contacts;
});
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop<bool>(false),
),
title: Text(L10n.of(context)!.whoCanSeeMyStories),
elevation: 0,
),
body: Column(
children: [
ListTile(
title: Text(L10n.of(context)!.whoCanSeeMyStoriesDesc),
leading: CircleAvatar(
backgroundColor: Theme.of(context).secondaryHeaderColor,
foregroundColor: Theme.of(context).colorScheme.secondary,
child: const Icon(Icons.lock),
),
),
const Divider(height: 1),
Expanded(
child: FutureBuilder<List<User>>(
future: loadContacts,
builder: (context, snapshot) {
final contacts = snapshot.data;
if (contacts == null) {
final error = snapshot.error;
if (error != null) {
return Center(
child: Text(error.toLocalizedString(context)),
);
}
return const Center(
child: CircularProgressIndicator.adaptive(),
);
}
_undecided = contacts.map((u) => u.id).toSet();
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, i) => SwitchListTile.adaptive(
value: _invite.contains(contacts[i].id),
onChanged: (b) => setState(
() => b
? _invite.add(contacts[i].id)
: _invite.remove(contacts[i].id),
),
secondary: Avatar(
mxContent: contacts[i].avatarUrl,
name: contacts[i].calcDisplayname(),
),
title: Text(contacts[i].calcDisplayname()),
),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: _inviteAction,
label: Text(L10n.of(context)!.publish),
backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: Theme.of(context).colorScheme.onSurface,
icon: const Icon(Icons.send_rounded),
),
);
}
}

View file

@ -5,11 +5,12 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/encryption.dart';
import 'package:matrix/encryption/utils/bootstrap.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/error_reporter.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import '../../utils/adaptive_bottom_sheet.dart';
import '../key_verification/key_verification_dialog.dart';
@ -140,7 +141,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
),
const SizedBox(height: 16),
if (_supportsSecureStorage)
CheckboxListTile(
CheckboxListTile.adaptive(
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
value: _storeInSecureStorage,
activeColor: Theme.of(context).colorScheme.primary,
@ -154,7 +155,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
Text(L10n.of(context)!.storeInSecureStorageDescription),
),
const SizedBox(height: 16),
CheckboxListTile(
CheckboxListTile.adaptive(
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0),
value: _recoveryKeyCopied,
activeColor: Theme.of(context).colorScheme.primary,
@ -272,7 +273,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
style: ElevatedButton.styleFrom(
foregroundColor:
Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).primaryColor,
backgroundColor: Theme.of(context).colorScheme.primary,
),
icon: _recoveryKeyInputLoading
? const CircularProgressIndicator.adaptive()
@ -311,11 +312,19 @@ class BootstrapDialogState extends State<BootstrapDialog> {
);
}
}
} catch (e, s) {
Logs().w('Unable to unlock SSSS', e, s);
} on InvalidPassphraseException catch (e) {
setState(
() => _recoveryKeyInputError =
L10n.of(context)!.oopsSomethingWentWrong,
e.toLocalizedString(context),
);
} catch (e, s) {
ErrorReporter(
context,
'Unable to open SSSS with recovery key',
).onErrorCallback(e, s);
setState(
() => _recoveryKeyInputError =
e.toLocalizedString(context),
);
} finally {
setState(
@ -344,9 +353,12 @@ class BootstrapDialogState extends State<BootstrapDialog> {
: () async {
final req = await showFutureLoadingDialog(
context: context,
future: () => widget.client
.userDeviceKeys[widget.client.userID!]!
.startVerification(),
future: () async {
await widget.client.updateUserDeviceKeys();
return widget.client
.userDeviceKeys[widget.client.userID!]!
.startVerification();
},
);
if (req.error != null) return;
await KeyVerificationDialog(request: req.result!)
@ -424,8 +436,17 @@ class BootstrapDialogState extends State<BootstrapDialog> {
body = Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('assets/backup.png', fit: BoxFit.contain),
Text(L10n.of(context)!.yourChatBackupHasBeenSetUp),
const Icon(
Icons.check_circle_rounded,
size: 120,
color: Colors.green,
),
const SizedBox(height: 16),
Text(
L10n.of(context)!.yourChatBackupHasBeenSetUp,
style: const TextStyle(fontSize: 20),
),
const SizedBox(height: 16),
],
);
buttons.add(

View file

@ -2,25 +2,11 @@ import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
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:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart';
import 'package:record/record.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat_view.dart';
@ -47,6 +33,19 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/app_lock.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart';
import 'package:record/record.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../utils/account_bundles.dart';
import '../../utils/localized_exception_extension.dart';
import '../../utils/matrix_sdk_extensions/matrix_file_extension.dart';
@ -56,10 +55,12 @@ import 'sticker_picker_dialog.dart';
class ChatPage extends StatelessWidget {
final String roomId;
final String? shareText;
const ChatPage({
super.key,
required this.roomId,
this.shareText,
});
@override
@ -84,6 +85,7 @@ class ChatPage extends StatelessWidget {
child: ChatPageWithRoom(
key: Key('chat_page_$roomId'),
room: room,
shareText: shareText,
),
),
if (FluffyThemes.isThreeColumnMode(context) &&
@ -108,18 +110,21 @@ class ChatPage extends StatelessWidget {
class ChatPageWithRoom extends StatefulWidget {
final Room room;
final String? shareText;
const ChatPageWithRoom({
super.key,
required this.room,
this.shareText,
});
@override
ChatController createState() => ChatController();
}
class ChatController extends State<ChatPageWithRoom> {
// #Pangea
class ChatController extends State<ChatPageWithRoom>
with WidgetsBindingObserver {
// #Pangea
final PangeaController pangeaController = MatrixState.pangeaController;
late Choreographer choreographer = Choreographer(pangeaController, this);
// Pangea#
@ -209,8 +214,6 @@ class ChatController extends State<ChatPageWithRoom> {
final int _loadHistoryCount = 100;
String inputText = '';
String pendingText = '';
bool showEmojiPicker = false;
@ -223,23 +226,10 @@ class ChatController extends State<ChatPageWithRoom> {
'Try to recreate a room with is not a DM room. This should not be possible from the UI!',
);
}
final success = await showFutureLoadingDialog(
await showFutureLoadingDialog(
context: context,
future: () async {
final client = room.client;
final waitForSync = client.onSync.stream
.firstWhere((s) => s.rooms?.leave?.containsKey(room.id) ?? false);
await room.leave();
await waitForSync;
//#Pangea
// return await client.startDirectChat(userId);
return await client.startDirectChat(userId, enableEncryption: false);
//Pangea#
},
future: () => room.invite(userId),
);
final roomId = success.result;
if (roomId == null) return;
context.go('/rooms/$roomId');
}
void leaveChat() async {
@ -259,18 +249,7 @@ class ChatController extends State<ChatPageWithRoom> {
// #Pangea
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),
),
),
);
rethrow;
}
await timeline!.requestHistory(historyCount: _loadHistoryCount);
}
void requestFuture() async {
@ -278,27 +257,15 @@ class ChatController extends State<ChatPageWithRoom> {
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;
}
final mostRecentEventId = timeline.events.first.eventId;
await timeline.requestFuture(historyCount: _loadHistoryCount);
setReadMarker(eventId: mostRecentEventId);
}
void _updateScrollController() {
if (!mounted) {
return;
}
setReadMarker();
if (!scrollController.hasClients) return;
if (timeline?.allowNewEvent == false ||
scrollController.position.pixels > 0 && _scrolledUp == false) {
@ -310,20 +277,14 @@ class ChatController extends State<ChatPageWithRoom> {
if (scrollController.position.pixels == 0 ||
scrollController.position.pixels == 64) {
requestFuture();
} else if (scrollController.position.pixels ==
scrollController.position.maxScrollExtent ||
scrollController.position.pixels + 64 ==
scrollController.position.maxScrollExtent) {
requestHistory();
}
}
void _loadDraft() async {
final prefs = await SharedPreferences.getInstance();
final draft = prefs.getString('draft_$roomId');
final draft = widget.shareText ?? prefs.getString('draft_$roomId');
if (draft != null && draft.isNotEmpty) {
sendController.text = draft;
setState(() => inputText = draft);
}
}
@ -390,7 +351,6 @@ class ChatController extends State<ChatPageWithRoom> {
if (fullyRead.isEmpty) return;
if (timeline!.events.any((event) => event.eventId == fullyRead)) {
Logs().v('Scroll up to visible event', fullyRead);
scrollToEventId(fullyRead);
setReadMarker();
return;
}
@ -419,6 +379,13 @@ class ChatController extends State<ChatPageWithRoom> {
Future<void>? loadTimelineFuture;
int? animateInEventIndex;
void onInsert(int i) {
// setState will be called by updateView() anyway
animateInEventIndex = i;
}
Future<void> _getTimeline({
String? eventContextId,
}) async {
@ -432,6 +399,7 @@ class ChatController extends State<ChatPageWithRoom> {
timeline = await room.getTimeline(
onUpdate: updateView,
eventContextId: eventContextId,
onInsert: onInsert,
);
// #Pangea
List<Event>? messageEvents =
@ -457,7 +425,10 @@ class ChatController extends State<ChatPageWithRoom> {
} catch (e, s) {
Logs().w('Unable to load timeline on event ID $eventContextId', e, s);
if (!mounted) return;
timeline = await room.getTimeline(onUpdate: updateView);
timeline = await room.getTimeline(
onUpdate: updateView,
onInsert: onInsert,
);
if (!mounted) return;
if (e is TimeoutException || e is IOException) {
_showScrollUpMaterialBanner(eventContextId!);
@ -481,6 +452,15 @@ class ChatController extends State<ChatPageWithRoom> {
return;
}
String? scrollToEventIdMarker;
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state != AppLifecycleState.resumed) return;
if (!_scrolledUp) return;
setReadMarker();
}
Future<void>? _setReadMarkerFuture;
void setReadMarker({String? eventId}) {
@ -605,7 +585,6 @@ class ChatController extends State<ChatPageWithRoom> {
choreo: choreo,
useType: useType,
)
//#Pangea
.then(
(String? msgEventId) {
GoogleAnalytics.sendMessage(
@ -655,7 +634,8 @@ class ChatController extends State<ChatPageWithRoom> {
);
setState(() {
inputText = pendingText;
sendController.text = pendingText;
_inputTextIsEmpty = pendingText.isEmpty;
replyEvent = null;
editEvent = null;
pendingText = '';
@ -845,7 +825,7 @@ class ChatController extends State<ChatPageWithRoom> {
}
void emojiPickerAction() {
// #Pangea
// #Pangea
if (choreographer.itController.isOpen) {
return;
}
@ -1128,6 +1108,9 @@ class ChatController extends State<ChatPageWithRoom> {
});
return;
}
setState(() {
scrollToEventIdMarker = eventId;
});
await scrollController.scrollToIndex(
eventIndex,
preferPosition: AutoScrollPosition.middle,
@ -1146,7 +1129,7 @@ class ChatController extends State<ChatPageWithRoom> {
);
});
await loadTimelineFuture;
setReadMarker(eventId: timeline!.events.first.eventId);
setReadMarker();
}
scrollController.jumpTo(0);
}
@ -1228,7 +1211,7 @@ class ChatController extends State<ChatPageWithRoom> {
void clearSelectedEvents() => setState(() {
selectedEvents.clear();
showEmojiPicker = false;
//#Pangea
//#Pangea
choreographer.messageOptions.resetSelectedDisplayLang();
//Pangea#
});
@ -1251,7 +1234,7 @@ class ChatController extends State<ChatPageWithRoom> {
setState(() {
pendingText = sendController.text;
editEvent = selectedEvents.first;
inputText = sendController.text =
sendController.text =
editEvent!.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: false,
@ -1295,7 +1278,7 @@ class ChatController extends State<ChatPageWithRoom> {
}
void onSelectMessage(Event event) {
// #Pangea
// #Pangea
if (choreographer.itController.isOpen) {
return;
}
@ -1413,12 +1396,18 @@ class ChatController extends State<ChatPageWithRoom> {
static const Duration _storeInputTimeout = Duration(milliseconds: 500);
void onInputBarChanged(String text) {
if (_inputTextIsEmpty != text.isEmpty) {
setReadMarker();
setState(() {
_inputTextIsEmpty = text.isEmpty;
});
}
_storeInputTimeoutTimer?.cancel();
_storeInputTimeoutTimer = Timer(_storeInputTimeout, () async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('draft_$roomId', text);
});
setReadMarker();
if (text.endsWith(' ') && Matrix.of(context).hasComplexBundles) {
final clients = currentRoomBundle;
for (final client in clients) {
@ -1427,8 +1416,7 @@ class ChatController extends State<ChatPageWithRoom> {
text.toLowerCase() == '${prefix.toLowerCase()} ') {
setSendingClient(client);
setState(() {
inputText = '';
sendController.text = '';
sendController.clear();
});
return;
}
@ -1453,9 +1441,10 @@ class ChatController extends State<ChatPageWithRoom> {
);
}
}
setState(() => inputText = text);
}
bool _inputTextIsEmpty = true;
bool get isArchived =>
{Membership.leave, Membership.ban}.contains(room.membership);
@ -1522,7 +1511,7 @@ class ChatController extends State<ChatPageWithRoom> {
void cancelReplyEventAction() => setState(() {
if (editEvent != null) {
inputText = sendController.text = pendingText;
sendController.text = pendingText;
pendingText = '';
}
replyEvent = null;

View file

@ -1,10 +1,3 @@
// Flutter imports:
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/events/message.dart';
@ -16,6 +9,9 @@ import 'package:fluffychat/pangea/widgets/chat/locked_chat_message.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
class ChatEventList extends StatelessWidget {
final ChatController controller;
@ -28,11 +24,16 @@ class ChatEventList extends StatelessWidget {
Widget build(BuildContext context) {
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
final events = controller.timeline!.events
.where((event) => event.isVisibleInGui)
.toList();
final animateInEventIndex = controller.animateInEventIndex;
// create a map of eventId --> index to greatly improve performance of
// ListView's findChildIndexCallback
final thisEventsKeyMap = <String, int>{};
for (var i = 0; i < controller.timeline!.events.length; i++) {
thisEventsKeyMap[controller.timeline!.events[i].eventId] = i;
for (var i = 0; i < events.length; i++) {
thisEventsKeyMap[events[i].eventId] = i;
}
return SelectionArea(
@ -84,73 +85,86 @@ class ChatEventList extends StatelessWidget {
// Pangea#
// Request history button or progress indicator:
if (i == controller.timeline!.events.length + 1) {
if (i == events.length + 1) {
if (controller.timeline!.isRequestingHistory) {
return const Center(
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
);
}
if (controller.timeline!.canRequestHistory) {
return Center(
child: IconButton(
onPressed: controller.requestHistory,
icon: const Icon(Icons.refresh_outlined),
),
return Builder(
builder: (context) {
WidgetsBinding.instance
.addPostFrameCallback((_) => controller.requestHistory);
return Center(
child: IconButton(
onPressed: controller.requestHistory,
icon: const Icon(Icons.refresh_outlined),
),
);
},
);
}
return const SizedBox.shrink();
}
i--;
// The message at this index:
// #Pangea
// final event = controller.timeline!.events[i - 1];
final event = controller.timeline!.events[i - 2];
// final event = events[i];
final event = events[i - 1];
// Pangea#
final animateIn = animateInEventIndex != null &&
controller.timeline!.events.length > animateInEventIndex &&
event == controller.timeline!.events[animateInEventIndex];
return AutoScrollTag(
key: ValueKey(event.eventId),
index: i - 1,
index: i,
controller: controller.scrollController,
child: event.isVisibleInGui
? Message(
event,
onSwipe: () => controller.replyAction(replyTo: event),
// #Pangea
onInfoTab: (_) => {},
// onInfoTab: controller.showEventInfo,
// Pangea#
onAvatarTab: (Event event) => showAdaptiveBottomSheet(
context: context,
builder: (c) => UserBottomSheet(
user: event.senderFromMemoryOrFallback,
outerContext: context,
onMention: () => controller.sendController.text +=
'${event.senderFromMemoryOrFallback.mention} ',
),
),
onSelect: controller.onSelectMessage,
scrollToEventId: (String eventId) =>
controller.scrollToEventId(eventId),
// #Pangea
// longPressSelect: controller.selectedEvents.isEmpty,
selectedDisplayLang: controller
.choreographer.messageOptions.selectedDisplayLang,
immersionMode: controller.choreographer.immersionMode,
definitions: controller.choreographer.definitionsEnabled,
// Pangea#
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(),
child: Message(
event,
animateIn: animateIn,
resetAnimateIn: () {
controller.animateInEventIndex = null;
},
onSwipe: () => controller.replyAction(replyTo: event),
// #Pangea
onInfoTab: (_) => {},
// onInfoTab: controller.showEventInfo,
// Pangea#
onAvatarTab: (Event event) => showAdaptiveBottomSheet(
context: context,
builder: (c) => UserBottomSheet(
user: event.senderFromMemoryOrFallback,
outerContext: context,
onMention: () => controller.sendController.text +=
'${event.senderFromMemoryOrFallback.mention} ',
),
),
highlightMarker:
controller.scrollToEventIdMarker == event.eventId,
onSelect: controller.onSelectMessage,
scrollToEventId: (String eventId) =>
controller.scrollToEventId(eventId),
// #Pangea
// longPressSelect: controller.selectedEvents.isEmpty,
selectedDisplayLang:
controller.choreographer.messageOptions.selectedDisplayLang,
immersionMode: controller.choreographer.immersionMode,
definitions: controller.choreographer.definitionsEnabled,
// Pangea#
selected: controller.selectedEvents
.any((e) => e.eventId == event.eventId),
timeline: controller.timeline!,
displayReadMarker:
controller.readMarkerEventId == event.eventId &&
controller.timeline?.allowNewEvent == false,
nextEvent: i + 1 < events.length ? events[i + 1] : null,
),
);
},
childCount: controller.timeline!.events.length + 2,
childCount: events.length + 2,
findChildIndexCallback: (key) =>
controller.findChildIndexCallback(key, thisEventsKeyMap),
),

View file

@ -1,17 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:animations/animations.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart';
import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
import 'package:matrix/matrix.dart';
import '../../config/themes.dart';
import 'chat.dart';
import 'input_bar.dart';
@ -116,8 +115,8 @@ class ChatInputRow extends StatelessWidget {
curve: FluffyThemes.animationCurve,
height: 56,
//#Pangea
// width: controller.inputText.isEmpty ? 56 : 0,
width: controller.inputText.isEmpty &&
// width: controller.sendController.text.isEmpty ? 56 : 0,
width: controller.sendController.text.isEmpty &&
controller.pangeaController.permissionsController
.showChatInputAddButton(controller.roomId)
? 56
@ -163,7 +162,7 @@ class ChatInputRow extends StatelessWidget {
contentPadding: const EdgeInsets.all(0),
),
),
//#Pangea
//#Pangea
// if (PlatformInfos.isMobile)
if (PlatformInfos.isMobile &&
controller.pangeaController.permissionsController
@ -295,8 +294,10 @@ class ChatInputRow extends StatelessWidget {
autofocus: false,
// Pangea#
keyboardType: TextInputType.multiline,
textInputAction:
AppConfig.sendOnEnter ? TextInputAction.send : null,
textInputAction: AppConfig.sendOnEnter == true &&
PlatformInfos.isMobile
? TextInputAction.send
: null,
// #Pangea
// onSubmitted: controller.onInputBarSubmitted,
onSubmitted: (String value) =>
@ -317,7 +318,7 @@ class ChatInputRow extends StatelessWidget {
),
),
if (PlatformInfos.platformCanRecord &&
controller.inputText.isEmpty)
controller.sendController.text.isEmpty)
Container(
height: 56,
alignment: Alignment.center,
@ -328,7 +329,7 @@ class ChatInputRow extends StatelessWidget {
),
),
if (!PlatformInfos.isMobile ||
controller.inputText.isNotEmpty)
controller.sendController.text.isNotEmpty)
// #Pangea
ChoreographerSendButton(controller: controller),
// Container(

View file

@ -1,13 +1,5 @@
// Flutter imports:
import 'package:flutter/material.dart';
import 'package:badges/badges.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
@ -24,8 +16,12 @@ import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/pages/class_analytics/measure_able.dart';
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
import 'package:fluffychat/widgets/connection_status_header.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
import '../../utils/stream_extension.dart';
import 'chat_emoji_picker.dart';
import 'chat_input_row.dart';
@ -40,7 +36,7 @@ class ChatView extends StatelessWidget {
List<Widget> _appBarActions(BuildContext context) {
if (controller.selectMode) {
return [
// #Pangea
// #Pangea
LanguageDisplayToggle(controller: controller),
// Pangea#
if (controller.canEditSelectedEvents)
@ -167,269 +163,268 @@ class ChatView extends StatelessWidget {
child: GestureDetector(
onTapDown: (_) => controller.setReadMarker(),
behavior: HitTestBehavior.opaque,
child: StreamBuilder(
stream: controller.room.onUpdate.stream
.rateLimit(const Duration(seconds: 1)),
builder: (context, snapshot) => FutureBuilder(
future: controller.loadTimelineFuture,
builder: (BuildContext context, snapshot) {
return Scaffold(
appBar: AppBar(
actionsIconTheme: IconThemeData(
color: controller.selectedEvents.isEmpty
? null
: Theme.of(context).colorScheme.primary,
),
leading: controller.selectMode
? IconButton(
icon: const Icon(Icons.close),
onPressed: controller.clearSelectedEvents,
tooltip: L10n.of(context)!.close,
color: Theme.of(context).colorScheme.primary,
)
: UnreadRoomsBadge(
filter: (r) =>
r.id != controller.roomId
// #Pangea
&&
!r.isAnalyticsRoom,
// Pangea#
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()),
),
titleSpacing: 0,
title: ChatAppBarTitle(controller),
actions: _appBarActions(context),
),
// #Pangea
// floatingActionButton: controller.showScrollDownButton &&
// controller.selectedEvents.isEmpty
floatingActionButton: controller.selectedEvents.isEmpty
? (controller.showScrollDownButton
// Pangea#
? Padding(
padding: const EdgeInsets.only(bottom: 56.0),
child: FloatingActionButton(
onPressed: controller.scrollDown,
heroTag: null,
mini: true,
child: const Icon(Icons.arrow_downward_outlined),
),
child: MouseRegion(
onEnter: (_) => controller.setReadMarker(),
child: StreamBuilder(
stream: controller.room.onUpdate.stream
.rateLimit(const Duration(seconds: 1)),
builder: (context, snapshot) => FutureBuilder(
future: controller.loadTimelineFuture,
builder: (BuildContext context, snapshot) {
return Scaffold(
appBar: AppBar(
actionsIconTheme: IconThemeData(
color: controller.selectedEvents.isEmpty
? null
: Theme.of(context).colorScheme.primary,
),
leading: controller.selectMode
? IconButton(
icon: const Icon(Icons.close),
onPressed: controller.clearSelectedEvents,
tooltip: L10n.of(context)!.close,
color: Theme.of(context).colorScheme.primary,
)
// #Pangea
: controller.choreographer.errorService.error != null
? ChoreographerHasErrorButton(
controller.pangeaController,
controller.choreographer.errorService.error!,
)
: controller.showPermissionsError
? LanguagePermissionsButtons(
choreographer: controller.choreographer,
roomID: controller.roomId,
)
: null)
// #Pangea
: null,
body: DropTarget(
onDragDone: controller.onDragDone,
onDragEntered: controller.onDragEntered,
onDragExited: controller.onDragExited,
child: Stack(
children: <Widget>[
if (Matrix.of(context).wallpaper != null)
Image.file(
Matrix.of(context).wallpaper!,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
filterQuality: FilterQuality.medium,
),
SafeArea(
child: Column(
children: <Widget>[
TombstoneDisplay(controller),
if (scrollUpBannerEventId != null)
Material(
color: Theme.of(context)
.colorScheme
.surfaceVariant,
shape: Border(
bottom: BorderSide(
width: 1,
color: Theme.of(context).dividerColor,
: UnreadRoomsBadge(
filter: (r) =>
r.id != controller.roomId
// #Pangea
&&
!r.isAnalyticsRoom,
// Pangea#
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()),
),
titleSpacing: 0,
title: ChatAppBarTitle(controller),
actions: _appBarActions(context),
),
// #Pangea
// floatingActionButton: controller.showScrollDownButton &&
// controller.selectedEvents.isEmpty
floatingActionButton: controller.selectedEvents.isEmpty
? (controller.showScrollDownButton
// Pangea#
? Padding(
padding: const EdgeInsets.only(bottom: 56.0),
child: FloatingActionButton(
onPressed: controller.scrollDown,
heroTag: null,
mini: true,
child:
const Icon(Icons.arrow_downward_outlined),
),
)
// #Pangea
: controller.choreographer.errorService.error != null
? ChoreographerHasErrorButton(
controller.pangeaController,
controller.choreographer.errorService.error!,
)
: controller.showPermissionsError
? LanguagePermissionsButtons(
choreographer: controller.choreographer,
roomID: controller.roomId,
)
: null)
// #Pangea
: null,
body: DropTarget(
onDragDone: controller.onDragDone,
onDragEntered: controller.onDragEntered,
onDragExited: controller.onDragExited,
child: Stack(
children: <Widget>[
SafeArea(
child: Column(
children: <Widget>[
TombstoneDisplay(controller),
if (scrollUpBannerEventId != null)
Material(
color: Theme.of(context)
.colorScheme
.surfaceVariant,
shape: Border(
bottom: BorderSide(
width: 1,
color: Theme.of(context).dividerColor,
),
),
child: ListTile(
leading: IconButton(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
icon: const Icon(Icons.close),
tooltip: L10n.of(context)!.close,
onPressed: () {
controller
.discardScrollUpBannerEventId();
controller.setReadMarker();
},
),
title: Text(
L10n.of(context)!.jumpToLastReadMessage,
),
contentPadding:
const EdgeInsets.only(left: 8),
trailing: TextButton(
onPressed: () {
controller.scrollToEventId(
scrollUpBannerEventId,
);
controller
.discardScrollUpBannerEventId();
},
child: Text(L10n.of(context)!.jump),
),
),
),
child: ListTile(
leading: IconButton(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
icon: const Icon(Icons.close),
tooltip: L10n.of(context)!.close,
onPressed: () {
controller.discardScrollUpBannerEventId();
controller.setReadMarker();
},
),
title: Text(
L10n.of(context)!.jumpToLastReadMessage,
),
contentPadding:
const EdgeInsets.only(left: 8),
trailing: TextButton(
onPressed: () {
controller.scrollToEventId(
scrollUpBannerEventId,
PinnedEvents(controller),
Expanded(
child: GestureDetector(
onTap: controller.clearSingleSelectedEvent,
child: Builder(
builder: (context) {
if (controller.timeline == null) {
return const Center(
child: CircularProgressIndicator
.adaptive(
strokeWidth: 2,
),
);
}
return ChatEventList(
controller: controller,
);
controller.discardScrollUpBannerEventId();
},
child: Text(L10n.of(context)!.jump),
),
),
),
PinnedEvents(controller),
Expanded(
child: GestureDetector(
onTap: controller.clearSingleSelectedEvent,
child: Builder(
builder: (context) {
if (controller.timeline == null) {
return const Center(
child:
CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
);
}
return ChatEventList(
controller: controller,
);
},
),
),
),
if (controller.room.canSendDefaultMessages &&
controller.room.membership == Membership.join)
// #Pangea
// Container(
ConditionalFlexible(
isScroll: controller.isRowScrollable,
child: ConditionalScroll(
if (controller.room.canSendDefaultMessages &&
controller.room.membership == Membership.join)
// #Pangea
// Container(
ConditionalFlexible(
isScroll: controller.isRowScrollable,
child: MeasurableWidget(
onChange: (size, position) {
controller.inputRowSize = size!.height;
},
child: Container(
// Pangea#
margin: EdgeInsets.only(
bottom: bottomSheetPadding,
left: bottomSheetPadding,
right: bottomSheetPadding,
),
constraints: const BoxConstraints(
maxWidth:
FluffyThemes.columnWidth * 2.5,
),
alignment: Alignment.center,
child: Material(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(
AppConfig.borderRadius,
),
bottomRight: Radius.circular(
AppConfig.borderRadius,
),
child: ConditionalScroll(
isScroll: controller.isRowScrollable,
child: MeasurableWidget(
onChange: (size, position) {
controller.inputRowSize = size!.height;
},
child: Container(
// Pangea#
margin: EdgeInsets.only(
bottom: bottomSheetPadding,
left: bottomSheetPadding,
right: bottomSheetPadding,
),
elevation: 4,
shadowColor: Colors.black.withAlpha(64),
clipBehavior: Clip.hardEdge,
color: Theme.of(context).brightness ==
Brightness.light
? Colors.white
: Colors.black,
child: controller
.room.isAbandonedDMRoom ==
true
? Row(
mainAxisAlignment:
MainAxisAlignment
.spaceEvenly,
children: [
TextButton.icon(
style: TextButton.styleFrom(
padding:
const EdgeInsets.all(
16,
constraints: const BoxConstraints(
maxWidth:
FluffyThemes.columnWidth * 2.5,
),
alignment: Alignment.center,
child: Material(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(
AppConfig.borderRadius,
),
bottomRight: Radius.circular(
AppConfig.borderRadius,
),
),
elevation: 4,
shadowColor:
Colors.black.withAlpha(64),
clipBehavior: Clip.hardEdge,
color: Theme.of(context).brightness ==
Brightness.light
? Colors.white
: Colors.black,
child: controller
.room.isAbandonedDMRoom ==
true
? Row(
mainAxisAlignment:
MainAxisAlignment
.spaceEvenly,
children: [
TextButton.icon(
style:
TextButton.styleFrom(
padding:
const EdgeInsets
.all(16),
foregroundColor:
Theme.of(context)
.colorScheme
.error,
),
foregroundColor:
Theme.of(context)
.colorScheme
.error,
),
icon: const Icon(
Icons.archive_outlined,
),
onPressed:
controller.leaveChat,
label: Text(
L10n.of(context)!.leave,
),
),
TextButton.icon(
style: TextButton.styleFrom(
padding:
const EdgeInsets.all(
16,
icon: const Icon(
Icons.archive_outlined,
),
onPressed:
controller.leaveChat,
label: Text(
L10n.of(context)!.leave,
),
),
icon: const Icon(
Icons.forum_outlined,
TextButton.icon(
style:
TextButton.styleFrom(
padding:
const EdgeInsets
.all(16),
),
icon: const Icon(
Icons.forum_outlined,
),
onPressed: controller
.recreateChat,
label: Text(
L10n.of(context)!
.reopenChat,
),
),
onPressed:
controller.recreateChat,
label: Text(
L10n.of(context)!
.reopenChat,
),
),
],
)
: Column(
mainAxisSize: MainAxisSize.min,
children: [
const ConnectionStatusHeader(),
ReactionsPicker(controller),
ReplyDisplay(controller),
ChatInputRow(controller),
ChatEmojiPicker(controller),
],
),
],
)
: Column(
mainAxisSize:
MainAxisSize.min,
children: [
const ConnectionStatusHeader(),
ReactionsPicker(controller),
ReplyDisplay(controller),
ChatInputRow(controller),
ChatEmojiPicker(controller),
],
),
),
),
),
),
),
),
if (controller.dragging)
Container(
color: Theme.of(context)
.scaffoldBackgroundColor
.withOpacity(0.9),
alignment: Alignment.center,
child: const Icon(
Icons.upload_outlined,
size: 100,
if (controller.dragging)
Container(
color: Theme.of(context)
.scaffoldBackgroundColor
.withOpacity(0.9),
alignment: Alignment.center,
child: const Icon(
Icons.upload_outlined,
size: 100,
),
),
),
],
],
),
),
),
],
],
),
),
),
);
},
);
},
),
),
),
),
@ -467,4 +462,4 @@ class ConditionalScroll extends StatelessWidget {
return child;
}
}
// #Pangea
// #Pangea

View file

@ -50,6 +50,8 @@ String commandHint(L10n l10n, String command) {
return l10n.commandHint_hug;
case 'cuddle':
return l10n.commandHint_cuddle;
case 'sendraw':
return l10n.commandHint_sendraw;
default:
return "";
}

View file

@ -1,49 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:url_launcher/link.dart';
import 'edit_widgets_dialog.dart';
class CupertinoWidgetsBottomSheet extends StatelessWidget {
final Room room;
const CupertinoWidgetsBottomSheet({super.key, required this.room});
@override
Widget build(BuildContext context) {
return CupertinoActionSheet(
title: Text(L10n.of(context)!.matrixWidgets),
actions: [
...room.widgets.map(
(widget) => Link(
builder: (context, callback) {
return CupertinoActionSheetAction(
onPressed: callback ?? () {},
child: Text(widget.name ?? widget.url),
);
},
target: LinkTarget.blank,
uri: Uri.parse(widget.url),
),
),
CupertinoActionSheetAction(
child: Text(L10n.of(context)!.editWidgets),
onPressed: () {
Navigator.of(context).pop();
showCupertinoDialog(
context: context,
builder: (context) => EditWidgetsDialog(room: room),
useRootNavigator: false,
);
},
),
CupertinoActionSheetAction(
onPressed: Navigator.of(context).pop,
child: Text(L10n.of(context)!.cancel),
),
],
);
}
}

View file

@ -1,34 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'add_widget_tile.dart';
class EditWidgetsDialog extends StatelessWidget {
final Room room;
const EditWidgetsDialog({super.key, required this.room});
@override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text(L10n.of(context)!.editWidgets),
children: [
...room.widgets.map(
(e) => ListTile(
title: Text(e.name ?? e.type),
leading: IconButton(
onPressed: () {
room.deleteWidget(e.id!);
Navigator.of(context).pop();
},
icon: const Icon(Icons.delete),
),
),
),
AddWidgetTile(room: room),
],
);
}
}

View file

@ -155,7 +155,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
final eventWaveForm = widget.event.content
.tryGetMap<String, dynamic>('org.matrix.msc1767.audio')
?.tryGetList<int>('waveform');
if (eventWaveForm == null) {
if (eventWaveForm == null || eventWaveForm.isEmpty) {
return List<int>.filled(AudioPlayerWidget.wavesCount, 500);
}
while (eventWaveForm.length < AudioPlayerWidget.wavesCount) {

View file

@ -6,6 +6,7 @@ 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:html/dom.dart' as dom;
import 'package:linkify/linkify.dart';
import 'package:matrix/matrix.dart';
@ -26,6 +27,36 @@ class HtmlMessage extends StatelessWidget {
this.textColor = Colors.black,
});
dom.Node _linkifyHtml(dom.Node element) {
for (final node in element.nodes) {
if (node is! dom.Text ||
(element is dom.Element && element.localName == 'code')) {
node.replaceWith(_linkifyHtml(node));
continue;
}
final parts = linkify(
node.text,
options: const LinkifyOptions(humanize: false),
);
if (!parts.any((part) => part is UrlElement)) {
continue;
}
final newHtml = parts
.map(
(linkifyElement) => linkifyElement is! UrlElement
? Uri.encodeComponent(linkifyElement.text)
: '<a href="${linkifyElement.text}">${linkifyElement.text}</a>',
)
.join(' ');
node.replaceWith(dom.Element.html('<p>$newHtml</p>'));
}
return element;
}
@override
Widget build(BuildContext context) {
// riot-web is notorious for creating bad reply fallback events from invalid messages which, if
@ -46,21 +77,6 @@ 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('');
final linkColor = textColor.withAlpha(150);
final blockquoteStyle = Style(
@ -73,87 +89,86 @@ class HtmlMessage extends StatelessWidget {
padding: HtmlPaddings.only(left: 6, bottom: 0),
);
final element = _linkifyHtml(HtmlParser.parseHTML(renderHtml));
// there is no need to pre-validate the html, as we validate it while rendering
return MouseRegion(
cursor: SystemMouseCursors.text,
child: 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': blockquoteStyle,
'tg-forward': blockquoteStyle,
'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),
),
},
extensions: [
RoomPillExtension(context, room),
CodeExtension(fontSize: fontSize),
MatrixMathExtension(
style: TextStyle(fontSize: fontSize, color: textColor),
),
const TableHtmlExtension(),
SpoilerExtension(textColor: textColor),
const ImageExtension(),
FontColorExtension(),
],
onLinkTap: (url, _, element) => UrlLauncher(
context,
url,
element?.text,
).launchUrl(),
onlyRenderTheseTags: const {
...allowedHtmlTags,
// Needed to make it work properly
'body',
'html',
},
shrinkWrap: true,
),
return Html.fromElement(
documentElement: element as dom.Element,
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': blockquoteStyle,
'tg-forward': blockquoteStyle,
'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),
),
},
extensions: [
RoomPillExtension(context, room),
CodeExtension(fontSize: fontSize),
MatrixMathExtension(
style: TextStyle(fontSize: fontSize, color: textColor),
),
const TableHtmlExtension(),
SpoilerExtension(textColor: textColor),
const ImageExtension(),
FontColorExtension(),
],
onLinkTap: (url, _, element) => UrlLauncher(
context,
url,
element?.text,
).launchUrl(),
onlyRenderTheseTags: const {
...allowedHtmlTags,
// Needed to make it work properly
'body',
'html',
},
shrinkWrap: true,
);
}

View file

@ -89,7 +89,11 @@ class ImageBubble extends StatelessWidget {
return Material(
shape: RoundedRectangleBorder(
borderRadius: borderRadius,
side: BorderSide(color: Theme.of(context).dividerColor),
side: BorderSide(
color: event.messageType == MessageTypes.Sticker
? Colors.transparent
: Theme.of(context).dividerColor,
),
),
child: InkWell(
onTap: () => _onTap(context),

View file

@ -1,17 +1,19 @@
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';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/models/language_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
import 'package:fluffychat/widgets/matrix.dart';
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';
import 'package:vibration/vibration.dart';
import '../../../config/app_config.dart';
import 'message_content.dart';
import 'message_reactions.dart';
@ -31,6 +33,9 @@ class Message extends StatelessWidget {
final bool longPressSelect;
final bool selected;
final Timeline timeline;
final bool highlightMarker;
final bool animateIn;
final void Function()? resetAnimateIn;
// #Pangea
final LanguageModel? selectedDisplayLang;
final bool immersionMode;
@ -49,6 +54,9 @@ class Message extends StatelessWidget {
required this.onSwipe,
this.selected = false,
required this.timeline,
this.highlightMarker = false,
this.animateIn = false,
this.resetAnimateIn,
// #Pangea
required this.selectedDisplayLang,
required this.immersionMode,
@ -82,7 +90,7 @@ class Message extends StatelessWidget {
final client = Matrix.of(context).client;
final ownMessage = event.senderId == client.userID;
final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft;
var color = Theme.of(context).colorScheme.onInverseSurface;
var color = Theme.of(context).colorScheme.surfaceVariant;
final displayTime = event.type == EventTypes.RoomCreate ||
nextEvent == null ||
!event.originServerTs.sameEnvironment(nextEvent!.originServerTs);
@ -138,215 +146,282 @@ class Message extends StatelessWidget {
);
// Pangea#
final row = Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: rowMainAxisAlignment,
children: [
if (sameSender || ownMessage)
SizedBox(
width: Avatar.defaultSize,
child: Center(
child: SizedBox(
width: 16,
height: 16,
child: event.status == EventStatus.sending
? const CircularProgressIndicator.adaptive(
strokeWidth: 2,
)
: event.status == EventStatus.error
? const Icon(Icons.error, color: Colors.red)
: null,
),
),
)
else
FutureBuilder<User?>(
future: event.fetchSenderUser(),
builder: (context, snapshot) {
final user = snapshot.data ?? event.senderFromMemoryOrFallback;
return Avatar(
mxContent: user.avatarUrl,
name: user.calcDisplayname(),
presenceUserId: user.stateKey,
onTap: () => onAvatarTab(event),
);
},
),
Expanded(
child: Column(
final resetAnimateIn = this.resetAnimateIn;
var animateIn = this.animateIn;
final row = StatefulBuilder(
builder: (context, setState) {
if (animateIn && resetAnimateIn != null) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
animateIn = false;
setState(resetAnimateIn);
});
}
return AnimatedSlide(
offset: Offset(0, animateIn ? 1 : 0),
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: rowMainAxisAlignment,
children: [
if (!sameSender)
Padding(
padding: const EdgeInsets.only(left: 8.0, bottom: 4),
child: ownMessage || event.room.isDirectChat
? const SizedBox(height: 12)
: FutureBuilder<User?>(
future: event.fetchSenderUser(),
builder: (context, snapshot) {
final displayname =
snapshot.data?.calcDisplayname() ??
event.senderFromMemoryOrFallback
.calcDisplayname();
return Text(
displayname,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: (Theme.of(context).brightness ==
Brightness.light
? displayname.color
: displayname.lightColorText),
),
);
},
),
),
Container(
alignment: alignment,
padding: const EdgeInsets.only(left: 8),
child: Material(
color: noBubble ? Colors.transparent : color,
borderRadius: borderRadius,
clipBehavior: Clip.antiAlias,
// #Pangea
child: CompositedTransformTarget(
link: MatrixState.pAnyState
.layerLinkAndKey(event.eventId)
.link,
child: Container(
key: MatrixState.pAnyState
.layerLinkAndKey(event.eventId)
.key,
// Pangea#
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
),
padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 1.5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (event.relationshipType == RelationshipTypes.reply)
FutureBuilder<Event?>(
future: event.getReplyEvent(timeline),
builder: (BuildContext context, snapshot) {
final replyEvent = snapshot.hasData
? snapshot.data!
: Event(
eventId: event.relationshipEventId!,
content: {
'msgtype': 'm.text',
'body': '...',
},
senderId: event.senderId,
type: 'm.room.message',
room: event.room,
status: EventStatus.sent,
originServerTs: DateTime.now(),
);
return InkWell(
onTap: () =>
scrollToEventId(replyEvent.eventId),
child: AbsorbPointer(
child: Container(
margin: const EdgeInsets.symmetric(
vertical: 4.0,
),
child: ReplyContent(
replyEvent,
ownMessage: ownMessage,
timeline: timeline,
),
),
),
);
},
),
MessageContent(
displayEvent,
textColor: textColor,
onInfoTab: onInfoTab,
borderRadius: borderRadius,
// #Pangea
selected: selected,
pangeaMessageEvent: pangeaMessageEvent,
selectedDisplayLang: selectedDisplayLang,
immersionMode: immersionMode,
definitions: definitions,
// Pangea#
),
if (event.hasAggregatedEvents(
timeline,
RelationshipTypes.edit,
)
// #Pangea
||
(pangeaMessageEvent.showUseType)
// Pangea#
)
Padding(
padding: const EdgeInsets.only(
top: 4.0,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// #Pangea
if (pangeaMessageEvent.showUseType) ...[
pangeaMessageEvent.useType.iconView(
context,
textColor.withAlpha(164),
),
const SizedBox(width: 4),
],
if (event.hasAggregatedEvents(
timeline,
RelationshipTypes.edit,
)) ...[
// Pangea#
Icon(
Icons.edit_outlined,
color: textColor.withAlpha(164),
size: 14,
),
Text(
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
style: TextStyle(
color: textColor.withAlpha(164),
fontSize: 12,
),
),
],
],
),
),
],
),
if (longPressSelect)
SizedBox(
height: 32,
width: Avatar.defaultSize,
child: Checkbox.adaptive(
value: selected,
shape: const CircleBorder(),
onChanged: (_) => onSelect(event),
),
)
else if (sameSender || ownMessage)
SizedBox(
width: Avatar.defaultSize,
child: Center(
child: SizedBox(
width: 16,
height: 16,
child: event.status == EventStatus.error
? const Icon(Icons.error, color: Colors.red)
: event.fileSendingStatus != null
? const CircularProgressIndicator.adaptive(
strokeWidth: 1,
)
: null,
),
),
)
else
FutureBuilder<User?>(
future: event.fetchSenderUser(),
builder: (context, snapshot) {
final user =
snapshot.data ?? event.senderFromMemoryOrFallback;
return Avatar(
mxContent: user.avatarUrl,
name: user.calcDisplayname(),
presenceUserId: user.stateKey,
onTap: () => onAvatarTab(event),
);
},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (!sameSender)
Padding(
padding: const EdgeInsets.only(left: 8.0, bottom: 4),
child: ownMessage || event.room.isDirectChat
? const SizedBox(height: 12)
: FutureBuilder<User?>(
future: event.fetchSenderUser(),
builder: (context, snapshot) {
final displayname =
snapshot.data?.calcDisplayname() ??
event.senderFromMemoryOrFallback
.calcDisplayname();
return Text(
displayname,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: (Theme.of(context).brightness ==
Brightness.light
? displayname.color
: displayname.lightColorText),
),
);
},
),
),
Container(
alignment: alignment,
padding: const EdgeInsets.only(left: 8),
child: GestureDetector(
onLongPress: longPressSelect
? null
: () {
onSelect(event);
// Android usually has a vibration effect on long press:
if (PlatformInfos.isAndroid) {
Vibration.hasVibrator().then((has) {
if (has == true) {
Vibration.vibrate(duration: 50);
}
});
}
},
child: AnimatedOpacity(
opacity: animateIn
? 0
: event.redacted ||
event.messageType ==
MessageTypes.BadEncrypted ||
event.status.isSending
? 0.5
: 1,
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
child: Material(
color: noBubble ? Colors.transparent : color,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: borderRadius,
),
// #Pangea
child: CompositedTransformTarget(
link: MatrixState.pAnyState
.layerLinkAndKey(event.eventId)
.link,
child: Container(
key: MatrixState.pAnyState
.layerLinkAndKey(event.eventId)
.key,
// Pangea#
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
),
padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 1.5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (event.relationshipType ==
RelationshipTypes.reply)
FutureBuilder<Event?>(
future: event.getReplyEvent(timeline),
builder:
(BuildContext context, snapshot) {
final replyEvent = snapshot.hasData
? snapshot.data!
: Event(
eventId: event
.relationshipEventId!,
content: {
'msgtype': 'm.text',
'body': '...',
},
senderId: event.senderId,
type: 'm.room.message',
room: event.room,
status: EventStatus.sent,
originServerTs:
DateTime.now(),
);
return Padding(
padding: const EdgeInsets.only(
bottom: 4.0,
),
child: InkWell(
borderRadius:
ReplyContent.borderRadius,
onTap: () => scrollToEventId(
replyEvent.eventId,
),
child: AbsorbPointer(
child: ReplyContent(
replyEvent,
ownMessage: ownMessage,
timeline: timeline,
),
),
),
);
},
),
MessageContent(
displayEvent,
textColor: textColor,
onInfoTab: onInfoTab,
borderRadius: borderRadius,
// #Pangea
selected: selected,
pangeaMessageEvent: pangeaMessageEvent,
selectedDisplayLang: selectedDisplayLang,
immersionMode: immersionMode,
definitions: definitions,
// Pangea#
),
if (event.hasAggregatedEvents(
timeline,
RelationshipTypes.edit,
) // #Pangea
||
(pangeaMessageEvent.showUseType)
// Pangea#
)
Padding(
padding: const EdgeInsets.only(
top: 4.0,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// #Pangea
if (pangeaMessageEvent
.showUseType) ...[
pangeaMessageEvent.useType
.iconView(
context,
textColor.withAlpha(164),
),
const SizedBox(width: 4),
],
if (event.hasAggregatedEvents(
timeline,
RelationshipTypes.edit,
)) ...[
// Pangea#
Icon(
Icons.edit_outlined,
color: textColor.withAlpha(164),
size: 14,
),
Text(
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
style: TextStyle(
color:
textColor.withAlpha(164),
fontSize: 12,
),
),
],
],
),
),
],
),
),
),
),
),
),
),
],
),
),
],
),
),
],
);
},
);
Widget container;
if (event.hasAggregatedEvents(timeline, RelationshipTypes.reaction) ||
displayTime ||
selected ||
displayReadMarker) {
final showReceiptsRow =
event.hasAggregatedEvents(timeline, RelationshipTypes.reaction);
if (showReceiptsRow || displayTime || selected || displayReadMarker) {
container = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
@ -355,9 +430,7 @@ class Message extends StatelessWidget {
if (displayTime || selected)
Padding(
padding: displayTime
? const EdgeInsets.symmetric(
vertical: 8.0,
)
? const EdgeInsets.symmetric(vertical: 8.0)
: EdgeInsets.zero,
child: Center(
child: Material(
@ -371,17 +444,17 @@ class Message extends StatelessWidget {
BorderRadius.circular(AppConfig.borderRadius / 2),
clipBehavior: Clip.antiAlias,
child: Padding(
padding: const EdgeInsets.all(6.0),
padding: const EdgeInsets.all(4.0),
child: Text(
event.originServerTs.localizedTime(context),
style: TextStyle(fontSize: 14 * AppConfig.fontSizeFactor),
style: TextStyle(fontSize: 13 * AppConfig.fontSizeFactor),
),
),
),
),
),
row,
if (event.hasAggregatedEvents(timeline, RelationshipTypes.reaction))
if (showReceiptsRow)
Padding(
padding: EdgeInsets.only(
top: 4.0,
@ -425,34 +498,77 @@ class Message extends StatelessWidget {
container = row;
}
if (event.messageType == MessageTypes.BadEncrypted) {
container = Opacity(opacity: 0.4, child: container);
}
return Swipeable(
key: ValueKey(event.eventId),
background: const Padding(
padding: EdgeInsets.symmetric(horizontal: 12.0),
child: Center(
child: Icon(Icons.check_outlined),
return Container(
alignment: Alignment.center,
color: selected
? Theme.of(context).colorScheme.secondaryContainer.withAlpha(100)
: highlightMarker
? Theme.of(context).colorScheme.tertiaryContainer.withAlpha(100)
: Colors.transparent,
child: Swipeable(
key: ValueKey(event.eventId),
background: const Padding(
padding: EdgeInsets.symmetric(horizontal: 12.0),
child: Center(
child: Icon(Icons.check_outlined),
),
),
),
direction: SwipeDirection.endToStart,
onSwipe: (_) => onSwipe(),
child: InkWell(
onTap: () => onSelect(event),
child: Container(
color: selected
? Theme.of(context).primaryColor.withAlpha(100)
: Theme.of(context).primaryColor.withAlpha(0),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 2.5,
direction: SwipeDirection.endToStart,
onSwipe: (_) => onSwipe(),
child: HoverBuilder(
builder: (context, hovered) => Stack(
children: [
Container(
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 2.5,
),
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 4.0,
),
child: container,
),
Positioned(
left: ownMessage ? null : 48,
right: ownMessage ? 4 : null,
bottom: showReceiptsRow ? 28 : 0,
child: AnimatedScale(
duration: Duration(
milliseconds:
(FluffyThemes.animationDuration.inMilliseconds / 2)
.floor(),
),
curve: FluffyThemes.animationCurve,
scale: !longPressSelect && hovered ? 1 : 0,
alignment: Alignment.center,
child: Material(
color: Theme.of(context)
.colorScheme
.tertiaryContainer
.withOpacity(0.9),
elevation:
Theme.of(context).appBarTheme.scrolledUnderElevation ??
4,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
shadowColor: Theme.of(context).appBarTheme.shadowColor,
child: SizedBox(
width: 32,
height: 32,
child: IconButton(
icon: Icon(
Icons.adaptive.more_outlined,
size: 16,
color:
Theme.of(context).colorScheme.onTertiaryContainer,
),
onPressed: () => onSelect(event),
),
),
),
),
),
],
),
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 4.0,
),
child: container,
),
),
);

View file

@ -101,6 +101,7 @@ class MessageContent extends StatelessWidget {
mxContent: sender.avatarUrl,
name: sender.calcDisplayname(),
presenceUserId: sender.stateKey,
client: event.room.client,
),
title: Text(sender.calcDisplayname()),
subtitle: Text(event.originServerTs.localizedTime(context)),

View file

@ -154,7 +154,7 @@ class _Reaction extends StatelessWidget {
border: reacted!
? Border.all(
width: 1,
color: Theme.of(context).primaryColor,
color: Theme.of(context).colorScheme.primary,
)
: null,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),

View file

@ -18,68 +18,71 @@ class ReplyContent extends StatelessWidget {
this.timeline,
});
static const BorderRadius borderRadius = BorderRadius.only(
topRight: Radius.circular(AppConfig.borderRadius / 2),
bottomRight: Radius.circular(AppConfig.borderRadius / 2),
);
@override
Widget build(BuildContext context) {
Widget replyBody;
final timeline = this.timeline;
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.onPrimaryContainer
: Theme.of(context).colorScheme.onBackground,
fontSize: fontSize,
),
);
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
width: 3,
height: fontSize * 2 + 6,
color: ownMessage
? Theme.of(context).colorScheme.onPrimaryContainer
: Theme.of(context).colorScheme.onBackground,
),
const SizedBox(width: 6),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<User?>(
future: displayEvent.fetchSenderUser(),
builder: (context, snapshot) {
return Text(
'${snapshot.data?.calcDisplayname() ?? displayEvent.senderFromMemoryOrFallback.calcDisplayname()}:',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
color: ownMessage
? Theme.of(context).colorScheme.onPrimaryContainer
: Theme.of(context).colorScheme.onBackground,
fontSize: fontSize,
),
);
},
),
replyBody,
],
return Material(
color: Theme.of(context).colorScheme.background.withOpacity(0.33),
borderRadius: borderRadius,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
width: 3,
height: fontSize * 2 + 16,
color: Theme.of(context).colorScheme.primary,
),
),
],
const SizedBox(width: 6),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<User?>(
future: displayEvent.fetchSenderUser(),
builder: (context, snapshot) {
return Text(
'${snapshot.data?.calcDisplayname() ?? displayEvent.senderFromMemoryOrFallback.calcDisplayname()}:',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
fontSize: fontSize,
),
);
},
),
Text(
displayEvent.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: false,
hideReply: true,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: ownMessage
? Theme.of(context).colorScheme.onPrimaryContainer
: Theme.of(context).colorScheme.onBackground,
fontSize: fontSize,
),
),
],
),
),
const SizedBox(width: 6),
],
),
);
}
}

View file

@ -401,9 +401,7 @@ class InputBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final useShortCuts = (PlatformInfos.isWeb ||
PlatformInfos.isDesktop ||
AppConfig.sendOnEnter);
final useShortCuts = (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile);
return Shortcuts(
shortcuts: !useShortCuts
? {}

View file

@ -5,6 +5,7 @@ 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:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
@ -17,10 +18,18 @@ class PinnedEvents extends StatelessWidget {
const PinnedEvents(this.controller, {super.key});
Future<void> _displayPinnedEventsDialog(
BuildContext context,
List<Event?> events,
) async {
Future<void> _displayPinnedEventsDialog(BuildContext context) async {
final eventsResult = await showFutureLoadingDialog(
context: context,
future: () => Future.wait(
controller.room.pinnedEventIds.map(
(eventId) => controller.room.getEventById(eventId),
),
),
);
final events = eventsResult.result;
if (events == null) return;
final eventId = events.length == 1
? events.single?.eventId
: await showConfirmationDialog<String>(
@ -51,23 +60,14 @@ class PinnedEvents extends StatelessWidget {
if (pinnedEventIds.isEmpty) {
return const SizedBox.shrink();
}
final completers = pinnedEventIds.map<Completer<Event?>>((e) {
final completer = Completer<Event?>();
controller.room
.getEventById(e)
.then((value) => completer.complete(value));
return completer;
});
return FutureBuilder<List<Event?>>(
future: Future.wait(completers.map((e) => e.future).toList()),
builder: (context, snapshot) {
final pinnedEvents = snapshot.data;
final event = (pinnedEvents != null && pinnedEvents.isNotEmpty)
? snapshot.data?.last
: null;
if (event == null || pinnedEvents == null) {
return Container();
return FutureBuilder<Event?>(
future: controller.room.getEventById(pinnedEventIds.last),
builder: (context, snapshot) {
final event = snapshot.data;
if (event == null) {
return const SizedBox.shrink();
}
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
@ -80,10 +80,7 @@ class PinnedEvents extends StatelessWidget {
),
),
child: InkWell(
onTap: () => _displayPinnedEventsDialog(
context,
pinnedEvents,
),
onTap: () => _displayPinnedEventsDialog(context),
child: Row(
children: [
IconButton(

View file

@ -64,7 +64,7 @@ class _EditContent extends StatelessWidget {
children: <Widget>[
Icon(
Icons.edit,
color: Theme.of(context).primaryColor,
color: Theme.of(context).colorScheme.primary,
),
Container(width: 15.0),
FutureBuilder<String>(

View file

@ -1,49 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:url_launcher/link.dart';
import 'edit_widgets_dialog.dart';
class WidgetsBottomSheet extends StatelessWidget {
final Room room;
const WidgetsBottomSheet({super.key, required this.room});
@override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) {
if (index == room.widgets.length) {
return ListTile(
leading: const Icon(Icons.edit),
title: Text(L10n.of(context)!.editWidgets),
onTap: () {
Navigator.of(context).pop();
showDialog(
context: context,
builder: (context) => EditWidgetsDialog(room: room),
useRootNavigator: false,
);
},
);
}
final widget = room.widgets[index];
return Link(
builder: (context, callback) {
return ListTile(
title: Text(widget.name ?? widget.url),
subtitle: Text(widget.type),
onTap: callback,
);
},
target: LinkTarget.blank,
uri: Uri.parse(widget.url),
);
},
itemCount: room.widgets.length + 1,
);
}
}

View file

@ -66,7 +66,7 @@ class ChatDetailsView extends StatelessWidget {
leading: const Center(child: BackButton()),
elevation: Theme.of(context).appBarTheme.elevation,
actions: <Widget>[
// #Pangea
// #Pangeas
// if (room.canonicalAlias.isNotEmpty)
// IconButton(
// tooltip: L10n.of(context)!.share,
@ -296,8 +296,10 @@ class ChatDetailsView extends StatelessWidget {
// ? L10n.of(context)!.noChatDescriptionYet
// : room.topic,
// options: const LinkifyOptions(humanize: false),
// linkStyle:
// const TextStyle(color: Colors.blueAccent),
// linkStyle: const TextStyle(
// color: Colors.blueAccent,
// decorationColor: Colors.blueAccent,
// ),
// style: TextStyle(
// fontSize: 14,
// fontStyle: room.topic.isEmpty
@ -349,29 +351,29 @@ class ChatDetailsView extends StatelessWidget {
// trailing: const Icon(Icons.chevron_right_outlined),
// ),
// if (!room.isDirectChat)
// ListTile(
// leading: CircleAvatar(
// backgroundColor:
// Theme.of(context).scaffoldBackgroundColor,
// foregroundColor: iconColor,
// child: const Icon(Icons.shield_outlined),
// ),
// title: Text(
// L10n.of(context)!.whoIsAllowedToJoinThisGroup,
// ),
// trailing: room.canChangeJoinRules
// ? const Icon(Icons.chevron_right_outlined)
// : null,
// subtitle: Text(
// room.joinRules?.getLocalizedString(
// MatrixLocals(L10n.of(context)!),
// ) ??
// L10n.of(context)!.none,
// ),
// onTap: room.canChangeJoinRules
// ? controller.setJoinRules
// : null,
// ListTile(
// leading: CircleAvatar(
// backgroundColor:
// Theme.of(context).scaffoldBackgroundColor,
// foregroundColor: iconColor,
// child: const Icon(Icons.shield_outlined),
// ),
// title: Text(
// L10n.of(context)!.whoIsAllowedToJoinThisGroup,
// ),
// trailing: room.canChangeJoinRules
// ? const Icon(Icons.chevron_right_outlined)
// : null,
// subtitle: Text(
// room.joinRules?.getLocalizedString(
// MatrixLocals(L10n.of(context)!),
// ) ??
// L10n.of(context)!.none,
// ),
// onTap: room.canChangeJoinRules
// ? controller.setJoinRules
// : null,
// ),
// if (!room.isDirectChat)
// ListTile(
// leading: CircleAvatar(
@ -396,7 +398,7 @@ class ChatDetailsView extends StatelessWidget {
// ? controller.setHistoryVisibility
// : null,
// ),
// if (room.joinRules == JoinRules.public)
// if (room.jsoinRules == JoinRules.public)
// ListTile(
// leading: CircleAvatar(
// backgroundColor:
@ -583,7 +585,8 @@ class ChatDetailsView extends StatelessWidget {
// ListTile(
// title: Text(L10n.of(context)!.inviteContact),
// leading: CircleAvatar(
// backgroundColor: Theme.of(context).primaryColor,
// backgroundColor:
// Theme.of(context).colorScheme.primary,
// foregroundColor: Colors.white,
// radius: Avatar.defaultSize / 2,
// child: const Icon(Icons.add_outlined),

View file

@ -15,7 +15,6 @@ import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
import 'package:fluffychat/pangea/widgets/subscription/subscription_snackbar.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/tor_stub.dart'
@ -90,6 +89,7 @@ class ChatListController extends State<ChatList>
void resetActiveSpaceId() {
setState(() {
selectedRoomIds.clear();
activeSpaceId = null;
//#Pangea
context.go("/rooms");
@ -99,11 +99,19 @@ class ChatListController extends State<ChatList>
void setActiveSpace(String? spaceId) {
setState(() {
selectedRoomIds.clear();
activeSpaceId = spaceId;
activeFilter = ActiveFilter.spaces;
});
}
void createNewSpace() async {
final spaceId = await context.push<String?>('/rooms/newspace');
if (spaceId != null) {
setActiveSpace(spaceId);
}
}
int get selectedIndex {
switch (activeFilter) {
case ActiveFilter.allChats:
@ -148,6 +156,7 @@ class ChatListController extends State<ChatList>
// #Pangea
debugPrint('onDestinationSelected $i');
// Pangea#
selectedRoomIds.clear();
activeFilter = getActiveFilterByDestination(i);
});
// #Pangea
@ -167,30 +176,24 @@ class ChatListController extends State<ChatList>
switch (activeFilter) {
case ActiveFilter.allChats:
return (room) =>
!room.isSpace &&
!room.isStoryRoom
// #Pangea
!room.isSpace // #Pangea
&&
!room.isAnalyticsRoom;
// Pangea#
// Pangea#;
case ActiveFilter.groups:
return (room) =>
!room.isSpace &&
!room.isDirectChat &&
!room.isStoryRoom
// #Pangea
!room.isDirectChat // #Pangea
&&
!room.isAnalyticsRoom;
// Pangea#
// Pangea#;
case ActiveFilter.messages:
return (room) =>
!room.isSpace &&
room.isDirectChat &&
!room.isStoryRoom
// #Pangea
room.isDirectChat // #Pangea
&&
!room.isAnalyticsRoom;
// Pangea#
// Pangea#;
case ActiveFilter.spaces:
return (r) => r.isSpace;
}
@ -615,26 +618,34 @@ class ChatListController extends State<ChatList>
}
void setStatus() async {
final client = Matrix.of(context).client;
final currentPresence = await client.fetchCurrentPresence(client.userID!);
final input = await showTextInputDialog(
useRootNavigator: false,
context: context,
title: L10n.of(context)!.setStatus,
message: L10n.of(context)!.leaveEmptyToClearStatus,
okLabel: L10n.of(context)!.ok,
cancelLabel: L10n.of(context)!.cancel,
textFields: [
DialogTextField(
hintText: L10n.of(context)!.statusExampleMessage,
maxLines: 6,
minLines: 1,
maxLength: 255,
initialText: currentPresence.statusMsg,
),
],
);
if (input == null) return;
if (!mounted) return;
await showFutureLoadingDialog(
context: context,
future: () => Matrix.of(context).client.setPresence(
Matrix.of(context).client.userID!,
PresenceType.online,
statusMsg: input.single,
),
future: () => client.setPresence(
client.userID!,
PresenceType.online,
statusMsg: input.single,
),
);
}
@ -754,7 +765,7 @@ class ChatListController extends State<ChatList>
if (mounted) {
GoogleAnalytics.analyticsUserUpdate(client.userID);
await pangeaController.subscriptionController.initialize();
await pangeaController.afterSyncAndFirstLoginInitialization(context);
pangeaController.afterSyncAndFirstLoginInitialization(context);
await pangeaController.inviteBotToExistingSpaces();
} else {
ErrorHandler.logError(

View file

@ -26,7 +26,12 @@ class ChatListViewBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
final roomSearchResult = controller.roomSearchResult;
final publicRooms = controller.roomSearchResult?.chunk
.where((room) => room.roomType != 'm.space')
.toList();
final publicSpaces = controller.roomSearchResult?.chunk
.where((room) => room.roomType == 'm.space')
.toList();
final userSearchResult = controller.userSearchResult;
final client = Matrix.of(context).client;
const dummyChatCount = 4;
@ -59,8 +64,7 @@ class ChatListViewBody extends StatelessWidget {
.where((s) => s.hasRoomUpdate)
.rateLimit(const Duration(seconds: 1)),
builder: (context, _) {
if (controller.activeFilter == ActiveFilter.spaces &&
!controller.isSearchMode) {
if (controller.activeFilter == ActiveFilter.spaces) {
return SpaceView(
controller,
scrollController: controller.scrollController,
@ -68,13 +72,6 @@ class ChatListViewBody extends StatelessWidget {
);
}
final rooms = controller.filteredRooms;
// Pangea#
// final displayStoriesHeader = {
// ActiveFilter.allChats,
// ActiveFilter.messages,
// }.contains(controller.activeFilter) &&
// client.storiesRooms.isNotEmpty;
// Pangea#
return SafeArea(
child: CustomScrollView(
controller: controller.scrollController,
@ -88,39 +85,12 @@ class ChatListViewBody extends StatelessWidget {
title: L10n.of(context)!.publicRooms,
icon: const Icon(Icons.explore_outlined),
),
AnimatedContainer(
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
height: roomSearchResult == null ||
roomSearchResult.chunk.isEmpty
? 0
: 106,
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
child: roomSearchResult == null
? null
: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: roomSearchResult.chunk.length,
itemBuilder: (context, i) => _SearchItem(
title: roomSearchResult.chunk[i].name ??
roomSearchResult.chunk[i].canonicalAlias
?.localpart ??
L10n.of(context)!.group,
avatar: roomSearchResult.chunk[i].avatarUrl,
onPressed: () => showAdaptiveBottomSheet(
context: context,
builder: (c) => PublicRoomBottomSheet(
roomAlias: roomSearchResult
.chunk[i].canonicalAlias ??
roomSearchResult.chunk[i].roomId,
outerContext: context,
chunk: roomSearchResult.chunk[i],
),
),
),
),
PublicRoomsHorizontalList(publicRooms: publicRooms),
SearchTitle(
title: L10n.of(context)!.publicSpaces,
icon: const Icon(Icons.workspaces_outlined),
),
PublicRoomsHorizontalList(publicRooms: publicSpaces),
SearchTitle(
title: L10n.of(context)!.users,
icon: const Icon(Icons.group_outlined),
@ -157,16 +127,12 @@ class ChatListViewBody extends StatelessWidget {
),
),
),
SearchTitle(
title: L10n.of(context)!.stories,
icon: const Icon(Icons.camera_alt_outlined),
),
],
// #Pangea
// if (displayStoriesHeader)
// StoriesHeader(
// key: const Key('stories_header'),
// filter: controller.searchController.text,
// if (!controller.isSearchMode &&
// controller.activeFilter != ActiveFilter.groups)
// StatusMessageList(
// onStatusEdit: controller.setStatus,
// ),
// Pangea#
const ConnectionStatusHeader(),
@ -312,6 +278,48 @@ class ChatListViewBody extends StatelessWidget {
}
}
class PublicRoomsHorizontalList extends StatelessWidget {
const PublicRoomsHorizontalList({
super.key,
required this.publicRooms,
});
final List<PublicRoomsChunk>? publicRooms;
@override
Widget build(BuildContext context) {
final publicRooms = this.publicRooms;
return AnimatedContainer(
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
height: publicRooms == null || publicRooms.isEmpty ? 0 : 106,
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
child: publicRooms == null
? null
: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: publicRooms.length,
itemBuilder: (context, i) => _SearchItem(
title: publicRooms[i].name ??
publicRooms[i].canonicalAlias?.localpart ??
L10n.of(context)!.group,
avatar: publicRooms[i].avatarUrl,
onPressed: () => showAdaptiveBottomSheet(
context: context,
builder: (c) => PublicRoomBottomSheet(
roomAlias:
publicRooms[i].canonicalAlias ?? publicRooms[i].roomId,
outerContext: context,
chunk: publicRooms[i],
),
),
),
),
);
}
}
class _SearchItem extends StatelessWidget {
final String title;
final Uri? avatar;

View file

@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
final ChatListController controller;
@ -17,7 +15,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
return SliverAppBar(
floating: true,
toolbarHeight: Theme.of(context).appBarTheme.toolbarHeight ?? 56,
toolbarHeight: 72,
pinned:
FluffyThemes.isColumnMode(context) || selectMode != SelectMode.normal,
scrolledUnderElevation: selectMode == SelectMode.normal ? 0 : null,
@ -56,19 +54,27 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
// borderRadius: BorderRadius.circular(99),
// ),
// hintText: L10n.of(context)!.searchChatsRooms,
// hintStyle: TextStyle(
// color: Theme.of(context).colorScheme.onPrimaryContainer,
// fontWeight: FontWeight.normal,
// ),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// prefixIcon: controller.isSearchMode
// ? IconButton(
// tooltip: L10n.of(context)!.cancel,
// icon: const Icon(Icons.close_outlined),
// onPressed: controller.cancelSearch,
// color: Theme.of(context).colorScheme.onBackground,
// color: Theme.of(context)
// .colorScheme
// .onPrimaryContainer,
// )
// : IconButton(
// onPressed: controller.startSearch,
// icon: Icon(
// Icons.search_outlined,
// color: Theme.of(context).colorScheme.onBackground,
// color: Theme.of(context)
// .colorScheme
// .onPrimaryContainer,
// ),
// ),
// suffixIcon: controller.isSearchMode

View file

@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/utils/get_chat_list_item_subtitle.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/room_status_extension.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
@ -42,25 +43,39 @@ class ChatListItem extends StatelessWidget {
if (room.membership == Membership.invite) {
final inviterId =
room.getState(EventTypes.RoomMember, room.client.userID!)?.senderId;
final profile = inviterId == null
? null
: await showFutureLoadingDialog(
context: context,
future: () => room.client.getProfileFromUserId(inviterId),
);
final consent = await showOkCancelAlertDialog(
final inviteAction = await showModalActionSheet<InviteActions>(
context: context,
title: L10n.of(context)!.inviteForMe,
message: L10n.of(context)!.youInvitedBy(
profile?.result?.displayName ??
profile?.result?.userId.localpart ??
L10n.of(context)!.user,
),
okLabel: L10n.of(context)!.joinRoom,
cancelLabel: L10n.of(context)!.delete,
barrierDismissible: false,
message: room.isDirectChat
? L10n.of(context)!.invitePrivateChat
: L10n.of(context)!.inviteGroupChat,
title: room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
actions: [
SheetAction(
key: InviteActions.accept,
label: L10n.of(context)!.accept,
icon: Icons.check_outlined,
isDefaultAction: true,
),
SheetAction(
key: InviteActions.decline,
label: L10n.of(context)!.decline,
icon: Icons.close_outlined,
isDestructiveAction: true,
),
SheetAction(
key: InviteActions.block,
label: L10n.of(context)!.block,
icon: Icons.block_outlined,
isDestructiveAction: true,
),
],
);
if (consent == OkCancelResult.cancel) {
if (inviteAction == null) return;
if (inviteAction == InviteActions.block) {
context.go('/rooms/settings/security/ignorelist', extra: inviterId);
return;
}
if (inviteAction == InviteActions.decline) {
await showFutureLoadingDialog(
context: context,
future: room.leave,
@ -110,10 +125,26 @@ class ChatListItem extends StatelessWidget {
room: room,
),
);
Matrix.of(context).shareContent = null;
} else {
room.sendEvent(shareContent);
final consent = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context)!.forward,
message: L10n.of(context)!.forwardMessageTo(
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
),
okLabel: L10n.of(context)!.forward,
cancelLabel: L10n.of(context)!.cancel,
);
if (consent == OkCancelResult.cancel) {
Matrix.of(context).shareContent = null;
return;
}
if (consent == OkCancelResult.ok) {
room.sendEvent(shareContent);
Matrix.of(context).shareContent = null;
}
}
Matrix.of(context).shareContent = null;
}
context.go('/rooms/${room.id}');
@ -176,228 +207,244 @@ class ChatListItem extends StatelessWidget {
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
clipBehavior: Clip.hardEdge,
color: backgroundColor,
child: ListTile(
visualDensity: const VisualDensity(vertical: -0.5),
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
onLongPress: onLongPress,
leading: selected
? SizedBox(
width: Avatar.defaultSize,
height: Avatar.defaultSize,
child: Material(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(Avatar.defaultSize),
child: const Icon(Icons.check, color: Colors.white),
),
)
: Avatar(
mxContent: room.avatar,
name: displayname,
onTap: onLongPress,
//#Pangea
littleIcon: room.roomTypeIcon,
// Pangea#
presenceUserId: room.directChatMatrixID,
presenceBackgroundColor: backgroundColor,
),
title: Row(
children: <Widget>[
Expanded(
child: Text(
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: unread
? const TextStyle(fontWeight: FontWeight.bold)
: null,
),
child: FutureBuilder(
future: room.loadHeroUsers(),
builder: (context, snapshot) => HoverBuilder(
builder: (context, hovered) => ListTile(
visualDensity: const VisualDensity(vertical: -0.5),
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
onLongPress: onLongPress,
leading: Avatar(
mxContent: room.avatar,
name: displayname,
//#Pangea
littleIcon: room.roomTypeIcon,
// Pangea#
presenceUserId: room.directChatMatrixID,
presenceBackgroundColor: backgroundColor,
),
if (isMuted)
const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Icon(
Icons.notifications_off_outlined,
size: 16,
),
),
if (room.isFavourite || room.membership == Membership.invite)
Padding(
padding: EdgeInsets.only(
right: hasNotifications ? 4.0 : 0.0,
),
child: Icon(
Icons.push_pin,
size: 16,
color: Theme.of(context).colorScheme.primary,
),
),
if (lastEvent != null && room.membership != Membership.invite)
Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Text(
lastEvent.originServerTs.localizedTimeShort(context),
style: TextStyle(
fontSize: 13,
color: unread
? Theme.of(context).colorScheme.secondary
: Theme.of(context).textTheme.bodyMedium!.color,
title: Row(
children: <Widget>[
Expanded(
child: Text(
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: unread
? const TextStyle(fontWeight: FontWeight.bold)
: null,
),
),
),
],
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (typingText.isEmpty &&
ownMessage &&
room.lastEvent!.status.isSending) ...[
const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
),
const SizedBox(width: 4),
],
AnimatedContainer(
width: typingText.isEmpty ? 0 : 18,
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
padding: const EdgeInsets.only(right: 4),
child: Icon(
Icons.edit_outlined,
color: Theme.of(context).colorScheme.secondary,
size: 14,
),
),
Expanded(
child: typingText.isNotEmpty
? Text(
typingText,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
maxLines: 1,
softWrap: false,
)
: FutureBuilder<String>(
// #Pangea
// future: room.lastEvent?.calcLocalizedBody(
// MatrixLocals(L10n.of(context)!),
// hideReply: true,
// hideEdit: true,
// plaintextBody: true,
// removeMarkdown: true,
// withSenderNamePrefix: !room.isDirectChat ||
// room.directChatMatrixID !=
// room.lastEvent?.senderId,
// ) ??
// Future.value(L10n.of(context)!.emptyChat),
future: room.lastEvent != null
? GetChatListItemSubtitle().getSubtitle(
context,
room.lastEvent,
MatrixState.pangeaController,
)
: Future.value(L10n.of(context)!.emptyChat),
// Pangea#
builder: (context, snapshot) {
return Text(
room.membership == Membership.invite
? room.isDirectChat
? L10n.of(context)!.invitePrivateChat
: L10n.of(context)!.inviteGroupChat
: snapshot.data ??
room.lastEvent?.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
hideEdit: true,
plaintextBody: true,
removeMarkdown: true,
withSenderNamePrefix:
!room.isDirectChat ||
room.directChatMatrixID !=
room.lastEvent?.senderId,
) ??
L10n.of(context)!.emptyChat,
softWrap: false,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: unread ? FontWeight.w600 : null,
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
decoration: room.lastEvent?.redacted == true
? TextDecoration.lineThrough
: null,
),
);
},
if (isMuted)
const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Icon(
Icons.notifications_off_outlined,
size: 16,
),
),
if (room.isFavourite || room.membership == Membership.invite)
Padding(
padding: EdgeInsets.only(
right: hasNotifications ? 4.0 : 0.0,
),
child: Icon(
Icons.push_pin,
size: 16,
color: Theme.of(context).colorScheme.primary,
),
),
if (lastEvent != null && room.membership != Membership.invite)
Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Text(
lastEvent.originServerTs.localizedTimeShort(context),
style: TextStyle(
fontSize: 13,
color: unread
? Theme.of(context).colorScheme.secondary
: Theme.of(context).textTheme.bodyMedium!.color,
),
),
),
],
),
const SizedBox(width: 8),
// #Pangea
if (room.locked ?? false)
const Padding(
padding: EdgeInsets.only(right: 4.0),
child: Icon(
Icons.lock_outlined,
size: 16,
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (typingText.isEmpty &&
ownMessage &&
room.lastEvent!.status.isSending) ...[
const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
),
const SizedBox(width: 4),
],
AnimatedContainer(
width: typingText.isEmpty ? 0 : 18,
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
padding: const EdgeInsets.only(right: 4),
child: Icon(
Icons.edit_outlined,
color: Theme.of(context).colorScheme.secondary,
size: 14,
),
),
),
// Pangea#
AnimatedContainer(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
padding: const EdgeInsets.symmetric(horizontal: 7),
height: unreadBubbleSize,
width: !hasNotifications && !unread && !room.hasNewMessages
? 0
: (unreadBubbleSize - 9) *
room.notificationCount.toString().length +
9,
decoration: BoxDecoration(
color: room.highlightCount > 0 ||
room.membership == Membership.invite
? Colors.red
: hasNotifications || room.markedUnread
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
child: Center(
child: hasNotifications
? Text(
room.notificationCount.toString(),
style: TextStyle(
color: room.highlightCount > 0
? Colors.white
: hasNotifications
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context)
.colorScheme
.onPrimaryContainer,
fontSize: 13,
Expanded(
child: typingText.isNotEmpty
? Text(
typingText,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
maxLines: 1,
softWrap: false,
)
: FutureBuilder<String>(
// #Pangea
// future: room.lastEvent?.calcLocalizedBody(
// MatrixLocals(L10n.of(context)!),
// hideReply: true,
// hideEdit: true,
// plaintextBody: true,
// removeMarkdown: true,
// withSenderNamePrefix: !room.isDirectChat ||
// room.directChatMatrixID !=
// room.lastEvent?.senderId,
// ) ??
// Future.value(L10n.of(context)!.emptyChat),
future: room.lastEvent != null
? GetChatListItemSubtitle().getSubtitle(
context,
room.lastEvent,
MatrixState.pangeaController,
)
: Future.value(L10n.of(context)!.emptyChat),
// Pangea#
builder: (context, snapshot) {
return Text(
room.membership == Membership.invite
? room.isDirectChat
? L10n.of(context)!.invitePrivateChat
: L10n.of(context)!.inviteGroupChat
: snapshot.data ??
room.lastEvent
?.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
hideEdit: true,
plaintextBody: true,
removeMarkdown: true,
withSenderNamePrefix:
!room.isDirectChat ||
room.directChatMatrixID !=
room.lastEvent?.senderId,
) ??
L10n.of(context)!.emptyChat,
softWrap: false,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: unread ? FontWeight.w600 : null,
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
decoration: room.lastEvent?.redacted == true
? TextDecoration.lineThrough
: null,
),
);
},
),
)
: const SizedBox.shrink(),
),
),
const SizedBox(width: 8),
// #Pangea
if (room.locked ?? false)
const Padding(
padding: EdgeInsets.only(right: 4.0),
child: Icon(
Icons.lock_outlined,
size: 16,
),
),
// Pangea#
AnimatedContainer(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
padding: const EdgeInsets.symmetric(horizontal: 7),
height: unreadBubbleSize,
width: !hasNotifications && !unread && !room.hasNewMessages
? 0
: (unreadBubbleSize - 9) *
room.notificationCount.toString().length +
9,
decoration: BoxDecoration(
color: room.highlightCount > 0 ||
room.membership == Membership.invite
? Colors.red
: hasNotifications || room.markedUnread
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.primaryContainer,
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
),
child: Center(
child: hasNotifications
? Text(
room.notificationCount.toString(),
style: TextStyle(
color: room.highlightCount > 0
? Colors.white
: hasNotifications
? Theme.of(context)
.colorScheme
.onPrimary
: Theme.of(context)
.colorScheme
.onPrimaryContainer,
fontSize: 13,
),
)
: const SizedBox.shrink(),
),
),
],
),
],
onTap: () => clickAction(context),
trailing: onForget == null
? hovered || selected
? IconButton(
color: selected
? Theme.of(context).colorScheme.primary
: null,
icon: Icon(
selected
? Icons.check_circle
: Icons.check_circle_outlined,
),
onPressed: onLongPress,
)
: null
: IconButton(
icon: const Icon(Icons.delete_outlined),
onPressed: onForget,
),
),
),
onTap: () => clickAction(context),
trailing: onForget == null
? null
: IconButton(
icon: const Icon(Icons.delete_outlined),
onPressed: onForget,
),
),
),
);
}
}
enum InviteActions {
accept,
decline,
block,
}

View file

@ -1,9 +1,4 @@
import 'package:flutter/material.dart';
import 'package:badges/badges.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
@ -13,6 +8,10 @@ import 'package:fluffychat/pangea/utils/chat_list_handle_space_tap.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import '../../widgets/matrix.dart';
import 'chat_list_body.dart';
import 'start_chat_fab.dart';
@ -247,6 +246,7 @@ class ChatListView extends StatelessWidget {
// activeFilter: controller.activeFilter,
// roomsIsEmpty: false,
// scrolledToTop: controller.scrolledToTop,
// createNewSpace: controller.createNewSpace,
// )
// : const SizedBox.shrink(),
// ),
@ -256,6 +256,7 @@ class ChatListView extends StatelessWidget {
roomsIsEmpty: false,
scrolledToTop: controller.scrolledToTop,
controller: controller,
createNewSpace: () {},
)
: null,
// Pangea#

View file

@ -1,11 +1,11 @@
import 'dart:developer';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:fluffychat/pangea/extensions/client_extension.dart';
import 'package:fluffychat/pangea/utils/class_code.dart';
import 'package:fluffychat/pangea/utils/find_conversation_partner_dialog.dart';
import 'package:fluffychat/pangea/utils/logout.dart';
import 'package:fluffychat/widgets/matrix.dart';
// Project imports:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -37,16 +37,6 @@ class ClientChooserButton extends StatelessWidget {
return <PopupMenuEntry<Object>>[
// #Pangea
// PopupMenuItem(
// value: SettingsAction.newStory,
// child: Row(
// children: [
// const Icon(Icons.camera_outlined),
// const SizedBox(width: 18),
// Text(L10n.of(context)!.yourStory),
// ],
// ),
// ),
// PopupMenuItem(
// value: SettingsAction.newGroup,
// child: Row(
// children: [
@ -141,6 +131,16 @@ class ClientChooserButton extends StatelessWidget {
),
),
// PopupMenuItem(
// value: SettingsAction.setStatus,
// child: Row(
// children: [
// const Icon(Icons.edit_outlined),
// const SizedBox(width: 18),
// Text(L10n.of(context)!.setStatus),
// ],
// ),
// ),
// PopupMenuItem(
// value: SettingsAction.invite,
// child: Row(
// children: [
@ -178,10 +178,7 @@ class ClientChooserButton extends StatelessWidget {
),
),
// #Pangea
// const PopupMenuItem(
// value: null,
// child: Divider(height: 1),
// ),
// const PopupMenuDivider(),
// for (final bundle in bundles) ...[
// if (matrix.accountBundles[bundle]!.length != 1 ||
// matrix.accountBundles[bundle]!.single!.userID != bundle)
@ -365,28 +362,23 @@ class ClientChooserButton extends StatelessWidget {
controller.setActiveBundle(object);
} else if (object is SettingsAction) {
switch (object) {
// #Pangea
// case SettingsAction.addAccount:
// final consent = await showOkCancelAlertDialog(
// context: context,
// title: L10n.of(context)!.addAccount,
// message: L10n.of(context)!.enableMultiAccounts,
// okLabel: L10n.of(context)!.next,
// cancelLabel: L10n.of(context)!.cancel,
// );
// if (consent != OkCancelResult.ok) return;
// context.go('/rooms/settings/addaccount');
// break;
// case SettingsAction.newStory:
// context.go('/rooms/stories/create');
// break;
// case SettingsAction.newGroup:
// context.go('/rooms/newgroup');
// break;
// case SettingsAction.newSpace:
// context.go('/rooms/newspace');
// break;
// Pangea#
case SettingsAction.addAccount:
final consent = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context)!.addAccount,
message: L10n.of(context)!.enableMultiAccounts,
okLabel: L10n.of(context)!.next,
cancelLabel: L10n.of(context)!.cancel,
);
if (consent != OkCancelResult.ok) return;
context.go('/rooms/settings/addaccount');
break;
case SettingsAction.newGroup:
context.go('/rooms/newgroup');
break;
case SettingsAction.newSpace:
controller.createNewSpace();
break;
case SettingsAction.invite:
FluffyShare.shareInviteLink(context);
break;
@ -396,6 +388,8 @@ class ClientChooserButton extends StatelessWidget {
case SettingsAction.archive:
context.go('/rooms/archive');
break;
case SettingsAction.setStatus:
controller.setStatus();
// #Pangea
case SettingsAction.newClass:
context.go('/rooms/newspace');
@ -504,12 +498,10 @@ class ClientChooserButton extends StatelessWidget {
}
enum SettingsAction {
// #Pangea
// addAccount,
// newStory,
// newGroup,
// newSpace,
// Pangea#
addAccount,
newGroup,
newSpace,
setStatus,
invite,
settings,
archive,

View file

@ -16,6 +16,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart';
import '../../utils/localized_exception_extension.dart';
@ -36,28 +37,79 @@ class SpaceView extends StatefulWidget {
}
class _SpaceViewState extends State<SpaceView> {
static final Map<String, Future<GetSpaceHierarchyResponse>> _requests = {};
static final Map<String, GetSpaceHierarchyResponse> _lastResponse = {};
String? prevBatch;
Object? error;
bool loading = false;
// #Pangea
StreamSubscription<SyncUpdate>? _roomSubscription;
// Pangea#
void _refresh() {
// #Pangea
if (mounted) {
// Pangea#
setState(() {
_requests.remove(widget.controller.activeSpaceId);
});
// #Pangea
}
// Pangea#
@override
void initState() {
loadHierarchy();
super.initState();
}
Future<GetSpaceHierarchyResponse> getFuture(String activeSpaceId) =>
_requests[activeSpaceId] ??= Matrix.of(context).client.getSpaceHierarchy(
activeSpaceId,
maxDepth: 1,
from: prevBatch,
);
// #Pangea
@override
void dispose() {
super.dispose();
_roomSubscription?.cancel();
}
// Pangea#
void _refresh() {
_lastResponse.remove(widget.controller.activeSpaceId);
loadHierarchy();
}
Future<GetSpaceHierarchyResponse> loadHierarchy([String? prevBatch]) async {
// #Pangea
if (widget.controller.activeSpaceId == null) {
return GetSpaceHierarchyResponse(
rooms: [],
nextBatch: null,
);
}
// Pangea#
final activeSpaceId = widget.controller.activeSpaceId!;
final client = Matrix.of(context).client;
final activeSpace = client.getRoomById(activeSpaceId);
await activeSpace?.postLoad();
setState(() {
error = null;
loading = true;
});
try {
final response = await client.getSpaceHierarchy(
activeSpaceId,
maxDepth: 1,
from: prevBatch,
);
if (prevBatch != null) {
response.rooms.insertAll(0, _lastResponse[activeSpaceId]?.rooms ?? []);
}
setState(() {
_lastResponse[activeSpaceId] = response;
});
return _lastResponse[activeSpaceId]!;
} catch (e) {
setState(() {
error = e;
});
rethrow;
} finally {
setState(() {
loading = false;
});
}
}
void _onJoinSpaceChild(SpaceRoomsChunk spaceChild) async {
final client = Matrix.of(context).client;
@ -137,7 +189,8 @@ class _SpaceViewState extends State<SpaceView> {
label: L10n.of(context)!.joinRoom,
icon: Icons.send_outlined,
),
if (spaceChild != null && (activeSpace?.canSendDefaultStates ?? false))
if (spaceChild != null &&
(activeSpace?.canChangeStateEvent(EventTypes.spaceChild) ?? false))
SheetAction(
key: SpaceChildContextAction.removeFromSpace,
label: L10n.of(context)!.removeFromSpace,
@ -225,27 +278,100 @@ class _SpaceViewState extends State<SpaceView> {
}
}
// #Pangea
StreamSubscription<SyncUpdate>? _roomSubscription;
void _addChatOrSubSpace() async {
final roomType = await showConfirmationDialog(
context: context,
title: L10n.of(context)!.addChatOrSubSpace,
actions: [
AlertDialogAction(
key: AddRoomType.subspace,
label: L10n.of(context)!.createNewSpace,
),
AlertDialogAction(
key: AddRoomType.chat,
label: L10n.of(context)!.createGroup,
),
],
);
if (roomType == null) return;
@override
void initState() {
super.initState();
final names = await showTextInputDialog(
context: context,
title: roomType == AddRoomType.subspace
? L10n.of(context)!.createNewSpace
: L10n.of(context)!.createGroup,
textFields: [
DialogTextField(
hintText: roomType == AddRoomType.subspace
? L10n.of(context)!.spaceName
: L10n.of(context)!.groupName,
minLines: 1,
maxLines: 1,
maxLength: 64,
validator: (text) {
if (text == null || text.isEmpty) {
return L10n.of(context)!.pleaseChoose;
}
return null;
},
),
DialogTextField(
hintText: L10n.of(context)!.chatDescription,
minLines: 4,
maxLines: 8,
maxLength: 255,
),
],
okLabel: L10n.of(context)!.create,
cancelLabel: L10n.of(context)!.cancel,
);
if (names == null) return;
final client = Matrix.of(context).client;
final result = await showFutureLoadingDialog(
context: context,
future: () async {
late final String roomId;
final activeSpace = client.getRoomById(
widget.controller.activeSpaceId!,
)!;
if (roomType == AddRoomType.subspace) {
roomId = await client.createSpace(
name: names.first,
topic: names.last.isEmpty ? null : names.last,
visibility: activeSpace.joinRules == JoinRules.public
? sdk.Visibility.public
: sdk.Visibility.private,
);
} else {
roomId = await client.createGroupChat(
groupName: names.first,
initialState: names.length > 1 && names.last.isNotEmpty
? [
sdk.StateEvent(
type: sdk.EventTypes.RoomTopic,
content: {'topic': names.last},
),
]
: null,
);
}
await activeSpace.setSpaceChild(roomId);
},
);
if (result.error != null) return;
_refresh();
}
// @override
@override
void dispose() {
super.dispose();
_roomSubscription?.cancel();
}
// Pangea#
@override
Widget build(BuildContext context) {
final client = Matrix.of(context).client;
final activeSpaceId = widget.controller.activeSpaceId;
final activeSpace = activeSpaceId == null
? null
: client.getRoomById(
activeSpaceId,
);
final allSpaces = client.rooms.where((room) => room.isSpace);
if (activeSpaceId == null) {
final rootSpaces = allSpaces
@ -259,73 +385,77 @@ class _SpaceViewState extends State<SpaceView> {
// Pangea#
.toList();
return CustomScrollView(
controller: widget.scrollController,
slivers: [
ChatListHeader(controller: widget.controller),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, i) {
final rootSpace = rootSpaces[i];
final displayname = rootSpace.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)!),
);
return Material(
color: Theme.of(context).colorScheme.background,
child: ListTile(
leading: Avatar(
mxContent: rootSpace.avatar,
name: displayname,
return SafeArea(
child: CustomScrollView(
controller: widget.scrollController,
slivers: [
ChatListHeader(controller: widget.controller),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, i) {
final rootSpace = rootSpaces[i];
final displayname = rootSpace.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)!),
);
return Material(
color: Theme.of(context).colorScheme.background,
child: ListTile(
leading: Avatar(
mxContent: rootSpace.avatar,
name: displayname,
// #Pangea
littleIcon: rootSpace.roomTypeIcon,
// Pangea#
),
title: Text(
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
// #Pangea
littleIcon: rootSpace.roomTypeIcon,
// Pangea#
),
title: Text(
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
// #Pangea
subtitle: Row(
children: [
Text(
rootSpace.membership == Membership.join
? L10n.of(context)!.numChats(
rootSpace.spaceChildren.length.toString(),
)
: L10n.of(context)!.youreInvited,
),
if (rootSpace.locked ?? false)
const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Icon(
Icons.lock_outlined,
size: 16,
),
subtitle: Row(
children: [
Text(
rootSpace.membership == Membership.join
? L10n.of(context)!.numChats(
rootSpace.spaceChildren.length.toString(),
)
: L10n.of(context)!.youreInvited,
),
],
if (rootSpace.locked ?? false)
const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Icon(
Icons.lock_outlined,
size: 16,
),
),
],
),
onTap: () => chatListHandleSpaceTap(
context,
widget.controller,
rootSpaces[i],
),
// subtitle: Text(
// L10n.of(context)!.numChats(
// rootSpace.spaceChildren.length.toString(),
// ),
// ),
// onTap: () =>
// widget.controller.setActiveSpace(rootSpace.id),
// Pangea#
onLongPress: () =>
_onSpaceChildContextMenu(null, rootSpace),
trailing: const Icon(Icons.chevron_right_outlined),
),
onTap: () => chatListHandleSpaceTap(
context,
widget.controller,
rootSpaces[i],
),
// subtitle: Text(
// L10n.of(context)!
// .numChats(rootSpace.spaceChildren.length.toString()),
// ),
// onTap: () => widget.controller.setActiveSpace(rootSpace.id),
// Pangea#
onLongPress: () =>
_onSpaceChildContextMenu(null, rootSpace),
trailing: const Icon(Icons.chevron_right_outlined),
),
);
},
childCount: rootSpaces.length,
);
},
childCount: rootSpaces.length,
),
),
),
],
],
),
);
}
@ -352,256 +482,297 @@ class _SpaceViewState extends State<SpaceView> {
.listen(refreshOnUpdate);
// Pangea#
return FutureBuilder<GetSpaceHierarchyResponse>(
future: getFuture(activeSpaceId),
builder: (context, snapshot) {
final response = snapshot.data;
final error = snapshot.error;
if (error != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(error.toLocalizedString(context)),
),
IconButton(
onPressed: _refresh,
icon: const Icon(Icons.refresh_outlined),
),
],
);
final parentSpace = allSpaces.firstWhereOrNull(
(space) =>
space.spaceChildren.any((child) => child.roomId == activeSpaceId),
);
return PopScope(
canPop: parentSpace == null,
onPopInvoked: (pop) async {
if (pop) return;
if (parentSpace != null) {
widget.controller.setActiveSpace(parentSpace.id);
}
if (response == null) {
return CustomScrollView(
slivers: [
ChatListHeader(controller: widget.controller),
const SliverFillRemaining(
child: Center(
child: CircularProgressIndicator.adaptive(),
},
child: SafeArea(
child: CustomScrollView(
controller: widget.scrollController,
slivers: [
ChatListHeader(controller: widget.controller),
SliverAppBar(
automaticallyImplyLeading: false,
primary: false,
titleSpacing: 0,
title: ListTile(
leading: BackButton(
onPressed: () =>
widget.controller.setActiveSpace(parentSpace?.id),
),
title: Text(
parentSpace == null
? L10n.of(context)!.allSpaces
: parentSpace.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)!),
),
),
trailing: IconButton(
icon: loading
? const CircularProgressIndicator.adaptive(strokeWidth: 2)
: const Icon(Icons.refresh_outlined),
onPressed: loading ? null : _refresh,
),
),
],
);
}
final parentSpace = allSpaces.firstWhereOrNull(
(space) =>
space.spaceChildren.any((child) => child.roomId == activeSpaceId),
);
// #Pangea
// final spaceChildren = response.rooms;
List<SpaceRoomsChunk> spaceChildren = response.rooms;
final space = Matrix.of(context).client.getRoomById(activeSpaceId);
if (space != null) {
final matchingSpaceChildren = space.spaceChildren
.where(
(spaceChild) => spaceChildren
.map((hierarchyMember) => hierarchyMember.roomId)
.contains(spaceChild.roomId),
)
.toList();
spaceChildren = spaceChildren
.where(
(spaceChild) =>
matchingSpaceChildren.any(
(matchingSpaceChild) =>
matchingSpaceChild.roomId == spaceChild.roomId &&
matchingSpaceChild.suggested == true,
) ||
[Membership.join, Membership.invite].contains(
Matrix.of(context)
.client
.getRoomById(spaceChild.roomId)
?.membership,
),
Builder(
builder: (context) {
final response = _lastResponse[activeSpaceId];
final error = this.error;
if (error != null) {
return SliverFillRemaining(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(error.toLocalizedString(context)),
),
IconButton(
onPressed: _refresh,
icon: const Icon(Icons.refresh_outlined),
),
],
),
)
.toList();
}
spaceChildren.sort((a, b) {
final bool aIsSpace = a.roomType == 'm.space';
final bool bIsSpace = b.roomType == 'm.space';
);
}
if (response == null) {
return SliverFillRemaining(
child: Center(
child: Text(L10n.of(context)!.loadingPleaseWait),
),
);
}
// #Pangea
// final spaceChildren = response.rooms;
List<SpaceRoomsChunk> spaceChildren = response.rooms;
final space =
Matrix.of(context).client.getRoomById(activeSpaceId);
if (space != null) {
final matchingSpaceChildren = space.spaceChildren
.where(
(spaceChild) => spaceChildren
.map((hierarchyMember) => hierarchyMember.roomId)
.contains(spaceChild.roomId),
)
.toList();
spaceChildren = spaceChildren
.where(
(spaceChild) =>
matchingSpaceChildren.any(
(matchingSpaceChild) =>
matchingSpaceChild.roomId ==
spaceChild.roomId &&
matchingSpaceChild.suggested == true,
) ||
[Membership.join, Membership.invite].contains(
Matrix.of(context)
.client
.getRoomById(spaceChild.roomId)
?.membership,
),
)
.toList();
}
spaceChildren.sort((a, b) {
final bool aIsSpace = a.roomType == 'm.space';
final bool bIsSpace = b.roomType == 'm.space';
if (aIsSpace && !bIsSpace) {
return -1;
} else if (!aIsSpace && bIsSpace) {
return 1;
}
return 0;
});
// Pangea#
final canLoadMore = response.nextBatch != null;
return PopScope(
canPop: parentSpace == null,
onPopInvoked: (pop) async {
if (pop) return;
if (parentSpace != null) {
widget.controller.setActiveSpace(parentSpace.id);
}
},
child: CustomScrollView(
controller: widget.scrollController,
slivers: [
ChatListHeader(controller: widget.controller),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, i) {
if (i == 0) {
return ListTile(
leading: BackButton(
onPressed: () =>
widget.controller.setActiveSpace(parentSpace?.id),
),
title: Text(
parentSpace == null
? L10n.of(context)!.allSpaces
: parentSpace.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)!),
if (aIsSpace && !bIsSpace) {
return -1;
} else if (!aIsSpace && bIsSpace) {
return 1;
}
return 0;
});
// Pangea#
final canLoadMore = response.nextBatch != null;
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, i) {
if (canLoadMore && i == spaceChildren.length) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: OutlinedButton.icon(
label: loading
? const LinearProgressIndicator()
: Text(L10n.of(context)!.loadMore),
icon: const Icon(Icons.chevron_right_outlined),
onPressed: loading
? null
: () {
loadHierarchy(response.nextBatch);
},
),
);
}
final spaceChild = spaceChildren[i];
final room = client.getRoomById(spaceChild.roomId);
if (room != null &&
!room.isSpace
// #Pangea
&&
room.membership != Membership.leave
// Pangea#
) {
return ChatListItem(
room,
onLongPress: () =>
_onSpaceChildContextMenu(spaceChild, room),
activeChat: widget.controller.activeChat == room.id,
);
}
final isSpace = spaceChild.roomType == 'm.space';
final topic = spaceChild.topic?.isEmpty ?? true
? null
: spaceChild.topic;
if (spaceChild.roomId == activeSpaceId) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SearchTitle(
title: spaceChild.name ??
spaceChild.canonicalAlias ??
'Space',
icon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
),
),
trailing: IconButton(
icon: snapshot.connectionState != ConnectionState.done
? const CircularProgressIndicator.adaptive()
: const Icon(Icons.refresh_outlined),
onPressed:
snapshot.connectionState != ConnectionState.done
? null
: _refresh,
),
);
}
i--;
if (canLoadMore && i == spaceChildren.length) {
return ListTile(
title: Text(L10n.of(context)!.loadMore),
trailing: const Icon(Icons.chevron_right_outlined),
onTap: () {
prevBatch = response.nextBatch;
_refresh();
},
);
}
final spaceChild = spaceChildren[i];
final room = client.getRoomById(spaceChild.roomId);
if (room != null &&
!room.isSpace
child: Avatar(
size: 24,
mxContent: spaceChild.avatarUrl,
name: spaceChild.name,
fontSize: 9,
),
),
color: Theme.of(context)
.colorScheme
.secondaryContainer
.withAlpha(128),
trailing: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
// #Pangea
// child: Icon(Icons.edit_outlined),
child: Icon(Icons.settings_outlined),
// Pangea#
),
// #Pangea
// onTap: () => _onJoinSpaceChild(spaceChild),
onTap: () => context.push(
'/spaces/${spaceChild.roomId}',
),
// Pangea#
),
// #Pangea
&&
room.membership != Membership.leave
// Pangea#
) {
return ChatListItem(
room,
onLongPress: () =>
_onSpaceChildContextMenu(spaceChild, room),
activeChat: widget.controller.activeChat == room.id,
);
}
final isSpace = spaceChild.roomType == 'm.space';
final topic = spaceChild.topic?.isEmpty ?? true
? null
: spaceChild.topic;
if (spaceChild.roomId == activeSpaceId) {
return SearchTitle(
title: spaceChild.name ??
spaceChild.canonicalAlias ??
'Space',
icon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Avatar(
size: 24,
// if (activeSpace?.canChangeStateEvent(
// EventTypes.spaceChild,
// ) ==
// true)
// Material(
// child: ListTile(
// leading: const CircleAvatar(
// child: Icon(Icons.group_add_outlined),
// ),
// title:
// Text(L10n.of(context)!.addChatOrSubSpace),
// trailing:
// const Icon(Icons.chevron_right_outlined),
// onTap: _addChatOrSubSpace,
// ),
// ),
// Pangea#
],
);
}
final name = spaceChild.name ??
spaceChild.canonicalAlias ??
L10n.of(context)!.chat;
if (widget.controller.isSearchMode &&
!name.toLowerCase().contains(
widget.controller.searchController.text,
)) {
return const SizedBox.shrink();
}
return Material(
child: ListTile(
leading: Avatar(
mxContent: spaceChild.avatarUrl,
name: spaceChild.name,
fontSize: 9,
//#Pangea
littleIcon: room?.roomTypeIcon,
//Pangea#
),
title: Row(
children: [
Expanded(
child: Text(
name,
maxLines: 1,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
if (!isSpace) ...[
const Icon(
Icons.people_outline,
size: 16,
),
const SizedBox(width: 4),
Text(
spaceChild.numJoinedMembers.toString(),
style: const TextStyle(fontSize: 14),
),
],
],
),
),
color: Theme.of(context)
.colorScheme
.secondaryContainer
.withAlpha(128),
trailing: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
// #Pangea
// child: Icon(Icons.edit_outlined),
child: Icon(Icons.settings_outlined),
// onTap: () => room?.isSpace == true
// ? widget.controller.setActiveSpace(room!.id)
// : _onSpaceChildContextMenu(spaceChild, room),
onTap: room?.isSpace ?? false
? () => chatListHandleSpaceTap(
context,
widget.controller,
room!,
)
: () => _onJoinSpaceChild(spaceChild),
// Pangea#
),
// #Pangea
// onTap: () => _onJoinSpaceChild(spaceChild),
onTap: () => context.push(
'/spaces/${spaceChild.roomId}',
),
// Pangea#
);
}
return ListTile(
leading: Avatar(
mxContent: spaceChild.avatarUrl,
name: spaceChild.name,
//#Pangea
littleIcon: room?.roomTypeIcon,
//Pangea#
),
title: Row(
children: [
Expanded(
child: Text(
spaceChild.name ??
spaceChild.canonicalAlias ??
L10n.of(context)!.chat,
maxLines: 1,
style:
const TextStyle(fontWeight: FontWeight.bold),
onLongPress: () =>
_onSpaceChildContextMenu(spaceChild, room),
subtitle: Text(
topic ??
(isSpace
? L10n.of(context)!.enterSpace
: L10n.of(context)!.enterRoom),
maxLines: 1,
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
),
),
if (!isSpace) ...[
const Icon(
Icons.people_outline,
size: 16,
),
const SizedBox(width: 4),
Text(
spaceChild.numJoinedMembers.toString(),
style: const TextStyle(fontSize: 14),
),
],
],
),
//#Pangea
// onTap: () => _onJoinSpaceChild(spaceChild),
onTap: room?.isSpace ?? false
? () => chatListHandleSpaceTap(
context,
widget.controller,
room!,
)
: () => _onJoinSpaceChild(spaceChild),
//Pangea#
onLongPress: () =>
_onSpaceChildContextMenu(spaceChild, room),
subtitle: Text(
topic ??
(isSpace
? L10n.of(context)!.enterSpace
: L10n.of(context)!.enterRoom),
maxLines: 1,
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
trailing: isSpace
? const Icon(Icons.chevron_right_outlined)
: null,
),
),
trailing: isSpace
? const Icon(Icons.chevron_right_outlined)
: null,
);
},
childCount: spaceChildren.length + 1 + (canLoadMore ? 1 : 0),
),
),
],
),
);
},
);
},
childCount: spaceChildren.length + (canLoadMore ? 1 : 0),
),
);
},
),
],
),
),
);
}
}
@ -616,3 +787,5 @@ enum SpaceChildContextAction {
addToSpace
// Pangea#
}
enum AddRoomType { chat, subspace }

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