Fluffychat merge (#2731)
* build: Update snapcraft * build: Follow up snapcraft build * build: Install flutter via git in snapcraft * chore: Follow up typo * fix: Request notification permissions on iOS before getToken * chore: Follow up request iOS permissions * Revert "chore: Follow up request iOS permissions" This reverts commit2625e89a33. * chore: Combine mimetype types in send file dialog logic * build: Update flutter to 3.29.0 * Translated using Weblate (Basque) Currently translated at 99.8% (758 of 759 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ * Revert "build: Update flutter to 3.29.0" * fix: Crash in settings when using MAS * build: Fix build tailwindcss for website * feat: Navigate in image viewer with keyboard keys * chore: Nicer colors for reactions * chore: Better error handling for image rendering * Translated using Weblate (Latvian) Currently translated at 100.0% (759 of 759 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ * fix: Index of numbered lists are off * fix(macos): update dependencies to make the build work This commit was generated mostly by running `flutter run -d macos` and then `pod update` in the `macos/` directory after that failed. * fix: never use a transition on the shell route Changing the PageBuilder here based on a MediaQuery causes the child to briefly be rendered twice with the same GlobalKey, blowing up the rendering. I believe this fixes https://github.com/krille-chan/fluffychat/issues/1534. * feat: New video file picker button * feat: Send optional message with images or files * chore: Follow up send file dialog design * chore: Follow up paddings in room input row * chore: Follow up paddings * chore: Follow up paddings * chore: Follow up input row * Translated using Weblate (Italian) Currently translated at 99.6% (756 of 759 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/it/ * Translated using Weblate (Latvian) Currently translated at 100.0% (759 of 759 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ * Translated using Weblate (Estonian) Currently translated at 100.0% (762 of 762 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (762 of 762 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ * chore: Follow up gallery picker * chore: Better no compression supported UX * fix: prevent users from creating spaces with empty names * fix: update condition in account deletion function to allow deletion to go through * Translated using Weblate (Latvian) Currently translated at 100.0% (762 of 762 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ * Translated using Weblate (Estonian) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ * Translated using Weblate (Basque) Currently translated at 99.8% (762 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ * Translated using Weblate (Galician) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ * Translated using Weblate (Latvian) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ * Translated using Weblate (Basque) Currently translated at 99.8% (762 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/id/ * Translated using Weblate (Korean) Currently translated at 96.4% (736 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ko/ * Translated using Weblate (Irish) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ga/ * Translated using Weblate (Filipino) Currently translated at 25.8% (197 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/fil/ * Translated using Weblate (Polish) Currently translated at 98.4% (751 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pl/ * Translated using Weblate (Polish) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pl/ * fix: Remove too sensitive dismiss gesture on chat list items * fix: Add missing <s> html tag to render * Translated using Weblate (Dutch) Currently translated at 81.6% (623 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * refactor: Remove custom font and emoji font workaround * build: Add android namespace * build: Update kotlin gradle plugin * Revert "build: Update kotlin gradle plugin" * feat: Add advanced configuration page * refactor: Improved UX for room upgrades * Translated using Weblate (French) Currently translated at 86.3% (659 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/fr/ * Translated using Weblate (Dutch) Currently translated at 82.0% (626 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 88.8% (678 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hant/ * Translated using Weblate (Dutch) Currently translated at 83.3% (636 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * Translated using Weblate (German) Currently translated at 93.9% (717 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ * Translated using Weblate (German) Currently translated at 93.9% (717 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ * Translated using Weblate (German) Currently translated at 93.9% (717 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ * Translated using Weblate (German) Currently translated at 95.6% (730 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ * Translated using Weblate (Dutch) Currently translated at 94.4% (721 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * Translated using Weblate (Spanish) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (763 of 763 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/es/ * Translated using Weblate (Polish) Currently translated at 99.8% (763 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pl/ * Translated using Weblate (Ukrainian) Currently translated at 93.3% (713 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/id/ * chore: divider when scrolled up * refactor: Easier shift enter logic for text input * Translated using Weblate (Irish) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ga/ * Translated using Weblate (Latvian) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ * Translated using Weblate (Estonian) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ * Translated using Weblate (Ukrainian) Currently translated at 94.8% (725 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/ * build: Downgrade packages and move to fixed flutter typeahead fork * chore: Use other join endpoint for room upgrades * chore: disable echoCancel for audio messages * chore: Simpler changing config variables Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up config editor Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Make push gateway configurable Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up code formatting * build: Update flutter 3.29.2 Signed-off-by: Krille <c.kussowski@famedly.com> * Revert "chore: Follow up code formatting" This reverts commit0f000f952f. * Revert "build: Update flutter 3.29.2" This reverts commitbfd23952b7. * refactor: Formatting * build: Update matrix dart sdk Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up update matrix dart sdk Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up formatting Signed-off-by: Krille <c.kussowski@famedly.com> * build: Update openssl to 0.5.0 Signed-off-by: Krille <c.kussowski@famedly.com> * build: Update gorouter package Signed-off-by: Krille <c.kussowski@famedly.com> * build: Update to flutter 3.29.2 Signed-off-by: Krille <c.kussowski@famedly.com> * Translated using Weblate (Dutch) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * Added translation using Weblate (Telugu) * Translated using Weblate (Dutch) Currently translated at 100.0% (764 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * Translated using Weblate (Telugu) Currently translated at 0.5% (4 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/te/ * Translated using Weblate (German) Currently translated at 96.5% (738 of 764 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ * Translated using Weblate (Estonian) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ * Translated using Weblate (Irish) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ga/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ * Translated using Weblate (Galician) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * Translated using Weblate (Latvian) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ * Translated using Weblate (Latvian) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/lv/ * Translated using Weblate (Ukrainian) Currently translated at 95.9% (734 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/id/ * fix: Consistent element padding between server picker and login view * refactor: Migrate more config options to config viewer Signed-off-by: Krille <c.kussowski@famedly.com> * refactor: Reuse unused kotlin imports Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Update pubspec.lock Signed-off-by: Krille <c.kussowski@famedly.com> * Revert "build: Install flutter via git in snapcraft" This reverts commitcd12f773fe. * chore: Update locale config for localizations Signed-off-by: Krille <c.kussowski@famedly.com> * build: Add libpciaccess0 package to snap Signed-off-by: Krille <c.kussowski@famedly.com> * Translated using Weblate (Dutch) Currently translated at 100.0% (765 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/nl/ * Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 93.3% (714 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hant/ * Translated using Weblate (Russian) Currently translated at 95.6% (732 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ru/ * chore: upgrade chewie and video_player packages This bumps the minimum Flutter version to 3.27. I think this is not an issue, sincee93fdebe20upgraded to 3.29.2 already. * fix: properly dispose VideoPlayerController This ensures that a playing video stops playing when we navigate away from the chat. I also reorganized the code a little. * feat: support inline video playback on macOS It turns out that video_player supports macOS, so we can simply enable it. * feat: clearly mark when a video is to be downloaded This shows a download icon instead of the play icon on top of the video if the video player isn't supported. * Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 93.7% (717 of 765 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hant/ * build: Add libpciaccess0 for snapcraft * build: Add libpciaccess-dev for snapcraft * build: use singleInstance as launchmode * fix: Null error in ClientChooserButton * chore: Improve avatar designg Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up new room design Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Correct availability of desktop builds * refactor: Replace user bottom sheet with menu and small dialog Signed-off-by: Krille <c.kussowski@famedly.com> * refactor: Replace user bottom sheet with menu and small dialog Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up dialog themes Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up dialog themes Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up dialog themes Signed-off-by: Krille <c.kussowski@famedly.com> * build: Update matrix dart sdk to 0.39.0 Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up user dialog theme * chore: Use Cupertino Activity Indicator in ChatEventList * chore: Follow up permissions slider dialog Signed-off-by: Krille <c.kussowski@famedly.com> * refactor: Implement avatar image viewer and adjust design Signed-off-by: Krille <c.kussowski@famedly.com> * feat: Filter for room members page and easier approve knocking users Signed-off-by: Krille <c.kussowski@famedly.com> * refactor: Move public room bottom sheet into dialog Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up public rooms dialog Signed-off-by: Krille <c.kussowski@famedly.com> * fix: Text scale factor in Linkify widgets Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Add matrix notifications for issues Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up matrix notification Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up matrix notification Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up matrix notification Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up matrix notification Signed-off-by: Krille <c.kussowski@famedly.com> * android updates * chore: update fetching of chat details display setting in message overlay positioner * fluffychat merge * build: Flutter 3.29.3 Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Nicer scaffold dialog for column mode Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up scaffold dialog Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up members list * chore: Follow up message design * chore: Follow up message design * chore: Follow up file message design Signed-off-by: Krille <c.kussowski@famedly.com> * build: Bump version to 1.26.0 Signed-off-by: Krille <c.kussowski@famedly.com> * chore: Follow up message design * build: Use 0.1.0 fcm_shared_isolate on ios Signed-off-by: Krille <c.kussowski@famedly.com> * chore: disable matrix notification github action * fix import error * make overlay message padding match message bubble padding --------- Signed-off-by: Krille <c.kussowski@famedly.com> Co-authored-by: Krille-chan <christian-kussowski@posteo.de> Co-authored-by: Krille <c.kussowski@famedly.com> Co-authored-by: xabirequejo <xabi.rn@gmail.com> Co-authored-by: Edgars Andersons <Edgars+Weblate@gaitenis.id.lv> Co-authored-by: Rafał Hirsch <rafal@hirsch.net> Co-authored-by: Angelo Schirinzi <Odi-3@users.noreply.hosted.weblate.org> Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com> Co-authored-by: Poesty Li <poesty7450@gmail.com> Co-authored-by: josé m <correoxm@disroot.org> Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org> Co-authored-by: Linerly <linerly@proton.me> Co-authored-by: kdh8219 <kdh8219@monamo.dev> Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com> Co-authored-by: searinminecraft <kitakita@disroot.org> Co-authored-by: Piotr Orzechowski <piotr@orzechowski.tech> Co-authored-by: Jelv <post@jelv.nl> Co-authored-by: Antonin Del Fabbro <message@antonin.one> Co-authored-by: Mare JP <seraphmare@gmail.com> Co-authored-by: nautilusx <translate@disroot.org> Co-authored-by: Very Able <veryable@proton.me> Co-authored-by: Kimby <kimisaes@naver.com> Co-authored-by: José Muñoz <dr.cabra@disroot.org> Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com> Co-authored-by: katakam chakri <katakam.chakri@gmail.com> Co-authored-by: ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝) <coool@mail.lv> Co-authored-by: xegim <ja3lpark@gmail.com> Co-authored-by: miullu <satou.ide@gmail.com> Co-authored-by: Yurt Page <yurtpage@gmail.com> Co-authored-by: Lenni <87639068+Lenni-builder@users.noreply.github.com>
This commit is contained in:
parent
bb3c4f30db
commit
0d30a8fab2
126 changed files with 5087 additions and 3839 deletions
6
.github/workflows/main_deploy.yaml
vendored
6
.github/workflows/main_deploy.yaml
vendored
|
|
@ -19,10 +19,6 @@ jobs:
|
|||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
- name: Remove Emoji Font
|
||||
run: |
|
||||
rm -rf fonts/NotoEmoji
|
||||
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
|
||||
- run: flutter pub get
|
||||
- name: Prepare web
|
||||
run: ./scripts/prepare-web.sh
|
||||
|
|
@ -82,4 +78,4 @@ jobs:
|
|||
- name: Update packages
|
||||
run: flutter pub get
|
||||
- name: Update sentry
|
||||
run: flutter packages pub run sentry_dart_plugin
|
||||
run: flutter packages pub run sentry_dart_plugin
|
||||
|
|
|
|||
26
.github/workflows/matrix_notification.yaml
vendored
Normal file
26
.github/workflows/matrix_notification.yaml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# name: Matrix Notification
|
||||
|
||||
# on:
|
||||
# issues:
|
||||
# types: [ opened ]
|
||||
# issue_comment:
|
||||
# types: [ created ]
|
||||
|
||||
# jobs:
|
||||
# notify:
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# steps:
|
||||
# - name: Send Matrix Notification
|
||||
# env:
|
||||
# MATRIX_URL: https://matrix.janian.de/_matrix/client/v3/rooms/${{ secrets.MATRIX_MANAGEMENT_ROOM }}/send/m.room.message
|
||||
# run: |
|
||||
# if [ "${{ github.event.action }}" == "opened" ]; then
|
||||
# PAYLOAD="{\"msgtype\": \"m.notice\", \"body\": \"New Issue from ${{ github.event.issue.user.login }}\\n${{ github.event.issue.title }}\\n\\n${{ github.event.issue.body }}\\n\\nURL: ${{ github.event.issue.html_url }}\"}"
|
||||
# elif [ "${{ github.event.action }}" == "created" ]; then
|
||||
# PAYLOAD="{\"msgtype\": \"m.notice\", \"body\": \"New Comment from ${{ github.event.comment.user.login }}\\n\\n${{ github.event.comment.body }}\\n\\nURL: ${{ github.event.comment.html_url }}\"}"
|
||||
# fi
|
||||
# curl -X POST -H "Authorization: Bearer ${{ secrets.MATRIX_BOT_TOKEN }}" \
|
||||
# -H "Content-Type: application/json" \
|
||||
# -d "$PAYLOAD" \
|
||||
# $MATRIX_URL
|
||||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
|
|
@ -48,10 +48,6 @@ jobs:
|
|||
cache: true
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install nodejs -y
|
||||
- name: Remove Emoji Font
|
||||
run: |
|
||||
rm -rf fonts/NotoEmoji
|
||||
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
|
||||
- run: flutter pub get
|
||||
- name: Prepare web
|
||||
run: ./scripts/prepare-web.sh
|
||||
|
|
|
|||
2
.github/workflows/versions.env
vendored
2
.github/workflows/versions.env
vendored
|
|
@ -1,2 +1,2 @@
|
|||
FLUTTER_VERSION=3.27.4
|
||||
FLUTTER_VERSION=3.29.3
|
||||
JAVA_VERSION=17
|
||||
|
|
|
|||
376
CHANGELOG.md
376
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
# Privacy
|
||||
|
||||
FluffyChat is available on Android, iOS and as a web version. Desktop versions for Windows, Linux and macOS may follow.
|
||||
FluffyChat is available on Android, iOS, Linux and as a web version. Desktop versions for Windows and macOS may follow.
|
||||
|
||||
* [Matrix](#matrix)
|
||||
* [Database](#database)
|
||||
|
|
@ -109,4 +109,4 @@ To enhance user safety and help protect against the sexual abuse and exploitatio
|
|||
|
||||
In addition to reporting messages, users can also report other users following a similar process.
|
||||
|
||||
We encourage server administrators to adhere to strict safety standards and provide mechanisms for addressing and moderating inappropriate content. For more information on the Matrix protocol and its safety standards, please refer to the following link: https://matrix.org/docs/older/moderation/
|
||||
We encourage server administrators to adhere to strict safety standards and provide mechanisms for addressing and moderating inappropriate content. For more information on the Matrix protocol and its safety standards, please refer to the following link: https://matrix.org/docs/older/moderation/
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@
|
|||
|
||||
* Also thanks to all translators and testers! With your help, fluffychat is now available in more than 12 languages.
|
||||
|
||||
* <a href="https://github.com/googlefonts/noto-emoji/">Noto Emoji Font</a> for the awesome emojis.
|
||||
|
||||
* <a href="https://github.com/madsrh/WoodenBeaver">WoodenBeaver</a> sound theme for the notification sound.
|
||||
|
||||
* The Matrix Foundation for making and maintaining the [emoji translations](https://github.com/matrix-org/matrix-spec/blob/main/data-definitions/sas-emoji.json) used for emoji verification, licensed Apache 2.0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.talktolearn.chat" android:installLocation="auto">
|
||||
android:installLocation="auto">
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
|
|
|
|||
|
|
@ -2,17 +2,9 @@
|
|||
|
||||
import com.famedly.fcm_shared_isolate.FcmSharedIsolateService
|
||||
|
||||
import chat.fluffy.fluffychat.MainActivity
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.view.FlutterMain
|
||||
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.WindowManager
|
||||
|
||||
class FcmPushService : FcmSharedIsolateService() {
|
||||
override fun getEngine(): FlutterEngine {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
<locale android:name="sr"/>
|
||||
<locale android:name="sv"/>
|
||||
<locale android:name="ta"/>
|
||||
<locale android:name="te"/>
|
||||
<locale android:name="th"/>
|
||||
<locale android:name="tr"/>
|
||||
<locale android:name="uk"/>
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheChatDescriptionTo": "{username} hat die Chat-Beschreibung geändert zu: \"{description}\"",
|
||||
"changedTheChatDescriptionTo": "{username} hat die Chatbeschreibung geändert in: '{description}'",
|
||||
"@changedTheChatDescriptionTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -217,7 +217,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheChatNameTo": "{username} hat den Chat-Namen geändert zu: \"{chatname}\"",
|
||||
"changedTheChatNameTo": "{username} hat den Chatnamen geändert in: '{chatname}'",
|
||||
"@changedTheChatNameTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -238,7 +238,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheDisplaynameTo": "{username} hat den Nicknamen geändert zu: \"{displayname}\"",
|
||||
"changedTheDisplaynameTo": "{username} hat den Spitznamen geändert in: '{displayname}'",
|
||||
"@changedTheDisplaynameTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -3247,5 +3247,47 @@
|
|||
"notificationRuleSuppressNoticesDescription": "Unterdrückt Benachrichtigungen von automatisierten Clients wie Bots.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleInviteForMe": "Einladung für mich",
|
||||
"@notificationRuleInviteForMe": {}
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleReaction": "Reaktion",
|
||||
"@notificationRuleReaction": {},
|
||||
"notificationRuleReactionDescription": "Unterdrückt Benachrichtigungen für Reaktionen.",
|
||||
"@notificationRuleReactionDescription": {},
|
||||
"notificationRuleSuppressEditsDescription": "Unterdrückt Benachrichtigungen für bearbeitete Nachrichten.",
|
||||
"@notificationRuleSuppressEditsDescription": {},
|
||||
"notificationRuleCall": "Anruf",
|
||||
"@notificationRuleCall": {},
|
||||
"notificationRuleCallDescription": "Benachrichtigt den Benutzer über Anrufe.",
|
||||
"@notificationRuleCallDescription": {},
|
||||
"notificationRuleEncrypted": "Verschlüsselt",
|
||||
"@notificationRuleEncrypted": {},
|
||||
"more": "Mehr",
|
||||
"@more": {},
|
||||
"notificationRuleSuppressEdits": "Unterdrückt Bearbeitungen",
|
||||
"@notificationRuleSuppressEdits": {},
|
||||
"notificationRuleRoomServerAclDescription": "Unterdrückt Benachrichtigungen für Raumserver-Zugriffskontrolllisten (ACL).",
|
||||
"@notificationRuleRoomServerAclDescription": {},
|
||||
"notificationRuleMessage": "Nachricht",
|
||||
"@notificationRuleMessage": {},
|
||||
"notificationRuleMessageDescription": "Informiert den Benutzer über allgemeine Nachrichten.",
|
||||
"@notificationRuleMessageDescription": {},
|
||||
"notificationRuleJitsi": "Jitsi",
|
||||
"@notificationRuleJitsi": {},
|
||||
"allDevices": "Alle Geräte",
|
||||
"@allDevices": {},
|
||||
"enterNewChat": "Neuen Chat starten",
|
||||
"@enterNewChat": {},
|
||||
"shareKeysWith": "Schlüssel teilen mit...",
|
||||
"@shareKeysWith": {},
|
||||
"shareKeysWithDescription": "Welchen Geräten sollte vertraut werden, damit sie deine Nachrichten in verschlüsselten Chats mitlesen können?",
|
||||
"@shareKeysWithDescription": {},
|
||||
"verifiedDevicesOnly": "Nur verifizierte Geräte",
|
||||
"@verifiedDevicesOnly": {},
|
||||
"takeAPhoto": "Foto aufnehmen",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Video aufnehmen",
|
||||
"@recordAVideo": {},
|
||||
"optionalMessage": "(Optionale) Nachricht...",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "Nicht unterstützt auf diesem Gerät",
|
||||
"@notSupportedOnThisDevice": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
"@repeatPassword": {},
|
||||
"notAnImage": "Not an image file.",
|
||||
"@notAnImage": {},
|
||||
"setCustomPermissionLevel": "Set custom permission level",
|
||||
"setPermissionsLevelDescription": "Please choose a predefined role below or enter a custom permission level between 0 and 100.",
|
||||
"ignoreUser": "Ignore user",
|
||||
"normalUser": "Normal user",
|
||||
"remove": "Remove",
|
||||
"@remove": {
|
||||
"type": "String",
|
||||
|
|
@ -102,6 +106,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"commandHint_roomupgrade": "Upgrade this room to the given room version",
|
||||
"commandHint_googly": "Send some googly eyes",
|
||||
"@commandHint_googly": {},
|
||||
"commandHint_cuddle": "Send a cuddle",
|
||||
|
|
@ -3206,6 +3211,7 @@
|
|||
"recordAVideo": "Record a video",
|
||||
"optionalMessage": "(Optional) message...",
|
||||
"notSupportedOnThisDevice": "Not supported on this device",
|
||||
"enterNewChat": "Enter new chat",
|
||||
"accountInformation": "Account information",
|
||||
"addGroupDescription": "Add a chat description",
|
||||
"addNewFriend": "Add new friend",
|
||||
|
|
@ -4863,7 +4869,7 @@
|
|||
"emptyChatWarningDesc": "You haven't invited anyone to your chat. Go to Chat settings to invite your contacts or the Bot. You can also do this later.",
|
||||
"areYouLikeMe": "Are you like me?",
|
||||
"tryAgainLater": "Too many attempts made. Please try again in 5 minutes.",
|
||||
"enterSpaceCode": "Enter the Space Code",
|
||||
"enterSpaceCode": "Enter space code",
|
||||
"shareSpaceLink": "Share link to space",
|
||||
"byUsingPangeaChat": "By using Pangea Chat, I agree to the ",
|
||||
"details": "Details",
|
||||
|
|
@ -4896,5 +4902,8 @@
|
|||
},
|
||||
"ban": "Ban",
|
||||
"unban": "Unban",
|
||||
"kick": "Kick"
|
||||
}
|
||||
"kick": "Kick",
|
||||
"approve": "Approve",
|
||||
"youHaveKnocked": "You have knocked",
|
||||
"pleaseWaitUntilInvited": "Please wait now, until someone from the room invites you."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"acceptedTheInvitation": "{username} aceptó la invitación",
|
||||
"acceptedTheInvitation": "👍 {username} aceptó la invitación",
|
||||
"@acceptedTheInvitation": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -68,7 +68,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"areGuestsAllowedToJoin": "¿Pueden unirse los usuarios visitantes?",
|
||||
"areGuestsAllowedToJoin": "¿Pueden unirse usuarios de visita?",
|
||||
"@areGuestsAllowedToJoin": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1943,7 +1943,7 @@
|
|||
},
|
||||
"scanQrCode": "Escanear código QR",
|
||||
"@scanQrCode": {},
|
||||
"homeserver": "Homeserver",
|
||||
"homeserver": "Servidor inicial",
|
||||
"@homeserver": {},
|
||||
"newChat": "Nuevo chat",
|
||||
"@newChat": {
|
||||
|
|
@ -2193,7 +2193,7 @@
|
|||
"@youAcceptedTheInvitation": {},
|
||||
"widgetEtherpad": "Nota de texto",
|
||||
"@widgetEtherpad": {},
|
||||
"commandHint_cuddle": "Mandar una carantoña",
|
||||
"commandHint_cuddle": "Enviar un abrazo",
|
||||
"@commandHint_cuddle": {},
|
||||
"supposedMxid": "Esto debería ser {mxid}",
|
||||
"@supposedMxid": {
|
||||
|
|
@ -2206,7 +2206,7 @@
|
|||
},
|
||||
"importFromZipFile": "Importar de un archivo .zip",
|
||||
"@importFromZipFile": {},
|
||||
"exportEmotePack": "Exportar paquete de emotes como .zip",
|
||||
"exportEmotePack": "Exportar paquete de emotes a .zip",
|
||||
"@exportEmotePack": {},
|
||||
"addChatDescription": "Añadir una descripción del chat...",
|
||||
"@addChatDescription": {},
|
||||
|
|
@ -2320,7 +2320,7 @@
|
|||
"@enterSpace": {},
|
||||
"pleaseEnterRecoveryKey": "Por favor, introduzca su clave de recuperación:",
|
||||
"@pleaseEnterRecoveryKey": {},
|
||||
"widgetNameError": "Por favor, introduzca un nombre a mostrar.",
|
||||
"widgetNameError": "Por favor proporciona un nombre para mostrar.",
|
||||
"@widgetNameError": {},
|
||||
"addWidget": "Añadir widget",
|
||||
"@addWidget": {},
|
||||
|
|
@ -3337,6 +3337,16 @@
|
|||
"@previous": {},
|
||||
"otherPartyNotLoggedIn": "La otra parte ahora mismo no está conectada y por tanto ¡no puede recibir mensajes!",
|
||||
"@otherPartyNotLoggedIn": {},
|
||||
"takeAPhoto": "Tomar foto",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Grabar video",
|
||||
"@recordAVideo": {},
|
||||
"optionalMessage": "(Opcional) mensaje...",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "No es compatible con este dispositivo",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"enterNewChat": "Ingresar a nuevo chat",
|
||||
"@enterNewChat": {},
|
||||
"accountInformation": "Información de la cuenta",
|
||||
"addGroupDescription": "Agregar una descripción al grupo",
|
||||
"alreadyHaveAnAccount": "¿Ya tiene una cuenta?",
|
||||
|
|
@ -5449,4 +5459,4 @@
|
|||
},
|
||||
"downloadGboard": "Descargar Gboard",
|
||||
"autocorrectNotAvailable": "Desafortunadamente, tu plataforma no es compatible actualmente con esta función. ¡Mantente atento a futuros desarrollos!"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3341,5 +3341,9 @@
|
|||
"optionalMessage": "Sõnum (kui soovid lisada)...",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "See pole antud seadmes toetatud",
|
||||
"@notSupportedOnThisDevice": {}
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"enterNewChat": "Liitu uue vestlusega",
|
||||
"@enterNewChat": {},
|
||||
"commandHint_roomupgrade": "Uuenda see jututuba antud jututoa versioonini",
|
||||
"@commandHint_roomupgrade": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3218,7 +3218,7 @@
|
|||
"@newChatRequest": {},
|
||||
"contentNotificationSettings": "Edukiaren jakinarazpenen ezarpenak",
|
||||
"@contentNotificationSettings": {},
|
||||
"notificationRuleContainsUserNameDescription": "Jakinarazten du mezuan erabiltzaile-izena aipatzen denean.",
|
||||
"notificationRuleContainsUserNameDescription": "Mezuan erabiltzaile-izena aipatzen denean jakinarazten du.",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMasterDescription": "Gainerako arauak gainidatzi eta jakinarazpenak ezgaitzen ditu.",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
|
|
@ -3240,5 +3240,104 @@
|
|||
}
|
||||
},
|
||||
"notificationRuleInviteForMe": "Gonbidapena niretzat",
|
||||
"@notificationRuleInviteForMe": {}
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleInviteForMeDescription": "Erabiltzailea gela batera gonbidatzen dutenean jakinarazten du.",
|
||||
"@notificationRuleInviteForMeDescription": {},
|
||||
"notificationRuleSuppressNotices": "Ezkutatu mezu automatikoak",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"notificationRuleSuppressNoticesDescription": "BOTen eta bestelako bezero automatikoen jakinarazpenak ezkutatzen ditu.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleMemberEvent": "Kideen gertaera",
|
||||
"@notificationRuleMemberEvent": {},
|
||||
"notificationRuleMemberEventDescription": "Kideen gertaeren jakinarazpenak ezkutatzen ditu.",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleIsUserMention": "Erabiltzailea aipatzea",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"notificationRuleContainsDisplayName": "Pantaila-izena dauka",
|
||||
"@notificationRuleContainsDisplayName": {},
|
||||
"notificationRuleIsUserMentionDescription": "Erabiltzailea mezu zuzen batean aipatzen dutenean jakinarazten du.",
|
||||
"@notificationRuleIsUserMentionDescription": {},
|
||||
"notificationRuleContainsDisplayNameDescription": "Mezu batek erabiltzailearen pantaila-izena duenean jakinarazten du.",
|
||||
"@notificationRuleContainsDisplayNameDescription": {},
|
||||
"notificationRuleIsRoomMention": "Gelaren aipamena",
|
||||
"@notificationRuleIsRoomMention": {},
|
||||
"notificationRuleIsRoomMentionDescription": "Gela aipatzen denean erabiltzailea jakinarazten du.",
|
||||
"@notificationRuleIsRoomMentionDescription": {},
|
||||
"notificationRuleRoomnotif": "Gelaren jakinarazpena",
|
||||
"@notificationRuleRoomnotif": {},
|
||||
"notificationRuleRoomnotifDescription": "Mezu batek '@room' duenean erabiltzaileari jakinarazten dio.",
|
||||
"@notificationRuleRoomnotifDescription": {},
|
||||
"notificationRuleTombstone": "Hilarria",
|
||||
"@notificationRuleTombstone": {},
|
||||
"notificationRuleReaction": "Erreakzioa",
|
||||
"@notificationRuleReaction": {},
|
||||
"notificationRuleReactionDescription": "Erreakzioen jakinarazpenak ezkutatzen ditu.",
|
||||
"@notificationRuleReactionDescription": {},
|
||||
"notificationRuleTombstoneDescription": "Gela desaktibatzeko mezuei buruz jakinarazten dio erabiltzaileari.",
|
||||
"@notificationRuleTombstoneDescription": {},
|
||||
"notificationRuleRoomServerAcl": "Gelaren zerbitzariaren ACLa",
|
||||
"@notificationRuleRoomServerAcl": {},
|
||||
"notificationRuleSuppressEdits": "Ezkutatu edizioak",
|
||||
"@notificationRuleSuppressEdits": {},
|
||||
"notificationRuleCall": "Deia",
|
||||
"@notificationRuleCall": {},
|
||||
"notificationRuleEncryptedRoomOneToOne": "Zifratutako bien arteko gela",
|
||||
"@notificationRuleEncryptedRoomOneToOne": {},
|
||||
"notificationRuleSuppressEditsDescription": "Editatutako mezuen jakinarazpenak ezkutatzen ditu.",
|
||||
"@notificationRuleSuppressEditsDescription": {},
|
||||
"notificationRuleCallDescription": "Erabiltzaileari deiei buruz jakinarazten dio.",
|
||||
"@notificationRuleCallDescription": {},
|
||||
"notificationRuleEncryptedRoomOneToOneDescription": "Erabiltzailea jakinarazten du zifratutako bien arteko geletako mezuei buruz.",
|
||||
"@notificationRuleEncryptedRoomOneToOneDescription": {},
|
||||
"notificationRuleRoomOneToOne": "Bien arteko gela",
|
||||
"@notificationRuleRoomOneToOne": {},
|
||||
"notificationRuleRoomOneToOneDescription": "Erabiltzailea jakinarazten du bien arteko geletako mezuei buruz.",
|
||||
"@notificationRuleRoomOneToOneDescription": {},
|
||||
"notificationRuleMessage": "Mezua",
|
||||
"@notificationRuleMessage": {},
|
||||
"notificationRuleMessageDescription": "Erabiltzailea jakinarazten du mezu orokorrei buruz.",
|
||||
"@notificationRuleMessageDescription": {},
|
||||
"notificationRuleEncrypted": "Zifratuak",
|
||||
"@notificationRuleEncrypted": {},
|
||||
"notificationRuleEncryptedDescription": "Erabiltzailea jakinarazten du zifratutako geletako mezuei buruz.",
|
||||
"@notificationRuleEncryptedDescription": {},
|
||||
"notificationRuleJitsi": "Jitsi",
|
||||
"@notificationRuleJitsi": {},
|
||||
"notificationRuleJitsiDescription": "Erabiltzailea jakinarazten du Jitsi widgetaren gertaerei buruz.",
|
||||
"@notificationRuleJitsiDescription": {},
|
||||
"notificationRuleServerAcl": "Ezkutatu zerbitzariaren ACL gertaerak",
|
||||
"@notificationRuleServerAcl": {},
|
||||
"notificationRuleServerAclDescription": "Zerbitzariaren ACL gertaerak ezkutatzen ditu.",
|
||||
"@notificationRuleServerAclDescription": {},
|
||||
"unknownPushRule": "Push arau ezezaguna '{rule}'",
|
||||
"@unknownPushRule": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"rule": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deletePushRuleCanNotBeUndone": "Jakinarazpen ezarpen hau ezabatzen baduzu, ezin da desegin.",
|
||||
"@deletePushRuleCanNotBeUndone": {},
|
||||
"shareKeysWith": "Partekatu gakoak…",
|
||||
"@shareKeysWith": {},
|
||||
"allDevices": "Gailu guztiekin",
|
||||
"@allDevices": {},
|
||||
"shareKeysWithDescription": "Zein gailu hartu beharko litzateke fidagarritzat zifratutako txaten mezuak irakur ditzaten?",
|
||||
"@shareKeysWithDescription": {},
|
||||
"crossVerifiedDevicesIfEnabled": "Egiaztapen gurutzatuko gailuekin, gaituta badaude",
|
||||
"@crossVerifiedDevicesIfEnabled": {},
|
||||
"verifiedDevicesOnly": "Egiaztatutako gailuekin soilik",
|
||||
"@verifiedDevicesOnly": {},
|
||||
"crossVerifiedDevices": "Egiaztapen gurutzatuko gailuekin",
|
||||
"@crossVerifiedDevices": {},
|
||||
"takeAPhoto": "Egin argazkia",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Grabatu bideoa",
|
||||
"@recordAVideo": {},
|
||||
"optionalMessage": "Mezua (aukerakoa)…",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "Ez da gailu honekin bateragarria",
|
||||
"@notSupportedOnThisDevice": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -908,7 +908,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"defaultPermissionLevel": "Default na antas ng pahintulot",
|
||||
"defaultPermissionLevel": "Default na antas ng pahintulot para sa mga bagong user",
|
||||
"@defaultPermissionLevel": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -962,5 +962,102 @@
|
|||
"@fontSize": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"noChatsFoundHere": "Walang pang mga chat na nahanap dito. Magsimula ng bagong chat kasama ang isang tao sa pamamagitan ng paggamit ng button sa ibaba. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"aboutHomeserver": "Tungkol sa {homeserver}",
|
||||
"@aboutHomeserver": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"homeserver": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"space": "Espasyo",
|
||||
"@space": {},
|
||||
"countChatsAndCountParticipants": "{chats} mga chat at {participants} mga kasali",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"chats": {
|
||||
"type": "int"
|
||||
},
|
||||
"participants": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"guestsAreForbidden": "Pinagbabawal ang mga bisita",
|
||||
"@guestsAreForbidden": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"guestsCanJoin": "Maaring sumali ang mga bisita",
|
||||
"@guestsCanJoin": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"forward": "I-forward",
|
||||
"@forward": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"fromJoining": "Mula sa pagsali",
|
||||
"@fromJoining": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"fromTheInvitation": "Mula sa imbitasyon",
|
||||
"@fromTheInvitation": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"goToTheNewRoom": "Pumunta sa bagong room",
|
||||
"@goToTheNewRoom": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"group": "Grupo",
|
||||
"@group": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"swipeRightToLeftToReply": "Mag-swipe pakaliwa o kanan para tumugon",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"noMoreChatsFound": "Wala nang mga chat na nahanap…",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Mga nasaling chat",
|
||||
"@joinedChats": {},
|
||||
"unread": "Hindi nabasa",
|
||||
"@unread": {},
|
||||
"spaces": "Mga Espasyo",
|
||||
"@spaces": {},
|
||||
"groupIsPublic": "Pampubliko ang grupo",
|
||||
"@groupIsPublic": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"groups": "Mga grupo",
|
||||
"@groups": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"chatDescription": "Paglalarawan ng chat",
|
||||
"@chatDescription": {},
|
||||
"chatDescriptionHasBeenChanged": "Nabago ang paglalarawan ng chat",
|
||||
"@chatDescriptionHasBeenChanged": {},
|
||||
"groupWith": "Grupo kasama kay/sa {displayname}",
|
||||
"@groupWith": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"displayname": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3091,5 +3091,7 @@
|
|||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginWithMatrixId": "Connexion avec l'identifiant Matrix",
|
||||
"@loginWithMatrixId": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3346,5 +3346,9 @@
|
|||
"notSupportedOnThisDevice": "Ní thacaítear leis ar an ngléas seo",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"optionalMessage": "Teachtaireacht (Roghnach)…",
|
||||
"@optionalMessage": {}
|
||||
"@optionalMessage": {},
|
||||
"enterNewChat": "Cuir isteach comhrá nua",
|
||||
"@enterNewChat": {},
|
||||
"commandHint_roomupgrade": "Uasghrádaigh an seomra seo go dtí an leagan seomra a thugtar",
|
||||
"@commandHint_roomupgrade": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3341,5 +3341,9 @@
|
|||
"takeAPhoto": "Facer foto",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Gravar vídeo",
|
||||
"@recordAVideo": {}
|
||||
"@recordAVideo": {},
|
||||
"enterNewChat": "Entrar na nova conversa",
|
||||
"@enterNewChat": {},
|
||||
"commandHint_roomupgrade": "Actualizar esta sala á versión de sala indicada",
|
||||
"@commandHint_roomupgrade": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3332,5 +3332,17 @@
|
|||
"appWantsToUseForLoginDescription": "Anda memperbolehkan aplikasi dan situs web membagikan informasi tentang Anda.",
|
||||
"@appWantsToUseForLoginDescription": {},
|
||||
"open": "Buka",
|
||||
"@open": {}
|
||||
"@open": {},
|
||||
"takeAPhoto": "Ambil foto",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Rekam video",
|
||||
"@recordAVideo": {},
|
||||
"optionalMessage": "Pesan (opsional)...",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "Tidak didukung pada perangkat ini",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"enterNewChat": "Masuk ke obrolan baru",
|
||||
"@enterNewChat": {},
|
||||
"commandHint_roomupgrade": "Tingkatkan ruangan ini ke versi ruangan yang ditentukan",
|
||||
"@commandHint_roomupgrade": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheChatDescriptionTo": "{username} ha cambiato la descrizione della chat in: «{description}»",
|
||||
"changedTheChatDescriptionTo": "{username} ha cambiato la descrizione della chat in: '{description}'",
|
||||
"@changedTheChatDescriptionTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -198,7 +198,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheChatNameTo": "{username} ha cambiato il nome della discussione in: «{chatname}»",
|
||||
"changedTheChatNameTo": "{username} ha cambiato il nome della discussione in: '{chatname}'",
|
||||
"@changedTheChatNameTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -303,7 +303,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheRoomAliases": "{username} ha cambiato il nome delle stanze",
|
||||
"changedTheRoomAliases": "{username} ha modificato gli alias della stanza",
|
||||
"@changedTheRoomAliases": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -1629,7 +1629,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"unknownEvent": "Evento sconosciuto «{type}»",
|
||||
"unknownEvent": "Evento sconosciuto '{type}'",
|
||||
"@unknownEvent": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -3185,5 +3185,152 @@
|
|||
}
|
||||
},
|
||||
"compress": "Comprimere",
|
||||
"@compress": {}
|
||||
"@compress": {},
|
||||
"contentNotificationSettings": "Impostazioni del contenuto di notifica",
|
||||
"@contentNotificationSettings": {},
|
||||
"generalNotificationSettings": "Impostazioni di notifica generale",
|
||||
"@generalNotificationSettings": {},
|
||||
"roomNotificationSettings": "Impostazioni di notifica della stanza",
|
||||
"@roomNotificationSettings": {},
|
||||
"userSpecificNotificationSettings": "Impostazioni di notifica specifiche dell'utente",
|
||||
"@userSpecificNotificationSettings": {},
|
||||
"otherNotificationSettings": "Altre impostazioni di notifica",
|
||||
"@otherNotificationSettings": {},
|
||||
"notificationRuleContainsUserName": "Contiene il nome utente",
|
||||
"@notificationRuleContainsUserName": {},
|
||||
"notificationRuleContainsUserNameDescription": "Notifica l'utente quando un messaggio contiene il proprio nome utente.",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMaster": "Silenzia tutte le notifiche",
|
||||
"@notificationRuleMaster": {},
|
||||
"notificationRuleMasterDescription": "Sovrascive tutte le altre regole e disabilita tutte le notifiche.",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
"notificationRuleSuppressNotices": "Silenziare i messaggi automatizzati",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"notificationRuleSuppressNoticesDescription": "Silenzia le notifiche da client automatizzati come i bot.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleInviteForMeDescription": "Notifica l'utente quando è invitato in una stanza.",
|
||||
"@notificationRuleInviteForMeDescription": {},
|
||||
"notificationRuleMemberEvent": "Eventi per i membri",
|
||||
"@notificationRuleMemberEvent": {},
|
||||
"notificationRuleInviteForMe": "Inviti per me",
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleIsUserMentionDescription": "Notifica l'utente quando viene menzionato direttamente in un messaggio.",
|
||||
"@notificationRuleIsUserMentionDescription": {},
|
||||
"notificationRuleContainsDisplayNameDescription": "Notifica l'utente quando un messaggio contiene il proprio nome visualizzato.",
|
||||
"@notificationRuleContainsDisplayNameDescription": {},
|
||||
"notificationRuleIsRoomMention": "Menzioni della stanza",
|
||||
"@notificationRuleIsRoomMention": {},
|
||||
"notificationRuleIsUserMention": "Menzioni dell'utente",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"notificationRuleRoomnotif": "Notifiche della stanza",
|
||||
"@notificationRuleRoomnotif": {},
|
||||
"notificationRuleRoomnotifDescription": "Notifica l'utente quando un messaggio contiene '@room'.",
|
||||
"@notificationRuleRoomnotifDescription": {},
|
||||
"notificationRuleTombstone": "Tombstone",
|
||||
"@notificationRuleTombstone": {},
|
||||
"notificationRuleTombstoneDescription": "Notifica all'utente i messaggi di disattivazione della stanza.",
|
||||
"@notificationRuleTombstoneDescription": {},
|
||||
"notificationRuleReaction": "Reazioni",
|
||||
"@notificationRuleReaction": {},
|
||||
"notificationRuleReactionDescription": "Silenzia le notifiche per le reazioni.",
|
||||
"@notificationRuleReactionDescription": {},
|
||||
"notificationRuleRoomServerAcl": "ACL del server della stanza",
|
||||
"@notificationRuleRoomServerAcl": {},
|
||||
"notificationRuleRoomServerAclDescription": "Silenzia le notifiche per gli elenchi di controllo degli accessi del server della stanza (ACL).",
|
||||
"@notificationRuleRoomServerAclDescription": {},
|
||||
"notificationRuleSuppressEdits": "Silenzia le modifiche",
|
||||
"@notificationRuleSuppressEdits": {},
|
||||
"notificationRuleSuppressEditsDescription": "Silenzia le notifiche per i messaggi modificati.",
|
||||
"@notificationRuleSuppressEditsDescription": {},
|
||||
"notificationRuleCallDescription": "Notifica all'utente le chiamate.",
|
||||
"@notificationRuleCallDescription": {},
|
||||
"notificationRuleEncryptedRoomOneToOne": "Stanze crittografate One-to-One",
|
||||
"@notificationRuleEncryptedRoomOneToOne": {},
|
||||
"notificationRuleRoomOneToOne": "Stanze One-to-One",
|
||||
"@notificationRuleRoomOneToOne": {},
|
||||
"notificationRuleRoomOneToOneDescription": "Notifica all'utente i messaggi nelle stanze one-to-one.",
|
||||
"@notificationRuleRoomOneToOneDescription": {},
|
||||
"notificationRuleMessage": "Messaggi",
|
||||
"@notificationRuleMessage": {},
|
||||
"notificationRuleMessageDescription": "Notifica all'utente i messaggi generali.",
|
||||
"@notificationRuleMessageDescription": {},
|
||||
"notificationRuleEncryptedDescription": "Notifica all'utente i messaggi nelle stanze crittografate.",
|
||||
"@notificationRuleEncryptedDescription": {},
|
||||
"notificationRuleEncrypted": "Crittografate",
|
||||
"@notificationRuleEncrypted": {},
|
||||
"notificationRuleJitsi": "Jitsi",
|
||||
"@notificationRuleJitsi": {},
|
||||
"notificationRuleJitsiDescription": "Notifica all'utente gli eventi del widget Jitsi.",
|
||||
"@notificationRuleJitsiDescription": {},
|
||||
"notificationRuleServerAcl": "Silenziare gli eventi ACL del server",
|
||||
"@notificationRuleServerAcl": {},
|
||||
"notificationRuleServerAclDescription": "Silenzia le notifiche per gli eventi ACL del server.",
|
||||
"@notificationRuleServerAclDescription": {},
|
||||
"unknownPushRule": "Regola push '{rule}' sconosciuta",
|
||||
"@unknownPushRule": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"rule": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deletePushRuleCanNotBeUndone": "Se si elimina questa impostazione di notifica, questo non può essere annullato.",
|
||||
"@deletePushRuleCanNotBeUndone": {},
|
||||
"more": "Di più",
|
||||
"@more": {},
|
||||
"newChatRequest": "📩 Nuova richiesta di chat",
|
||||
"@newChatRequest": {},
|
||||
"shareKeysWith": "Condividi le chiavi con...",
|
||||
"@shareKeysWith": {},
|
||||
"shareKeysWithDescription": "Quali dispositivi dovrebbero essere fidati in modo che possano leggere i tuoi messaggi in chat crittografate?",
|
||||
"@shareKeysWithDescription": {},
|
||||
"allDevices": "Tutti i dispositivi",
|
||||
"@allDevices": {},
|
||||
"crossVerifiedDevicesIfEnabled": "Verifica incrociata dei dispositivi, se abilitata",
|
||||
"@crossVerifiedDevicesIfEnabled": {},
|
||||
"crossVerifiedDevices": "Dispositivi con verifica incrociata",
|
||||
"@crossVerifiedDevices": {},
|
||||
"verifiedDevicesOnly": "Solo dispositivi verificati",
|
||||
"@verifiedDevicesOnly": {},
|
||||
"appWantsToUseForLogin": "Usa '{server}' per accedere",
|
||||
"@appWantsToUseForLogin": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"server": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"open": "Apri",
|
||||
"@open": {},
|
||||
"appWantsToUseForLoginDescription": "Con la presente consenti all'app e al sito web di condividere informazioni su di te.",
|
||||
"@appWantsToUseForLoginDescription": {},
|
||||
"appIntroduction": "FluffyChat ti consente di chattare con i tuoi amici attraverso diverse app di messaggistica. Ulteriori informazioni su https://matrix.org o semplicemente tocca *Continua*.",
|
||||
"@appIntroduction": {},
|
||||
"waitingForServer": "In attesa del server...",
|
||||
"@waitingForServer": {},
|
||||
"synchronizingPleaseWaitCounter": " Sincronizzazione… ({percentage}%)",
|
||||
"@synchronizingPleaseWaitCounter": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"percentage": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notificationRuleMemberEventDescription": "Silenzia le notifiche per gli eventi dei membri.",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleContainsDisplayName": "Contiene nome visualizzato",
|
||||
"@notificationRuleContainsDisplayName": {},
|
||||
"notificationRuleIsRoomMentionDescription": "Notifica l'utente quando c'è una menzione della stanza.",
|
||||
"@notificationRuleIsRoomMentionDescription": {},
|
||||
"notificationRuleCall": "Chiamate",
|
||||
"@notificationRuleCall": {},
|
||||
"notificationRuleEncryptedRoomOneToOneDescription": "Notifica all'utente i messaggi in stanze crittografate one-to-one.",
|
||||
"@notificationRuleEncryptedRoomOneToOneDescription": {},
|
||||
"previous": "Precedente",
|
||||
"@previous": {},
|
||||
"otherPartyNotLoggedIn": "L'altra parte non è attualmente connessa e quindi non può ricevere messaggi!",
|
||||
"@otherPartyNotLoggedIn": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"groupIsPublic": "그룹 채팅 공개됨",
|
||||
"groupIsPublic": "그룹 채팅 공개",
|
||||
"@groupIsPublic": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -204,7 +204,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"enableEncryption": "암호화 켜기",
|
||||
"enableEncryption": "암호화 사용",
|
||||
"@enableEncryption": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1501,7 +1501,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"spaceIsPublic": "스페이스가 공개됨",
|
||||
"spaceIsPublic": "스페이스 공개",
|
||||
"@spaceIsPublic": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2395,7 +2395,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"fileIsTooBigForServer": "전송에 실패했습니다. 서버는 {max}치가 넘는 파일을 지원하지 않습니다.",
|
||||
"fileIsTooBigForServer": "전송에 실패했습니다. 서버는 {max}가 넘는 파일을 지원하지 않습니다.",
|
||||
"@fileIsTooBigForServer": {},
|
||||
"callingPermissions": "통화 권한",
|
||||
"@callingPermissions": {},
|
||||
|
|
@ -2628,7 +2628,7 @@
|
|||
"@hydrateTorLong": {},
|
||||
"custom": "커스텀",
|
||||
"@custom": {},
|
||||
"noBackupWarning": "경고! 채팅 백업을 켜지 않을경우, 당신은 암호화된 메시지에대한 접근권한을 잃을것입니다. 로그아웃 하기 전에 채팅을 백업하는것이 강력히 권장됩니다.",
|
||||
"noBackupWarning": "경고! 채팅 백업을 켜지 않을경우, 당신은 암호화된 메시지에 대한 접근권한을 잃을것 입니다. 로그아웃 하기 전에 채팅을 백업하는것이 강력히 권장됩니다.",
|
||||
"@noBackupWarning": {},
|
||||
"storeInSecureStorageDescription": "이 기기의 보안 스토리지에 복구키를 저장합니다.",
|
||||
"@storeInSecureStorageDescription": {},
|
||||
|
|
@ -2976,7 +2976,7 @@
|
|||
"@knockRestricted": {},
|
||||
"swipeRightToLeftToReply": "오른쪽에서 왼쪽으로 스와이프해서 답장",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"alwaysUse24HourFormat": "아니요",
|
||||
"alwaysUse24HourFormat": "false",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
|
|
@ -3046,13 +3046,13 @@
|
|||
},
|
||||
"noChatsFoundHere": "대화가 발견되지 않았습니다. 아래 버튼을 사용하여 새 대화를 시작해 보세요. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"changeTheVisibilityOfChatHistory": "대화 기록 표시 여부 바꾸기",
|
||||
"changeTheVisibilityOfChatHistory": "채팅 기록 표시 여부 바꾸기",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"changeTheCanonicalRoomAlias": "메인 공개 대화 주소 변경",
|
||||
"changeTheCanonicalRoomAlias": "메인 공개 채팅 주소 바꾸기",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendCanceled": "전송 최소됨",
|
||||
"@sendCanceled": {},
|
||||
"homeserverDescription": "모든 데이터는 이메일 제공자와 마찬가지로 Homeserver(이) 에 저장됩니다. 모든 사람과 여전히 소통할 수 있는 동안 사용하고 싶은 Homeserver(이) 를 선택할 수 있습니다. https://matrix.org에서 자세히 알아보세요.",
|
||||
"homeserverDescription": "당신의 모든 데이터는 이메일과 흡사하게 당신의 홈서버에 저장됩니다. 당신이 소통하고 싶은 사람들과 다른 서버를 사용해도 무관하니 당신이 원하는 홈서버를 선택해도 됩니다. https://matrix.org에서 자세히 알아보세요.",
|
||||
"@homeserverDescription": {},
|
||||
"sendingAttachmentCountOfCount": "첨부파일 {length}개중 {index}번째 전송 중...",
|
||||
"@sendingAttachmentCountOfCount": {
|
||||
|
|
@ -3075,27 +3075,27 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"noContactInformationProvided": "서버가 유효한 연락처 정보를 제공하지 않습니다",
|
||||
"noContactInformationProvided": "서버가 유효한 연락처 정보를 제공하지 않음",
|
||||
"@noContactInformationProvided": {},
|
||||
"welcomeText": "안녕하세요 👋 제 이름은 FluffyChat이에요. 당신은 Htpps://matrix.org에 호환되는 어떤 Homeserver에도 가입할 수 있어요. 그리고 아무나 대화하세요! 저는 큰 대화 네트워크랍니다! 😄",
|
||||
"welcomeText": "안녕하세요 👋 FluffyChat이에요. 당신은 htpps://matrix.org와 호환되는 모든 홈서버를 사용할 수 있어요. 그리고 모두와 대화해보세요. 거대한 분산 대화망이니까요!",
|
||||
"@welcomeText": {},
|
||||
"changeGeneralChatSettings": "일반 대화 설정 번경하기",
|
||||
"changeGeneralChatSettings": "일반 채팅 설정 번경하기",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"inviteOtherUsers": "다른 사용자를 이 대화에 초대하기",
|
||||
"inviteOtherUsers": "다른 사용자를 이 채팅에 초대하기",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "대화 권한 변경하기",
|
||||
"changeTheChatPermissions": "채팅 권한 바꾸기",
|
||||
"@changeTheChatPermissions": {},
|
||||
"calculatingFileSize": "파일 크기 계산 중...",
|
||||
"@calculatingFileSize": {},
|
||||
"prepareSendingAttachment": "첨부된 파일 전송 준비 중...",
|
||||
"@prepareSendingAttachment": {},
|
||||
"oneOfYourDevicesIsNotVerified": "당신의 기기 중 하나가 인증되지 않았습니다",
|
||||
"oneOfYourDevicesIsNotVerified": "당신의 기기 중 하나가 인증되지 않았음",
|
||||
"@oneOfYourDevicesIsNotVerified": {},
|
||||
"noticeChatBackupDeviceVerification": "참고: 모든 기기를 대화 백업에 연결하면 자동으로 인증됩니다.",
|
||||
"noticeChatBackupDeviceVerification": "참고: 모든 기기에 채팅 백업을 설정하면 자동으로 서로 인증됩니다.",
|
||||
"@noticeChatBackupDeviceVerification": {},
|
||||
"opacity": "불투명:",
|
||||
"@opacity": {},
|
||||
"setWallpaper": "배경화면 정하기",
|
||||
"setWallpaper": "배경화면 설정하기",
|
||||
"@setWallpaper": {},
|
||||
"manageAccount": "계정 관리하기",
|
||||
"@manageAccount": {},
|
||||
|
|
@ -3108,11 +3108,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"contactServerAdmin": "서버 관리자 연락하기",
|
||||
"contactServerAdmin": "서버 관리자에게 연락하기",
|
||||
"@contactServerAdmin": {},
|
||||
"contactServerSecurity": "서버 보안관 연락하기",
|
||||
"contactServerSecurity": "서버 보안 관리자에게 연락하기",
|
||||
"@contactServerSecurity": {},
|
||||
"supportPage": "페이지 돕기",
|
||||
"supportPage": "지원 페이지",
|
||||
"@supportPage": {},
|
||||
"name": "이름",
|
||||
"@name": {},
|
||||
|
|
@ -3122,23 +3122,23 @@
|
|||
"@version": {},
|
||||
"website": "웹사이트",
|
||||
"@website": {},
|
||||
"changeTheDescriptionOfTheGroup": "대화의 설명 바꾸기",
|
||||
"changeTheDescriptionOfTheGroup": "채팅 설명 바꾸기",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"sendRoomNotifications": "@room 알림 보내기",
|
||||
"@sendRoomNotifications": {},
|
||||
"chatPermissionsDescription": "이 대화에서 특정 작업에 필요한 파워 레벨을 정의합니다. 파워 레벨 0, 50, 100은 일반적으로 사용자, 관리자, 관리자를 나타내지만, 모든 등급이 가능합니다.",
|
||||
"chatPermissionsDescription": "이 채팅에서 특정 작업에 요구할 권한 레벨을 정의합니다. 권한 레벨 0, 50, 100은 일반적으로 유저, 관리자, 운영자를 나타내지만, 모든 숫자가 가능합니다.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"loginWithMatrixId": "Matrix-ID로 로그인",
|
||||
"@loginWithMatrixId": {},
|
||||
"discoverHomeservers": "Homeserver 찾아보기",
|
||||
"discoverHomeservers": "홈서버 찾아보기",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "Homeserver(이) 가 무엇인가요?",
|
||||
"whatIsAHomeserver": "홈서버가 무엇인가요?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "호환되는 Homeserver(이) 가 아닌 것 같습니다. URL이 올바르게 입력됐나요?",
|
||||
"doesNotSeemToBeAValidHomeserver": "호환되는 홈서버가 아닌 것 같습니다. URL을 올바르게 입력됐나요?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {},
|
||||
"continueText": "계속하기",
|
||||
"@continueText": {},
|
||||
"updateInstalled": "🎉 새 {version} 가 설치되었습니다!",
|
||||
"updateInstalled": "🎉 {version} 업데이트가 설치되었습니다!",
|
||||
"@updateInstalled": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -3213,5 +3213,67 @@
|
|||
}
|
||||
},
|
||||
"waitingForServer": "서버를 기다리는중...",
|
||||
"@waitingForServer": {}
|
||||
"@waitingForServer": {},
|
||||
"contentNotificationSettings": "콘텐츠 알림 설정",
|
||||
"@contentNotificationSettings": {},
|
||||
"generalNotificationSettings": "일반 알림 설정",
|
||||
"@generalNotificationSettings": {},
|
||||
"roomNotificationSettings": "채팅방 알림 설정",
|
||||
"@roomNotificationSettings": {},
|
||||
"otherNotificationSettings": "기타 알림 설정",
|
||||
"@otherNotificationSettings": {},
|
||||
"notificationRuleContainsUserName": "유저 이름을 포함함",
|
||||
"@notificationRuleContainsUserName": {},
|
||||
"notificationRuleMaster": "모든 알림 음소거",
|
||||
"@notificationRuleMaster": {},
|
||||
"notificationRuleContainsUserNameDescription": "메시지가 유저의 이름을 포함할때 알림합니다.",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMasterDescription": "모든 규칙을 무시하고 모든 알림을 비활성화합니다.",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
"notificationRuleSuppressNotices": "자동화된 메시지 무시",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"notificationRuleSuppressNoticesDescription": "봇을 비롯한 자동화된 메시지로부터 발생하는 알림을 무시합니다.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleInviteForMe": "초대를 받음",
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleInviteForMeDescription": "채팅방에 초대받았을 때 알림합니다.",
|
||||
"@notificationRuleInviteForMeDescription": {},
|
||||
"notificationRuleMemberEvent": "멤버 이벤트",
|
||||
"@notificationRuleMemberEvent": {},
|
||||
"notificationRuleMemberEventDescription": "멤버 이벤트로 발생하는 알림을 무시합니다.",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleIsUserMentionDescription": "유저가 메시지에 멘션됐을 때 알림합니다.",
|
||||
"@notificationRuleIsUserMentionDescription": {},
|
||||
"notificationRuleContainsDisplayName": "표시 이름을 포함함",
|
||||
"@notificationRuleContainsDisplayName": {},
|
||||
"notificationRuleIsUserMention": "유저가 멘션됨",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"notificationRuleContainsDisplayNameDescription": "메시지에 표시 이름이 포함되면 알림합니다.",
|
||||
"@notificationRuleContainsDisplayNameDescription": {},
|
||||
"notificationRuleIsRoomMention": "방 멘션",
|
||||
"@notificationRuleIsRoomMention": {},
|
||||
"notificationRuleIsRoomMentionDescription": "방 멘션이 있을경우 알림합니다.",
|
||||
"@notificationRuleIsRoomMentionDescription": {},
|
||||
"notificationRuleRoomnotif": "방 알림",
|
||||
"@notificationRuleRoomnotif": {},
|
||||
"notificationRuleRoomnotifDescription": "메시지가 '@room'을 포함하면 알림합니다.",
|
||||
"@notificationRuleRoomnotifDescription": {},
|
||||
"notificationRuleTombstone": "비활성화",
|
||||
"@notificationRuleTombstone": {},
|
||||
"notificationRuleTombstoneDescription": "채팅방 비활성화 메시지를 알림합니다.",
|
||||
"@notificationRuleTombstoneDescription": {},
|
||||
"notificationRuleReaction": "반응",
|
||||
"@notificationRuleReaction": {},
|
||||
"notificationRuleReactionDescription": "반응으로 발생하는 알림을 무시합니다.",
|
||||
"@notificationRuleReactionDescription": {},
|
||||
"notificationRuleRoomServerAcl": "채팅방 서버 ACL",
|
||||
"@notificationRuleRoomServerAcl": {},
|
||||
"notificationRuleRoomServerAclDescription": "채팅방 서버의 접근 권한(ACL)으로부터 오는 알림을 무시합니다.",
|
||||
"@notificationRuleRoomServerAclDescription": {},
|
||||
"notificationRuleSuppressEdits": "수정 음소거",
|
||||
"@notificationRuleSuppressEdits": {},
|
||||
"notificationRuleSuppressEditsDescription": "수정된 메시지로부터 오는 알림을 무시합니다.",
|
||||
"@notificationRuleSuppressEditsDescription": {},
|
||||
"notificationRuleCall": "전화",
|
||||
"@notificationRuleCall": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"passphraseOrKey": "paroles vārdkopa vai atkopšanas atslēga",
|
||||
"passphraseOrKey": "paroles vārdkopa vai atkopes atslēga",
|
||||
"@passphraseOrKey": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -205,7 +205,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"verifySuccess": "Apliecināšana veiksmīga.",
|
||||
"verifySuccess": "Apliecināšana bija sekmīga.",
|
||||
"@verifySuccess": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -385,7 +385,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"askSSSSSign": "Lai varētu parakstīt otru cilvēku, lūgums ievadīt savu drošo krātuves paroles vārdkopu vai atkopšanas atslēgu.",
|
||||
"askSSSSSign": "Lai varētu parakstīt otru cilvēku, lūgums ievadīt savu drošo krātuves paroles vārdkopu vai atkopes atslēgu.",
|
||||
"@askSSSSSign": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -624,7 +624,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"incorrectPassphraseOrKey": "Nepareiza paroles vārdkopa vai atkopšanas atslēga",
|
||||
"incorrectPassphraseOrKey": "Nepareiza paroles vārdkopa vai atkopes atslēga",
|
||||
"@incorrectPassphraseOrKey": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -636,7 +636,7 @@
|
|||
},
|
||||
"reopenChat": "Atkārtoti atvērt tērzēšanu",
|
||||
"@reopenChat": {},
|
||||
"pleaseEnterRecoveryKey": "Lūgums ievadīt savu atkopšanas atslēgu:",
|
||||
"pleaseEnterRecoveryKey": "Lūgums ievadīt savu atkopes atslēgu:",
|
||||
"@pleaseEnterRecoveryKey": {},
|
||||
"create": "Izveidot",
|
||||
"@create": {
|
||||
|
|
@ -707,7 +707,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"noKeyForThisMessage": "Tā var notikt, ja ziņa tika nosūtīta, pirms pieteicies savā kontā šajā ierīcē.\n\nIr arī iespējams, ka sūtītājs noliedza Tavu ierīci vai kaut kas nogāja greizi ar interneta savienojumu.\n\nVai ziņas ir lasāmas citā sesijā? Tad Tu vari pārsūtīt ziņo no tās. Jādodas uz Iestatījumi > Ierīces un jāpārliecinās, ka ierīces viena otru ir apliecinājušas. Kad nākamreiz atvērsi istabu un abas sesijas būs priekšplānā, atslēgas tiks automātiski pārsūtītas.\n\nVai nevēlies zaudēt atslēgas, kad atsakies vai maini ierīces? Jāpārliecinās, ka iestatījumos ir iespējota tērzēšanu rezerves kopija.",
|
||||
"noKeyForThisMessage": "Tā var notikt, ja ziņa tika nosūtīta, pirms pieteicies savā kontā šajā ierīcē.\n\nIr arī iespējams, ka sūtītājs noliedza Tavu ierīci vai kaut kas nogāja greizi ar interneta savienojumu.\n\nVai ziņas ir lasāmas citā sesijā? Tad Tu vari pārsūtīt ziņu no tās. Jādodas uz Iestatījumi > Ierīces un jāpārliecinās, ka ierīces viena otru ir apliecinājušas. Kad nākamreiz atvērsi istabu un abas sesijas būs priekšplānā, atslēgas tiks automātiski pārsūtītas.\n\nVai nevēlies zaudēt atslēgas, kad atsakies vai maini ierīces? Jāpārliecinās, ka iestatījumos ir iespējota tērzēšanu rezerves kopija.",
|
||||
"@noKeyForThisMessage": {},
|
||||
"enableEncryptionWarning": "Vairs nebūs iespējams atspējot šifrēšanu. Vai tiešām to darīt?",
|
||||
"@enableEncryptionWarning": {
|
||||
|
|
@ -751,7 +751,7 @@
|
|||
"@hydrateTor": {},
|
||||
"pushNotificationsNotAvailable": "Pašpiegādes paziņojumi nav pieejami",
|
||||
"@pushNotificationsNotAvailable": {},
|
||||
"passwordRecovery": "Paroles atjaunošana",
|
||||
"passwordRecovery": "Paroles atkope",
|
||||
"@passwordRecovery": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -786,7 +786,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"wipeChatBackup": "Notīrīt tērzēšanu rezerves kopiju, lai izveidotu jaunu atkopšanas atslēgu?",
|
||||
"wipeChatBackup": "Notīrīt tērzēšanu rezerves kopiju, lai izveidotu jaunu atkopes atslēgu?",
|
||||
"@wipeChatBackup": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -816,7 +816,7 @@
|
|||
},
|
||||
"signInWithPassword": "Pieteikties ar paroli",
|
||||
"@signInWithPassword": {},
|
||||
"lastActiveAgo": "Pēdējoreiz redzēts: {localizedTimeShort}",
|
||||
"lastActiveAgo": "Pēdējoreiz tiešsaistē: {localizedTimeShort}",
|
||||
"@lastActiveAgo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -864,7 +864,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"noEmotesFound": "Netika atrastas emocijas. 😕",
|
||||
"noEmotesFound": "Netika atrasta neviena emocija. 😕",
|
||||
"@noEmotesFound": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1429,7 +1429,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"pleaseEnterRecoveryKeyDescription": "Lai atslēgtu savas vecās ziņas, lūgums ievadīt savu atkopšanas atslēgu, kas tika izveidota iepriekšējā sesijā. Atkopšanas atslēga NAV parole.",
|
||||
"pleaseEnterRecoveryKeyDescription": "Lai atslēgtu savas vecās ziņas, lūgums ievadīt savu atkopes atslēgu, kas tika izveidota iepriekšējā sesijā. Atkopes atslēga NAV parole.",
|
||||
"@pleaseEnterRecoveryKeyDescription": {},
|
||||
"guestsAreForbidden": "Viesi nav ļauti",
|
||||
"@guestsAreForbidden": {
|
||||
|
|
@ -1656,7 +1656,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"recoveryKey": "Atkopšanas atslēga",
|
||||
"recoveryKey": "Atkopes atslēga",
|
||||
"@recoveryKey": {},
|
||||
"redactMessage": "Labot ziņu",
|
||||
"@redactMessage": {
|
||||
|
|
@ -1680,7 +1680,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"chooseAStrongPassword": "Jāizvēlas spēcīga parole",
|
||||
"chooseAStrongPassword": "Jāizvēlas droša parole",
|
||||
"@chooseAStrongPassword": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2007,7 +2007,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"noChatDescriptionYet": "Vēl nav izveidots tērzēšanas apraksts.",
|
||||
"noChatDescriptionYet": "Tērzēšanas apraksts vēl nav izveidots.",
|
||||
"@noChatDescriptionYet": {},
|
||||
"defaultPermissionLevel": "Noklusējuma atļauju līmenis jauniem lietotājiem",
|
||||
"@defaultPermissionLevel": {
|
||||
|
|
@ -2082,7 +2082,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"commandHint_op": "Iestatīt norādītā lietotāja spēka līmeni (noklusējums: 50)",
|
||||
"commandHint_op": "Iestatīt norādītā lietotāja pilnvaru līmeni (noklusējums: 50)",
|
||||
"@commandHint_op": {
|
||||
"type": "String",
|
||||
"description": "Usage hint for the command /op"
|
||||
|
|
@ -2214,7 +2214,7 @@
|
|||
"@shareInviteLink": {},
|
||||
"commandHint_markasdm": "Atzīmēt kā tiešo ziņu istabu norādītajam Matrix Id",
|
||||
"@commandHint_markasdm": {},
|
||||
"recoveryKeyLost": "Pazaudēta atkopšanas atslēga?",
|
||||
"recoveryKeyLost": "Pazaudēta atkopes atslēga?",
|
||||
"@recoveryKeyLost": {},
|
||||
"cuddleContent": "{senderName} samīļo Tevi",
|
||||
"@cuddleContent": {
|
||||
|
|
@ -2348,7 +2348,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"chatBackupDescription": "Iepriekšējās ziņas ir aizsargātas ar atkopšanas atslēgu. Lūgums nodrošināt, ka tā netiek pazaudēta.",
|
||||
"chatBackupDescription": "Iepriekšējās ziņas ir aizsargātas ar atkopes atslēgu. Lūgums nodrošināt, ka tā netiek pazaudēta.",
|
||||
"@chatBackupDescription": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2412,7 +2412,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"editBlockedServers": "Labot liegtos serveros",
|
||||
"editBlockedServers": "Labot liegtos serverus",
|
||||
"@editBlockedServers": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2561,7 +2561,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"storeInSecureStorageDescription": "Glabāt atkopšanas atslēgu šīs ierīces drošajā krātuvē.",
|
||||
"storeInSecureStorageDescription": "Glabāt atkopes atslēgu šīs ierīces drošajā krātuvē.",
|
||||
"@storeInSecureStorageDescription": {},
|
||||
"openChat": "Atvērt tērzēšanu",
|
||||
"@openChat": {},
|
||||
|
|
@ -2623,7 +2623,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"appLockDescription": "Aizslēgt lietotni ar PIN kodu, kad tā netiek izmantota",
|
||||
"appLockDescription": "Aizslēgt lietotni, kad tā netiek izmantota, ar PIN kodu",
|
||||
"@appLockDescription": {},
|
||||
"globalChatId": "Vispārējais tērzēšanas Id",
|
||||
"@globalChatId": {},
|
||||
|
|
@ -2653,13 +2653,13 @@
|
|||
"@overview": {},
|
||||
"notifyMeFor": "Paziņot man par",
|
||||
"@notifyMeFor": {},
|
||||
"wrongRecoveryKey": "Atvaino... Nešķiet, ka šī būtu pareiza atkopšanas atslēga.",
|
||||
"wrongRecoveryKey": "Atvaino... Nešķiet, ka šī būtu pareiza atkopes atslēga.",
|
||||
"@wrongRecoveryKey": {},
|
||||
"block": "Izslēgt",
|
||||
"@block": {},
|
||||
"hideMemberChangesInPublicChats": "Paslēpt dalībnieku izmaiņas publiskajās tērzēšanās",
|
||||
"@hideMemberChangesInPublicChats": {},
|
||||
"passwordRecoverySettings": "Paroles atjaunošanas iestatījumi",
|
||||
"passwordRecoverySettings": "Paroles atkopes iestatījumi",
|
||||
"@passwordRecoverySettings": {},
|
||||
"blockedUsers": "Atslēgtie lietotāji",
|
||||
"@blockedUsers": {},
|
||||
|
|
@ -2932,9 +2932,9 @@
|
|||
"type": "String",
|
||||
"count": {}
|
||||
},
|
||||
"verifyOtherDeviceDescription": "Kad apliecini citu ierīci, šīs ierīces var apmainīt atslēgas, palielinot vispārējo drošību. 💪 Kad uzsāc apliecināšanu, abās ierīcēs lietotnē parādīsies uznirstošais logs. Tajā būs redzamas dažādas emocijzīmes vai skaitļi, kas jāsalīdzina abās ierīcēs. Vislabāk, ja abas ierīces ir pieejams, pirms tiek uzsākta apliecināšana. 🤳",
|
||||
"verifyOtherDeviceDescription": "Kad apliecini citu ierīci, šīs ierīces var apmainīt atslēgas, palielinot vispārējo drošību. 💪 Pēc apliecināšanas uzsākšanas abās ierīcēs lietotnē parādīsies uznirstošais logs. Tajā būs redzamas dažādas emocijzīmes vai skaitļi, kas jāsalīdzina abās ierīcēs. Vislabāk, ja abas ierīces ir pieejamas, pirms tiek uzsākta apliecināšana. 🤳",
|
||||
"@verifyOtherDeviceDescription": {},
|
||||
"swipeRightToLeftToReply": "Pavilkt pa labi, lai atbildētu",
|
||||
"swipeRightToLeftToReply": "Pavilkt no labās puses uz kreiso, lai atbildētu",
|
||||
"@swipeRightToLeftToReply": {},
|
||||
"searchIn": "Meklēt tērzēšanā \"{chat}\"...",
|
||||
"@searchIn": {
|
||||
|
|
@ -3052,7 +3052,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "Vairs nav tērzēšanu...",
|
||||
"noMoreChatsFound": "Vairs netika atrasta neviena tērzēšana...",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Tērzēšanas, kurās piedalos",
|
||||
"@joinedChats": {},
|
||||
|
|
@ -3311,5 +3311,17 @@
|
|||
"crossVerifiedDevicesIfEnabled": "Savstarpēji apliecinātas ierīces, ja iespējots",
|
||||
"@crossVerifiedDevicesIfEnabled": {},
|
||||
"verifiedDevicesOnly": "Tikai apliecinātas ierīces",
|
||||
"@verifiedDevicesOnly": {}
|
||||
"@verifiedDevicesOnly": {},
|
||||
"optionalMessage": "(Pēc izvēles) Ziņojums...",
|
||||
"@optionalMessage": {},
|
||||
"takeAPhoto": "Uzņemt attēlu",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Ierakstīt video",
|
||||
"@recordAVideo": {},
|
||||
"notSupportedOnThisDevice": "Šajā ierīcē nav atbalstīts",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"enterNewChat": "Ieiet jaunajā tērzēšanā",
|
||||
"@enterNewChat": {},
|
||||
"commandHint_roomupgrade": "Uzlabot šo istabu uz norādīto istabas versiju",
|
||||
"@commandHint_roomupgrade": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"anyoneCanJoin": "Iedereen kan deelnemen",
|
||||
"anyoneCanJoin": "Iedereen kan toetreden",
|
||||
"@anyoneCanJoin": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -294,7 +294,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheJoinRules": "{username} heeft de deelnameregels gewijzigd",
|
||||
"changedTheJoinRules": "{username} heeft de toetredingsregels gewijzigd",
|
||||
"@changedTheJoinRules": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -303,7 +303,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheJoinRulesTo": "{username} heeft de deelnameregels gewijzigd in: {joinRules}",
|
||||
"changedTheJoinRulesTo": "{username} heeft de toetredingsregels gewijzigd in: {joinRules}",
|
||||
"@changedTheJoinRulesTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -352,7 +352,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"changeTheme": "Stijl veranderen",
|
||||
"changeTheme": "Je stijl veranderen",
|
||||
"@changeTheme": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -426,7 +426,7 @@
|
|||
"type": "String",
|
||||
"description": "Usage hint for the command /invite"
|
||||
},
|
||||
"commandHint_join": "Deelnemen aan de kamer",
|
||||
"commandHint_join": "Toetreden tot de vermelde kamer",
|
||||
"@commandHint_join": {
|
||||
"type": "String",
|
||||
"description": "Usage hint for the command /join"
|
||||
|
|
@ -530,7 +530,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"containsUserName": "Bevat gebruikersnaam",
|
||||
"containsUserName": "Bevat inlognaam",
|
||||
"@containsUserName": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -587,7 +587,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"createNewSpace": "Nieuwe space",
|
||||
"createNewSpace": "Maak nieuwe space aan",
|
||||
"@createNewSpace": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -844,7 +844,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"fromJoining": "Vanaf deelname",
|
||||
"fromJoining": "Vanaf toetreden",
|
||||
"@fromJoining": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1027,7 +1027,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"joinRoom": "Deelnemen",
|
||||
"joinRoom": "Toetreden tot de kamer",
|
||||
"@joinRoom": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1208,7 +1208,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"noGoogleServicesWarning": "Firebase Cloud Messaging lijkt niet beschikbaar op je apparaat. Om nog steeds meldingen te krijgen, adviseren we om ntfy te installeren. Met ntfy of een andere Unified Push provider kun je meldingen ontvangen op een veilige manier. Je kunt ntfy downloaden van de PlayStore of van F-Droid.",
|
||||
"noGoogleServicesWarning": "Firebase Cloud Messaging lijkt niet beschikbaar op je apparaat. Om nog steeds pushmeldingen te krijgen, adviseren we om ntfy te installeren. Met ntfy of een andere Unified Push provider kun je pushmeldingen ontvangen op een veilige manier. Je kunt ntfy downloaden van de PlayStore of van F-Droid.",
|
||||
"@noGoogleServicesWarning": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1245,7 +1245,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"notifications": "Notificaties",
|
||||
"notifications": "Meldingen",
|
||||
"@notifications": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1423,7 +1423,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"publicRooms": "Publieke Kamers",
|
||||
"publicRooms": "Openbare kamers",
|
||||
"@publicRooms": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -1938,7 +1938,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"username": "Gebruikersnaam",
|
||||
"username": "Inlognaam",
|
||||
"@username": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2010,7 +2010,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"waitingPartnerEmoji": "Wachten tot partner de emoji accepteert …",
|
||||
"waitingPartnerEmoji": "Wachten tot je partner de emoji accepteert…",
|
||||
"@waitingPartnerEmoji": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2055,7 +2055,7 @@
|
|||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"withTheseAddressesRecoveryDescription": "Met deze adressen kan je je wachtwoord herstellen.",
|
||||
"withTheseAddressesRecoveryDescription": "Met deze adressen kun je je wachtwoord herstellen.",
|
||||
"@withTheseAddressesRecoveryDescription": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2243,7 +2243,7 @@
|
|||
"@widgetVideo": {},
|
||||
"widgetEtherpad": "Tekstnotitie",
|
||||
"@widgetEtherpad": {},
|
||||
"separateChatTypes": "Gescheiden directe chats en groepen",
|
||||
"separateChatTypes": "Directe chats en groepen los weergeven",
|
||||
"@separateChatTypes": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2405,7 +2405,7 @@
|
|||
"@otherCallingPermissions": {},
|
||||
"newGroup": "Nieuwe groep",
|
||||
"@newGroup": {},
|
||||
"newSpace": "Nieuwe space",
|
||||
"newSpace": "Space aanmaken",
|
||||
"@newSpace": {},
|
||||
"enterRoom": "Kamer betreden",
|
||||
"@enterRoom": {},
|
||||
|
|
@ -2431,7 +2431,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"commandHint_googly": "Wat wiebelogen versturen",
|
||||
"commandHint_googly": "Wiebel-ogen versturen",
|
||||
"@commandHint_googly": {},
|
||||
"commandHint_cuddle": "Een knuffel versturen",
|
||||
"@commandHint_cuddle": {},
|
||||
|
|
@ -2527,7 +2527,7 @@
|
|||
"@exportEmotePack": {},
|
||||
"replace": "Vervang",
|
||||
"@replace": {},
|
||||
"report": "rapporteer",
|
||||
"report": "Rapporteer",
|
||||
"@report": {},
|
||||
"reportErrorDescription": "😭 Oh nee. Er is iets misgegaan. Probeer het later nog eens. Als je wilt, kun je de bug rapporteren aan de ontwikkelaars.",
|
||||
"@reportErrorDescription": {},
|
||||
|
|
@ -2539,11 +2539,11 @@
|
|||
"@signInWithPassword": {},
|
||||
"chatPermissions": "Chat toestemmingen",
|
||||
"@chatPermissions": {},
|
||||
"chatDescription": "Chatbeschrijving",
|
||||
"chatDescription": "Chatomschrijving",
|
||||
"@chatDescription": {},
|
||||
"chatDescriptionHasBeenChanged": "Chatbeschrijving gewijzigd",
|
||||
"chatDescriptionHasBeenChanged": "Chatomschrijving gewijzigd",
|
||||
"@chatDescriptionHasBeenChanged": {},
|
||||
"noChatDescriptionYet": "Nog geen chatbeschrijving gemaakt.",
|
||||
"noChatDescriptionYet": "Nog geen chatomschrijving gemaakt.",
|
||||
"@noChatDescriptionYet": {},
|
||||
"tryAgain": "Opnieuw proberen",
|
||||
"@tryAgain": {},
|
||||
|
|
@ -2569,7 +2569,7 @@
|
|||
"@inviteContactToGroupQuestion": {},
|
||||
"optionalRedactReason": "(Optioneel) Reden voor aanpassing van dit bericht...",
|
||||
"@optionalRedactReason": {},
|
||||
"addChatDescription": "Voeg een chatbeschrijving toe...",
|
||||
"addChatDescription": "Voeg een chatomschrijving toe...",
|
||||
"@addChatDescription": {},
|
||||
"invalidServerName": "Foute servernaam",
|
||||
"@invalidServerName": {},
|
||||
|
|
@ -2588,7 +2588,7 @@
|
|||
},
|
||||
"directChat": "Directe chat",
|
||||
"@directChat": {},
|
||||
"setChatDescription": "Chatbeschrijving instellen",
|
||||
"setChatDescription": "Chatomschrijving instellen",
|
||||
"@setChatDescription": {},
|
||||
"setTheme": "Thema instellen:",
|
||||
"@setTheme": {},
|
||||
|
|
@ -2600,7 +2600,7 @@
|
|||
"@inviteGroupChat": {},
|
||||
"invitePrivateChat": "📨 Privé-chat uitnodiging",
|
||||
"@invitePrivateChat": {},
|
||||
"emoteKeyboardNoRecents": "Recent-gebruikte emoticons zullen hier verschijnen...",
|
||||
"emoteKeyboardNoRecents": "Recent gebruikte emoticons zullen hier verschijnen...",
|
||||
"@emoteKeyboardNoRecents": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
|
|
@ -2622,7 +2622,7 @@
|
|||
"@removeDevicesDescription": {},
|
||||
"unbanUserDescription": "De persoon zal weer in staat zijn om de chat te betreden als ze het proberen.",
|
||||
"@unbanUserDescription": {},
|
||||
"pushNotificationsNotAvailable": "Meldingen zijn niet beschikbaar",
|
||||
"pushNotificationsNotAvailable": "Pushmeldingen zijn niet beschikbaar",
|
||||
"@pushNotificationsNotAvailable": {},
|
||||
"makeAdminDescription": "Wanneer je deze persoon beheerder maakt kun je dit niet ongedaan maken als jullie dezelfde rechten hebben.",
|
||||
"@makeAdminDescription": {},
|
||||
|
|
@ -2642,13 +2642,13 @@
|
|||
"@roomUpgradeDescription": {},
|
||||
"pleaseEnterANumber": "Vul een getal in groter dan 0",
|
||||
"@pleaseEnterANumber": {},
|
||||
"kickUserDescription": "De persoon is verwijderd uit de chat, maar is niet verbannen. In publieke chats kan de persoon op elk moment opnieuw deelnemen.",
|
||||
"kickUserDescription": "De persoon is verwijderd uit de chat, maar is niet verbannen. In openbare chats kan de persoon op elk moment opnieuw deelnemen.",
|
||||
"@kickUserDescription": {},
|
||||
"alwaysUse24HourFormat": "true",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"joinSpace": "Deelname aan space",
|
||||
"joinSpace": "Toetreden tot de space",
|
||||
"@joinSpace": {},
|
||||
"block": "Blokkeren",
|
||||
"@block": {},
|
||||
|
|
@ -2665,11 +2665,11 @@
|
|||
"@swipeRightToLeftToReply": {},
|
||||
"calls": "Gesprekken",
|
||||
"@calls": {},
|
||||
"customEmojisAndStickers": "Aangepaste emojis and stickers",
|
||||
"customEmojisAndStickers": "Aangepaste emoticons en stickers",
|
||||
"@customEmojisAndStickers": {},
|
||||
"accessAndVisibilityDescription": "Wie mag meedoen in deze chat en hoe de chat ontdekt kan worden.",
|
||||
"accessAndVisibilityDescription": "Wie mag toetreden tot deze chat en hoe de chat ontdekt kan worden.",
|
||||
"@accessAndVisibilityDescription": {},
|
||||
"customEmojisAndStickersBody": "Voeg toe of deel aangepaste emojis of stickers die gebruikt kunnen worden in elke chat.",
|
||||
"customEmojisAndStickersBody": "Voeg toe of deel aangepaste emoji's of stickers die gebruikt kunnen worden in elke chat.",
|
||||
"@customEmojisAndStickersBody": {},
|
||||
"hideRedactedMessages": "Verberg verwijderde berichten",
|
||||
"@hideRedactedMessages": {},
|
||||
|
|
@ -2677,7 +2677,7 @@
|
|||
"@hideRedactedMessagesBody": {},
|
||||
"hideInvalidOrUnknownMessageFormats": "Verberg ongeldige of onbekende berichtformaten",
|
||||
"@hideInvalidOrUnknownMessageFormats": {},
|
||||
"passwordRecoverySettings": "Wachtwoord herstel instellingen",
|
||||
"passwordRecoverySettings": "Wachtwoordherstel-instellingen",
|
||||
"@passwordRecoverySettings": {},
|
||||
"youInvitedToBy": "📩 Je bent uitgenodigd via een link voor:\n{alias}",
|
||||
"@youInvitedToBy": {
|
||||
|
|
@ -2687,15 +2687,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"knock": "Kloppen",
|
||||
"knock": "Klop",
|
||||
"@knock": {},
|
||||
"overview": "Overzicht",
|
||||
"@overview": {},
|
||||
"hidePresences": "Verberg Status Lijst?",
|
||||
"hidePresences": "Verberg statuslijst?",
|
||||
"@hidePresences": {},
|
||||
"noOneCanJoin": "Niemand kan deelnemen",
|
||||
"@noOneCanJoin": {},
|
||||
"yourGlobalUserIdIs": "Je globale gebruikers-ID is: ",
|
||||
"yourGlobalUserIdIs": "Je Matrix ID is: ",
|
||||
"@yourGlobalUserIdIs": {},
|
||||
"appLockDescription": "Vergendel de app wanneer het niet gebruikt wordt met een pincode",
|
||||
"@appLockDescription": {},
|
||||
|
|
@ -2711,11 +2711,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"publicSpaces": "Publieke spaces",
|
||||
"publicSpaces": "Openbare spaces",
|
||||
"@publicSpaces": {},
|
||||
"blockUsername": "Negeer gebruikersnaam",
|
||||
"blockUsername": "Negeer inlognaam",
|
||||
"@blockUsername": {},
|
||||
"publicChatAddresses": "Publieke chat adressen",
|
||||
"publicChatAddresses": "Openbare chat adressen",
|
||||
"@publicChatAddresses": {},
|
||||
"createNewAddress": "Creëer nieuw adres",
|
||||
"@createNewAddress": {},
|
||||
|
|
@ -2733,7 +2733,7 @@
|
|||
},
|
||||
"noMoreChatsFound": "Geen chats gevonden...",
|
||||
"@noMoreChatsFound": {},
|
||||
"joinedChats": "Deelnemende chats",
|
||||
"joinedChats": "Chats waaraan je deelneemt",
|
||||
"@joinedChats": {},
|
||||
"knocking": "Kloppen",
|
||||
"@knocking": {},
|
||||
|
|
@ -2741,7 +2741,7 @@
|
|||
"@space": {},
|
||||
"spaces": "Spaces",
|
||||
"@spaces": {},
|
||||
"unread": "Zet als ongelezen",
|
||||
"unread": "Ongelezen",
|
||||
"@unread": {},
|
||||
"databaseBuildErrorBody": "Het aanmaken van de SQlite database is mislukt. De app probeert nu een traditionele database te gebruiken. Meldt alsjeblieft deze fout aan de ontwikkelaars via deze {url}. De foutmelding is: {error}",
|
||||
"@databaseBuildErrorBody": {
|
||||
|
|
@ -2757,7 +2757,7 @@
|
|||
},
|
||||
"groupName": "Groepsnaam",
|
||||
"@groupName": {},
|
||||
"changeGeneralChatSettings": "Wijzig algemene chat instellingen",
|
||||
"changeGeneralChatSettings": "Algemene chat instellingen wijzigen",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"restricted": "Beperkt",
|
||||
"@restricted": {},
|
||||
|
|
@ -2765,7 +2765,7 @@
|
|||
"@searchForUsers": {},
|
||||
"searchMore": "Zoek meer...",
|
||||
"@searchMore": {},
|
||||
"noPublicLinkHasBeenCreatedYet": "Publieke link is nog niet gecreëerd",
|
||||
"noPublicLinkHasBeenCreatedYet": "Openbare link is nog niet gecreëerd",
|
||||
"@noPublicLinkHasBeenCreatedYet": {},
|
||||
"groupCanBeFoundViaSearch": "Groep kan gevonden worden via zoeken",
|
||||
"@groupCanBeFoundViaSearch": {},
|
||||
|
|
@ -2794,12 +2794,12 @@
|
|||
},
|
||||
"noDatabaseEncryption": "Database versleuteling is niet ondersteund op dit platform",
|
||||
"@noDatabaseEncryption": {},
|
||||
"thereAreCountUsersBlocked": "Nu zijn er {count} personen geblokkeerd.",
|
||||
"thereAreCountUsersBlocked": "Momenteel zijn er {count} personen geblokkeerd.",
|
||||
"@thereAreCountUsersBlocked": {
|
||||
"type": "String",
|
||||
"count": {}
|
||||
},
|
||||
"markAsUnread": "Markeer als ongelezen",
|
||||
"markAsUnread": "Als ongelezen markeren",
|
||||
"@markAsUnread": {},
|
||||
"userLevel": "{level} - Persoon",
|
||||
"@userLevel": {
|
||||
|
|
@ -2819,7 +2819,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"adminLevel": "{level} - Administrator",
|
||||
"adminLevel": "{level} - Beheerder",
|
||||
"@adminLevel": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -2832,7 +2832,7 @@
|
|||
"@stickers": {},
|
||||
"nothingFound": "Niets gevonden...",
|
||||
"@nothingFound": {},
|
||||
"gallery": "Gallerij",
|
||||
"gallery": "Galerij",
|
||||
"@gallery": {},
|
||||
"transparent": "Transparant",
|
||||
"@transparent": {},
|
||||
|
|
@ -2840,11 +2840,11 @@
|
|||
"@incomingMessages": {},
|
||||
"discover": "Ontdek",
|
||||
"@discover": {},
|
||||
"commandHint_ignore": "Negeer de gegeven matrix ID",
|
||||
"commandHint_ignore": "Negeer de gegeven Matrix ID",
|
||||
"@commandHint_ignore": {},
|
||||
"noChatsFoundHere": "Hier zijn nog geen chats. Begin een nieuwe chat met iemand door op de onderstaande knop te klikken. ⤵️",
|
||||
"@noChatsFoundHere": {},
|
||||
"unableToJoinChat": "Kan niet deelnemen aan chat. Misschien heeft de andere partij het gesprek al afgesloten.",
|
||||
"unableToJoinChat": "Kan niet toetreden tot de chat. Misschien heeft de andere partij het gesprek al afgesloten.",
|
||||
"@unableToJoinChat": {},
|
||||
"aboutHomeserver": "Over {homeserver}",
|
||||
"@aboutHomeserver": {
|
||||
|
|
@ -2893,8 +2893,452 @@
|
|||
},
|
||||
"website": "Website",
|
||||
"@website": {},
|
||||
"hideMemberChangesInPublicChats": "Verberg persoon veranderingen in publieke chats",
|
||||
"hideMemberChangesInPublicChats": "Verberg persoon veranderingen in openbare chats",
|
||||
"@hideMemberChangesInPublicChats": {},
|
||||
"hideMemberChangesInPublicChatsBody": "Verberg in de tijdlijn van de chat als iemand zich aanmeldt bij een openbare chat of deze verlaat om de leesbaarheid te verbeteren.",
|
||||
"@hideMemberChangesInPublicChatsBody": {}
|
||||
"@hideMemberChangesInPublicChatsBody": {},
|
||||
"startConversation": "Start gesprek",
|
||||
"@startConversation": {},
|
||||
"usersMustKnock": "Personen moeten kloppen",
|
||||
"@usersMustKnock": {},
|
||||
"noUsersFoundWithQuery": "Helaas kan er geen persoon gevonden worden met \"{query}\". Controleer of je een typfout hebt gemaakt.",
|
||||
"@noUsersFoundWithQuery": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"query": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"createGroupAndInviteUsers": "Maak groep en nodig personen uit",
|
||||
"@createGroupAndInviteUsers": {},
|
||||
"userWouldLikeToChangeTheChat": "{user} wil graag deelnemen aan de chat.",
|
||||
"@userWouldLikeToChangeTheChat": {
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"chatCanBeDiscoveredViaSearchOnServer": "Chat kan worden gevonden via een zoekopdracht op {server}",
|
||||
"@chatCanBeDiscoveredViaSearchOnServer": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"server": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wrongRecoveryKey": "Helaas... dit lijkt niet de correcte herstelsleutel.",
|
||||
"@wrongRecoveryKey": {},
|
||||
"synchronizingPleaseWaitCounter": " Synchroniseren… ({percentage}%)",
|
||||
"@synchronizingPleaseWaitCounter": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"percentage": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"databaseMigrationTitle": "Database is geoptimaliseerd",
|
||||
"@databaseMigrationTitle": {},
|
||||
"databaseMigrationBody": "Een ogenblik. Dit kan even duren.",
|
||||
"@databaseMigrationBody": {},
|
||||
"commandHint_sendraw": "Stuur kale json",
|
||||
"@commandHint_sendraw": {},
|
||||
"passwordIsWrong": "Je ingevoerde wachtwoord is fout",
|
||||
"@passwordIsWrong": {},
|
||||
"newPassword": "Nieuw wachtwoord",
|
||||
"@newPassword": {},
|
||||
"pleaseChooseAStrongPassword": "Kies a.j.b. een sterk wachtwoord",
|
||||
"@pleaseChooseAStrongPassword": {},
|
||||
"publicLink": "Openbare link",
|
||||
"@publicLink": {},
|
||||
"select": "Selecteer",
|
||||
"@select": {},
|
||||
"leaveEmptyToClearStatus": "Laat leeg om je status te resetten.",
|
||||
"@leaveEmptyToClearStatus": {},
|
||||
"addChatOrSubSpace": "Voeg chat of subspace toe",
|
||||
"@addChatOrSubSpace": {},
|
||||
"subspace": "Subspace",
|
||||
"@subspace": {},
|
||||
"pleaseEnterYourCurrentPassword": "Vul je huidige wachtwoord in",
|
||||
"@pleaseEnterYourCurrentPassword": {},
|
||||
"passwordsDoNotMatch": "Wachtwoorden komen niet overeen",
|
||||
"@passwordsDoNotMatch": {},
|
||||
"decline": "Weiger",
|
||||
"@decline": {},
|
||||
"thisDevice": "Dit apparaat:",
|
||||
"@thisDevice": {},
|
||||
"contentNotificationSettings": "Contentmelding instellingen",
|
||||
"@contentNotificationSettings": {},
|
||||
"roomNotificationSettings": "Kamermelding instellingen",
|
||||
"@roomNotificationSettings": {},
|
||||
"userSpecificNotificationSettings": "Persoon specifieke melding instellingen",
|
||||
"@userSpecificNotificationSettings": {},
|
||||
"otherNotificationSettings": "Andere melding instellingen",
|
||||
"@otherNotificationSettings": {},
|
||||
"notificationRuleContainsUserName": "Bevat naam van persoon",
|
||||
"@notificationRuleContainsUserName": {},
|
||||
"notificationRuleContainsUserNameDescription": "Stuurt een melding als een bericht de persoon vermeld.",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMaster": "Alle meldingen uitschakelen",
|
||||
"@notificationRuleMaster": {},
|
||||
"notificationRuleMasterDescription": "Overschrijf alle andere regels en meldingen uitschakelen.",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
"notificationRuleMemberEventDescription": "Meldingen voor kamer-gebeurtenissen uitschakelen.",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleIsUserMention": "Persoonvermelding",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"initAppError": "Er is een fout opgetreden bij het laden van de app",
|
||||
"@initAppError": {},
|
||||
"requestedKeyVerification": "{sender} vraagt een sleutelverificatie",
|
||||
"@requestedKeyVerification": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"sender": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sessionLostBody": "Je sessie is verlopen. Meldt alsjeblieft deze fout aan de ontwikkelaars via deze link {url}. De foutmelding is: {error}",
|
||||
"@sessionLostBody": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"url": {
|
||||
"type": "String"
|
||||
},
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendTypingNotificationsDescription": "Andere deelnemers in de chat kunnen zien wanneer je een bericht aan het typen bent.",
|
||||
"@sendTypingNotificationsDescription": {},
|
||||
"sendCanceled": "Versturen geannuleerd",
|
||||
"@sendCanceled": {},
|
||||
"opacity": "Doorzichtigheid:",
|
||||
"@opacity": {},
|
||||
"verifyOtherUserDescription": "Als je een persoon verifieert ben je er zeker van dat je echt met haar contact hebt. 💪\n\nWanneer je een verificatie start ziet de persoon een popup in de app. Hier staat een serie van emoji's of getallen die je met elkaar moet vergelijken.\n\nDe beste manier om dit te doen is in persoon of met een videogesprek. 👭",
|
||||
"@verifyOtherUserDescription": {},
|
||||
"changeTheVisibilityOfChatHistory": "Zichtbaarheid van de chat-geschiedenis wijzigen",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"whatIsAHomeserver": "Wat is een server?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"sendRoomNotifications": "@room-meldingen versturen",
|
||||
"@sendRoomNotifications": {},
|
||||
"noticeChatBackupDeviceVerification": "Opmerking: Als al je apparaten zijn verbonden met de chat back-up worden ze automatisch geverifieerd.",
|
||||
"@noticeChatBackupDeviceVerification": {},
|
||||
"notificationRuleMemberEvent": "Kamer-gebeurtenis",
|
||||
"@notificationRuleMemberEvent": {},
|
||||
"notificationRuleSuppressNotices": "Automatische berichten uitschakelen",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"setWallpaper": "Wallpaper instellen",
|
||||
"@setWallpaper": {},
|
||||
"oneOfYourDevicesIsNotVerified": "Een van jouw apparaten is niet geverifieerd",
|
||||
"@oneOfYourDevicesIsNotVerified": {},
|
||||
"contactServerAdmin": "Contact opnemen met serverbeheerder",
|
||||
"@contactServerAdmin": {},
|
||||
"manageAccount": "Account beheren",
|
||||
"@manageAccount": {},
|
||||
"noContactInformationProvided": "Server geeft geen geldige contactinformatie",
|
||||
"@noContactInformationProvided": {},
|
||||
"waitingForServer": "Wachten op server...",
|
||||
"@waitingForServer": {},
|
||||
"generalNotificationSettings": "Algemene melding instellingen",
|
||||
"@generalNotificationSettings": {},
|
||||
"notificationRuleInviteForMeDescription": "Stuur een melding wanneer een persoon wordt uitgenodigd voor een kamer.",
|
||||
"@notificationRuleInviteForMeDescription": {},
|
||||
"notificationRuleSuppressNoticesDescription": "Meldingen van automatische accounts zoals bots uitschakelen.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleInviteForMe": "Uitnodiging voor mij",
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"inviteOtherUsers": "Personen voor deze chat uitnodigen",
|
||||
"@inviteOtherUsers": {},
|
||||
"changeTheChatPermissions": "Chat-rechten wijzigen",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheCanonicalRoomAlias": "Standaard openbaar chat-adres wijzigen",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"blur": "Vervaag:",
|
||||
"@blur": {},
|
||||
"isReadyForKeyVerification": "{sender} is klaar voor de sleutelverificatie",
|
||||
"@isReadyForKeyVerification": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"sender": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"startedKeyVerification": "{sender} start een sleutelverificatie",
|
||||
"@startedKeyVerification": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"sender": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"acceptedKeyVerification": "{sender} accepteerde de sleutelverificatie",
|
||||
"@acceptedKeyVerification": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"sender": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"canceledKeyVerification": "{sender} annuleerde de sleutelverificatie",
|
||||
"@canceledKeyVerification": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"sender": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"knockRestricted": "Kloppen is beperkt",
|
||||
"@knockRestricted": {},
|
||||
"goToSpace": "Ga naar space: {space}",
|
||||
"@goToSpace": {
|
||||
"type": "String",
|
||||
"space": {}
|
||||
},
|
||||
"contactServerSecurity": "Contact opnemen met serverbeveiliger",
|
||||
"@contactServerSecurity": {},
|
||||
"newChatRequest": "📩 Nieuw chat verzoek",
|
||||
"@newChatRequest": {},
|
||||
"updateInstalled": "🎉 Update {version} geïnstalleerd!",
|
||||
"@updateInstalled": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"version": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"discoverHomeservers": "Ontdek servers",
|
||||
"@discoverHomeservers": {},
|
||||
"changelog": "Wijzigingengeschiedenis",
|
||||
"@changelog": {},
|
||||
"loginWithMatrixId": "Inloggen met Matrix ID",
|
||||
"@loginWithMatrixId": {},
|
||||
"calculatingFileSize": "Bestandsgrootte berekenen...",
|
||||
"@calculatingFileSize": {},
|
||||
"sendingAttachment": "Bijlage versturen...",
|
||||
"@sendingAttachment": {},
|
||||
"generatingVideoThumbnail": "Video-voorbeeld genereren...",
|
||||
"@generatingVideoThumbnail": {},
|
||||
"prepareSendingAttachment": "Bijlage versturen voorbereiden...",
|
||||
"@prepareSendingAttachment": {},
|
||||
"compressVideo": "Video comprimeren...",
|
||||
"@compressVideo": {},
|
||||
"serverLimitReached": "Server limiet bereikt! Wacht {seconds} seconden...",
|
||||
"@serverLimitReached": {
|
||||
"type": "integer",
|
||||
"placeholders": {
|
||||
"seconds": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "Versie",
|
||||
"@version": {},
|
||||
"supportPage": "Supportpagina",
|
||||
"@supportPage": {},
|
||||
"serverInformation": "Server-informatie:",
|
||||
"@serverInformation": {},
|
||||
"name": "Naam",
|
||||
"@name": {},
|
||||
"verifyOtherDeviceDescription": "Een geverifieerd ander apparaat zorgt ervoor dat de apparaten sleutels uitwisselen, wat je beveiliging versterkt. 💪 Als je de verificatie start verschijnt er een popup op beide apparaten. Hier staat een reeks emoji's of getallen die je met elkaar moet vergelijken. Het is handig om beide apparaten bij de hand te hebben voordat je de verificatie start. 🤳",
|
||||
"@verifyOtherDeviceDescription": {},
|
||||
"commandHint_unignore": "Herstel de negeerde Matrix ID",
|
||||
"@commandHint_unignore": {},
|
||||
"forwardMessageTo": "Bericht doorsturen naar {roomName}?",
|
||||
"@forwardMessageTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"roomName": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restoreSessionBody": "De app probeert nu je sessie te herstellen van een back-up. Meldt alsjeblieft deze fout aan de ontwikkelaars via deze link {url}. De foutmelding is: {error}",
|
||||
"@restoreSessionBody": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"url": {
|
||||
"type": "String"
|
||||
},
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendReadReceipts": "Leesbevestigingen versturen",
|
||||
"@sendReadReceipts": {},
|
||||
"formattedMessages": "Opgemaakte berichten",
|
||||
"@formattedMessages": {},
|
||||
"chatPermissionsDescription": "Stel het gewenste rechten-niveau in voor bepaalde acties in deze chat. Het rechten-niveau 0, 50 en 100 zijn gebruikelijk voor deelnemer, moderator en beheerder, maar elke verdeling is mogelijk.",
|
||||
"@chatPermissionsDescription": {},
|
||||
"changeTheDescriptionOfTheGroup": "Chatomschrijving wijzigen",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"userRole": "Rol",
|
||||
"@userRole": {},
|
||||
"minimumPowerLevel": "{level} is het minimale rechten-niveau.",
|
||||
"@minimumPowerLevel": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"level": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendReadReceiptsDescription": "Andere deelnemers van de chat kunnen zien of je een bericht hebt gelezen.",
|
||||
"@sendReadReceiptsDescription": {},
|
||||
"formattedMessagesDescription": "Geef rijke berichtinhoud weer zoals vetgedrukte tekst met markdown.",
|
||||
"@formattedMessagesDescription": {},
|
||||
"verifyOtherUser": "🔐 Persoon verifiëren",
|
||||
"@verifyOtherUser": {},
|
||||
"verifyOtherDevice": "🔐 Ander apparaat verifiëren",
|
||||
"@verifyOtherDevice": {},
|
||||
"doesNotSeemToBeAValidHomeserver": "Dit lijkt geen ondersteunde server. Verkeerde URL?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {},
|
||||
"sendingAttachmentCountOfCount": "Bijlage versturen {index} van {length}...",
|
||||
"@sendingAttachmentCountOfCount": {
|
||||
"type": "integer",
|
||||
"placeholders": {
|
||||
"index": {
|
||||
"type": "int"
|
||||
},
|
||||
"length": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"continueText": "Doorgaan",
|
||||
"@continueText": {},
|
||||
"welcomeText": "Hallo hallo 👋 Dit is FluffyChat. Je kan inloggen op elke server die werkt met https://matrix.org. En dan chat je met iedereen. Het is een groot decentraal chat-netwerk!",
|
||||
"@welcomeText": {},
|
||||
"appWantsToUseForLogin": "Inloggen met '{server}'",
|
||||
"@appWantsToUseForLogin": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"server": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appWantsToUseForLoginDescription": "Hierbij sta je toe dat de app en website informatie over je delen.",
|
||||
"@appWantsToUseForLoginDescription": {},
|
||||
"open": "Open",
|
||||
"@open": {},
|
||||
"appIntroduction": "FluffyChat laat je chatten met je vrienden tussen verschillende chat-netwerken. Lees meer op https://matrix.org of tik *Continue*.",
|
||||
"@appIntroduction": {},
|
||||
"completedKeyVerification": "{sender} ronde de sleutelverificatie af",
|
||||
"@completedKeyVerification": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"sender": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"homeserverDescription": "Al je data is opgeslagen op de server, net als bij een email-leverancier. Je kan kiezen welke server je gebruikt en toch communiceren met iedereen. Lees meer op https://matrix.org.",
|
||||
"@homeserverDescription": {},
|
||||
"notificationRuleContainsDisplayName": "Bevat de naam",
|
||||
"@notificationRuleContainsDisplayName": {},
|
||||
"notificationRuleIsUserMentionDescription": "Stuur een melding als de persoon direct genoemd wordt in een bericht.",
|
||||
"@notificationRuleIsUserMentionDescription": {},
|
||||
"notificationRuleContainsDisplayNameDescription": "Stuur een melding als de persoon genoemd wordt in het bericht.",
|
||||
"@notificationRuleContainsDisplayNameDescription": {},
|
||||
"notificationRuleIsRoomMention": "Kamervermelding",
|
||||
"@notificationRuleIsRoomMention": {},
|
||||
"notificationRuleIsRoomMentionDescription": "Stuur een melding naar de persoon als er in een kamervermelding is.",
|
||||
"@notificationRuleIsRoomMentionDescription": {},
|
||||
"notificationRuleRoomnotif": "Kamermelding",
|
||||
"@notificationRuleRoomnotif": {},
|
||||
"notificationRuleRoomnotifDescription": "Stuur een melding naar de persoon wanneer een bericht '@room' bevat.",
|
||||
"@notificationRuleRoomnotifDescription": {},
|
||||
"notificationRuleTombstone": "Sleutingsbericht",
|
||||
"@notificationRuleTombstone": {},
|
||||
"notificationRuleReaction": "Reactie",
|
||||
"@notificationRuleReaction": {},
|
||||
"notificationRuleRoomServerAcl": "Kamer Server ACL",
|
||||
"@notificationRuleRoomServerAcl": {},
|
||||
"notificationRuleTombstoneDescription": "Stuur een melding naar de persoon over kamersluitingsberichten.",
|
||||
"@notificationRuleTombstoneDescription": {},
|
||||
"notificationRuleReactionDescription": "Meldingen voor reacties uitschakelen.",
|
||||
"@notificationRuleReactionDescription": {},
|
||||
"notificationRuleRoomServerAclDescription": "Meldingen voor kamer server toegangscontrolelijst (ACL) uitschakelen.",
|
||||
"@notificationRuleRoomServerAclDescription": {},
|
||||
"notificationRuleSuppressEdits": "Bewerkingen uitschakelen",
|
||||
"@notificationRuleSuppressEdits": {},
|
||||
"notificationRuleCall": "Oproep",
|
||||
"@notificationRuleCall": {},
|
||||
"notificationRuleSuppressEditsDescription": "Meldingen voor bewerkte berichten uitschakelen.",
|
||||
"@notificationRuleSuppressEditsDescription": {},
|
||||
"notificationRuleEncryptedRoomOneToOneDescription": "Stuur een melding naar de persoon over berichten in versleutelde een-op-een kamers.",
|
||||
"@notificationRuleEncryptedRoomOneToOneDescription": {},
|
||||
"notificationRuleEncryptedRoomOneToOne": "Versleutelde een-op-een kamer",
|
||||
"@notificationRuleEncryptedRoomOneToOne": {},
|
||||
"notificationRuleRoomOneToOne": "Een-op-een kamer",
|
||||
"@notificationRuleRoomOneToOne": {},
|
||||
"notificationRuleMessage": "Bericht",
|
||||
"@notificationRuleMessage": {},
|
||||
"notificationRuleEncrypted": "Versleuteld",
|
||||
"@notificationRuleEncrypted": {},
|
||||
"notificationRuleRoomOneToOneDescription": "Stuur een melding naar de persoon over berichten in een-op-een kamers.",
|
||||
"@notificationRuleRoomOneToOneDescription": {},
|
||||
"notificationRuleMessageDescription": "Stuur een melding naar de persoon over algemene berichten.",
|
||||
"@notificationRuleMessageDescription": {},
|
||||
"notificationRuleJitsi": "Jitsi",
|
||||
"@notificationRuleJitsi": {},
|
||||
"notificationRuleEncryptedDescription": "Stuur een melding naar de persoon over berichten in versleutelde kamers.",
|
||||
"@notificationRuleEncryptedDescription": {},
|
||||
"notificationRuleJitsiDescription": "Stuur een melding naar de persoon over Jitsi widget gebeurtenissen.",
|
||||
"@notificationRuleJitsiDescription": {},
|
||||
"unknownPushRule": "Onbekende notificatieregel '{rule}'",
|
||||
"@unknownPushRule": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"rule": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notificationRuleServerAcl": "Server ACL gebeurtenissen uitschakelen",
|
||||
"@notificationRuleServerAcl": {},
|
||||
"notificationRuleServerAclDescription": "Meldingen over server ACL gebeurtenissen uitschakelen.",
|
||||
"@notificationRuleServerAclDescription": {},
|
||||
"more": "Meer",
|
||||
"@more": {},
|
||||
"enterNewChat": "Nieuwe chat openen",
|
||||
"@enterNewChat": {},
|
||||
"crossVerifiedDevices": "Kruislings geverifieerde apparaten",
|
||||
"@crossVerifiedDevices": {},
|
||||
"allDevices": "Alle apparaten",
|
||||
"@allDevices": {},
|
||||
"shareKeysWithDescription": "Welke apparaten moeten vertrouwd worden zodat ze je berichten kunnen lezen in versleutelde chats?",
|
||||
"@shareKeysWithDescription": {},
|
||||
"verifiedDevicesOnly": "Alleen geverifieerde apparaten",
|
||||
"@verifiedDevicesOnly": {},
|
||||
"crossVerifiedDevicesIfEnabled": "Kruislings geverifieerde apparaten als ingeschakeld",
|
||||
"@crossVerifiedDevicesIfEnabled": {},
|
||||
"shareKeysWith": "Deel sleutels met...",
|
||||
"@shareKeysWith": {},
|
||||
"notificationRuleCallDescription": "Stuur een melding naar de persoon over oproepen.",
|
||||
"@notificationRuleCallDescription": {},
|
||||
"deletePushRuleCanNotBeUndone": "Als je deze melding-instelling verwijderd, kan dit niet ongedaan gemaakt worden.",
|
||||
"@deletePushRuleCanNotBeUndone": {},
|
||||
"takeAPhoto": "Neem een foto",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Neem een video",
|
||||
"@recordAVideo": {},
|
||||
"optionalMessage": "(Optioneel) bericht...",
|
||||
"@optionalMessage": {},
|
||||
"notSupportedOnThisDevice": "Niet ondersteund op dit apparaat",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"commandHint_roomupgrade": "Upgradeer deze kamer naar de aangegeven kamerversie",
|
||||
"@commandHint_roomupgrade": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3258,15 +3258,15 @@
|
|||
"@notificationRuleCall": {},
|
||||
"notificationRuleRoomServerAclDescription": "Wyłącza powiadomienia dla list kontroli dostępu (ACL) serwerów pokojów.",
|
||||
"@notificationRuleRoomServerAclDescription": {},
|
||||
"notificationRuleRoomServerAcl": "Listo kontroli dostępu serwerów pokojów",
|
||||
"notificationRuleRoomServerAcl": "Lista kontroli dostępu serwerów pokojów",
|
||||
"@notificationRuleRoomServerAcl": {},
|
||||
"notificationRuleEncryptedRoomOneToOne": "Szyfrowane pokoje jeden na jeden",
|
||||
"notificationRuleEncryptedRoomOneToOne": "Szyfrowane pokoje „jeden na jeden”",
|
||||
"@notificationRuleEncryptedRoomOneToOne": {},
|
||||
"notificationRuleCallDescription": "Powiadamia o przychodzących połączeniach.",
|
||||
"@notificationRuleCallDescription": {},
|
||||
"notificationRuleRoomOneToOne": "Pokoje jeden na jeden",
|
||||
"notificationRuleRoomOneToOne": "Pokoje „jeden na jeden”",
|
||||
"@notificationRuleRoomOneToOne": {},
|
||||
"notificationRuleRoomOneToOneDescription": "Powiadamia o wiadomościach w pokojach jeden na jeden (one-to-one).",
|
||||
"notificationRuleRoomOneToOneDescription": "Powiadamia o wiadomościach w pokojach „jeden na jeden” (one-to-one).",
|
||||
"@notificationRuleRoomOneToOneDescription": {},
|
||||
"notificationRuleMessage": "Wiadomości",
|
||||
"@notificationRuleMessage": {},
|
||||
|
|
@ -3279,7 +3279,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"notificationRuleEncryptedRoomOneToOneDescription": "Powiadamia o wiadomościach w szyfrowanych pokojach jeden na jeden (one-to-one).",
|
||||
"notificationRuleEncryptedRoomOneToOneDescription": "Powiadamia o wiadomościach w szyfrowanych pokojach „jeden na jeden” (one-to-one).",
|
||||
"@notificationRuleEncryptedRoomOneToOneDescription": {},
|
||||
"notificationRuleEncrypted": "Zaszyfrowane pokoje",
|
||||
"@notificationRuleEncrypted": {},
|
||||
|
|
|
|||
|
|
@ -2476,7 +2476,7 @@
|
|||
"@noOtherDevicesFound": {},
|
||||
"reportErrorDescription": "😭 О, нет. Что-то пошло не так. При желании вы можете сообщить об этой ошибке разработчикам.",
|
||||
"@reportErrorDescription": {},
|
||||
"report": "сообщить",
|
||||
"report": "пожаловаться",
|
||||
"@report": {},
|
||||
"allRooms": "Все группы",
|
||||
"@allRooms": {
|
||||
|
|
@ -3184,5 +3184,91 @@
|
|||
}
|
||||
},
|
||||
"compress": "Сжатие",
|
||||
"@compress": {}
|
||||
"@compress": {},
|
||||
"notificationRuleMessage": "Сообщение",
|
||||
"@notificationRuleMessage": {},
|
||||
"appWantsToUseForLoginDescription": "Вы позволяете приложению и веб-сайту делиться информацией о вас.",
|
||||
"@appWantsToUseForLoginDescription": {},
|
||||
"newChatRequest": "📩 Запрос нового чата",
|
||||
"@newChatRequest": {},
|
||||
"allDevices": "Все устройства",
|
||||
"@allDevices": {},
|
||||
"roomNotificationSettings": "Настройки уведомлений комнаты",
|
||||
"@roomNotificationSettings": {},
|
||||
"notificationRuleContainsUserName": "Содержит имя пользователя",
|
||||
"@notificationRuleContainsUserName": {},
|
||||
"notificationRuleMaster": "Отключить все уведомления",
|
||||
"@notificationRuleMaster": {},
|
||||
"notificationRuleSuppressNoticesDescription": "Отключить уведомления от автоматизированных клиентов, таких как боты.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleInviteForMe": "Приглашение для меня",
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleMemberEventDescription": "Отключить уведомления о событиях о членстве.",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleIsRoomMention": "Упоминание комнаты",
|
||||
"@notificationRuleIsRoomMention": {},
|
||||
"notificationRuleReactionDescription": "Отключить уведомления о реакциях.",
|
||||
"@notificationRuleReactionDescription": {},
|
||||
"notificationRuleCall": "Звонок",
|
||||
"@notificationRuleCall": {},
|
||||
"notificationRuleSuppressEditsDescription": "Отключить уведомления о отредактированных сообщениях.",
|
||||
"@notificationRuleSuppressEditsDescription": {},
|
||||
"notificationRuleEncrypted": "Зашифровано",
|
||||
"@notificationRuleEncrypted": {},
|
||||
"more": "Больше",
|
||||
"@more": {},
|
||||
"synchronizingPleaseWaitCounter": " Синхронизация… ({percentage}%)",
|
||||
"@synchronizingPleaseWaitCounter": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"percentage": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"appWantsToUseForLogin": "Использовать '{server}' для входа",
|
||||
"@appWantsToUseForLogin": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"server": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentNotificationSettings": "Настройки уведомлений по тексту",
|
||||
"@contentNotificationSettings": {},
|
||||
"generalNotificationSettings": "Общие настройки уведомлений",
|
||||
"@generalNotificationSettings": {},
|
||||
"userSpecificNotificationSettings": "Настроки уведомлений пользователя",
|
||||
"@userSpecificNotificationSettings": {},
|
||||
"otherNotificationSettings": "Другие настройки уведомлений",
|
||||
"@otherNotificationSettings": {},
|
||||
"notificationRuleContainsUserNameDescription": "Уведомляет пользователя когда сообщение содержит его имя пользователя.",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMasterDescription": "Перекрывает все другие правила и отключает все уведомления.",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
"notificationRuleSuppressNotices": "Отключить автоматические сообщения",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"notificationRuleIsUserMention": "Упоминание пользователя",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"notificationRuleContainsDisplayName": "Содержит отображаемое имя",
|
||||
"@notificationRuleContainsDisplayName": {},
|
||||
"notificationRuleReaction": "Реакция",
|
||||
"@notificationRuleReaction": {},
|
||||
"takeAPhoto": "Снять фото",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "Записать видео",
|
||||
"@recordAVideo": {},
|
||||
"enterNewChat": "Введите новый чат",
|
||||
"@enterNewChat": {},
|
||||
"otherPartyNotLoggedIn": "Другая сторона в настоящее время не вошла в систему и поэтому не может получать сообщения!",
|
||||
"@otherPartyNotLoggedIn": {},
|
||||
"open": "Открыть",
|
||||
"@open": {},
|
||||
"waitingForServer": "Ожидание сервера...",
|
||||
"@waitingForServer": {},
|
||||
"appIntroduction": "FluffyChat позволяет вам общаться с друзьями с разными мессенджерами. Узнайте больше на https://matrix.org или просто нажмите *Продолжить*.",
|
||||
"@appIntroduction": {},
|
||||
"previous": "Предыдущий",
|
||||
"@previous": {}
|
||||
}
|
||||
|
|
|
|||
15
assets/l10n/intl_te.arb
Normal file
15
assets/l10n/intl_te.arb
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"alwaysUse24HourFormat": "తప్పుడు",
|
||||
"@alwaysUse24HourFormat": {
|
||||
"description": "Set to true to always display time of day in 24 hour format."
|
||||
},
|
||||
"notAnImage": "ఇమేజ్ ఫైల్ కాదు.",
|
||||
"@notAnImage": {},
|
||||
"repeatPassword": "పాస్వర్డ్ను పునరావృతం చేయండి",
|
||||
"@repeatPassword": {},
|
||||
"remove": "తొలగించు",
|
||||
"@remove": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -3232,5 +3232,49 @@
|
|||
"contentNotificationSettings": "Налаштування сповіщень про вміст",
|
||||
"@contentNotificationSettings": {},
|
||||
"generalNotificationSettings": "Загальні налаштування сповіщень",
|
||||
"@generalNotificationSettings": {}
|
||||
"@generalNotificationSettings": {},
|
||||
"roomNotificationSettings": "Налаштування сповіщень кімнати",
|
||||
"@roomNotificationSettings": {},
|
||||
"userSpecificNotificationSettings": "Налаштування сповіщень для користувача",
|
||||
"@userSpecificNotificationSettings": {},
|
||||
"otherNotificationSettings": "Інші налаштування сповіщень",
|
||||
"@otherNotificationSettings": {},
|
||||
"notificationRuleContainsUserName": "Містить ім'я користувача",
|
||||
"@notificationRuleContainsUserName": {},
|
||||
"notificationRuleContainsUserNameDescription": "Сповіщає користувача, коли повідомлення містить його ім'я користувача.",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMaster": "Вимкнути всі сповіщення",
|
||||
"@notificationRuleMaster": {},
|
||||
"notificationRuleMasterDescription": "Перевизначає всі інші правила і вимикає всі сповіщення.",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
"notificationRuleSuppressNotices": "Заборонити автоматичні повідомлення",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"notificationRuleInviteForMe": "Запрошення мене",
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleInviteForMeDescription": "Сповіщає користувача, коли його запрошують до кімнати.",
|
||||
"@notificationRuleInviteForMeDescription": {},
|
||||
"notificationRuleMemberEvent": "Події участі",
|
||||
"@notificationRuleMemberEvent": {},
|
||||
"notificationRuleMemberEventDescription": "Забороняє сповіщення про події учасників.",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleSuppressNoticesDescription": "Забороняє сповіщення від автоматизованих клієнтів, таких як боти.",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleIsUserMention": "Згадки користувачів",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"commandHint_roomupgrade": "Оновити цю кімнату до версії даної кімнати",
|
||||
"@commandHint_roomupgrade": {},
|
||||
"notificationRuleIsUserMentionDescription": "Сповіщає користувачів, коли безпосередньо їх згадують у повідомленні.",
|
||||
"@notificationRuleIsUserMentionDescription": {},
|
||||
"notificationRuleContainsDisplayName": "Містить показуване ім’я",
|
||||
"@notificationRuleContainsDisplayName": {},
|
||||
"notificationRuleIsRoomMentionDescription": "Сповіщає користувача, коли є згадка всієї кімнати.",
|
||||
"@notificationRuleIsRoomMentionDescription": {},
|
||||
"notificationRuleRoomnotif": "Сповіщення кімнати",
|
||||
"@notificationRuleRoomnotif": {},
|
||||
"notificationRuleRoomnotifDescription": "Сповіщає користувача, коли повідомлення містить '@room'.",
|
||||
"@notificationRuleRoomnotifDescription": {},
|
||||
"notificationRuleContainsDisplayNameDescription": "Сповіщає користувача, коли повідомлення містить показуване ім'я.",
|
||||
"@notificationRuleContainsDisplayNameDescription": {},
|
||||
"notificationRuleIsRoomMention": "Згадки кімнати",
|
||||
"@notificationRuleIsRoomMention": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3801,4 +3801,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3333,5 +3333,17 @@
|
|||
"crossVerifiedDevicesIfEnabled": "交叉验证设备(如启用)",
|
||||
"@crossVerifiedDevicesIfEnabled": {},
|
||||
"verifiedDevicesOnly": "仅已验证设备",
|
||||
"@verifiedDevicesOnly": {}
|
||||
"@verifiedDevicesOnly": {},
|
||||
"optionalMessage": "(可选)消息…",
|
||||
"@optionalMessage": {},
|
||||
"takeAPhoto": "拍照",
|
||||
"@takeAPhoto": {},
|
||||
"recordAVideo": "录像",
|
||||
"@recordAVideo": {},
|
||||
"notSupportedOnThisDevice": "此设备上不受支持",
|
||||
"@notSupportedOnThisDevice": {},
|
||||
"enterNewChat": "进入新聊天",
|
||||
"@enterNewChat": {},
|
||||
"commandHint_roomupgrade": "将此聊天室升级到给定的聊天室版本",
|
||||
"@commandHint_roomupgrade": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheChatDescriptionTo": "{username} 變更了聊天室介紹為:「{description}」",
|
||||
"changedTheChatDescriptionTo": "{username} 把聊天室介紹變更為:「{description}」",
|
||||
"@changedTheChatDescriptionTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -198,7 +198,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"changedTheChatNameTo": "{username} 變更了聊天室名稱為:「{chatname}」",
|
||||
"changedTheChatNameTo": "{username} 把聊天室名稱變更為:「{chatname}」",
|
||||
"@changedTheChatNameTo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -1403,7 +1403,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"sentAnAudio": "{username} 傳送了一個音訊",
|
||||
"sentAnAudio": "🎤{username} 傳送了一個音訊",
|
||||
"@sentAnAudio": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -1412,7 +1412,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"sentAPicture": "{username} 傳送了一張圖片",
|
||||
"sentAPicture": "🖼️{username} 傳送了一張圖片",
|
||||
"@sentAPicture": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -1421,7 +1421,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"sentASticker": "{username} 傳送了貼圖",
|
||||
"sentASticker": "😊{username} 傳送了貼圖",
|
||||
"@sentASticker": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -1430,7 +1430,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"sentAVideo": "{username} 傳送了影片",
|
||||
"sentAVideo": "🎥{username} 傳送了影片",
|
||||
"@sentAVideo": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
|
|
@ -2750,7 +2750,7 @@
|
|||
"@disableEncryptionWarning": {},
|
||||
"deviceKeys": "裝置密鑰:",
|
||||
"@deviceKeys": {},
|
||||
"fileIsTooBigForServer": "伺服器報告該文件太大,無法傳送。",
|
||||
"fileIsTooBigForServer": "無法發送!該伺服器僅支援最大到 {max} 的附件。",
|
||||
"@fileIsTooBigForServer": {},
|
||||
"fileHasBeenSavedAt": "文件已保存在 {path}",
|
||||
"@fileHasBeenSavedAt": {
|
||||
|
|
@ -3043,5 +3043,211 @@
|
|||
"type": "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"doesNotSeemToBeAValidHomeserver": "似乎不是能匹配的歸屬伺服器。伺服器域名打錯了嗎?",
|
||||
"@doesNotSeemToBeAValidHomeserver": {},
|
||||
"noticeChatBackupDeviceVerification": "注意:當您將所有裝置連線到聊天備份時,它們會自動驗證。",
|
||||
"@noticeChatBackupDeviceVerification": {},
|
||||
"sendCanceled": "傳送取消",
|
||||
"@sendCanceled": {},
|
||||
"changelog": "變更日誌",
|
||||
"@changelog": {},
|
||||
"changeTheCanonicalRoomAlias": "變更公開聊天室的主要地址",
|
||||
"@changeTheCanonicalRoomAlias": {},
|
||||
"sendImages": "傳送{count}張圖片",
|
||||
"@sendImages": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginWithMatrixId": "以Matrix-ID登入",
|
||||
"@loginWithMatrixId": {},
|
||||
"inviteOtherUsers": "邀請其他用戶進入本聊天",
|
||||
"@inviteOtherUsers": {},
|
||||
"sendRoomNotifications": "傳送一條 @room 群提醒",
|
||||
"@sendRoomNotifications": {},
|
||||
"updateInstalled": "🎉已成功安裝{version}版本!",
|
||||
"@updateInstalled": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"version": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"oneOfYourDevicesIsNotVerified": "你的其中一個裝置尚未驗證",
|
||||
"@oneOfYourDevicesIsNotVerified": {},
|
||||
"chatPermissionsDescription": "定義此聊天中某些操作需要哪個權限等級。 權限等級0、50和100通常代表使用者、版主和管理員,但任何分級都是可能的。",
|
||||
"@chatPermissionsDescription": {},
|
||||
"changeGeneralChatSettings": "變更一般聊天設定",
|
||||
"@changeGeneralChatSettings": {},
|
||||
"manageAccount": "帳號管理",
|
||||
"@manageAccount": {},
|
||||
"changeTheChatPermissions": "變更聊天室權限",
|
||||
"@changeTheChatPermissions": {},
|
||||
"changeTheVisibilityOfChatHistory": "變更過往聊天記錄可見度",
|
||||
"@changeTheVisibilityOfChatHistory": {},
|
||||
"homeserverDescription": "您的所有資料都儲存在歸屬伺服器上,就像電子郵件提供商一樣。 您可以選擇要使用的歸屬伺服器,同時您仍然可以與每個人溝通。 請訪問https://matrix.org瞭解更多資訊。",
|
||||
"@homeserverDescription": {},
|
||||
"sendingAttachment": "附件傳送中…",
|
||||
"@sendingAttachment": {},
|
||||
"compressVideo": "影片壓縮中…",
|
||||
"@compressVideo": {},
|
||||
"opacity": "不透明度:",
|
||||
"@opacity": {},
|
||||
"aboutHomeserver": "關於{homeserver}",
|
||||
"@aboutHomeserver": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"homeserver": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noChatsFoundHere": "還沒開始聊天嗎?點擊下方按鈕找個人聊聊吧⤵",
|
||||
"@noChatsFoundHere": {},
|
||||
"changeTheDescriptionOfTheGroup": "變更聊天室說明",
|
||||
"@changeTheDescriptionOfTheGroup": {},
|
||||
"discoverHomeservers": "探索歸屬伺服器",
|
||||
"@discoverHomeservers": {},
|
||||
"whatIsAHomeserver": "什麼是歸屬伺服器?",
|
||||
"@whatIsAHomeserver": {},
|
||||
"calculatingFileSize": "正在計算檔案大小…",
|
||||
"@calculatingFileSize": {},
|
||||
"prepareSendingAttachment": "準備傳送附件…",
|
||||
"@prepareSendingAttachment": {},
|
||||
"generatingVideoThumbnail": "生成影片縮圖中…",
|
||||
"@generatingVideoThumbnail": {},
|
||||
"sendingAttachmentCountOfCount": "附件傳送中 {index}/{length}…",
|
||||
"@sendingAttachmentCountOfCount": {
|
||||
"type": "integer",
|
||||
"placeholders": {
|
||||
"index": {
|
||||
"type": "int"
|
||||
},
|
||||
"length": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serverLimitReached": "已達伺服器上限! 請稍等{seconds}秒…",
|
||||
"@serverLimitReached": {
|
||||
"type": "integer",
|
||||
"placeholders": {
|
||||
"seconds": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"welcomeText": "嘿,嘿👋這是FluffyChat。 您可以登入任何與https://matrix.org相容的歸屬伺服器後和任何人聊天。 這是一個巨大的去中心化訊息網路!",
|
||||
"@welcomeText": {},
|
||||
"setWallpaper": "設定背景樣式",
|
||||
"@setWallpaper": {},
|
||||
"noContactInformationProvided": "伺服器沒有提供任何有效的聯絡資訊",
|
||||
"@noContactInformationProvided": {},
|
||||
"contactServerAdmin": "聯繫伺服器管理員",
|
||||
"@contactServerAdmin": {},
|
||||
"contactServerSecurity": "聯繫伺服器安管",
|
||||
"@contactServerSecurity": {},
|
||||
"continueText": "繼續",
|
||||
"@continueText": {},
|
||||
"blur": "模糊:",
|
||||
"@blur": {},
|
||||
"synchronizingPleaseWaitCounter": " 同步中… ({percentage}%)",
|
||||
"@synchronizingPleaseWaitCounter": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"percentage": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentNotificationSettings": "內容通知設定",
|
||||
"@contentNotificationSettings": {},
|
||||
"generalNotificationSettings": "常規通知設定",
|
||||
"@generalNotificationSettings": {},
|
||||
"roomNotificationSettings": "聊天室通知設定",
|
||||
"@roomNotificationSettings": {},
|
||||
"userSpecificNotificationSettings": "用戶特定通知設定",
|
||||
"@userSpecificNotificationSettings": {},
|
||||
"otherNotificationSettings": "其他通知設定",
|
||||
"@otherNotificationSettings": {},
|
||||
"notificationRuleContainsUserName": "包含用户名稱",
|
||||
"@notificationRuleContainsUserName": {},
|
||||
"notificationRuleContainsUserNameDescription": "當訊息帶有用户名稱時通知用戶。",
|
||||
"@notificationRuleContainsUserNameDescription": {},
|
||||
"notificationRuleMaster": "靜音所有通知",
|
||||
"@notificationRuleMaster": {},
|
||||
"notificationRuleMasterDescription": "覆蓋所有其他規則並禁止所有通知。",
|
||||
"@notificationRuleMasterDescription": {},
|
||||
"notificationRuleInviteForMe": "邀請我",
|
||||
"@notificationRuleInviteForMe": {},
|
||||
"notificationRuleSuppressNoticesDescription": "隱藏來自bot等的自動化消息。",
|
||||
"@notificationRuleSuppressNoticesDescription": {},
|
||||
"notificationRuleSuppressNotices": "隱藏自動化消息",
|
||||
"@notificationRuleSuppressNotices": {},
|
||||
"notificationRuleMemberEvent": "成員事件",
|
||||
"@notificationRuleMemberEvent": {},
|
||||
"notificationRuleMemberEventDescription": "隱藏成員事件的通知。",
|
||||
"@notificationRuleMemberEventDescription": {},
|
||||
"notificationRuleIsUserMention": "用户提及",
|
||||
"@notificationRuleIsUserMention": {},
|
||||
"notificationRuleInviteForMeDescription": "當用户被邀請到聊天室時,通知他們。",
|
||||
"@notificationRuleInviteForMeDescription": {},
|
||||
"commandHint_roomupgrade": "升級此聊天室至指定版本",
|
||||
"@commandHint_roomupgrade": {},
|
||||
"serverInformation": "伺服器資訊 :",
|
||||
"@serverInformation": {},
|
||||
"name": "名稱",
|
||||
"@name": {},
|
||||
"website": "網站",
|
||||
"@website": {},
|
||||
"compress": "壓縮",
|
||||
"@compress": {},
|
||||
"newChatRequest": "📩 新的聊天邀請",
|
||||
"@newChatRequest": {},
|
||||
"enterNewChat": "進入新聊天室",
|
||||
"@enterNewChat": {},
|
||||
"version": "版本",
|
||||
"@version": {},
|
||||
"unableToJoinChat": "無法加入聊天室。對話可能以被其他方結束。",
|
||||
"@unableToJoinChat": {},
|
||||
"appWantsToUseForLogin": "使用伺服器「{server} 」登入",
|
||||
"@appWantsToUseForLogin": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"server": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"italicText": "斜體",
|
||||
"@italicText": {},
|
||||
"boldText": "粗體",
|
||||
"@boldText": {},
|
||||
"strikeThrough": "刪除線",
|
||||
"@strikeThrough": {},
|
||||
"pleaseFillOut": "請填充",
|
||||
"@pleaseFillOut": {},
|
||||
"invalidUrl": "無效 url",
|
||||
"@invalidUrl": {},
|
||||
"appWantsToUseForLoginDescription": "你特此允許該應用程式和網站分享關於你的信息。",
|
||||
"@appWantsToUseForLoginDescription": {},
|
||||
"open": "打開",
|
||||
"@open": {},
|
||||
"waitingForServer": "等待伺服器中...",
|
||||
"@waitingForServer": {},
|
||||
"appIntroduction": "FluffyChat 讓你和你的朋友跨越工具聊天。在 https://matrix.org 了解更多或*繼續*。",
|
||||
"@appIntroduction": {},
|
||||
"previous": "上一個",
|
||||
"@previous": {},
|
||||
"otherPartyNotLoggedIn": "對方現未登入,未能接收訊息 !",
|
||||
"@otherPartyNotLoggedIn": {},
|
||||
"supportPage": "幫助頁面",
|
||||
"@supportPage": {},
|
||||
"addLink": "插入連結",
|
||||
"@addLink": {}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,96 +0,0 @@
|
|||
-------------------------------
|
||||
UBUNTU FONT LICENCE Version 1.0
|
||||
-------------------------------
|
||||
|
||||
PREAMBLE
|
||||
This licence allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely. The fonts, including any derivative works, can be
|
||||
bundled, embedded, and redistributed provided the terms of this licence
|
||||
are met. The fonts and derivatives, however, cannot be released under
|
||||
any other licence. The requirement for fonts to remain under this
|
||||
licence does not require any document created using the fonts or their
|
||||
derivatives to be published under this licence, as long as the primary
|
||||
purpose of the document is not to be a vehicle for the distribution of
|
||||
the fonts.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this licence and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Original Version" refers to the collection of Font Software components
|
||||
as received under this licence.
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to
|
||||
a new environment.
|
||||
|
||||
"Copyright Holder(s)" refers to all individuals and companies who have a
|
||||
copyright ownership of the Font Software.
|
||||
|
||||
"Substantially Changed" refers to Modified Versions which can be easily
|
||||
identified as dissimilar to the Font Software by users of the Font
|
||||
Software comparing the Original Version with the Modified Version.
|
||||
|
||||
To "Propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification and with or without charging
|
||||
a redistribution fee), making available to the public, and in some
|
||||
countries other activities as well.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
This licence does not grant any rights under trademark law and all such
|
||||
rights are reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of the Font Software, to propagate the Font Software, subject to
|
||||
the below conditions:
|
||||
|
||||
1) Each copy of the Font Software must contain the above copyright
|
||||
notice and this licence. These can be included either as stand-alone
|
||||
text files, human-readable headers or in the appropriate machine-
|
||||
readable metadata fields within text or binary files as long as those
|
||||
fields can be easily viewed by the user.
|
||||
|
||||
2) The font name complies with the following:
|
||||
(a) The Original Version must retain its name, unmodified.
|
||||
(b) Modified Versions which are Substantially Changed must be renamed to
|
||||
avoid use of the name of the Original Version or similar names entirely.
|
||||
(c) Modified Versions which are not Substantially Changed must be
|
||||
renamed to both (i) retain the name of the Original Version and (ii) add
|
||||
additional naming elements to distinguish the Modified Version from the
|
||||
Original Version. The name of such Modified Versions must be the name of
|
||||
the Original Version, with "derivative X" where X represents the name of
|
||||
the new work, appended to that name.
|
||||
|
||||
3) The name(s) of the Copyright Holder(s) and any contributor to the
|
||||
Font Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except (i) as required by this licence, (ii) to
|
||||
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
|
||||
their explicit written permission.
|
||||
|
||||
4) The Font Software, modified or unmodified, in part or in whole, must
|
||||
be distributed entirely under this licence, and must not be distributed
|
||||
under any other licence. The requirement for fonts to remain under this
|
||||
licence does not affect any document created using the Font Software,
|
||||
except any version of the Font Software extracted from a document
|
||||
created using the Font Software may only be distributed under this
|
||||
licence.
|
||||
|
||||
TERMINATION
|
||||
This licence becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
|
||||
DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -11,6 +11,7 @@ abstract class AppConfig {
|
|||
// #Pangea
|
||||
static String get applicationName => _applicationName;
|
||||
static String? _applicationWelcomeMessage;
|
||||
|
||||
static String? get applicationWelcomeMessage => _applicationWelcomeMessage;
|
||||
// #Pangea
|
||||
// static String _defaultHomeserver = 'matrix.org';
|
||||
|
|
@ -154,9 +155,6 @@ abstract class AppConfig {
|
|||
'https://sygnal.pangea.chat/_matrix/push/v1/notify';
|
||||
static const String? pushNotificationsPusherFormat = null;
|
||||
// Pangea#
|
||||
static const String emojiFontName = 'Noto Emoji';
|
||||
static const String emojiFontUrl =
|
||||
'https://github.com/googlefonts/noto-emoji/';
|
||||
static const double borderRadius = 18.0;
|
||||
static const double columnWidth = 360.0;
|
||||
static final Uri homeserverList = Uri(
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import 'package:fluffychat/pangea/spaces/utils/join_with_alias.dart';
|
|||
import 'package:fluffychat/pangea/spaces/utils/join_with_link.dart';
|
||||
import 'package:fluffychat/pangea/subscription/pages/settings_subscription.dart';
|
||||
import 'package:fluffychat/pangea/user/pages/find_partner.dart';
|
||||
import 'package:fluffychat/widgets/config_viewer.dart';
|
||||
import 'package:fluffychat/widgets/layouts/empty_page.dart';
|
||||
import 'package:fluffychat/widgets/layouts/two_column_layout.dart';
|
||||
import 'package:fluffychat/widgets/log_view.dart';
|
||||
|
|
@ -130,6 +131,14 @@ abstract class AppRoutes {
|
|||
const LogViewer(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/configs',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
const ConfigViewer(),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
GoRoute(
|
||||
path: '/join_with_link',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
abstract class SettingKeys {
|
||||
static const String renderHtml = 'chat.fluffy.renderHtml';
|
||||
static const String hideRedactedEvents = 'chat.fluffy.hideRedactedEvents';
|
||||
|
|
@ -30,9 +32,68 @@ abstract class SettingKeys {
|
|||
'chat.fluffy.swipeRightToLeftToReply';
|
||||
static const String experimentalVoip = 'chat.fluffy.experimental_voip';
|
||||
static const String showPresences = 'chat.fluffy.show_presences';
|
||||
static const String displayChatDetailsColumn =
|
||||
'chat.fluffy.display_chat_details_column';
|
||||
static const String noEncryptionWarningShown =
|
||||
'chat.fluffy.no_encryption_warning_shown';
|
||||
static const String shareKeysWith = 'chat.fluffy.share_keys_with';
|
||||
}
|
||||
|
||||
enum AppSettings<T> {
|
||||
audioRecordingNumChannels<int>('audioRecordingNumChannels', 1),
|
||||
audioRecordingAutoGain<bool>('audioRecordingAutoGain', true),
|
||||
audioRecordingEchoCancel<bool>('audioRecordingEchoCancel', false),
|
||||
audioRecordingNoiseSuppress<bool>('audioRecordingNoiseSuppress', true),
|
||||
audioRecordingBitRate<int>('audioRecordingBitRate', 64000),
|
||||
// #Pangea
|
||||
// audioRecordingSamplingRate<int>('audioRecordingSamplingRate', 44100),
|
||||
audioRecordingSamplingRate<int>('audioRecordingSamplingRate', 22050),
|
||||
// Pangea#
|
||||
pushNotificationsGatewayUrl<String>(
|
||||
'pushNotificationsGatewayUrl',
|
||||
'https://push.fluffychat.im/_matrix/push/v1/notify',
|
||||
),
|
||||
pushNotificationsPusherFormat<String>(
|
||||
'pushNotificationsPusherFormat',
|
||||
'event_id_only',
|
||||
),
|
||||
shareKeysWith<String>('chat.fluffy.share_keys_with_2', 'all'),
|
||||
noEncryptionWarningShown<bool>(
|
||||
'chat.fluffy.no_encryption_warning_shown',
|
||||
false,
|
||||
),
|
||||
displayChatDetailsColumn(
|
||||
'chat.fluffy.display_chat_details_column',
|
||||
false,
|
||||
);
|
||||
|
||||
final String key;
|
||||
final T defaultValue;
|
||||
|
||||
const AppSettings(this.key, this.defaultValue);
|
||||
}
|
||||
|
||||
extension AppSettingsBoolExtension on AppSettings<bool> {
|
||||
bool getItem(SharedPreferences store) => store.getBool(key) ?? defaultValue;
|
||||
|
||||
Future<void> setItem(SharedPreferences store, bool value) =>
|
||||
store.setBool(key, value);
|
||||
}
|
||||
|
||||
extension AppSettingsStringExtension on AppSettings<String> {
|
||||
String getItem(SharedPreferences store) =>
|
||||
store.getString(key) ?? defaultValue;
|
||||
|
||||
Future<void> setItem(SharedPreferences store, String value) =>
|
||||
store.setString(key, value);
|
||||
}
|
||||
|
||||
extension AppSettingsIntExtension on AppSettings<int> {
|
||||
int getItem(SharedPreferences store) => store.getInt(key) ?? defaultValue;
|
||||
|
||||
Future<void> setItem(SharedPreferences store, int value) =>
|
||||
store.setInt(key, value);
|
||||
}
|
||||
|
||||
extension AppSettingsDoubleExtension on AppSettings<double> {
|
||||
double getItem(SharedPreferences store) =>
|
||||
store.getDouble(key) ?? defaultValue;
|
||||
|
||||
Future<void> setItem(SharedPreferences store, double value) =>
|
||||
store.setDouble(key, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,27 +18,6 @@ abstract class FluffyThemes {
|
|||
static bool isThreeColumnMode(BuildContext context) =>
|
||||
MediaQuery.of(context).size.width > FluffyThemes.columnWidth * 3.5;
|
||||
|
||||
static const fallbackTextStyle = TextStyle(
|
||||
fontFamily: 'Ubuntu',
|
||||
fontFamilyFallback: ['NotoEmoji'],
|
||||
);
|
||||
|
||||
static var fallbackTextTheme = const TextTheme(
|
||||
bodyLarge: fallbackTextStyle,
|
||||
bodyMedium: fallbackTextStyle,
|
||||
labelLarge: fallbackTextStyle,
|
||||
bodySmall: fallbackTextStyle,
|
||||
labelSmall: fallbackTextStyle,
|
||||
displayLarge: fallbackTextStyle,
|
||||
displayMedium: fallbackTextStyle,
|
||||
displaySmall: fallbackTextStyle,
|
||||
headlineMedium: fallbackTextStyle,
|
||||
headlineSmall: fallbackTextStyle,
|
||||
titleLarge: fallbackTextStyle,
|
||||
titleMedium: fallbackTextStyle,
|
||||
titleSmall: fallbackTextStyle,
|
||||
);
|
||||
|
||||
static LinearGradient backgroundGradient(
|
||||
BuildContext context,
|
||||
int alpha,
|
||||
|
|
@ -77,11 +56,6 @@ abstract class FluffyThemes {
|
|||
useMaterial3: true,
|
||||
brightness: brightness,
|
||||
colorScheme: colorScheme,
|
||||
// #Pangea
|
||||
// causes memory leak on iOS
|
||||
// textTheme: fallbackTextTheme,
|
||||
// textTheme: scaleTextTheme(Theme.of(context).textTheme, MediaQuery.of(context).size),
|
||||
// Pangea#
|
||||
dividerColor: brightness == Brightness.dark
|
||||
? colorScheme.surfaceContainerHighest
|
||||
: colorScheme.surfaceContainer,
|
||||
|
|
@ -105,7 +79,14 @@ abstract class FluffyThemes {
|
|||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
filled: false,
|
||||
),
|
||||
chipTheme: ChipThemeData(
|
||||
showCheckmark: false,
|
||||
backgroundColor: colorScheme.surfaceContainer,
|
||||
side: BorderSide.none,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
toolbarHeight: isColumnMode ? 72 : 56,
|
||||
|
|
@ -197,6 +178,7 @@ extension BubbleColorTheme on ThemeData {
|
|||
Color get bubbleColor => brightness == Brightness.light
|
||||
? colorScheme.primary
|
||||
: colorScheme.primaryContainer;
|
||||
|
||||
Color get onBubbleColor => brightness == Brightness.light
|
||||
? colorScheme.onPrimary
|
||||
: colorScheme.onPrimaryContainer;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import '../key_verification/key_verification_dialog.dart';
|
|||
class BootstrapDialog extends StatefulWidget {
|
||||
final bool wipe;
|
||||
final Client client;
|
||||
|
||||
const BootstrapDialog({
|
||||
super.key,
|
||||
this.wipe = false,
|
||||
|
|
@ -132,7 +133,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
minLines: 2,
|
||||
maxLines: 4,
|
||||
readOnly: true,
|
||||
style: const TextStyle(fontFamily: 'UbuntuMono'),
|
||||
style: const TextStyle(fontFamily: 'RobotoMono'),
|
||||
controller: TextEditingController(text: key),
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.all(16),
|
||||
|
|
@ -257,7 +258,7 @@ class BootstrapDialogState extends State<BootstrapDialog> {
|
|||
? null
|
||||
: [AutofillHints.password],
|
||||
controller: _recoveryKeyTextEditingController,
|
||||
style: const TextStyle(fontFamily: 'UbuntuMono'),
|
||||
style: const TextStyle(fontFamily: 'RobotoMono'),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
hintStyle: TextStyle(
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
final AutoScrollController scrollController = AutoScrollController();
|
||||
|
||||
FocusNode inputFocus = FocusNode();
|
||||
late final FocusNode inputFocus;
|
||||
StreamSubscription<html.Event>? onFocusSub;
|
||||
|
||||
Timer? typingCoolDown;
|
||||
|
|
@ -323,8 +323,26 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
|
||||
KeyEventResult _shiftEnterKeyHandling(FocusNode node, KeyEvent evt) {
|
||||
if (!HardwareKeyboard.instance.isShiftPressed &&
|
||||
evt.logicalKey.keyLabel == 'Enter') {
|
||||
if (evt is KeyDownEvent) {
|
||||
send();
|
||||
}
|
||||
return KeyEventResult.handled;
|
||||
} else {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
inputFocus = FocusNode(
|
||||
onKeyEvent: (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile)
|
||||
? _shiftEnterKeyHandling
|
||||
: null,
|
||||
);
|
||||
|
||||
scrollController.addListener(_updateScrollController);
|
||||
inputFocus.addListener(_inputFocusListener);
|
||||
|
||||
|
|
@ -332,8 +350,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
WidgetsBinding.instance.addPostFrameCallback(_shareItems);
|
||||
super.initState();
|
||||
_displayChatDetailsColumn = ValueNotifier(
|
||||
Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ??
|
||||
false,
|
||||
AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store),
|
||||
);
|
||||
|
||||
sendingClient = Matrix.of(context).client;
|
||||
|
|
@ -924,8 +941,12 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
});
|
||||
}
|
||||
|
||||
void sendFileAction() async {
|
||||
final files = await selectFiles(context, allowMultiple: true);
|
||||
void sendFileAction({FileSelectorType type = FileSelectorType.any}) async {
|
||||
final files = await selectFiles(
|
||||
context,
|
||||
allowMultiple: true,
|
||||
type: type,
|
||||
);
|
||||
if (files.isEmpty) return;
|
||||
await showAdaptiveDialog(
|
||||
context: context,
|
||||
|
|
@ -949,27 +970,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
|
||||
void sendImageAction() async {
|
||||
final files = await selectFiles(
|
||||
context,
|
||||
allowMultiple: true,
|
||||
// #Pangea
|
||||
// type: FileSelectorType.images,
|
||||
type: FileSelectorType.media,
|
||||
// Pangea#
|
||||
);
|
||||
if (files.isEmpty) return;
|
||||
|
||||
await showAdaptiveDialog(
|
||||
context: context,
|
||||
builder: (c) => SendFileDialog(
|
||||
files: files,
|
||||
room: room,
|
||||
outerContext: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void openCameraAction() async {
|
||||
// Make sure the textfield is unfocused before opening the camera
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
|
|
@ -1235,12 +1235,13 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
// Pangea#
|
||||
)
|
||||
: null;
|
||||
// #Pangea
|
||||
// if (reasonInput == null) return;
|
||||
if (reasonInput == null) {
|
||||
// #Pangea
|
||||
clearSelectedEvents();
|
||||
// Pangea#
|
||||
return;
|
||||
}
|
||||
// Pangea#
|
||||
final reason = reasonInput.isEmpty ? null : reasonInput;
|
||||
for (final event in selectedEvents) {
|
||||
await showFutureLoadingDialog(
|
||||
|
|
@ -1572,35 +1573,23 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
|
||||
void goToNewRoomAction() async {
|
||||
if (OkCancelResult.ok !=
|
||||
await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).goToTheNewRoom,
|
||||
message: room
|
||||
.getState(EventTypes.RoomTombstone)!
|
||||
.parsedTombstoneContent
|
||||
.body,
|
||||
okLabel: L10n.of(context).ok,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
final result = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
final roomId = room.client.joinRoom(
|
||||
room
|
||||
.getState(EventTypes.RoomTombstone)!
|
||||
.parsedTombstoneContent
|
||||
.replacementRoom,
|
||||
);
|
||||
await room.leave();
|
||||
return roomId;
|
||||
},
|
||||
future: () => room.client.joinRoomById(
|
||||
room
|
||||
.getState(EventTypes.RoomTombstone)!
|
||||
.parsedTombstoneContent
|
||||
.replacementRoom,
|
||||
),
|
||||
);
|
||||
if (result.error != null) return;
|
||||
if (!mounted) return;
|
||||
context.go('/rooms/${result.result!}');
|
||||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.leave,
|
||||
);
|
||||
if (result.error == null) {
|
||||
context.go('/rooms/${result.result!}');
|
||||
}
|
||||
}
|
||||
|
||||
void onSelectMessage(Event event) {
|
||||
|
|
@ -1671,7 +1660,10 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
sendFileAction();
|
||||
}
|
||||
if (choice == 'image') {
|
||||
sendImageAction();
|
||||
sendFileAction(type: FileSelectorType.images);
|
||||
}
|
||||
if (choice == 'video') {
|
||||
sendFileAction(type: FileSelectorType.videos);
|
||||
}
|
||||
if (choice == 'camera') {
|
||||
openCameraAction();
|
||||
|
|
@ -1954,10 +1946,10 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
late final ValueNotifier<bool> _displayChatDetailsColumn;
|
||||
|
||||
void toggleDisplayChatDetailsColumn() async {
|
||||
await Matrix.of(context).store.setBool(
|
||||
SettingKeys.displayChatDetailsColumn,
|
||||
!_displayChatDetailsColumn.value,
|
||||
);
|
||||
await AppSettings.displayChatDetailsColumn.setItem(
|
||||
Matrix.of(context).store,
|
||||
!_displayChatDetailsColumn.value,
|
||||
);
|
||||
_displayChatDetailsColumn.value = !_displayChatDetailsColumn.value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class ChatAppBarListTile extends StatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Linkify(
|
||||
text: title,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
|
|
@ -9,16 +9,15 @@ import 'package:fluffychat/pages/chat/chat.dart';
|
|||
import 'package:fluffychat/pages/chat/events/message.dart';
|
||||
import 'package:fluffychat/pages/chat/seen_by_row.dart';
|
||||
import 'package:fluffychat/pages/chat/typing_indicators.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_message.dart';
|
||||
import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart';
|
||||
import 'package:fluffychat/utils/account_config.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';
|
||||
|
||||
class ChatEventList extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
||||
const ChatEventList({
|
||||
super.key,
|
||||
required this.controller,
|
||||
|
|
@ -27,14 +26,10 @@ class ChatEventList extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final timeline = controller.timeline;
|
||||
if (timeline == null) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (timeline == null) {
|
||||
return const Center(child: CupertinoActivityIndicator());
|
||||
}
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final colors = [
|
||||
|
|
@ -99,6 +94,7 @@ class ChatEventList extends StatelessWidget {
|
|||
// Request history button or progress indicator:
|
||||
if (i == events.length + 1) {
|
||||
if (timeline.isRequestingHistory) {
|
||||
// #Pangea
|
||||
// return const Center(
|
||||
// child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
// );
|
||||
|
|
@ -185,15 +181,8 @@ class ChatEventList extends StatelessWidget {
|
|||
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} ',
|
||||
),
|
||||
),
|
||||
onMention: () => controller.sendController.text +=
|
||||
'${event.senderFromMemoryOrFallback.mention} ',
|
||||
highlightMarker:
|
||||
controller.scrollToEventIdMarker == event.eventId,
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -112,88 +112,120 @@ class ChatInputRow extends StatelessWidget {
|
|||
AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
width: controller.sendController.text.isNotEmpty ? 0 : height,
|
||||
height: height,
|
||||
width: controller.sendController.text.isEmpty ? height : 0,
|
||||
alignment: Alignment.center,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: PopupMenuButton<String>(
|
||||
icon: const Icon(Icons.add_outlined),
|
||||
icon: const Icon(Icons.add_circle_outline),
|
||||
iconColor: theme.colorScheme.onPrimaryContainer,
|
||||
onSelected: controller.onAddPopupMenuButtonSelected,
|
||||
itemBuilder: (BuildContext context) =>
|
||||
<PopupMenuEntry<String>>[
|
||||
PopupMenuItem<String>(
|
||||
value: 'file',
|
||||
child: ListTile(
|
||||
leading: const CircleAvatar(
|
||||
backgroundColor: Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
child: Icon(Icons.attachment_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).sendFile),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'image',
|
||||
child: ListTile(
|
||||
leading: const CircleAvatar(
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
child: Icon(Icons.image_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).sendImage),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
if (PlatformInfos.isMobile)
|
||||
PopupMenuItem<String>(
|
||||
value: 'camera',
|
||||
child: ListTile(
|
||||
leading: const CircleAvatar(
|
||||
backgroundColor: Colors.purple,
|
||||
foregroundColor: Colors.white,
|
||||
child: Icon(Icons.camera_alt_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).openCamera),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
if (PlatformInfos.isMobile)
|
||||
PopupMenuItem<String>(
|
||||
value: 'camera-video',
|
||||
child: ListTile(
|
||||
leading: const CircleAvatar(
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
child: Icon(Icons.videocam_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).openVideoCamera),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
if (PlatformInfos.isMobile)
|
||||
PopupMenuItem<String>(
|
||||
value: 'location',
|
||||
child: ListTile(
|
||||
leading: const CircleAvatar(
|
||||
backgroundColor: Colors.brown,
|
||||
foregroundColor: Colors.white,
|
||||
child: Icon(Icons.gps_fixed_outlined),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
foregroundColor: theme.colorScheme.primaryContainer,
|
||||
child: const Icon(Icons.gps_fixed_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).shareLocation),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'image',
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.colorScheme.onPrimaryContainer,
|
||||
foregroundColor: theme.colorScheme.primaryContainer,
|
||||
child: const Icon(Icons.photo_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).sendImage),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'video',
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.colorScheme.onPrimaryContainer,
|
||||
foregroundColor: theme.colorScheme.primaryContainer,
|
||||
child: const Icon(Icons.video_camera_back_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).sendVideo),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'file',
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.colorScheme.onPrimaryContainer,
|
||||
foregroundColor: theme.colorScheme.primaryContainer,
|
||||
child: const Icon(Icons.attachment_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).sendFile),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (PlatformInfos.isMobile)
|
||||
AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
width: controller.sendController.text.isNotEmpty ? 0 : height,
|
||||
height: height,
|
||||
alignment: Alignment.center,
|
||||
decoration: const BoxDecoration(),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: PopupMenuButton(
|
||||
icon: const Icon(Icons.camera_alt_outlined),
|
||||
onSelected: controller.onAddPopupMenuButtonSelected,
|
||||
iconColor: theme.colorScheme.onPrimaryContainer,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem<String>(
|
||||
value: 'camera-video',
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
foregroundColor: theme.colorScheme.primaryContainer,
|
||||
child: const Icon(Icons.videocam_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).recordAVideo),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'camera',
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
foregroundColor: theme.colorScheme.primaryContainer,
|
||||
child: const Icon(Icons.camera_alt_outlined),
|
||||
),
|
||||
title: Text(L10n.of(context).takeAPhoto),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: height,
|
||||
width: height,
|
||||
alignment: Alignment.center,
|
||||
child: IconButton(
|
||||
tooltip: L10n.of(context).emojis,
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
icon: PageTransitionSwitcher(
|
||||
transitionBuilder: (
|
||||
Widget child,
|
||||
|
|
|
|||
|
|
@ -190,11 +190,6 @@ class ChatView extends StatelessWidget {
|
|||
if (scrollUpBannerEventId != null) {
|
||||
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
|
||||
}
|
||||
final tombstoneEvent =
|
||||
controller.room.getState(EventTypes.RoomTombstone);
|
||||
if (tombstoneEvent != null) {
|
||||
appbarBottomHeight += ChatAppBarListTile.fixedHeight;
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actionsIconTheme: IconThemeData(
|
||||
|
|
@ -233,18 +228,6 @@ class ChatView extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
PinnedEvents(controller),
|
||||
if (tombstoneEvent != null)
|
||||
ChatAppBarListTile(
|
||||
title: tombstoneEvent.parsedTombstoneContent.body,
|
||||
leading: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Icon(Icons.upgrade_outlined),
|
||||
),
|
||||
trailing: TextButton(
|
||||
onPressed: controller.goToNewRoomAction,
|
||||
child: Text(L10n.of(context).goToTheNewRoom),
|
||||
),
|
||||
),
|
||||
if (scrollUpBannerEventId != null)
|
||||
ChatAppBarListTile(
|
||||
leading: IconButton(
|
||||
|
|
@ -329,7 +312,21 @@ class ChatView extends StatelessWidget {
|
|||
child: ChatEventList(controller: controller),
|
||||
),
|
||||
),
|
||||
if (controller.room.canSendDefaultMessages &&
|
||||
if (controller.room.isExtinct)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: bottomSheetPadding,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(Icons.chevron_right),
|
||||
label: Text(L10n.of(context).enterNewChat),
|
||||
onPressed: controller.goToNewRoomAction,
|
||||
),
|
||||
)
|
||||
else if (controller.room.canSendDefaultMessages &&
|
||||
controller.room.membership == Membership.join)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ String commandHint(L10n l10n, String command) {
|
|||
return l10n.commandHint_ignore;
|
||||
case 'unignore':
|
||||
return l10n.commandHint_unignore;
|
||||
case 'roomupgrade':
|
||||
return l10n.commandHint_roomupgrade;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ extension EventInfoDialogExtension on Event {
|
|||
class EventInfoDialog extends StatelessWidget {
|
||||
final Event event;
|
||||
final L10n l10n;
|
||||
|
||||
const EventInfoDialog({
|
||||
required this.event,
|
||||
required this.l10n,
|
||||
|
|
@ -42,10 +43,8 @@ class EventInfoDialog extends StatelessWidget {
|
|||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).messageInfo),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_downward_outlined),
|
||||
leading: CloseButton(
|
||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||
tooltip: L10n.of(context).close,
|
||||
),
|
||||
),
|
||||
body: ListView(
|
||||
|
|
|
|||
|
|
@ -561,20 +561,27 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
// Pangea#
|
||||
) ...[
|
||||
const SizedBox(height: 8),
|
||||
Linkify(
|
||||
text: fileDescription,
|
||||
style: TextStyle(
|
||||
color: widget.color,
|
||||
fontSize: widget.fontSize,
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: widget.linkColor,
|
||||
fontSize: widget.fontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: widget.linkColor,
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: widget.color,
|
||||
fontSize: widget.fontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: widget.linkColor,
|
||||
fontSize: widget.fontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: widget.linkColor,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
],
|
||||
],
|
||||
|
|
@ -586,6 +593,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
/// To use a MatrixFile as an AudioSource for the just_audio package
|
||||
class MatrixFileAudioSource extends StreamAudioSource {
|
||||
final MatrixFile file;
|
||||
|
||||
MatrixFileAudioSource(this.file);
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
static const Set<String> allowedHtmlTags = {
|
||||
'font',
|
||||
'del',
|
||||
's',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
|
|
@ -445,7 +446,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
if (node.parent?.localName == 'ol')
|
||||
TextSpan(
|
||||
text:
|
||||
'${(node.parent?.nodes.indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ',
|
||||
'${(node.parent?.nodes.whereType<dom.Element>().toList().indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ',
|
||||
),
|
||||
..._renderWithLineBreaks(
|
||||
node.nodes,
|
||||
|
|
@ -466,7 +467,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
border: Border(
|
||||
left: BorderSide(
|
||||
color: textColor,
|
||||
width: 3,
|
||||
width: 5,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -514,7 +515,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
),
|
||||
textStyle: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontFamily: 'UbuntuMono',
|
||||
fontFamily: 'RobotoMono',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -638,6 +639,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
'strong' => const TextStyle(fontWeight: FontWeight.bold),
|
||||
'em' || 'i' => const TextStyle(fontStyle: FontStyle.italic),
|
||||
'del' ||
|
||||
's' ||
|
||||
'strikethrough' =>
|
||||
const TextStyle(decoration: TextDecoration.lineThrough),
|
||||
'u' => const TextStyle(decoration: TextDecoration.underline),
|
||||
|
|
@ -671,6 +673,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// #Pangea
|
||||
// return Text.rich(
|
||||
dom.Node parsed = parser.parse(html).body ?? dom.Element.html('');
|
||||
if (tokens != null) {
|
||||
parsed = _tokenizeHtml(parsed, html, List.from(tokens!));
|
||||
|
|
@ -687,9 +690,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
},
|
||||
// Pangea#
|
||||
child: Text.rich(
|
||||
// #Pangea
|
||||
textScaler: TextScaler.noScaling,
|
||||
// Pangea#
|
||||
_renderHtml(
|
||||
|
|
|
|||
|
|
@ -79,12 +79,19 @@ class ImageBubble extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final borderRadius =
|
||||
var borderRadius =
|
||||
this.borderRadius ?? BorderRadius.circular(AppConfig.borderRadius);
|
||||
|
||||
final fileDescription = event.fileDescription;
|
||||
final textColor = this.textColor;
|
||||
|
||||
if (fileDescription != null) {
|
||||
borderRadius = borderRadius.copyWith(
|
||||
bottomLeft: Radius.zero,
|
||||
bottomRight: Radius.zero,
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
|
|
@ -122,20 +129,29 @@ class ImageBubble extends StatelessWidget {
|
|||
if (fileDescription != null && textColor != null)
|
||||
SizedBox(
|
||||
width: width,
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize:
|
||||
AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize:
|
||||
AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import 'package:swipe_to_action/swipe_to_action.dart';
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/events/room_creation_state_event.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/enums/use_type.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
|
|
@ -17,12 +16,12 @@ import 'package:fluffychat/utils/file_description.dart';
|
|||
import 'package:fluffychat/utils/string_color.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart';
|
||||
import '../../../config/app_config.dart';
|
||||
import 'message_content.dart';
|
||||
import 'message_reactions.dart';
|
||||
import 'reply_content.dart';
|
||||
import 'state_message.dart';
|
||||
import 'verification_request_content.dart';
|
||||
|
||||
class Message extends StatelessWidget {
|
||||
final Event event;
|
||||
|
|
@ -30,10 +29,10 @@ class Message extends StatelessWidget {
|
|||
final Event? previousEvent;
|
||||
final bool displayReadMarker;
|
||||
final void Function(Event) onSelect;
|
||||
final void Function(Event) onAvatarTab;
|
||||
final void Function(Event) onInfoTab;
|
||||
final void Function(String) scrollToEventId;
|
||||
final void Function() onSwipe;
|
||||
final void Function() onMention;
|
||||
final bool longPressSelect;
|
||||
final bool selected;
|
||||
final Timeline timeline;
|
||||
|
|
@ -42,12 +41,12 @@ class Message extends StatelessWidget {
|
|||
final void Function()? resetAnimateIn;
|
||||
final bool wallpaperMode;
|
||||
final ScrollController scrollController;
|
||||
final List<Color> colors;
|
||||
// #Pangea
|
||||
final bool immersionMode;
|
||||
final ChatController controller;
|
||||
final bool isButton;
|
||||
// Pangea#
|
||||
final List<Color> colors;
|
||||
|
||||
const Message(
|
||||
this.event, {
|
||||
|
|
@ -57,7 +56,6 @@ class Message extends StatelessWidget {
|
|||
this.longPressSelect = false,
|
||||
required this.onSelect,
|
||||
required this.onInfoTab,
|
||||
required this.onAvatarTab,
|
||||
required this.scrollToEventId,
|
||||
required this.onSwipe,
|
||||
this.selected = false,
|
||||
|
|
@ -66,13 +64,14 @@ class Message extends StatelessWidget {
|
|||
this.animateIn = false,
|
||||
this.resetAnimateIn,
|
||||
this.wallpaperMode = false,
|
||||
required this.onMention,
|
||||
required this.scrollController,
|
||||
required this.colors,
|
||||
// #Pangea
|
||||
required this.immersionMode,
|
||||
required this.controller,
|
||||
this.isButton = false,
|
||||
// Pangea#
|
||||
required this.colors,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -127,7 +126,7 @@ class Message extends StatelessWidget {
|
|||
|
||||
if (event.type == EventTypes.Message &&
|
||||
event.messageType == EventTypes.KeyVerificationRequest) {
|
||||
return VerificationRequestContent(event: event, timeline: timeline);
|
||||
return StateMessage(event);
|
||||
}
|
||||
|
||||
final client = Matrix.of(context).client;
|
||||
|
|
@ -201,10 +200,6 @@ class Message extends StatelessWidget {
|
|||
event.onlyEmotes &&
|
||||
event.numberEmotes > 0 &&
|
||||
event.numberEmotes <= 3);
|
||||
final noPadding = {
|
||||
MessageTypes.File,
|
||||
MessageTypes.Audio,
|
||||
}.contains(event.messageType);
|
||||
|
||||
if (ownMessage) {
|
||||
// #Pangea
|
||||
|
|
@ -310,10 +305,14 @@ class Message extends StatelessWidget {
|
|||
return Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
onTap: () => showMemberActionsPopupMenu(
|
||||
context: context,
|
||||
user: user,
|
||||
onMention: onMention,
|
||||
),
|
||||
presenceUserId: user.stateKey,
|
||||
presenceBackgroundColor:
|
||||
wallpaperMode ? Colors.transparent : null,
|
||||
onTap: () => onAvatarTab(event),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -442,12 +441,6 @@ class Message extends StatelessWidget {
|
|||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
padding: noBubble || noPadding
|
||||
? EdgeInsets.zero
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth:
|
||||
FluffyThemes.columnWidth *
|
||||
|
|
@ -497,23 +490,35 @@ class Message extends StatelessWidget {
|
|||
padding:
|
||||
const EdgeInsets
|
||||
.only(
|
||||
bottom: 4.0,
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
),
|
||||
child: InkWell(
|
||||
child: Material(
|
||||
color: Colors
|
||||
.transparent,
|
||||
borderRadius:
|
||||
ReplyContent
|
||||
.borderRadius,
|
||||
onTap: () =>
|
||||
scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage:
|
||||
ownMessage,
|
||||
timeline:
|
||||
timeline,
|
||||
child: InkWell(
|
||||
borderRadius:
|
||||
ReplyContent
|
||||
.borderRadius,
|
||||
onTap: () =>
|
||||
scrollToEventId(
|
||||
replyEvent
|
||||
.eventId,
|
||||
),
|
||||
child:
|
||||
AbsorbPointer(
|
||||
child:
|
||||
ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage:
|
||||
ownMessage,
|
||||
timeline:
|
||||
timeline,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -537,68 +542,41 @@ class Message extends StatelessWidget {
|
|||
// Pangea#
|
||||
),
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes
|
||||
.edit,
|
||||
)
|
||||
// #Pangea
|
||||
||
|
||||
(pangeaMessageEvent
|
||||
?.showUseType ??
|
||||
false)
|
||||
// Pangea#
|
||||
)
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
))
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
bottom: 8.0,
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
// #Pangea
|
||||
if (pangeaMessageEvent
|
||||
?.showUseType ??
|
||||
false) ...[
|
||||
pangeaMessageEvent!
|
||||
.msgUseType
|
||||
.iconView(
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor
|
||||
.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
displayEvent
|
||||
.originServerTs
|
||||
.localizedTimeShort(
|
||||
context,
|
||||
textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
],
|
||||
if (event
|
||||
.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes
|
||||
.edit,
|
||||
)) ...[
|
||||
// Pangea#
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
style: TextStyle(
|
||||
color: textColor
|
||||
.withAlpha(
|
||||
164,
|
||||
),
|
||||
size: 14,
|
||||
fontSize: 11,
|
||||
),
|
||||
Text(
|
||||
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
|
||||
style: TextStyle(
|
||||
color: textColor
|
||||
.withAlpha(
|
||||
164,
|
||||
),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/events/video_player.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_rich_text.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
|
|
@ -34,6 +33,7 @@ class MessageContent extends StatelessWidget {
|
|||
final Color linkColor;
|
||||
final void Function(Event)? onInfoTab;
|
||||
final BorderRadius borderRadius;
|
||||
final Timeline timeline;
|
||||
// #Pangea
|
||||
final PangeaMessageEvent? pangeaMessageEvent;
|
||||
//question: are there any performance benefits to using booleans
|
||||
|
|
@ -47,7 +47,6 @@ class MessageContent extends StatelessWidget {
|
|||
final bool isTransitionAnimation;
|
||||
final ReadingAssistanceMode? readingAssistanceMode;
|
||||
// Pangea#
|
||||
final Timeline timeline;
|
||||
|
||||
const MessageContent(
|
||||
this.event, {
|
||||
|
|
@ -55,6 +54,8 @@ class MessageContent extends StatelessWidget {
|
|||
super.key,
|
||||
required this.timeline,
|
||||
required this.textColor,
|
||||
required this.linkColor,
|
||||
required this.borderRadius,
|
||||
// #Pangea
|
||||
this.pangeaMessageEvent,
|
||||
required this.immersionMode,
|
||||
|
|
@ -65,8 +66,6 @@ class MessageContent extends StatelessWidget {
|
|||
this.isTransitionAnimation = false,
|
||||
this.readingAssistanceMode,
|
||||
// Pangea#
|
||||
required this.linkColor,
|
||||
required this.borderRadius,
|
||||
});
|
||||
|
||||
// #Pangea
|
||||
|
|
@ -270,31 +269,38 @@ class MessageContent extends StatelessWidget {
|
|||
if (event.messageType == MessageTypes.Emote) {
|
||||
html = '* $html';
|
||||
}
|
||||
return HtmlMessage(
|
||||
html: html,
|
||||
textColor: textColor,
|
||||
room: event.room,
|
||||
// #Pangea
|
||||
event: event,
|
||||
overlayController: overlayController,
|
||||
controller: controller,
|
||||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: prevEvent,
|
||||
isSelected: overlayController != null ? isSelected : null,
|
||||
onClick: event.isActivityMessage ? null : onClick,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
// Pangea#
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: HtmlMessage(
|
||||
html: html,
|
||||
textColor: textColor,
|
||||
room: event.room,
|
||||
fontSize:
|
||||
AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize:
|
||||
AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
// #Pangea
|
||||
event: event,
|
||||
overlayController: overlayController,
|
||||
controller: controller,
|
||||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: prevEvent,
|
||||
isSelected: overlayController != null ? isSelected : null,
|
||||
onClick: event.isActivityMessage ? null : onClick,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
// Pangea#
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
);
|
||||
}
|
||||
// else we fall through to the normal message rendering
|
||||
|
|
@ -379,47 +385,41 @@ class MessageContent extends StatelessWidget {
|
|||
// Pangea#
|
||||
|
||||
// #Pangea
|
||||
|
||||
final messageTextStyle =
|
||||
AppConfig.messageTextStyle(event, textColor);
|
||||
|
||||
if (immersionMode && pangeaMessageEvent != null) {
|
||||
return Flexible(
|
||||
child: PangeaRichText(
|
||||
style: messageTextStyle,
|
||||
if (pangeaMessageEvent != null &&
|
||||
pangeaMessageEvent!.shouldShowToolbar) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: MessageTokenText(
|
||||
pangeaMessageEvent: pangeaMessageEvent!,
|
||||
immersionMode: immersionMode,
|
||||
isOverlay: overlayController != null,
|
||||
controller: controller,
|
||||
tokens:
|
||||
pangeaMessageEvent!.messageDisplayRepresentation?.tokens,
|
||||
style: messageTextStyle,
|
||||
onClick: onClick,
|
||||
isSelected: overlayController != null ? isSelected : null,
|
||||
messageMode: overlayController?.toolbarMode,
|
||||
isHighlighted: (PangeaToken token) =>
|
||||
overlayController?.toolbarMode.associatedActivityType !=
|
||||
null &&
|
||||
overlayController?.practiceSelection
|
||||
?.hasActiveActivityByToken(
|
||||
overlayController!
|
||||
.toolbarMode.associatedActivityType!,
|
||||
token,
|
||||
) ==
|
||||
true,
|
||||
overlayController: overlayController,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (pangeaMessageEvent != null &&
|
||||
pangeaMessageEvent!.shouldShowToolbar) {
|
||||
return MessageTokenText(
|
||||
pangeaMessageEvent: pangeaMessageEvent!,
|
||||
tokens:
|
||||
pangeaMessageEvent!.messageDisplayRepresentation?.tokens,
|
||||
style: messageTextStyle,
|
||||
onClick: onClick,
|
||||
isSelected: overlayController != null ? isSelected : null,
|
||||
messageMode: overlayController?.toolbarMode,
|
||||
isHighlighted: (PangeaToken token) =>
|
||||
overlayController?.toolbarMode.associatedActivityType !=
|
||||
null &&
|
||||
overlayController?.practiceSelection
|
||||
?.hasActiveActivityByToken(
|
||||
overlayController!
|
||||
.toolbarMode.associatedActivityType!,
|
||||
token,
|
||||
) ==
|
||||
true,
|
||||
overlayController: overlayController,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
);
|
||||
}
|
||||
|
||||
// Pangea#
|
||||
|
||||
return
|
||||
|
|
@ -433,27 +433,34 @@ class MessageContent extends StatelessWidget {
|
|||
prevEvent: prevEvent,
|
||||
child:
|
||||
// Pangea#
|
||||
Linkify(
|
||||
text: event.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
// #Pangea
|
||||
// style: TextStyle(
|
||||
// color: textColor,
|
||||
// fontSize: bigEmotes ? fontSize * 5 : fontSize,
|
||||
// decoration: event.redacted ? TextDecoration.lineThrough : null,
|
||||
// ),
|
||||
style: messageTextStyle,
|
||||
// Pangea#
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: fontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
child: Linkify(
|
||||
text: event.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
),
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
// #Pangea
|
||||
// style: TextStyle(
|
||||
// color: textColor,
|
||||
// fontSize: bigEmotes ? fontSize * 5 : fontSize,
|
||||
// decoration: event.redacted ? TextDecoration.lineThrough : null,
|
||||
// ),
|
||||
style: messageTextStyle,
|
||||
// Pangea#
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: fontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -511,13 +518,19 @@ class _ButtonContent extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onPressed,
|
||||
child: Text(
|
||||
'$icon $label',
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: fontSize,
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onPressed,
|
||||
child: Text(
|
||||
'$icon $label',
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -30,83 +30,79 @@ class MessageDownloadContent extends StatelessWidget {
|
|||
?.tryGet<String>('mimetype')
|
||||
?.toUpperCase() ??
|
||||
'UNKNOWN');
|
||||
final sizeString = event.sizeString;
|
||||
final sizeString = event.sizeString ?? '?MB';
|
||||
final fileDescription = event.fileDescription;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 8,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => event.saveFile(context),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.file_download_outlined,
|
||||
color: textColor,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Flexible(
|
||||
child: Text(
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
onTap: () => event.saveFile(context),
|
||||
child: Container(
|
||||
width: 400,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 16,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: textColor.withAlpha(32),
|
||||
child: Icon(Icons.file_download_outlined, color: textColor),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
filename,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
filetype,
|
||||
style: TextStyle(
|
||||
color: linkColor,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
if (sizeString != null)
|
||||
Text(
|
||||
sizeString,
|
||||
style: TextStyle(
|
||||
color: linkColor,
|
||||
),
|
||||
'$sizeString | $filetype',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(color: textColor, fontSize: 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (fileDescription != null)
|
||||
Linkify(
|
||||
text: fileDescription,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
if (fileDescription != null) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:collection/collection.dart' show IterableExtension;
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -111,7 +112,9 @@ class _Reaction extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final textColor =
|
||||
theme.brightness == Brightness.dark ? Colors.white : Colors.black;
|
||||
final color = theme.colorScheme.surface;
|
||||
final color = reacted == true
|
||||
? theme.bubbleColor
|
||||
: theme.colorScheme.surfaceContainerHigh;
|
||||
Widget content;
|
||||
if (reactionKey.startsWith('mxc://')) {
|
||||
content = Row(
|
||||
|
|
@ -144,7 +147,7 @@ class _Reaction extends StatelessWidget {
|
|||
content = Text(
|
||||
renderKey.toString() + (count > 1 ? ' $count' : ''),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
color: reacted == true ? theme.onBubbleColor : textColor,
|
||||
fontSize: DefaultTextStyle.of(context).style.fontSize,
|
||||
),
|
||||
);
|
||||
|
|
@ -156,12 +159,6 @@ class _Reaction extends StatelessWidget {
|
|||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: reacted!
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
|
|
|
|||
1
lib/pages/chat/events/old.dart
Normal file
1
lib/pages/chat/events/old.dart
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -10,14 +10,12 @@ class ReplyContent extends StatelessWidget {
|
|||
final Event replyEvent;
|
||||
final bool ownMessage;
|
||||
final Timeline? timeline;
|
||||
final Color? backgroundColor;
|
||||
|
||||
const ReplyContent(
|
||||
this.replyEvent, {
|
||||
this.ownMessage = false,
|
||||
super.key,
|
||||
this.timeline,
|
||||
this.backgroundColor,
|
||||
});
|
||||
|
||||
static const BorderRadius borderRadius = BorderRadius.only(
|
||||
|
|
@ -40,16 +38,18 @@ class ReplyContent extends StatelessWidget {
|
|||
: theme.colorScheme.tertiary;
|
||||
|
||||
return Material(
|
||||
color: backgroundColor ??
|
||||
theme.colorScheme.surface.withAlpha(ownMessage ? 50 : 80),
|
||||
color: Colors.transparent,
|
||||
borderRadius: borderRadius,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 3,
|
||||
width: 5,
|
||||
height: fontSize * 2 + 16,
|
||||
color: color,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Flexible(
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import 'package:fluffychat/widgets/avatar.dart';
|
|||
class RoomCreationStateEvent extends StatefulWidget {
|
||||
// Pangea#
|
||||
final Event event;
|
||||
|
||||
const RoomCreationStateEvent({required this.event, super.key});
|
||||
|
||||
// #Pangea
|
||||
|
|
@ -61,52 +62,59 @@ class RoomCreationStateEventState extends State<RoomCreationStateEvent> {
|
|||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32.0),
|
||||
child: Center(
|
||||
child: Material(
|
||||
color: theme.colorScheme.surface.withAlpha(128),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: event.room.avatar,
|
||||
name: roomName,
|
||||
// #Pangea
|
||||
userId: event.room.directChatMatrixID,
|
||||
useRive: true,
|
||||
// Pangea#
|
||||
size: Avatar.defaultSize * 2,
|
||||
),
|
||||
Text(
|
||||
roomName,
|
||||
style: theme.textTheme.headlineSmall,
|
||||
),
|
||||
Text(
|
||||
'${event.originServerTs.localizedTime(context)} | ${l10n.countParticipants((event.room.summary.mJoinedMemberCount ?? 1) + (event.room.summary.mInvitedMemberCount ?? 0))}',
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
// #Pangea
|
||||
InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.clickMessage,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 16.0,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 256),
|
||||
child: Material(
|
||||
color: theme.colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: event.room.avatar,
|
||||
name: roomName,
|
||||
size: Avatar.defaultSize * 2,
|
||||
// #Pangea
|
||||
userId: event.room.directChatMatrixID,
|
||||
useRive: true,
|
||||
// Pangea#
|
||||
),
|
||||
onClose: () => setState(() {}),
|
||||
),
|
||||
if (_members <= 1 && InstructionsEnum.clickMessage.isToggledOff)
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.emptyChatWarning,
|
||||
padding: EdgeInsets.only(
|
||||
Text(
|
||||
roomName,
|
||||
style: theme.textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'${event.originServerTs.localizedTime(context)} | ${l10n.countParticipants((event.room.summary.mJoinedMemberCount ?? 1) + (event.room.summary.mInvitedMemberCount ?? 0))}',
|
||||
style: theme.textTheme.labelSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
// #Pangea
|
||||
InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.clickMessage,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 16.0,
|
||||
),
|
||||
onClose: () => setState(() {}),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
if (_members <= 1 &&
|
||||
InstructionsEnum.clickMessage.isToggledOff)
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.emptyChatWarning,
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 16.0,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../../config/app_config.dart';
|
||||
|
||||
class VerificationRequestContent extends StatelessWidget {
|
||||
final Event event;
|
||||
final Timeline timeline;
|
||||
|
||||
const VerificationRequestContent({
|
||||
required this.event,
|
||||
required this.timeline,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final events = event.aggregatedEvents(timeline, 'm.reference');
|
||||
final done = events.where((e) => e.type == EventTypes.KeyVerificationDone);
|
||||
final start =
|
||||
events.where((e) => e.type == EventTypes.KeyVerificationStart);
|
||||
final cancel =
|
||||
events.where((e) => e.type == EventTypes.KeyVerificationCancel);
|
||||
final fullyDone = done.length >= 2;
|
||||
final started = start.isNotEmpty;
|
||||
final canceled = cancel.isNotEmpty;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 4.0,
|
||||
),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: theme.colorScheme.surface,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.lock_outlined,
|
||||
color: canceled
|
||||
? Colors.red
|
||||
: (fullyDone ? Colors.green : Colors.grey),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
canceled
|
||||
? 'Error ${cancel.first.content.tryGet<String>('code')}: ${cancel.first.content.tryGet<String>('reason')}'
|
||||
: (fullyDone
|
||||
? L10n.of(context).verifySuccess
|
||||
: (started
|
||||
? L10n.of(context).loadingPleaseWait
|
||||
: L10n.of(context).newVerificationRequest)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ class EventVideoPlayer extends StatefulWidget {
|
|||
// #Pangea
|
||||
final ChatController? chatController;
|
||||
// Pangea#
|
||||
|
||||
const EventVideoPlayer(
|
||||
this.event, {
|
||||
this.textColor,
|
||||
|
|
@ -45,33 +46,38 @@ class EventVideoPlayer extends StatefulWidget {
|
|||
}
|
||||
|
||||
class EventVideoPlayerState extends State<EventVideoPlayer> {
|
||||
ChewieController? _chewieManager;
|
||||
ChewieController? _chewieController;
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
bool _isDownloading = false;
|
||||
String? _networkUri;
|
||||
File? _tmpFile;
|
||||
|
||||
// The video_player package only doesn't support Windows and Linux.
|
||||
final _supportsVideoPlayer =
|
||||
!PlatformInfos.isWindows && !PlatformInfos.isLinux;
|
||||
|
||||
// #Pangea
|
||||
StreamSubscription? _stopVideoSubscription;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
_stopVideoSubscription = widget.chatController?.stopMediaStream.stream
|
||||
.listen((_) => _chewieManager?.pause());
|
||||
super.initState();
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
void _downloadAction() async {
|
||||
if (PlatformInfos.isDesktop) {
|
||||
if (!_supportsVideoPlayer) {
|
||||
widget.event.saveFile(context);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isDownloading = true);
|
||||
|
||||
try {
|
||||
final videoFile = await widget.event.downloadAndDecryptAttachment();
|
||||
|
||||
// Dispose the controllers if we already have them.
|
||||
_disposeControllers();
|
||||
late VideoPlayerController videoPlayerController;
|
||||
|
||||
// Create the VideoPlayerController from the contents of videoFile.
|
||||
if (kIsWeb) {
|
||||
final blob = html.Blob([videoFile.bytes]);
|
||||
_networkUri = html.Url.createObjectUrlFromBlob(blob);
|
||||
final networkUri = Uri.parse(html.Url.createObjectUrlFromBlob(blob));
|
||||
videoPlayerController = VideoPlayerController.networkUrl(networkUri);
|
||||
} else {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final fileName = Uri.encodeComponent(
|
||||
|
|
@ -81,25 +87,28 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
if (await file.exists() == false) {
|
||||
await file.writeAsBytes(videoFile.bytes);
|
||||
}
|
||||
_tmpFile = file;
|
||||
}
|
||||
final tmpFile = _tmpFile;
|
||||
final networkUri = _networkUri;
|
||||
if (kIsWeb && networkUri != null && _chewieManager == null) {
|
||||
_chewieManager ??= ChewieController(
|
||||
videoPlayerController:
|
||||
VideoPlayerController.networkUrl(Uri.parse(networkUri)),
|
||||
autoPlay: true,
|
||||
autoInitialize: true,
|
||||
);
|
||||
} else if (!kIsWeb && tmpFile != null && _chewieManager == null) {
|
||||
_chewieManager ??= ChewieController(
|
||||
useRootNavigator: false,
|
||||
videoPlayerController: VideoPlayerController.file(tmpFile),
|
||||
autoPlay: true,
|
||||
autoInitialize: true,
|
||||
);
|
||||
videoPlayerController = VideoPlayerController.file(file);
|
||||
}
|
||||
_videoPlayerController = videoPlayerController;
|
||||
|
||||
await videoPlayerController.initialize();
|
||||
|
||||
// Create a ChewieController on top.
|
||||
_chewieController = ChewieController(
|
||||
videoPlayerController: videoPlayerController,
|
||||
useRootNavigator: !kIsWeb,
|
||||
autoPlay: true,
|
||||
autoInitialize: true,
|
||||
);
|
||||
|
||||
// #Pangea
|
||||
_stopVideoSubscription?.cancel();
|
||||
_stopVideoSubscription =
|
||||
widget.chatController?.stopMediaStream.stream.listen((_) {
|
||||
_videoPlayerController?.pause();
|
||||
_chewieController?.pause();
|
||||
});
|
||||
// Pangea#
|
||||
} on IOException catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
|
|
@ -109,18 +118,23 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
} catch (e, s) {
|
||||
ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s);
|
||||
} finally {
|
||||
// Workaround for Chewie needs time to get the aspectRatio
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
setState(() => _isDownloading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_chewieManager?.dispose();
|
||||
void _disposeControllers() {
|
||||
_chewieController?.dispose();
|
||||
_videoPlayerController?.dispose();
|
||||
_chewieController = null;
|
||||
_videoPlayerController = null;
|
||||
// #Pangea
|
||||
_stopVideoSubscription?.cancel();
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposeControllers();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +154,7 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
|
||||
const width = 300.0;
|
||||
|
||||
final chewieManager = _chewieManager;
|
||||
final chewieController = _chewieController;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
|
|
@ -150,8 +164,8 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: SizedBox(
|
||||
height: width,
|
||||
child: chewieManager != null
|
||||
? Center(child: Chewie(controller: chewieManager))
|
||||
child: chewieController != null
|
||||
? Center(child: Chewie(controller: chewieController))
|
||||
: Stack(
|
||||
children: [
|
||||
if (hasThumbnail)
|
||||
|
|
@ -181,7 +195,9 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.play_circle_outlined),
|
||||
: _supportsVideoPlayer
|
||||
? const Icon(Icons.play_circle_outlined)
|
||||
: const Icon(Icons.file_download_outlined),
|
||||
tooltip: _isDownloading
|
||||
? L10n.of(context).loadingPleaseWait
|
||||
: L10n.of(context).videoWithSize(
|
||||
|
|
@ -197,20 +213,29 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
|
|||
if (fileDescription != null && textColor != null && linkColor != null)
|
||||
SizedBox(
|
||||
width: width,
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize:
|
||||
AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize:
|
||||
AppConfig.fontSizeFactor * AppConfig.messageFontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: linkColor,
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -6,14 +6,11 @@ import 'package:emojis/emoji.dart';
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:pasteboard/pasteboard.dart';
|
||||
import 'package:slugify/slugify.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_text_controller.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/utils/shrinkable_text.dart';
|
||||
import 'package:fluffychat/utils/markdown_context_builder.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/mxc_image.dart';
|
||||
import '../../widgets/avatar.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
|
|
@ -251,7 +248,7 @@ class InputBar extends StatelessWidget {
|
|||
children: [
|
||||
Text(
|
||||
commandExample(command),
|
||||
style: const TextStyle(fontFamily: 'UbuntuMono'),
|
||||
style: const TextStyle(fontFamily: 'RobotoMono'),
|
||||
),
|
||||
Text(
|
||||
hint,
|
||||
|
|
@ -271,7 +268,7 @@ class InputBar extends StatelessWidget {
|
|||
waitDuration: const Duration(days: 1), // don't show on hover
|
||||
child: Container(
|
||||
padding: padding,
|
||||
child: Text(label, style: const TextStyle(fontFamily: 'UbuntuMono')),
|
||||
child: Text(label, style: const TextStyle(fontFamily: 'RobotoMono')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -421,226 +418,166 @@ class InputBar extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final useShortCuts = (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile);
|
||||
// #Pangea
|
||||
final enableAutocorrect = MatrixState
|
||||
.pangeaController.userController.profile.toolSettings.enableAutocorrect;
|
||||
// Pangea#
|
||||
return Shortcuts(
|
||||
shortcuts: !useShortCuts
|
||||
? {}
|
||||
: {
|
||||
LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.enter):
|
||||
NewLineIntent(),
|
||||
LogicalKeySet(LogicalKeyboardKey.enter): SubmitLineIntent(),
|
||||
LogicalKeySet(
|
||||
LogicalKeyboardKey.controlLeft,
|
||||
LogicalKeyboardKey.keyM,
|
||||
): PasteLineIntent(),
|
||||
},
|
||||
child: Actions(
|
||||
actions: !useShortCuts
|
||||
? {}
|
||||
: {
|
||||
NewLineIntent: CallbackAction(
|
||||
onInvoke: (i) {
|
||||
final val = controller!.value;
|
||||
final selection = val.selection.start;
|
||||
final messageWithoutNewLine =
|
||||
'${controller!.text.substring(0, val.selection.start)}\n${controller!.text.substring(val.selection.end)}';
|
||||
controller!.value = TextEditingValue(
|
||||
text: messageWithoutNewLine,
|
||||
selection: TextSelection.fromPosition(
|
||||
TextPosition(offset: selection + 1),
|
||||
),
|
||||
);
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SubmitLineIntent: CallbackAction(
|
||||
onInvoke: (i) {
|
||||
onSubmitted!(controller!.text);
|
||||
return null;
|
||||
},
|
||||
),
|
||||
PasteLineIntent: CallbackAction(
|
||||
onInvoke: (i) async {
|
||||
final image = await Pasteboard.image;
|
||||
if (image != null) {
|
||||
onSubmitImage!(image);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
},
|
||||
child: TypeAheadField<Map<String, String?>>(
|
||||
direction: VerticalDirection.up,
|
||||
hideOnEmpty: true,
|
||||
hideOnLoading: true,
|
||||
// #Pangea
|
||||
// if should obscure text (to make it looks that a message has been sent after sending fake message),
|
||||
// use hideTextController
|
||||
return TypeAheadField<Map<String, String?>>(
|
||||
direction: VerticalDirection.up,
|
||||
hideOnEmpty: true,
|
||||
hideOnLoading: true,
|
||||
// #Pangea
|
||||
// if should obscure text (to make it looks that a message has been sent after sending fake message),
|
||||
// use hideTextController
|
||||
|
||||
// controller: controller,
|
||||
// controller: controller,
|
||||
controller:
|
||||
(controller?.choreographer.chatController.obscureText) ?? false
|
||||
? controller?.choreographer.chatController.hideTextController
|
||||
: controller,
|
||||
// Pangea#
|
||||
focusNode: focusNode,
|
||||
hideOnSelect: false,
|
||||
debounceDuration: const Duration(milliseconds: 50),
|
||||
// show suggestions after 50ms idle time (default is 300)
|
||||
// #Pangea
|
||||
builder: (context, _, focusNode) {
|
||||
final textField = TextField(
|
||||
enableSuggestions: enableAutocorrect,
|
||||
readOnly: controller != null &&
|
||||
(controller!.choreographer.isRunningIT ||
|
||||
controller!.choreographer.chatController.obscureText),
|
||||
autocorrect: enableAutocorrect,
|
||||
controller:
|
||||
(controller?.choreographer.chatController.obscureText) ?? false
|
||||
? controller?.choreographer.chatController.hideTextController
|
||||
: controller,
|
||||
// Pangea#
|
||||
focusNode: focusNode,
|
||||
hideOnSelect: false,
|
||||
debounceDuration: const Duration(milliseconds: 50),
|
||||
// show suggestions after 50ms idle time (default is 300)
|
||||
// #Pangea
|
||||
builder: (context, _, focusNode) {
|
||||
final textField = TextField(
|
||||
enableSuggestions: enableAutocorrect,
|
||||
readOnly: controller != null &&
|
||||
(controller!.choreographer.isRunningIT ||
|
||||
controller!.choreographer.chatController.obscureText),
|
||||
autocorrect: enableAutocorrect,
|
||||
controller: (controller
|
||||
?.choreographer.chatController.obscureText) ??
|
||||
false
|
||||
? controller?.choreographer.chatController.hideTextController
|
||||
: controller,
|
||||
focusNode: focusNode,
|
||||
contextMenuBuilder: (c, e) => markdownContextBuilder(
|
||||
c,
|
||||
e,
|
||||
_,
|
||||
),
|
||||
contentInsertionConfiguration: ContentInsertionConfiguration(
|
||||
onContentInserted: (KeyboardInsertedContent content) {
|
||||
final data = content.data;
|
||||
if (data == null) return;
|
||||
contextMenuBuilder: (c, e) => markdownContextBuilder(
|
||||
c,
|
||||
e,
|
||||
_,
|
||||
),
|
||||
contentInsertionConfiguration: ContentInsertionConfiguration(
|
||||
onContentInserted: (KeyboardInsertedContent content) {
|
||||
final data = content.data;
|
||||
if (data == null) return;
|
||||
|
||||
final file = MatrixFile(
|
||||
mimeType: content.mimeType,
|
||||
bytes: data,
|
||||
name: content.uri.split('/').last,
|
||||
);
|
||||
room.sendFileEvent(
|
||||
file,
|
||||
shrinkImageMaxDimension: 1600,
|
||||
);
|
||||
},
|
||||
),
|
||||
minLines: minLines,
|
||||
maxLines: maxLines,
|
||||
keyboardType: keyboardType!,
|
||||
textInputAction: textInputAction,
|
||||
autofocus: autofocus!,
|
||||
inputFormatters: [
|
||||
//LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
//setting max character count to 1000
|
||||
//after max, nothing else can be typed
|
||||
LengthLimitingTextInputFormatter(1000),
|
||||
],
|
||||
onSubmitted: (text) {
|
||||
// fix for library for now
|
||||
// it sets the types for the callback incorrectly
|
||||
onSubmitted!(text);
|
||||
},
|
||||
style: controller?.exceededMaxLength ?? false
|
||||
? const TextStyle(color: Colors.red)
|
||||
: null,
|
||||
onTap: () {
|
||||
controller?.onInputTap(
|
||||
context,
|
||||
fNode: focusNode,
|
||||
);
|
||||
},
|
||||
decoration: decoration!,
|
||||
onChanged: (text) {
|
||||
// fix for the library for now
|
||||
// it sets the types for the callback incorrectly
|
||||
onChanged!(text);
|
||||
},
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
);
|
||||
// fix for issue with typing not working sometimes on Firefox and Safari
|
||||
return Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: [
|
||||
if (controller != null && controller!.text.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: ShrinkableText(
|
||||
text: hintText,
|
||||
maxWidth: double.infinity,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
kIsWeb ? SelectionArea(child: textField) : textField,
|
||||
],
|
||||
final file = MatrixFile(
|
||||
mimeType: content.mimeType,
|
||||
bytes: data,
|
||||
name: content.uri.split('/').last,
|
||||
);
|
||||
room.sendFileEvent(
|
||||
file,
|
||||
shrinkImageMaxDimension: 1600,
|
||||
);
|
||||
},
|
||||
),
|
||||
minLines: minLines,
|
||||
maxLines: maxLines,
|
||||
keyboardType: keyboardType!,
|
||||
textInputAction: textInputAction,
|
||||
autofocus: autofocus!,
|
||||
inputFormatters: [
|
||||
//LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
//setting max character count to 1000
|
||||
//after max, nothing else can be typed
|
||||
LengthLimitingTextInputFormatter(1000),
|
||||
],
|
||||
onSubmitted: (text) {
|
||||
// fix for library for now
|
||||
// it sets the types for the callback incorrectly
|
||||
onSubmitted!(text);
|
||||
},
|
||||
style: controller?.exceededMaxLength ?? false
|
||||
? const TextStyle(color: Colors.red)
|
||||
: null,
|
||||
onTap: () {
|
||||
controller?.onInputTap(
|
||||
context,
|
||||
fNode: focusNode,
|
||||
);
|
||||
},
|
||||
// builder: (context, controller, focusNode) => TextField(
|
||||
// controller: controller,
|
||||
// focusNode: focusNode,
|
||||
// contextMenuBuilder: (c, e) =>
|
||||
// markdownContextBuilder(c, e, controller),
|
||||
// contentInsertionConfiguration: ContentInsertionConfiguration(
|
||||
// onContentInserted: (KeyboardInsertedContent content) {
|
||||
// final data = content.data;
|
||||
// if (data == null) return;
|
||||
decoration: decoration!,
|
||||
onChanged: (text) {
|
||||
// fix for the library for now
|
||||
// it sets the types for the callback incorrectly
|
||||
onChanged!(text);
|
||||
},
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
);
|
||||
// fix for issue with typing not working sometimes on Firefox and Safari
|
||||
return Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: [
|
||||
if (controller != null && controller!.text.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: ShrinkableText(
|
||||
text: hintText,
|
||||
maxWidth: double.infinity,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
kIsWeb ? SelectionArea(child: textField) : textField,
|
||||
],
|
||||
);
|
||||
},
|
||||
// builder: (context, controller, focusNode) => TextField(
|
||||
// controller: controller,
|
||||
// focusNode: focusNode,
|
||||
// contextMenuBuilder: (c, e) => markdownContextBuilder(c, e, controller),
|
||||
// contentInsertionConfiguration: ContentInsertionConfiguration(
|
||||
// onContentInserted: (KeyboardInsertedContent content) {
|
||||
// final data = content.data;
|
||||
// if (data == null) return;
|
||||
|
||||
// final file = MatrixFile(
|
||||
// mimeType: content.mimeType,
|
||||
// bytes: data,
|
||||
// name: content.uri.split('/').last,
|
||||
// );
|
||||
// room.sendFileEvent(
|
||||
// file,
|
||||
// shrinkImageMaxDimension: 1600,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
// keyboardType: keyboardType!,
|
||||
// textInputAction: textInputAction,
|
||||
// autofocus: autofocus!,
|
||||
// inputFormatters: [
|
||||
// LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
// ],
|
||||
// onSubmitted: (text) {
|
||||
// // fix for library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onSubmitted!(text);
|
||||
// },
|
||||
// decoration: decoration!,
|
||||
// onChanged: (text) {
|
||||
// // fix for the library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onChanged!(text);
|
||||
// },
|
||||
// textCapitalization: TextCapitalization.sentences,
|
||||
// ),
|
||||
// Pangea#
|
||||
suggestionsCallback: getSuggestions,
|
||||
itemBuilder: (c, s) =>
|
||||
buildSuggestion(c, s, Matrix.of(context).client),
|
||||
onSelected: (Map<String, String?> suggestion) =>
|
||||
insertSuggestion(context, suggestion),
|
||||
errorBuilder: (BuildContext context, Object? error) =>
|
||||
const SizedBox.shrink(),
|
||||
loadingBuilder: (BuildContext context) => const SizedBox.shrink(),
|
||||
// fix loading briefly flickering a dark box
|
||||
emptyBuilder: (BuildContext context) => const SizedBox
|
||||
.shrink(), // fix loading briefly showing no suggestions
|
||||
),
|
||||
),
|
||||
// final file = MatrixFile(
|
||||
// mimeType: content.mimeType,
|
||||
// bytes: data,
|
||||
// name: content.uri.split('/').last,
|
||||
// );
|
||||
// room.sendFileEvent(
|
||||
// file,
|
||||
// shrinkImageMaxDimension: 1600,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
// keyboardType: keyboardType!,
|
||||
// textInputAction: textInputAction,
|
||||
// autofocus: autofocus!,
|
||||
// inputFormatters: [
|
||||
// LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
// ],
|
||||
// onSubmitted: (text) {
|
||||
// // fix for library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onSubmitted!(text);
|
||||
// },
|
||||
// decoration: decoration!,
|
||||
// onChanged: (text) {
|
||||
// // fix for the library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onChanged!(text);
|
||||
// },
|
||||
// textCapitalization: TextCapitalization.sentences,
|
||||
// ),
|
||||
// Pangea#
|
||||
suggestionsCallback: getSuggestions,
|
||||
itemBuilder: (c, s) => buildSuggestion(c, s, Matrix.of(context).client),
|
||||
onSelected: (Map<String, String?> suggestion) =>
|
||||
insertSuggestion(context, suggestion),
|
||||
errorBuilder: (BuildContext context, Object? error) =>
|
||||
const SizedBox.shrink(),
|
||||
loadingBuilder: (BuildContext context) => const SizedBox.shrink(),
|
||||
// fix loading briefly flickering a dark box
|
||||
emptyBuilder: (BuildContext context) =>
|
||||
const SizedBox.shrink(), // fix loading briefly showing no suggestions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NewLineIntent extends Intent {}
|
||||
|
||||
class SubmitLineIntent extends Intent {}
|
||||
|
||||
class PasteLineIntent extends Intent {}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ import 'package:record/record.dart';
|
|||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/utils/update_version_dialog.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'events/audio_player.dart';
|
||||
|
||||
class RecordingDialog extends StatefulWidget {
|
||||
|
|
@ -35,25 +37,17 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
|
||||
String? fileName;
|
||||
|
||||
static const int bitRate = 64000;
|
||||
// #Pangea
|
||||
// static const int samplingRate = 44100;
|
||||
static const int samplingRate = 22050;
|
||||
// Pangea#
|
||||
|
||||
Future<void> startRecording() async {
|
||||
final store = Matrix.of(context).store;
|
||||
try {
|
||||
// #Pangea
|
||||
// final codec = kIsWeb
|
||||
// // Web seems to create webm instead of ogg when using opus encoder
|
||||
// // which does not play on iOS right now. So we use wav for now:
|
||||
// ? AudioEncoder.wav
|
||||
// // Everywhere else we use opus if supported by the platform:
|
||||
// : await _audioRecorder.isEncoderSupported(AudioEncoder.opus)
|
||||
// ? AudioEncoder.opus
|
||||
// : AudioEncoder.aacLc;
|
||||
const codec = AudioEncoder.wav;
|
||||
// Pangea#
|
||||
final codec = kIsWeb
|
||||
// Web seems to create webm instead of ogg when using opus encoder
|
||||
// which does not play on iOS right now. So we use wav for now:
|
||||
? AudioEncoder.wav
|
||||
// Everywhere else we use opus if supported by the platform:
|
||||
: await _audioRecorder.isEncoderSupported(AudioEncoder.opus)
|
||||
? AudioEncoder.opus
|
||||
: AudioEncoder.aacLc;
|
||||
fileName =
|
||||
'recording${DateTime.now().microsecondsSinceEpoch}.${codec.fileExtension}';
|
||||
String? path;
|
||||
|
|
@ -71,17 +65,17 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
|
||||
// #Pangea
|
||||
final isNotError = await showUpdateVersionDialog(
|
||||
future: () =>
|
||||
future: () async =>
|
||||
// Pangea#
|
||||
|
||||
_audioRecorder.start(
|
||||
const RecordConfig(
|
||||
bitRate: bitRate,
|
||||
sampleRate: samplingRate,
|
||||
numChannels: 1,
|
||||
autoGain: true,
|
||||
echoCancel: true,
|
||||
noiseSuppress: true,
|
||||
await _audioRecorder.start(
|
||||
RecordConfig(
|
||||
bitRate: AppSettings.audioRecordingBitRate.getItem(store),
|
||||
sampleRate: AppSettings.audioRecordingSamplingRate.getItem(store),
|
||||
numChannels: AppSettings.audioRecordingNumChannels.getItem(store),
|
||||
autoGain: AppSettings.audioRecordingAutoGain.getItem(store),
|
||||
echoCancel: AppSettings.audioRecordingEchoCancel.getItem(store),
|
||||
noiseSuppress:
|
||||
AppSettings.audioRecordingNoiseSuppress.getItem(store),
|
||||
encoder: codec,
|
||||
),
|
||||
path: path ?? '',
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ class ReplyDisplay extends StatelessWidget {
|
|||
? ReplyContent(
|
||||
controller.replyEvent!,
|
||||
timeline: controller.timeline!,
|
||||
backgroundColor: Colors.transparent,
|
||||
)
|
||||
: _EditContent(
|
||||
controller.editEvent?.getDisplayEvent(controller.timeline!),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
|
|
@ -16,6 +13,7 @@ import 'package:fluffychat/utils/other_party_can_receive.dart';
|
|||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/utils/size_string.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/dialog_text_field.dart';
|
||||
import '../../utils/resize_video.dart';
|
||||
|
||||
class SendFileDialog extends StatefulWidget {
|
||||
|
|
@ -40,6 +38,8 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
/// Images smaller than 20kb don't need compression.
|
||||
static const int minSizeToCompress = 20 * 1000;
|
||||
|
||||
final TextEditingController _labelTextController = TextEditingController();
|
||||
|
||||
Future<void> _send() async {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(widget.outerContext);
|
||||
final l10n = L10n.of(context);
|
||||
|
|
@ -96,11 +96,14 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
scaffoldMessenger.clearSnackBars();
|
||||
}
|
||||
|
||||
final label = _labelTextController.text.trim();
|
||||
|
||||
try {
|
||||
await widget.room.sendFileEvent(
|
||||
file,
|
||||
thumbnail: thumbnail,
|
||||
shrinkImageMaxDimension: compress ? 1600 : null,
|
||||
extraContent: label.isEmpty ? null : {'body': label},
|
||||
);
|
||||
} on MatrixException catch (e) {
|
||||
final retryAfterMs = e.retryAfterMs;
|
||||
|
|
@ -124,7 +127,8 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
await widget.room.sendFileEvent(
|
||||
file,
|
||||
thumbnail: thumbnail,
|
||||
shrinkImageMaxDimension: compress ? null : 1600,
|
||||
shrinkImageMaxDimension: compress ? 1600 : null,
|
||||
extraContent: label.isEmpty ? null : {'body': label},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -188,6 +192,9 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
sendStr = L10n.of(context).sendVideo;
|
||||
}
|
||||
|
||||
final compressionSupported =
|
||||
uniqueFileType != 'video' || PlatformInfos.isMobile;
|
||||
|
||||
return FutureBuilder<String>(
|
||||
future: _calcCombinedFileSize(),
|
||||
builder: (context, snapshot) {
|
||||
|
|
@ -198,129 +205,189 @@ class SendFileDialogState extends State<SendFileDialog> {
|
|||
title: Text(sendStr),
|
||||
content: SizedBox(
|
||||
width: 256,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
if (uniqueFileType == 'image')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: SizedBox(
|
||||
height: 256,
|
||||
child: Center(
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.files.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, i) => Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 2,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
if (uniqueFileType == 'image')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: SizedBox(
|
||||
height: 256,
|
||||
child: Center(
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.files.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, i) => Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 2,
|
||||
),
|
||||
color: Colors.black,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: FutureBuilder(
|
||||
future: widget.files[i].readAsBytes(),
|
||||
builder: (context, snapshot) {
|
||||
final bytes = snapshot.data;
|
||||
if (bytes == null) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator
|
||||
.adaptive(),
|
||||
);
|
||||
}
|
||||
if (snapshot.error != null) {
|
||||
Logs().w(
|
||||
'Unable to preview image',
|
||||
snapshot.error,
|
||||
snapshot.stackTrace,
|
||||
);
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
width: 256,
|
||||
height: 256,
|
||||
child: Icon(
|
||||
Icons.broken_image_outlined,
|
||||
size: 64,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Image.memory(
|
||||
bytes,
|
||||
height: 256,
|
||||
width: widget.files.length == 1
|
||||
? 256 - 36
|
||||
: null,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, e, s) {
|
||||
Logs()
|
||||
.w('Unable to preview image', e, s);
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
width: 256,
|
||||
height: 256,
|
||||
child: Icon(
|
||||
Icons.broken_image_outlined,
|
||||
size: 64,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: kIsWeb
|
||||
? Image.network(
|
||||
widget.files[i].path,
|
||||
height: 256,
|
||||
)
|
||||
: Image.file(
|
||||
File(widget.files[i].path),
|
||||
height: 256,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (uniqueFileType != 'image')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Row(
|
||||
if (uniqueFileType != 'image')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
uniqueFileType == null
|
||||
? Icons.description_outlined
|
||||
: uniqueFileType == 'video'
|
||||
? Icons.video_file_outlined
|
||||
: uniqueFileType == 'audio'
|
||||
? Icons.audio_file_outlined
|
||||
: Icons.description_outlined,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
fileName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
'$sizeString - $fileTypes',
|
||||
style: theme.textTheme.labelSmall,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.files.length == 1)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: DialogTextField(
|
||||
controller: _labelTextController,
|
||||
labelText: L10n.of(context).optionalMessage,
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
maxLength: 255,
|
||||
counterText: '',
|
||||
),
|
||||
),
|
||||
// Workaround for SwitchListTile.adaptive crashes in CupertinoDialog
|
||||
if ({'image', 'video'}.contains(uniqueFileType))
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
uniqueFileType == null
|
||||
? Icons.description_outlined
|
||||
: uniqueFileType == 'video'
|
||||
? Icons.video_file_outlined
|
||||
: uniqueFileType == 'audio'
|
||||
? Icons.audio_file_outlined
|
||||
: Icons.description_outlined,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
if ({TargetPlatform.iOS, TargetPlatform.macOS}
|
||||
.contains(theme.platform))
|
||||
CupertinoSwitch(
|
||||
value: compressionSupported && compress,
|
||||
onChanged: compressionSupported
|
||||
? (v) => setState(() => compress = v)
|
||||
: null,
|
||||
)
|
||||
else
|
||||
Switch.adaptive(
|
||||
value: compressionSupported && compress,
|
||||
onChanged: compressionSupported
|
||||
? (v) => setState(() => compress = v)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
fileName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
'$sizeString - $fileTypes',
|
||||
style: theme.textTheme.labelSmall,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).compress,
|
||||
style: theme.textTheme.titleMedium,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!compress)
|
||||
Text(
|
||||
' ($sizeString)',
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
if (!compressionSupported)
|
||||
Text(
|
||||
L10n.of(context).notSupportedOnThisDevice,
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Workaround for SwitchListTile.adaptive crashes in CupertinoDialog
|
||||
if ({'image', 'video'}.contains(uniqueFileType))
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if ({TargetPlatform.iOS, TargetPlatform.macOS}
|
||||
.contains(theme.platform))
|
||||
CupertinoSwitch(
|
||||
value: compress,
|
||||
onChanged: uniqueFileType == 'video' &&
|
||||
!PlatformInfos.isMobile
|
||||
? null
|
||||
: (v) => setState(() => compress = v),
|
||||
)
|
||||
else
|
||||
Switch.adaptive(
|
||||
value: compress,
|
||||
onChanged: uniqueFileType == 'video' &&
|
||||
!PlatformInfos.isMobile
|
||||
? null
|
||||
: (v) => setState(() => compress = v),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).compress,
|
||||
style: theme.textTheme.titleMedium,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!compress)
|
||||
Text(
|
||||
' ($sizeString)',
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart' hide Visibility;
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_page.dart';
|
||||
|
|
@ -179,10 +180,13 @@ class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
|||
)) {
|
||||
return;
|
||||
}
|
||||
await showFutureLoadingDialog(
|
||||
final result = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.client.upgradeRoom(room.id, newVersion),
|
||||
);
|
||||
if (result.error != null) return;
|
||||
if (!mounted) return;
|
||||
context.go('/rooms/${room.id}');
|
||||
}
|
||||
|
||||
Future<void> addAlias() async {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
|
|||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../utils/url_launcher.dart';
|
||||
import '../../widgets/mxc_image_viewer.dart';
|
||||
import '../../widgets/qr_code_viewer.dart';
|
||||
|
||||
class ChatDetailsView extends StatelessWidget {
|
||||
|
|
@ -37,6 +38,9 @@ class ChatDetailsView extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
final directChatMatrixID = room.directChatMatrixID;
|
||||
final roomAvatar = room.avatar;
|
||||
|
||||
return StreamBuilder(
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == room.id),
|
||||
|
|
@ -57,7 +61,7 @@ class ChatDetailsView extends StatelessWidget {
|
|||
const Center(child: BackButton()),
|
||||
elevation: theme.appBarTheme.elevation,
|
||||
actions: <Widget>[
|
||||
if (room.canonicalAlias.isNotEmpty) ...[
|
||||
if (room.canonicalAlias.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: L10n.of(context).share,
|
||||
icon: const Icon(Icons.qr_code_rounded),
|
||||
|
|
@ -65,8 +69,16 @@ class ChatDetailsView extends StatelessWidget {
|
|||
context,
|
||||
room.canonicalAlias,
|
||||
),
|
||||
)
|
||||
else if (directChatMatrixID != null)
|
||||
IconButton(
|
||||
tooltip: L10n.of(context).share,
|
||||
icon: const Icon(Icons.qr_code_rounded),
|
||||
onPressed: () => showQrCodeViewer(
|
||||
context,
|
||||
directChatMatrixID,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (controller.widget.embeddedCloseButton == null)
|
||||
ChatSettingsPopupMenu(room, false),
|
||||
],
|
||||
|
|
@ -101,6 +113,13 @@ class ChatDetailsView extends StatelessWidget {
|
|||
userId: room.directChatMatrixID,
|
||||
// Pangea#
|
||||
size: Avatar.defaultSize * 2.5,
|
||||
onTap: roomAvatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
MxcImageViewer(roomAvatar),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
if (!room.isDirectChat &&
|
||||
|
|
@ -227,6 +246,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
text: room.topic.isEmpty
|
||||
? L10n.of(context).noChatDescriptionYet
|
||||
: room.topic,
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: const TextStyle(
|
||||
color: Colors.blueAccent,
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/level_display_name.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart';
|
||||
import '../../widgets/avatar.dart';
|
||||
import '../user_bottom_sheet/user_bottom_sheet.dart';
|
||||
|
||||
class ParticipantListItem extends StatelessWidget {
|
||||
final User user;
|
||||
|
|
@ -32,74 +31,71 @@ class ParticipantListItem extends StatelessWidget {
|
|||
? L10n.of(context).moderator
|
||||
: '';
|
||||
|
||||
return Opacity(
|
||||
opacity: user.membership == Membership.join ? 1 : 0.5,
|
||||
child: ListTile(
|
||||
onTap: () => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
user: user,
|
||||
outerContext: context,
|
||||
return ListTile(
|
||||
onTap: () => showMemberActionsPopupMenu(context: context, user: user),
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
user.calcDisplayname(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
// #Pangea
|
||||
LevelDisplayName(userId: user.id),
|
||||
// Pangea#
|
||||
if (permissionBatch.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: user.powerLevel >= 100
|
||||
? theme.colorScheme.tertiary
|
||||
: theme.colorScheme.tertiaryContainer,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
user.calcDisplayname(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
permissionBatch,
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: user.powerLevel >= 100
|
||||
? theme.colorScheme.onTertiary
|
||||
: theme.colorScheme.onTertiaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
LevelDisplayName(userId: user.id),
|
||||
// Pangea#
|
||||
if (permissionBatch.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: user.powerLevel >= 100
|
||||
? theme.colorScheme.tertiary
|
||||
: theme.colorScheme.tertiaryContainer,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
membershipBatch == null
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
permissionBatch,
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: user.powerLevel >= 100
|
||||
? theme.colorScheme.onTertiary
|
||||
: theme.colorScheme.onTertiaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
membershipBatch == null
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.secondaryHeaderColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
membershipBatch,
|
||||
style: theme.textTheme.labelSmall,
|
||||
child: Center(
|
||||
child: Text(
|
||||
membershipBatch,
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Text(
|
||||
user.id,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
leading: Avatar(
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Text(
|
||||
user.id,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
leading: Opacity(
|
||||
opacity: user.membership == Membership.join ? 1 : 0.5,
|
||||
child: Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
presenceUserId: user.stateKey,
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
deviceKeys[i].ed25519Key?.beautified ??
|
||||
L10n.of(context).unknownEncryptionAlgorithm,
|
||||
style: TextStyle(
|
||||
fontFamily: 'UbuntuMono',
|
||||
fontFamily: 'RobotoMono',
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -218,24 +218,21 @@ class ChatListController extends State<ChatList>
|
|||
return (room) => !room.isAnalyticsRoom && !room.isSpace;
|
||||
// Pangea#
|
||||
case ActiveFilter.messages:
|
||||
// #Pangea
|
||||
// return (room) => !room.isSpace && room.isDirectChat;
|
||||
return (room) =>
|
||||
!room.isSpace &&
|
||||
room.isDirectChat // #Pangea
|
||||
&&
|
||||
!room.isAnalyticsRoom;
|
||||
!room.isSpace && room.isDirectChat && !room.isAnalyticsRoom;
|
||||
// Pangea#
|
||||
case ActiveFilter.groups:
|
||||
// #Pangea
|
||||
// return (room) => !room.isSpace && !room.isDirectChat;
|
||||
return (room) =>
|
||||
!room.isSpace &&
|
||||
!room.isDirectChat // #Pangea
|
||||
&&
|
||||
!room.isAnalyticsRoom;
|
||||
!room.isSpace && !room.isDirectChat && !room.isAnalyticsRoom;
|
||||
// Pangea#
|
||||
case ActiveFilter.unread:
|
||||
return (room) =>
|
||||
room.isUnreadOrInvited // #Pangea
|
||||
&&
|
||||
!room.isAnalyticsRoom;
|
||||
// #Pangea
|
||||
// return (room) => room.isUnreadOrInvited;
|
||||
return (room) => room.isUnreadOrInvited && !room.isAnalyticsRoom;
|
||||
// Pangea#
|
||||
case ActiveFilter.spaces:
|
||||
return (room) => room.isSpace;
|
||||
|
|
|
|||
|
|
@ -11,13 +11,11 @@ import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
|||
import 'package:fluffychat/pages/chat_list/dummy_chat_list_item.dart';
|
||||
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||
import 'package:fluffychat/pages/chat_list/space_view.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pangea/chat_list/widgets/pangea_chat_list_header.dart';
|
||||
import 'package:fluffychat/pangea/public_spaces/pangea_public_room_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/public_room_dialog.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/user_dialog.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/hover_builder.dart';
|
||||
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
|
||||
import '../../config/themes.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
|
|
@ -135,12 +133,9 @@ class ChatListViewBody extends StatelessWidget {
|
|||
// .results[i].userId.localpart ??
|
||||
// L10n.of(context).unknownDevice,
|
||||
// avatar: userSearchResult.results[i].avatarUrl,
|
||||
// onPressed: () => showAdaptiveBottomSheet(
|
||||
// onPressed: () => UserDialog.show(
|
||||
// context: context,
|
||||
// builder: (c) => UserBottomSheet(
|
||||
// profile: userSearchResult.results[i],
|
||||
// outerContext: context,
|
||||
// ),
|
||||
// profile: userSearchResult.results[i],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
|
|
@ -175,16 +170,16 @@ class ChatListViewBody extends StatelessWidget {
|
|||
),
|
||||
// #Pangea
|
||||
// if (client.rooms.isNotEmpty && !controller.isSearchMode)
|
||||
// SizedBox(
|
||||
// height: 64,
|
||||
// child: ListView(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 12.0,
|
||||
// vertical: 12.0,
|
||||
// ),
|
||||
// shrinkWrap: true,
|
||||
// scrollDirection: Axis.horizontal,
|
||||
if (!controller.isSearchMode)
|
||||
// SizedBox(
|
||||
// height: 64,
|
||||
// child: ListView(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 12.0,
|
||||
// vertical: 16.0,
|
||||
// ),
|
||||
// shrinkWrap: true,
|
||||
// scrollDirection: Axis.horizontal,
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
|
|
@ -211,64 +206,26 @@ class ChatListViewBody extends StatelessWidget {
|
|||
ActiveFilter.spaces,
|
||||
]
|
||||
.map(
|
||||
(filter) => Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: HoverBuilder(
|
||||
builder: (context, hovered) =>
|
||||
AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
onTap: () =>
|
||||
controller.setActiveFilter(filter),
|
||||
// #Pangea
|
||||
child: UnreadRoomsBadge(
|
||||
filter: (_) =>
|
||||
filter == ActiveFilter.unread,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
top: -12,
|
||||
end: -6,
|
||||
),
|
||||
// Pangea#
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: filter ==
|
||||
controller.activeFilter
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme
|
||||
.secondaryContainer,
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
filter.toLocalizedString(context),
|
||||
style: TextStyle(
|
||||
fontWeight: filter ==
|
||||
controller.activeFilter
|
||||
? FontWeight.w500
|
||||
: FontWeight.normal,
|
||||
color: filter ==
|
||||
controller.activeFilter
|
||||
? theme
|
||||
.colorScheme.onPrimary
|
||||
: theme.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// (filter) => Padding(
|
||||
(filter) => UnreadRoomsBadge(
|
||||
filter: (_) => filter == ActiveFilter.unread,
|
||||
badgePosition: BadgePosition.topEnd(
|
||||
top: -12,
|
||||
end: -6,
|
||||
),
|
||||
child: Padding(
|
||||
// Pangea#
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
child: FilterChip(
|
||||
selected:
|
||||
filter == controller.activeFilter,
|
||||
onSelected: (_) =>
|
||||
controller.setActiveFilter(filter),
|
||||
label: Text(
|
||||
filter.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -410,7 +367,10 @@ class PublicRoomsHorizontalListState extends State<PublicRoomsHorizontalList> {
|
|||
Scrollbar(
|
||||
thumbVisibility: true,
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
child:
|
||||
// Pangea#
|
||||
ListView.builder(
|
||||
// #Pangea
|
||||
controller: _scrollController,
|
||||
// Pangea#
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
|
@ -423,15 +383,11 @@ class PublicRoomsHorizontalListState extends State<PublicRoomsHorizontalList> {
|
|||
L10n.of(context).chat,
|
||||
// Pangea#
|
||||
avatar: publicRooms[i].avatarUrl,
|
||||
onPressed: () => showAdaptiveBottomSheet(
|
||||
onPressed: () => showAdaptiveDialog(
|
||||
context: context,
|
||||
// #Pangea
|
||||
// builder: (c) => PublicRoomBottomSheet(
|
||||
builder: (c) => PangeaPublicRoomBottomSheet(
|
||||
// Pangea#
|
||||
builder: (c) => PublicRoomDialog(
|
||||
roomAlias: publicRooms[i].canonicalAlias ??
|
||||
publicRooms[i].roomId,
|
||||
outerContext: context,
|
||||
chunk: publicRooms[i],
|
||||
),
|
||||
),
|
||||
|
|
@ -533,12 +489,9 @@ class UserSearchResultsListState extends State<UserSearchResultsList> {
|
|||
widget.userSearchResult.results[i].userId.localpart ??
|
||||
L10n.of(context).unknownDevice,
|
||||
avatar: widget.userSearchResult.results[i].avatarUrl,
|
||||
onPressed: () => showAdaptiveBottomSheet(
|
||||
onPressed: () => UserDialog.show(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
profile: widget.userSearchResult.results[i],
|
||||
outerContext: context,
|
||||
),
|
||||
profile: widget.userSearchResult.results[i],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -109,351 +109,331 @@ class ChatListItem extends StatelessWidget {
|
|||
: room.getState(EventTypes.RoomMember, lastEvent.senderId) == null;
|
||||
final space = this.space;
|
||||
|
||||
return Dismissible(
|
||||
key: ValueKey(room.id),
|
||||
confirmDismiss: (_) => archiveAction(context),
|
||||
onDismissed: (_) {
|
||||
// Empty dismissed callback to trigger the dismiss animation
|
||||
},
|
||||
background: Material(
|
||||
color: theme.colorScheme.errorContainer,
|
||||
child: Icon(
|
||||
Icons.archive_outlined,
|
||||
color: theme.colorScheme.onErrorContainer,
|
||||
),
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 1,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 1,
|
||||
),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: backgroundColor,
|
||||
child: FutureBuilder(
|
||||
future: room.loadHeroUsers(),
|
||||
builder: (context, snapshot) => HoverBuilder(
|
||||
builder: (context, listTileHovered) => ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -0.5),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
onLongPress: () => onLongPress?.call(context),
|
||||
leading: HoverBuilder(
|
||||
builder: (context, hovered) => AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
child: SizedBox(
|
||||
width: Avatar.defaultSize,
|
||||
height: Avatar.defaultSize,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (space != null)
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
child: Avatar(
|
||||
border: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
),
|
||||
mxContent: space.avatar,
|
||||
size: Avatar.defaultSize * 0.75,
|
||||
name: space.getLocalizedDisplayname(),
|
||||
// #Pangea
|
||||
userId: space.directChatMatrixID,
|
||||
useRive: true,
|
||||
// Pangea#
|
||||
onTap: () => onLongPress?.call(context),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Avatar(
|
||||
border: space == null
|
||||
? room.isSpace
|
||||
? BorderSide(
|
||||
width: 1,
|
||||
color: theme.dividerColor,
|
||||
)
|
||||
: null
|
||||
: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
borderRadius: room.isSpace
|
||||
? BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
)
|
||||
: null,
|
||||
mxContent: room.avatar,
|
||||
size: space != null
|
||||
? Avatar.defaultSize * 0.75
|
||||
: Avatar.defaultSize,
|
||||
name: displayname,
|
||||
presenceUserId: directChatMatrixId,
|
||||
presenceBackgroundColor: backgroundColor,
|
||||
onTap: () => onLongPress?.call(context),
|
||||
),
|
||||
),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: backgroundColor,
|
||||
child: FutureBuilder(
|
||||
future: room.loadHeroUsers(),
|
||||
builder: (context, snapshot) => HoverBuilder(
|
||||
builder: (context, listTileHovered) => ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -0.5),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
onLongPress: () => onLongPress?.call(context),
|
||||
leading: HoverBuilder(
|
||||
builder: (context, hovered) => AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
child: SizedBox(
|
||||
width: Avatar.defaultSize,
|
||||
height: Avatar.defaultSize,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (space != null)
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: GestureDetector(
|
||||
left: 0,
|
||||
child: Avatar(
|
||||
border: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
),
|
||||
mxContent: space.avatar,
|
||||
size: Avatar.defaultSize * 0.75,
|
||||
name: space.getLocalizedDisplayname(),
|
||||
// #Pangea
|
||||
userId: space.directChatMatrixID,
|
||||
useRive: true,
|
||||
// Pangea#
|
||||
onTap: () => onLongPress?.call(context),
|
||||
child: AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: listTileHovered ? 1.0 : 0.0,
|
||||
child: Material(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: const Icon(
|
||||
Icons.arrow_drop_down_circle_outlined,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Avatar(
|
||||
border: space == null
|
||||
? room.isSpace
|
||||
? BorderSide(
|
||||
width: 1,
|
||||
color: theme.dividerColor,
|
||||
)
|
||||
: null
|
||||
: BorderSide(
|
||||
width: 2,
|
||||
color: backgroundColor ??
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
borderRadius: room.isSpace
|
||||
? BorderRadius.circular(
|
||||
AppConfig.borderRadius / 4,
|
||||
)
|
||||
: null,
|
||||
mxContent: room.avatar,
|
||||
size: space != null
|
||||
? Avatar.defaultSize * 0.75
|
||||
: Avatar.defaultSize,
|
||||
name: displayname,
|
||||
presenceUserId: directChatMatrixId,
|
||||
presenceBackgroundColor: backgroundColor,
|
||||
onTap: () => onLongPress?.call(context),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: GestureDetector(
|
||||
onTap: () => onLongPress?.call(context),
|
||||
child: AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: listTileHovered ? 1.0 : 0.0,
|
||||
child: Material(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: const Icon(
|
||||
Icons.arrow_drop_down_circle_outlined,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
displayname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.w500
|
||||
: null,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
displayname,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.w500
|
||||
: null,
|
||||
),
|
||||
),
|
||||
if (isMuted)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 4.0),
|
||||
child: Icon(
|
||||
Icons.notifications_off_outlined,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
if (room.isFavourite)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: hasNotifications ? 4.0 : 0.0,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
size: 16,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
if (!room.isSpace &&
|
||||
lastEvent != null &&
|
||||
room.membership != Membership.invite)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: Text(
|
||||
lastEvent.originServerTs.localizedTimeShort(context),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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),
|
||||
),
|
||||
if (isMuted)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 4.0),
|
||||
child: Icon(
|
||||
Icons.edit_outlined,
|
||||
color: theme.colorScheme.secondary,
|
||||
size: 14,
|
||||
Icons.notifications_off_outlined,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: room.isSpace && room.membership == Membership.join
|
||||
? Text(
|
||||
L10n.of(context).countChatsAndCountParticipants(
|
||||
// #Pangea
|
||||
// room.spaceChildren.length,
|
||||
room.spaceChildCount,
|
||||
// Pangea#
|
||||
(room.summary.mJoinedMemberCount ?? 1),
|
||||
),
|
||||
style:
|
||||
TextStyle(color: theme.colorScheme.outline),
|
||||
)
|
||||
: typingText.isNotEmpty
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
if (room.isFavourite)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: hasNotifications ? 4.0 : 0.0,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.push_pin,
|
||||
size: 16,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
if (!room.isSpace &&
|
||||
lastEvent != null &&
|
||||
room.membership != Membership.invite)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: Text(
|
||||
lastEvent.originServerTs.localizedTimeShort(context),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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.colorScheme.secondary,
|
||||
size: 14,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: room.isSpace && room.membership == Membership.join
|
||||
? Text(
|
||||
L10n.of(context).countChatsAndCountParticipants(
|
||||
// #Pangea
|
||||
: room.lastEvent != null
|
||||
? ChatListItemSubtitle(
|
||||
event: room.lastEvent,
|
||||
style: TextStyle(
|
||||
fontWeight:
|
||||
unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color:
|
||||
theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
// Pangea#
|
||||
: FutureBuilder(
|
||||
key: ValueKey(
|
||||
'${lastEvent?.eventId}_${lastEvent?.type}_${lastEvent?.redacted}',
|
||||
),
|
||||
future: needLastEventSender
|
||||
? lastEvent.calcLocalizedBody(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix:
|
||||
(!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent
|
||||
?.senderId),
|
||||
)
|
||||
// room.spaceChildren.length,
|
||||
room.spaceChildCount,
|
||||
// Pangea#
|
||||
(room.summary.mJoinedMemberCount ?? 1),
|
||||
),
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
)
|
||||
: typingText.isNotEmpty
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
// #Pangea
|
||||
: room.lastEvent != null
|
||||
? ChatListItemSubtitle(
|
||||
event: room.lastEvent,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
initialData:
|
||||
lastEvent?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix: (!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId),
|
||||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? room
|
||||
.getState(
|
||||
EventTypes.RoomMember,
|
||||
room.client.userID!,
|
||||
)
|
||||
?.content
|
||||
.tryGet<String>('reason') ??
|
||||
(isDirectChat
|
||||
? L10n.of(context)
|
||||
.newChatRequest
|
||||
// #Pangea
|
||||
// : L10n.of(context)
|
||||
// .inviteGroupChat)
|
||||
: L10n.of(context)
|
||||
.inviteChat)
|
||||
// Pangea#
|
||||
: snapshot.data ??
|
||||
L10n.of(context).emptyChat,
|
||||
softWrap: false,
|
||||
maxLines:
|
||||
room.notificationCount >= 1 ? 2 : 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: unread || room.hasNewMessages
|
||||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.outline,
|
||||
decoration:
|
||||
room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
),
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
// Pangea#
|
||||
: FutureBuilder(
|
||||
key: ValueKey(
|
||||
'${lastEvent?.eventId}_${lastEvent?.type}_${lastEvent?.redacted}',
|
||||
),
|
||||
future: needLastEventSender
|
||||
? lastEvent.calcLocalizedBody(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix:
|
||||
(!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent
|
||||
?.senderId),
|
||||
)
|
||||
: null,
|
||||
initialData:
|
||||
lastEvent?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix: (!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId),
|
||||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? room
|
||||
.getState(
|
||||
EventTypes.RoomMember,
|
||||
room.client.userID!,
|
||||
)
|
||||
?.content
|
||||
.tryGet<String>('reason') ??
|
||||
(isDirectChat
|
||||
? L10n.of(context)
|
||||
.newChatRequest
|
||||
// #Pangea
|
||||
// : L10n.of(context)
|
||||
// .inviteGroupChat)
|
||||
: L10n.of(context).inviteChat)
|
||||
// Pangea#
|
||||
: snapshot.data ??
|
||||
L10n.of(context).emptyChat,
|
||||
softWrap: false,
|
||||
maxLines:
|
||||
room.notificationCount >= 1 ? 2 : 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: unread || room.hasNewMessages
|
||||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.outline,
|
||||
decoration:
|
||||
room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7),
|
||||
height: unreadBubbleSize,
|
||||
width: !hasNotifications && !unread && !room.hasNewMessages
|
||||
? 0
|
||||
: (unreadBubbleSize - 9) *
|
||||
room.notificationCount.toString().length +
|
||||
9,
|
||||
decoration: BoxDecoration(
|
||||
color: room.highlightCount > 0 ||
|
||||
room.membership == Membership.invite
|
||||
? theme.colorScheme.error
|
||||
: hasNotifications || room.markedUnread
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7),
|
||||
height: unreadBubbleSize,
|
||||
width:
|
||||
!hasNotifications && !unread && !room.hasNewMessages
|
||||
? 0
|
||||
: (unreadBubbleSize - 9) *
|
||||
room.notificationCount.toString().length +
|
||||
9,
|
||||
decoration: BoxDecoration(
|
||||
color: room.highlightCount > 0 ||
|
||||
room.membership == Membership.invite
|
||||
? theme.colorScheme.onError
|
||||
: hasNotifications || room.markedUnread
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: hasNotifications
|
||||
? Text(
|
||||
room.notificationCount.toString(),
|
||||
style: TextStyle(
|
||||
color: room.highlightCount > 0 ||
|
||||
room.membership == Membership.invite
|
||||
? theme.colorScheme.onError
|
||||
: hasNotifications
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onPrimaryContainer,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: onTap,
|
||||
trailing: onForget == null
|
||||
? null
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.delete_outlined),
|
||||
onPressed: onForget,
|
||||
),
|
||||
child: hasNotifications
|
||||
? Text(
|
||||
room.notificationCount.toString(),
|
||||
style: TextStyle(
|
||||
color: room.highlightCount > 0 ||
|
||||
room.membership == Membership.invite
|
||||
? theme.colorScheme.onError
|
||||
: hasNotifications
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onPrimaryContainer,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: onTap,
|
||||
trailing: onForget == null
|
||||
? null
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.delete_outlined),
|
||||
onPressed: onForget,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -99,44 +99,43 @@ class ClientChooserButton extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
...matrix.accountBundles[bundle]!.map(
|
||||
(client) => PopupMenuItem(
|
||||
value: client,
|
||||
child: FutureBuilder<Profile?>(
|
||||
// analyzer does not understand this type cast for error
|
||||
// handling
|
||||
//
|
||||
// ignore: unnecessary_cast
|
||||
future: (client!.fetchOwnProfile() as Future<Profile?>)
|
||||
.onError((e, s) => null),
|
||||
builder: (context, snapshot) => Row(
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name:
|
||||
snapshot.data?.displayName ?? client.userID!.localpart,
|
||||
size: 32,
|
||||
...matrix.accountBundles[bundle]!
|
||||
.whereType<Client>()
|
||||
.where((client) => client.isLogged())
|
||||
.map(
|
||||
(client) => PopupMenuItem(
|
||||
value: client,
|
||||
child: FutureBuilder<Profile?>(
|
||||
future: client.fetchOwnProfile(),
|
||||
builder: (context, snapshot) => Row(
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name: snapshot.data?.displayName ??
|
||||
client.userID!.localpart,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
snapshot.data?.displayName ??
|
||||
client.userID!.localpart!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
onPressed: () => controller.editBundlesForAccount(
|
||||
client.userID,
|
||||
bundle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
snapshot.data?.displayName ?? client.userID!.localpart!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
onPressed: () => controller.editBundlesForAccount(
|
||||
client.userID,
|
||||
bundle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.addAccount,
|
||||
|
|
|
|||
|
|
@ -16,20 +16,16 @@ import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
|||
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/public_spaces/pangea_public_room_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pangea/spaces/widgets/knocking_users_indicator.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/public_room_dialog.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
enum AddRoomType {
|
||||
chat,
|
||||
subspace,
|
||||
}
|
||||
enum AddRoomType { chat, subspace }
|
||||
|
||||
class SpaceView extends StatefulWidget {
|
||||
final String spaceId;
|
||||
|
|
@ -263,13 +259,9 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
final client = Matrix.of(context).client;
|
||||
final space = client.getRoomById(widget.spaceId);
|
||||
|
||||
final joined = await showAdaptiveBottomSheet<bool>(
|
||||
final joined = await showAdaptiveDialog<bool>(
|
||||
context: context,
|
||||
// #Pangea
|
||||
// builder: (_) => PublicRoomBottomSheet(
|
||||
builder: (_) => PangeaPublicRoomBottomSheet(
|
||||
// Pangea#
|
||||
outerContext: context,
|
||||
builder: (_) => PublicRoomDialog(
|
||||
chunk: item,
|
||||
via: space?.spaceChildren
|
||||
.firstWhereOrNull(
|
||||
|
|
@ -280,7 +272,10 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
);
|
||||
if (mounted && joined == true) {
|
||||
setState(() {
|
||||
// #Pangea
|
||||
// _discoveredChildren.remove(item);
|
||||
_discoveredChildren?.remove(item);
|
||||
// Pangea#
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -734,7 +729,10 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
},
|
||||
),
|
||||
SliverList.builder(
|
||||
// #Pangea
|
||||
// itemCount: _discoveredChildren.length + 2,
|
||||
itemCount: (_discoveredChildren?.length ?? 0) + 2,
|
||||
// Pangea#
|
||||
itemBuilder: (context, i) {
|
||||
if (i == 0) {
|
||||
return SearchTitle(
|
||||
|
|
@ -743,7 +741,10 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
);
|
||||
}
|
||||
i--;
|
||||
// #Pangea
|
||||
// if (i == _discoveredChildren.length) {
|
||||
if (i == (_discoveredChildren?.length ?? 0)) {
|
||||
// Pangea#
|
||||
if (_noMoreRooms) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
|
|
@ -761,7 +762,10 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
vertical: 2.0,
|
||||
),
|
||||
child: TextButton(
|
||||
// #Pangea
|
||||
// onPressed: _isLoading ? null : _loadHierarchy,
|
||||
onPressed: _isLoading ? null : loadHierarchy,
|
||||
// Pangea#
|
||||
child: _isLoading
|
||||
? LinearProgressIndicator(
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
|
@ -772,7 +776,10 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
),
|
||||
);
|
||||
}
|
||||
// #Pangea
|
||||
// final item = _discoveredChildren[i];
|
||||
final item = _discoveredChildren![i];
|
||||
// Pangea#
|
||||
final displayname = item.name ??
|
||||
item.canonicalAlias ??
|
||||
L10n.of(context).emptyChat;
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/hover_builder.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../widgets/adaptive_dialogs/user_dialog.dart';
|
||||
|
||||
class StatusMessageList extends StatelessWidget {
|
||||
final void Function() onStatusEdit;
|
||||
|
||||
const StatusMessageList({
|
||||
required this.onStatusEdit,
|
||||
super.key,
|
||||
|
|
@ -24,12 +24,9 @@ class StatusMessageList extends StatelessWidget {
|
|||
final client = Matrix.of(context).client;
|
||||
if (profile.userId == client.userID) return onStatusEdit();
|
||||
|
||||
showAdaptiveBottomSheet(
|
||||
UserDialog.show(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
profile: profile,
|
||||
outerContext: context,
|
||||
),
|
||||
profile: profile,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -293,6 +290,7 @@ extension on CachedPresence {
|
|||
(currentlyActive == true
|
||||
? DateTime.now()
|
||||
: DateTime.fromMillisecondsSinceEpoch(0));
|
||||
|
||||
LinearGradient get gradient => presence.isOnline == true
|
||||
? LinearGradient(
|
||||
colors: [
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'chat_members_view.dart';
|
||||
|
||||
class ChatMembersPage extends StatefulWidget {
|
||||
final String roomId;
|
||||
|
||||
const ChatMembersPage({required this.roomId, super.key});
|
||||
|
||||
@override
|
||||
|
|
@ -20,16 +20,27 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
List<User>? members;
|
||||
List<User>? filteredMembers;
|
||||
Object? error;
|
||||
Membership membershipFilter = Membership.join;
|
||||
|
||||
final TextEditingController filterController = TextEditingController();
|
||||
|
||||
// #Pangea
|
||||
StreamSubscription? _subscription;
|
||||
// Pangea#
|
||||
void setMembershipFilter(Membership membership) {
|
||||
membershipFilter = membership;
|
||||
setFilter();
|
||||
}
|
||||
|
||||
void setFilter([_]) async {
|
||||
final filter = filterController.text.toLowerCase().trim();
|
||||
|
||||
final members = this
|
||||
.members
|
||||
?.where(
|
||||
(member) =>
|
||||
membershipFilter == Membership.join ||
|
||||
member.membership == membershipFilter,
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (filter.isEmpty) {
|
||||
setState(() {
|
||||
filteredMembers = members
|
||||
|
|
@ -49,7 +60,8 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
});
|
||||
}
|
||||
|
||||
void refreshMembers() async {
|
||||
void refreshMembers([_]) async {
|
||||
Logs().d('Load room members from', widget.roomId);
|
||||
try {
|
||||
setState(() {
|
||||
error = null;
|
||||
|
|
@ -58,15 +70,16 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
.client
|
||||
.getRoomById(widget.roomId)
|
||||
?.requestParticipants(
|
||||
// #Pangea
|
||||
// without setting cache to true, each call to requestParticipants will
|
||||
// result in a new entry in the roomState stream, because the member roomState is not
|
||||
// stored in the database. This causes an infinite loop with the roomState listener.
|
||||
[Membership.join, Membership.invite, Membership.knock],
|
||||
false,
|
||||
true,
|
||||
// Pangea#
|
||||
);
|
||||
// #Pangea
|
||||
// [...Membership.values]..remove(Membership.leave),
|
||||
// without setting cache to true, each call to requestParticipants will
|
||||
// result in a new entry in the roomState stream, because the member roomState is not
|
||||
// stored in the database. This causes an infinite loop with the roomState listener.
|
||||
[...Membership.values]..remove(Membership.leave),
|
||||
false,
|
||||
true,
|
||||
// Pangea#
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
|
|
@ -83,29 +96,34 @@ class ChatMembersController extends State<ChatMembersPage> {
|
|||
}
|
||||
}
|
||||
|
||||
StreamSubscription? _updateSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refreshMembers();
|
||||
// #Pangea
|
||||
_subscription = Matrix.of(context)
|
||||
|
||||
_updateSub = Matrix.of(context)
|
||||
.client
|
||||
.onRoomState
|
||||
.onSync
|
||||
.stream
|
||||
.where((update) => update.roomId == widget.roomId)
|
||||
.rateLimit(const Duration(seconds: 1))
|
||||
.listen((_) => refreshMembers());
|
||||
// Pangea#
|
||||
.where(
|
||||
(syncUpdate) =>
|
||||
syncUpdate.rooms?.join?[widget.roomId]?.timeline?.events
|
||||
?.any((state) => state.type == EventTypes.RoomMember) ??
|
||||
false,
|
||||
)
|
||||
.listen(refreshMembers);
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription?.cancel();
|
||||
_updateSub?.cancel();
|
||||
// #Pangea
|
||||
filterController.dispose();
|
||||
// Pangea#
|
||||
super.dispose();
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ChatMembersView(this);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import '../../widgets/layouts/max_width_body.dart';
|
||||
|
|
@ -11,6 +12,7 @@ import 'chat_members.dart';
|
|||
|
||||
class ChatMembersView extends StatelessWidget {
|
||||
final ChatMembersController controller;
|
||||
|
||||
const ChatMembersView(this.controller, {super.key});
|
||||
|
||||
@override
|
||||
|
|
@ -84,29 +86,89 @@ class ChatMembersView extends StatelessWidget {
|
|||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: members.length + 1,
|
||||
itemBuilder: (context, i) => i == 0
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: controller.filterController,
|
||||
onChanged: controller.setFilter,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
itemBuilder: (context, i) {
|
||||
if (i == 0) {
|
||||
final availableFilters = Membership.values
|
||||
.where(
|
||||
(membership) =>
|
||||
controller.members?.any(
|
||||
(member) => member.membership == membership,
|
||||
) ??
|
||||
false,
|
||||
)
|
||||
.toList();
|
||||
availableFilters
|
||||
.sort((a, b) => a == Membership.join ? -1 : 1);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: controller.filterController,
|
||||
onChanged: controller.setFilter,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor:
|
||||
theme.colorScheme.secondaryContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
hintText: L10n.of(context).search,
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
hintText: L10n.of(context).search,
|
||||
),
|
||||
),
|
||||
)
|
||||
: ParticipantListItem(members[i - 1]),
|
||||
if (availableFilters.length > 1)
|
||||
SizedBox(
|
||||
height: 64,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 12.0,
|
||||
),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: availableFilters.length,
|
||||
itemBuilder: (context, i) => Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
child: FilterChip(
|
||||
label: Text(
|
||||
switch (availableFilters[i]) {
|
||||
Membership.ban =>
|
||||
L10n.of(context).banned,
|
||||
Membership.invite =>
|
||||
L10n.of(context).invited,
|
||||
Membership.join =>
|
||||
L10n.of(context).all,
|
||||
Membership.knock =>
|
||||
L10n.of(context).knocking,
|
||||
Membership.leave =>
|
||||
L10n.of(context).leftTheChat,
|
||||
},
|
||||
),
|
||||
selected: controller.membershipFilter ==
|
||||
availableFilters[i],
|
||||
onSelected: (_) =>
|
||||
controller.setMembershipFilter(
|
||||
availableFilters[i],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
i--;
|
||||
return ParticipantListItem(members[i]);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ class _MessageSearchResultListTile extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
subtitle: Linkify(
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||
child: SelectableLinkify(
|
||||
text: L10n.of(context).appIntroduction,
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
textAlign: TextAlign.center,
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.secondary,
|
||||
|
|
@ -169,6 +171,19 @@ class HomeserverPickerView extends StatelessWidget {
|
|||
content: Linkify(
|
||||
text: L10n.of(context)
|
||||
.homeserverDescription,
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context)
|
||||
.scale(1),
|
||||
options: const LinkifyOptions(
|
||||
humanize: false,
|
||||
),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
decorationColor:
|
||||
theme.colorScheme.primary,
|
||||
),
|
||||
onOpen: (link) =>
|
||||
launchUrlString(link.url),
|
||||
),
|
||||
actions: [
|
||||
AdaptiveDialogAction(
|
||||
|
|
|
|||
|
|
@ -69,25 +69,29 @@ class ImageViewerView extends StatelessWidget {
|
|||
body: HoverBuilder(
|
||||
builder: (context, hovered) => Stack(
|
||||
children: [
|
||||
PageView.builder(
|
||||
controller: controller.pageController,
|
||||
itemCount: controller.allEvents.length,
|
||||
itemBuilder: (context, i) => InteractiveViewer(
|
||||
minScale: 1.0,
|
||||
maxScale: 10.0,
|
||||
onInteractionEnd: controller.onInteractionEnds,
|
||||
child: Center(
|
||||
child: Hero(
|
||||
tag: controller.allEvents[i].eventId,
|
||||
child: GestureDetector(
|
||||
// Ignore taps to not go back here:
|
||||
onTap: () {},
|
||||
child: MxcImage(
|
||||
key: ValueKey(controller.allEvents[i].eventId),
|
||||
event: controller.allEvents[i],
|
||||
fit: BoxFit.contain,
|
||||
isThumbnail: false,
|
||||
animated: true,
|
||||
KeyboardListener(
|
||||
focusNode: controller.focusNode,
|
||||
onKeyEvent: controller.onKeyEvent,
|
||||
child: PageView.builder(
|
||||
controller: controller.pageController,
|
||||
itemCount: controller.allEvents.length,
|
||||
itemBuilder: (context, i) => InteractiveViewer(
|
||||
minScale: 1.0,
|
||||
maxScale: 10.0,
|
||||
onInteractionEnd: controller.onInteractionEnds,
|
||||
child: Center(
|
||||
child: Hero(
|
||||
tag: controller.allEvents[i].eventId,
|
||||
child: GestureDetector(
|
||||
// Ignore taps to not go back here:
|
||||
onTap: () {},
|
||||
child: MxcImage(
|
||||
key: ValueKey(controller.allEvents[i].eventId),
|
||||
event: controller.allEvents[i],
|
||||
fit: BoxFit.contain,
|
||||
isThumbnail: false,
|
||||
animated: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import 'package:universal_html/html.dart' as html;
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/invitation_selection/invitation_selection.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/level_display_name.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/room_settings_constants.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/refer_friends_dialog.dart';
|
||||
|
|
@ -20,10 +19,10 @@ import 'package:fluffychat/pangea/common/config/environment.dart';
|
|||
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../widgets/adaptive_dialogs/user_dialog.dart';
|
||||
|
||||
class InvitationSelectionView extends StatelessWidget {
|
||||
final InvitationSelectionController controller;
|
||||
|
|
@ -455,13 +454,9 @@ class _InviteContactListTile extends StatelessWidget {
|
|||
mxContent: profile.avatarUrl,
|
||||
name: profile.displayName,
|
||||
presenceUserId: profile.userId,
|
||||
onTap: () => showAdaptiveBottomSheet(
|
||||
onTap: () => UserDialog.show(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
user: user,
|
||||
profile: profile,
|
||||
outerContext: context,
|
||||
),
|
||||
profile: profile,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class LoginView extends StatelessWidget {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: TextField(
|
||||
readOnly: controller.loadingSignIn,
|
||||
autocorrect: false,
|
||||
|
|
@ -79,7 +79,7 @@ class LoginView extends StatelessWidget {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: TextField(
|
||||
readOnly: controller.loadingSignIn,
|
||||
autocorrect: false,
|
||||
|
|
@ -110,7 +110,7 @@ class LoginView extends StatelessWidget {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
|
|
@ -125,7 +125,7 @@ class LoginView extends StatelessWidget {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: TextButton(
|
||||
onPressed: controller.loadingSignIn
|
||||
? () {}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/pages/new_private_chat/new_private_chat_view.dart';
|
||||
import 'package:fluffychat/pages/new_private_chat/qr_scanner_modal.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/utils/url_launcher.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../widgets/adaptive_dialogs/user_dialog.dart';
|
||||
|
||||
class NewPrivateChat extends StatefulWidget {
|
||||
const NewPrivateChat({super.key});
|
||||
|
|
@ -98,12 +98,9 @@ class NewPrivateChatController extends State<NewPrivateChat> {
|
|||
);
|
||||
}
|
||||
|
||||
void openUserModal(Profile profile) => showAdaptiveBottomSheet(
|
||||
void openUserModal(Profile profile) => UserDialog.show(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
profile: profile,
|
||||
outerContext: context,
|
||||
),
|
||||
profile: profile,
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import 'package:fluffychat/widgets/avatar.dart';
|
|||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:fluffychat/widgets/navigation_rail.dart';
|
||||
import '../../widgets/mxc_image_viewer.dart';
|
||||
import 'settings.dart';
|
||||
|
||||
class SettingsView extends StatelessWidget {
|
||||
|
|
@ -74,6 +75,7 @@ class SettingsView extends StatelessWidget {
|
|||
future: controller.profileFuture,
|
||||
builder: (context, snapshot) {
|
||||
final profile = snapshot.data;
|
||||
final avatar = profile?.avatarUrl;
|
||||
final mxid = Matrix.of(context).client.userID ??
|
||||
L10n.of(context).user;
|
||||
final displayname =
|
||||
|
|
@ -85,12 +87,19 @@ class SettingsView extends StatelessWidget {
|
|||
child: Stack(
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: profile?.avatarUrl,
|
||||
mxContent: avatar,
|
||||
name: displayname,
|
||||
// #Pangea
|
||||
userId: profile?.userId,
|
||||
// Pangea#
|
||||
size: Avatar.defaultSize * 2.5,
|
||||
onTap: avatar != null
|
||||
? () => showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
MxcImageViewer(avatar),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
if (profile != null)
|
||||
Positioned(
|
||||
|
|
|
|||
|
|
@ -169,6 +169,8 @@ class SettingsHomeserverView extends StatelessWidget {
|
|||
title: const Text('Federation Base URL'),
|
||||
subtitle: Linkify(
|
||||
text: data.federationBaseUrl.toString(),
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
|
|
@ -231,6 +233,8 @@ class SettingsHomeserverView extends StatelessWidget {
|
|||
title: const Text('Base URL'),
|
||||
subtitle: Linkify(
|
||||
text: wellKnown.mHomeserver.baseUrl.toString(),
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
|
|
@ -244,6 +248,8 @@ class SettingsHomeserverView extends StatelessWidget {
|
|||
title: const Text('Identity Server:'),
|
||||
subtitle: Linkify(
|
||||
text: identityServer.baseUrl.toString(),
|
||||
textScaleFactor:
|
||||
MediaQuery.textScalerOf(context).scale(1),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
|
|
|
|||
|
|
@ -138,10 +138,10 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||
|
||||
void changeShareKeysWith(ShareKeysWith? shareKeysWith) async {
|
||||
if (shareKeysWith == null) return;
|
||||
Matrix.of(context).store.setString(
|
||||
SettingKeys.shareKeysWith,
|
||||
shareKeysWith.name,
|
||||
);
|
||||
AppSettings.shareKeysWith.setItem(
|
||||
Matrix.of(context).store,
|
||||
shareKeysWith.name,
|
||||
);
|
||||
Matrix.of(context).client.shareKeysWith = shareKeysWith;
|
||||
setState(() {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'settings_security.dart';
|
|||
|
||||
class SettingsSecurityView extends StatelessWidget {
|
||||
final SettingsSecurityController controller;
|
||||
|
||||
const SettingsSecurityView(this.controller, {super.key});
|
||||
|
||||
@override
|
||||
|
|
@ -143,7 +144,7 @@ class SettingsSecurityView extends StatelessWidget {
|
|||
leading: const Icon(Icons.vpn_key_outlined),
|
||||
subtitle: SelectableText(
|
||||
Matrix.of(context).client.fingerprintKey.beautified,
|
||||
style: const TextStyle(fontFamily: 'UbuntuMono'),
|
||||
style: const TextStyle(fontFamily: 'RobotoMono'),
|
||||
),
|
||||
),
|
||||
if (capabilities?.mChangePassword?.enabled != false ||
|
||||
|
|
|
|||
|
|
@ -1,298 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/permission_slider_dialog.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'user_bottom_sheet_view.dart';
|
||||
|
||||
enum UserBottomSheetAction {
|
||||
// #Pangea
|
||||
// report,
|
||||
// Pangea#
|
||||
mention,
|
||||
ban,
|
||||
kick,
|
||||
unban,
|
||||
message,
|
||||
ignore,
|
||||
}
|
||||
|
||||
class LoadProfileBottomSheet extends StatelessWidget {
|
||||
final String userId;
|
||||
final BuildContext outerContext;
|
||||
|
||||
const LoadProfileBottomSheet({
|
||||
super.key,
|
||||
required this.userId,
|
||||
required this.outerContext,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<ProfileInformation>(
|
||||
future: Matrix.of(outerContext)
|
||||
.client
|
||||
.getUserProfile(userId)
|
||||
.timeout(const Duration(seconds: 3)),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done &&
|
||||
snapshot.data != null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: CloseButton(
|
||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||
),
|
||||
),
|
||||
body: const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
);
|
||||
}
|
||||
return UserBottomSheet(
|
||||
outerContext: outerContext,
|
||||
profile: Profile(
|
||||
userId: userId,
|
||||
avatarUrl: snapshot.data?.avatarUrl,
|
||||
displayName: snapshot.data?.displayname,
|
||||
),
|
||||
profileSearchError: snapshot.error,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserBottomSheet extends StatefulWidget {
|
||||
final User? user;
|
||||
final Profile? profile;
|
||||
final Function? onMention;
|
||||
final BuildContext outerContext;
|
||||
final Object? profileSearchError;
|
||||
|
||||
const UserBottomSheet({
|
||||
super.key,
|
||||
this.user,
|
||||
this.profile,
|
||||
required this.outerContext,
|
||||
this.onMention,
|
||||
this.profileSearchError,
|
||||
}) : assert(user != null || profile != null);
|
||||
|
||||
@override
|
||||
UserBottomSheetController createState() => UserBottomSheetController();
|
||||
}
|
||||
|
||||
class UserBottomSheetController extends State<UserBottomSheet> {
|
||||
void participantAction(UserBottomSheetAction action) async {
|
||||
final user = widget.user;
|
||||
final userId = user?.id ?? widget.profile?.userId;
|
||||
if (userId == null) throw ('user or profile must not be null!');
|
||||
|
||||
switch (action) {
|
||||
// #Pangea
|
||||
// case UserBottomSheetAction.report:
|
||||
// if (user == null) throw ('User must not be null for this action!');
|
||||
|
||||
// final score = await showModalActionPopup<int>(
|
||||
// context: context,
|
||||
// title: L10n.of(context).reportUser,
|
||||
// message: L10n.of(context).howOffensiveIsThisContent,
|
||||
// cancelLabel: L10n.of(context).cancel,
|
||||
// actions: [
|
||||
// AdaptiveModalAction(
|
||||
// value: -100,
|
||||
// label: L10n.of(context).extremeOffensive,
|
||||
// ),
|
||||
// AdaptiveModalAction(
|
||||
// value: -50,
|
||||
// label: L10n.of(context).offensive,
|
||||
// ),
|
||||
// AdaptiveModalAction(
|
||||
// value: 0,
|
||||
// label: L10n.of(context).inoffensive,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// if (score == null) return;
|
||||
// final reason = await showTextInputDialog(
|
||||
// useRootNavigator: false,
|
||||
// context: context,
|
||||
// title: L10n.of(context).whyDoYouWantToReportThis,
|
||||
// okLabel: L10n.of(context).ok,
|
||||
// cancelLabel: L10n.of(context).cancel,
|
||||
// hintText: L10n.of(context).reason,
|
||||
// );
|
||||
// if (reason == null || reason.isEmpty) return;
|
||||
|
||||
// final result = await showFutureLoadingDialog(
|
||||
// context: context,
|
||||
// future: () => Matrix.of(widget.outerContext).client.reportEvent(
|
||||
// user.room.id,
|
||||
// user.id,
|
||||
// reason: reason,
|
||||
// score: score,
|
||||
// ),
|
||||
// );
|
||||
// if (result.error != null) return;
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(content: Text(L10n.of(context).contentHasBeenReported)),
|
||||
// );
|
||||
// break;
|
||||
// Pangea#
|
||||
case UserBottomSheetAction.mention:
|
||||
if (user == null) throw ('User must not be null for this action!');
|
||||
Navigator.of(context).pop();
|
||||
widget.onMention!();
|
||||
break;
|
||||
case UserBottomSheetAction.ban:
|
||||
if (user == null) throw ('User must not be null for this action!');
|
||||
if (await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).yes,
|
||||
cancelLabel: L10n.of(context).no,
|
||||
message: L10n.of(context).banUserDescription,
|
||||
) ==
|
||||
OkCancelResult.ok) {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user.ban(),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
break;
|
||||
case UserBottomSheetAction.unban:
|
||||
if (user == null) throw ('User must not be null for this action!');
|
||||
if (await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).yes,
|
||||
cancelLabel: L10n.of(context).no,
|
||||
message: L10n.of(context).unbanUserDescription,
|
||||
) ==
|
||||
OkCancelResult.ok) {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user.unban(),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
break;
|
||||
case UserBottomSheetAction.kick:
|
||||
if (user == null) throw ('User must not be null for this action!');
|
||||
if (await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).yes,
|
||||
cancelLabel: L10n.of(context).no,
|
||||
// #Pangea
|
||||
// message: L10n.of(context).kickUserDescription,
|
||||
message: user.id == BotName.byEnvironment &&
|
||||
!user.room.isSpace &&
|
||||
!user.room.isDirectChat
|
||||
? L10n.of(context).kickBotWarning
|
||||
: L10n.of(context).kickUserDescription,
|
||||
// Pangea#
|
||||
) ==
|
||||
OkCancelResult.ok) {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user.kick(),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
break;
|
||||
case UserBottomSheetAction.message:
|
||||
Navigator.of(context).pop();
|
||||
// Workaround for https://github.com/flutter/flutter/issues/27495
|
||||
await Future.delayed(FluffyThemes.animationDuration);
|
||||
|
||||
final roomIdResult = await showFutureLoadingDialog(
|
||||
context: widget.outerContext,
|
||||
future: () => Matrix.of(widget.outerContext).client.startDirectChat(
|
||||
user?.id ?? widget.profile!.userId,
|
||||
// #Pangea
|
||||
enableEncryption: false,
|
||||
// Pangea#
|
||||
),
|
||||
);
|
||||
final roomId = roomIdResult.result;
|
||||
if (roomId == null) return;
|
||||
widget.outerContext.go('/rooms/$roomId');
|
||||
break;
|
||||
case UserBottomSheetAction.ignore:
|
||||
Navigator.of(context).pop();
|
||||
// Workaround for https://github.com/flutter/flutter/issues/27495
|
||||
await Future.delayed(FluffyThemes.animationDuration);
|
||||
final userId = user?.id ?? widget.profile?.userId;
|
||||
widget.outerContext
|
||||
.go('/rooms/settings/security/ignorelist', extra: userId);
|
||||
}
|
||||
}
|
||||
|
||||
Object? sendError;
|
||||
|
||||
final TextEditingController sendController = TextEditingController();
|
||||
|
||||
void knockAccept() async {
|
||||
final user = widget.user!;
|
||||
final result = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user.room.invite(user.id),
|
||||
);
|
||||
if (result.error != null) return;
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
void knockDecline() async {
|
||||
final user = widget.user!;
|
||||
final result = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user.room.kick(user.id),
|
||||
);
|
||||
if (result.error != null) return;
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
void setPowerLevel(int? newLevel) async {
|
||||
final user = widget.user;
|
||||
if (user == null) throw ('User must not be null for this action!');
|
||||
|
||||
final level = newLevel ??
|
||||
await showPermissionChooser(
|
||||
context,
|
||||
currentLevel: user.powerLevel,
|
||||
);
|
||||
if (level == null) return;
|
||||
|
||||
if (level == 100) {
|
||||
final consent = await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).yes,
|
||||
cancelLabel: L10n.of(context).no,
|
||||
message: L10n.of(context).makeAdminDescription,
|
||||
);
|
||||
if (consent != OkCancelResult.ok) return;
|
||||
}
|
||||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user.setPower(level),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => UserBottomSheetView(this);
|
||||
}
|
||||
|
|
@ -1,405 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/level_display_name.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/utils/url_launcher.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/presence_builder.dart';
|
||||
import 'package:fluffychat/widgets/qr_code_viewer.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'user_bottom_sheet.dart';
|
||||
|
||||
class UserBottomSheetView extends StatelessWidget {
|
||||
final UserBottomSheetController controller;
|
||||
|
||||
const UserBottomSheetView(this.controller, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = controller.widget.user;
|
||||
final userId = (user?.id ?? controller.widget.profile?.userId)!;
|
||||
final displayname = (user?.calcDisplayname() ??
|
||||
controller.widget.profile?.displayName ??
|
||||
controller.widget.profile?.userId.localpart)!;
|
||||
final avatarUrl = user?.avatarUrl ?? controller.widget.profile?.avatarUrl;
|
||||
|
||||
final client = Matrix.of(controller.widget.outerContext).client;
|
||||
final profileSearchError = controller.widget.profileSearchError;
|
||||
final dmRoomId = client.getDirectChatFromUserId(userId);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Center(
|
||||
child: CloseButton(
|
||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||
),
|
||||
),
|
||||
centerTitle: false,
|
||||
title: Text(displayname),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: IconButton(
|
||||
onPressed: () => showQrCodeViewer(context, userId),
|
||||
icon: const Icon(Icons.qr_code_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: StreamBuilder<Object>(
|
||||
stream: user?.room.client.onSync.stream.where(
|
||||
(syncUpdate) =>
|
||||
syncUpdate.rooms?.join?[user.room.id]?.timeline?.events?.any(
|
||||
(state) => state.type == EventTypes.RoomPowerLevels,
|
||||
) ??
|
||||
false,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final theme = Theme.of(context);
|
||||
return ListView(
|
||||
children: [
|
||||
if (user?.membership == Membership.knock)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Material(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: ListTile(
|
||||
minVerticalPadding: 16,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Text(
|
||||
// #Pangea
|
||||
// L10n.of(context)
|
||||
// .userWouldLikeToChangeTheChat(displayname),
|
||||
(user?.room.isSpace ?? false)
|
||||
? L10n.of(context)
|
||||
.userWouldLikeToChangeTheSpace(displayname)
|
||||
: L10n.of(context)
|
||||
.userWouldLikeToChangeTheChat(displayname),
|
||||
// Pangea#
|
||||
),
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
foregroundColor: theme.colorScheme.primary,
|
||||
iconColor: theme.colorScheme.primary,
|
||||
),
|
||||
onPressed: controller.knockAccept,
|
||||
icon: const Icon(Icons.check_outlined),
|
||||
label: Text(L10n.of(context).accept),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.errorContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onErrorContainer,
|
||||
iconColor: theme.colorScheme.onErrorContainer,
|
||||
),
|
||||
onPressed: controller.knockDecline,
|
||||
icon: const Icon(Icons.cancel_outlined),
|
||||
label: Text(L10n.of(context).decline),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Avatar(
|
||||
client: Matrix.of(controller.widget.outerContext).client,
|
||||
mxContent: avatarUrl,
|
||||
name: displayname,
|
||||
size: Avatar.defaultSize * 2.5,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () => FluffyShare.share(
|
||||
userId,
|
||||
context,
|
||||
copyOnly: true,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.copy_outlined,
|
||||
size: 14,
|
||||
),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: theme.colorScheme.onSurface,
|
||||
iconColor: theme.colorScheme.onSurface,
|
||||
),
|
||||
label: Text(
|
||||
userId,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
PresenceBuilder(
|
||||
userId: userId,
|
||||
client: client,
|
||||
builder: (context, presence) {
|
||||
if (presence == null ||
|
||||
(presence.presence == PresenceType.offline &&
|
||||
presence.lastActiveTimestamp == null)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final dotColor = presence.presence.isOnline
|
||||
? Colors.green
|
||||
: presence.presence.isUnavailable
|
||||
? Colors.orange
|
||||
: Colors.grey;
|
||||
|
||||
final lastActiveTimestamp =
|
||||
presence.lastActiveTimestamp;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 16),
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: dotColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (presence.currentlyActive == true)
|
||||
Text(
|
||||
L10n.of(context).currentlyActive,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodySmall,
|
||||
)
|
||||
else if (lastActiveTimestamp != null)
|
||||
Text(
|
||||
L10n.of(context).lastActiveAgo(
|
||||
lastActiveTimestamp
|
||||
.localizedTimeShort(context),
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
// #Pangea
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: LevelDisplayName(userId: userId),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
PresenceBuilder(
|
||||
userId: userId,
|
||||
client: client,
|
||||
builder: (context, presence) {
|
||||
final status = presence?.statusMsg;
|
||||
if (status == null || status.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return ListTile(
|
||||
title: SelectableLinkify(
|
||||
text: status,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
linkStyle: const TextStyle(
|
||||
color: Colors.blueAccent,
|
||||
decorationColor: Colors.blueAccent,
|
||||
),
|
||||
onOpen: (url) =>
|
||||
UrlLauncher(context, url.url).launchUrl(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (userId != client.userID)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => controller.participantAction(
|
||||
UserBottomSheetAction.message,
|
||||
),
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
label: Text(
|
||||
dmRoomId == null
|
||||
? L10n.of(context).startConversation
|
||||
: L10n.of(context).sendAMessage,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (controller.widget.onMention != null)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.alternate_email_outlined),
|
||||
title: Text(L10n.of(context).mention),
|
||||
onTap: () => controller
|
||||
.participantAction(UserBottomSheetAction.mention),
|
||||
),
|
||||
if (user != null) ...[
|
||||
Divider(color: theme.dividerColor),
|
||||
ListTile(
|
||||
title: Text(L10n.of(context).userRole),
|
||||
leading: const Icon(Icons.admin_panel_settings_outlined),
|
||||
trailing: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
child: DropdownButton<int>(
|
||||
onChanged: user.canChangeUserPowerLevel ||
|
||||
// Workaround until https://github.com/famedly/matrix-dart-sdk/pull/1765
|
||||
(user.room.canChangePowerLevel &&
|
||||
user.id == user.room.client.userID)
|
||||
? controller.setPowerLevel
|
||||
: null,
|
||||
value: {0, 50, 100}.contains(user.powerLevel)
|
||||
? user.powerLevel
|
||||
: null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
underline: const SizedBox.shrink(),
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: 0,
|
||||
child: Text(
|
||||
L10n.of(context).userLevel(
|
||||
user.powerLevel < 50 ? user.powerLevel : 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 50,
|
||||
child: Text(
|
||||
L10n.of(context).moderatorLevel(
|
||||
user.powerLevel >= 50 && user.powerLevel < 100
|
||||
? user.powerLevel
|
||||
: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 100,
|
||||
child: Text(
|
||||
L10n.of(context).adminLevel(
|
||||
user.powerLevel >= 100 ? user.powerLevel : 100,
|
||||
),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: null,
|
||||
child: Text(L10n.of(context).custom),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Divider(color: theme.dividerColor),
|
||||
if (user != null && user.canKick)
|
||||
ListTile(
|
||||
textColor: theme.colorScheme.error,
|
||||
iconColor: theme.colorScheme.error,
|
||||
// #Pangea
|
||||
// title: Text(L10n.of(context).kickFromChat),
|
||||
title: Text(L10n.of(context).kick),
|
||||
// Pangea#
|
||||
leading: const Icon(Icons.exit_to_app_outlined),
|
||||
onTap: () =>
|
||||
controller.participantAction(UserBottomSheetAction.kick),
|
||||
),
|
||||
if (user != null &&
|
||||
user.canBan &&
|
||||
user.membership != Membership.ban)
|
||||
ListTile(
|
||||
textColor: theme.colorScheme.onErrorContainer,
|
||||
iconColor: theme.colorScheme.onErrorContainer,
|
||||
// #Pangea
|
||||
// title: Text(L10n.of(context).banFromChat),
|
||||
title: Text(L10n.of(context).ban),
|
||||
// Pangea#
|
||||
leading: const Icon(Icons.warning_sharp),
|
||||
onTap: () =>
|
||||
controller.participantAction(UserBottomSheetAction.ban),
|
||||
)
|
||||
else if (user != null &&
|
||||
user.canBan &&
|
||||
user.membership == Membership.ban)
|
||||
ListTile(
|
||||
// #Pangea
|
||||
// title: Text(L10n.of(context).unbanFromChat),
|
||||
title: Text(L10n.of(context).unban),
|
||||
// Pangea#
|
||||
leading: const Icon(Icons.warning_outlined),
|
||||
onTap: () =>
|
||||
controller.participantAction(UserBottomSheetAction.unban),
|
||||
),
|
||||
// #Pangea
|
||||
// if (user != null && user.id != client.userID)
|
||||
// ListTile(
|
||||
// textColor: theme.colorScheme.onErrorContainer,
|
||||
// iconColor: theme.colorScheme.onErrorContainer,
|
||||
// title: Text(L10n.of(context).reportUser),
|
||||
// leading: const Icon(Icons.gavel_outlined),
|
||||
// onTap: () => controller
|
||||
// .participantAction(UserBottomSheetAction.report),
|
||||
// ),
|
||||
// Pangea#
|
||||
if (profileSearchError != null)
|
||||
ListTile(
|
||||
leading: const Icon(
|
||||
Icons.warning_outlined,
|
||||
color: Colors.orange,
|
||||
),
|
||||
subtitle: Text(
|
||||
L10n.of(context).profileNotFound,
|
||||
style: const TextStyle(color: Colors.orange),
|
||||
),
|
||||
),
|
||||
if (userId != client.userID &&
|
||||
!client.ignoredUsers.contains(userId)
|
||||
// #Pangea
|
||||
&&
|
||||
userId != BotName.byEnvironment
|
||||
// Pangea#
|
||||
)
|
||||
ListTile(
|
||||
textColor: theme.colorScheme.onErrorContainer,
|
||||
iconColor: theme.colorScheme.onErrorContainer,
|
||||
leading: const Icon(Icons.block_outlined),
|
||||
title: Text(L10n.of(context).block),
|
||||
onTap: () => controller
|
||||
.participantAction(UserBottomSheetAction.ignore),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,369 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class PangeaPublicRoomBottomSheet extends StatefulWidget {
|
||||
final String? roomAlias;
|
||||
final BuildContext outerContext;
|
||||
final PublicRoomsChunk? chunk;
|
||||
final List<String>? via;
|
||||
|
||||
PangeaPublicRoomBottomSheet({
|
||||
this.roomAlias,
|
||||
required this.outerContext,
|
||||
this.chunk,
|
||||
this.via,
|
||||
super.key,
|
||||
}) {
|
||||
assert(roomAlias != null || chunk != null);
|
||||
}
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => PangeaPublicRoomBottomSheetState();
|
||||
}
|
||||
|
||||
class PangeaPublicRoomBottomSheetState
|
||||
extends State<PangeaPublicRoomBottomSheet> {
|
||||
BuildContext get outerContext => widget.outerContext;
|
||||
String? get roomAlias => widget.roomAlias;
|
||||
PublicRoomsChunk? get chunk => widget.chunk;
|
||||
List<String>? get via => widget.via;
|
||||
|
||||
final TextEditingController _codeController = TextEditingController();
|
||||
late Client client;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
client = Matrix.of(outerContext).client;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_codeController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Room? get room => client.getRoomById(chunk!.roomId);
|
||||
bool get _isRoomMember => room != null && room!.membership == Membership.join;
|
||||
bool get _isKnockRoom => widget.chunk?.joinRule == 'knock';
|
||||
|
||||
Future<void> _joinWithCode() async {
|
||||
final resp =
|
||||
await MatrixState.pangeaController.classController.joinClasswithCode(
|
||||
context,
|
||||
_codeController.text,
|
||||
notFoundError: L10n.of(context).notTheCodeError,
|
||||
);
|
||||
if (!resp.isError) {
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
}
|
||||
|
||||
void _goToRoom(String roomID) {
|
||||
if (chunk?.roomType != 'm.space' && !client.getRoomById(roomID)!.isSpace) {
|
||||
outerContext.go("/rooms/$roomID");
|
||||
} else {
|
||||
MatrixState.pangeaController.classController
|
||||
.setActiveSpaceIdInChatListController(roomID);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _joinRoom() async {
|
||||
if (_isRoomMember) {
|
||||
_goToRoom(room!.id);
|
||||
Navigator.of(context).pop();
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await showFutureLoadingDialog<String>(
|
||||
context: context,
|
||||
future: () async {
|
||||
final roomId = await client.joinRoom(
|
||||
roomAlias ?? chunk!.roomId,
|
||||
serverName: via,
|
||||
);
|
||||
|
||||
final room = client.getRoomById(roomId);
|
||||
if (room == null || room.membership != Membership.join) {
|
||||
await client.waitForRoomInSync(roomId, join: true);
|
||||
}
|
||||
return roomId;
|
||||
},
|
||||
);
|
||||
|
||||
if (result.result != null) {
|
||||
_goToRoom(result.result!);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _knockRoom() async {
|
||||
if (_isRoomMember) {
|
||||
_goToRoom(room!.id);
|
||||
Navigator.of(context).pop();
|
||||
return;
|
||||
}
|
||||
|
||||
await showFutureLoadingDialog<String>(
|
||||
context: context,
|
||||
future: () async => client.knockRoom(
|
||||
roomAlias ?? chunk!.roomId,
|
||||
serverName: via,
|
||||
),
|
||||
onSuccess: () => L10n.of(context).knockSpaceSuccess,
|
||||
delay: false,
|
||||
);
|
||||
}
|
||||
|
||||
bool testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias;
|
||||
|
||||
Future<PublicRoomsChunk> search() async {
|
||||
final chunk = this.chunk;
|
||||
if (chunk != null) return chunk;
|
||||
final query = await Matrix.of(outerContext).client.queryPublicRooms(
|
||||
server: roomAlias!.domain,
|
||||
filter: PublicRoomQueryFilter(
|
||||
genericSearchTerm: roomAlias,
|
||||
),
|
||||
);
|
||||
if (!query.chunk.any(testRoom)) {
|
||||
throw (L10n.of(outerContext).noRoomsFound);
|
||||
}
|
||||
return query.chunk.firstWhere(testRoom);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final roomAlias = this.roomAlias ?? chunk?.canonicalAlias;
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(
|
||||
chunk?.name ?? roomAlias ?? chunk?.roomId ?? 'Unknown',
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
actions: [
|
||||
Center(
|
||||
child: CloseButton(
|
||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: FutureBuilder<PublicRoomsChunk>(
|
||||
future: search(),
|
||||
builder: (context, snapshot) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
spacing: 32.0,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: chunk?.avatarUrl,
|
||||
name: chunk?.name,
|
||||
size: 160.0,
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 160.0,
|
||||
child: Column(
|
||||
spacing: 16.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
const Icon(Icons.group),
|
||||
Text(
|
||||
L10n.of(context).countParticipants(
|
||||
chunk?.numJoinedMembers ?? 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (chunk?.topic != null)
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
chunk!.topic!,
|
||||
softWrap: true,
|
||||
maxLines: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
spacing: 8.0,
|
||||
children: _isKnockRoom
|
||||
? [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _codeController,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
disabledBorder: InputBorder.none,
|
||||
hintText:
|
||||
L10n.of(context).enterSpaceCode,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed: _joinWithCode,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.zero,
|
||||
bottomLeft: Radius.zero,
|
||||
topRight: Radius.circular(24),
|
||||
bottomRight: Radius.circular(24),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).join),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: _knockRoom,
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Symbols.door_open,
|
||||
size: 20.0,
|
||||
),
|
||||
Text(L10n.of(context).askToJoin),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (roomAlias != null)
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
FluffyShare.share(
|
||||
"${Environment.frontendURL}/#/join_with_alias?alias=${Uri.encodeComponent(roomAlias)}",
|
||||
context,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.share_outlined,
|
||||
size: 20.0,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
L10n.of(context).shareSpaceLink,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
ElevatedButton(
|
||||
onPressed: _joinRoom,
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.join_full_outlined,
|
||||
size: 20.0,
|
||||
),
|
||||
Text(L10n.of(context).join),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (roomAlias != null)
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
FluffyShare.share(
|
||||
"${Environment.frontendURL}/#/join_with_alias?alias=${Uri.encodeComponent(roomAlias)}",
|
||||
context,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.share_outlined,
|
||||
size: 20.0,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
L10n.of(context).shareSpaceLink,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
|
||||
import 'package:fluffychat/pangea/public_spaces/pangea_public_room_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/public_room_dialog.dart';
|
||||
import 'package:fluffychat/widgets/mxc_image.dart';
|
||||
|
||||
class PublicSpaceCard extends StatelessWidget {
|
||||
|
|
@ -25,14 +24,10 @@ class PublicSpaceCard extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
|
||||
return PressableButton(
|
||||
onPressed: () => showAdaptiveBottomSheet(
|
||||
onPressed: () => showAdaptiveDialog(
|
||||
context: context,
|
||||
// #Pangea
|
||||
// builder: (c) => PublicRoomBottomSheet(
|
||||
builder: (c) => PangeaPublicRoomBottomSheet(
|
||||
// Pangea#
|
||||
builder: (c) => PublicRoomDialog(
|
||||
roomAlias: space.canonicalAlias ?? space.roomId,
|
||||
outerContext: context,
|
||||
chunk: space,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -270,8 +270,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
: AppConfig.selectModeInputBarHeight;
|
||||
|
||||
bool get _showDetails =>
|
||||
(Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ??
|
||||
false) &&
|
||||
AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store) &&
|
||||
FluffyThemes.isThreeColumnMode(context) &&
|
||||
widget.chatController.room.membership == Membership.join;
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class OverlayCenterContent extends StatelessWidget {
|
|||
controller: chatController,
|
||||
overlayController: overlayController,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: prevEvent,
|
||||
previousEvent: prevEvent,
|
||||
timeline: chatController.timeline!,
|
||||
sizeAnimation: sizeAnimation,
|
||||
// there's a split seconds between when the transition animation starts and
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class OverlayMessage extends StatelessWidget {
|
|||
final MessageOverlayController overlayController;
|
||||
final ChatController controller;
|
||||
final Event? nextEvent;
|
||||
final Event? prevEvent;
|
||||
final Event? previousEvent;
|
||||
final Timeline timeline;
|
||||
final bool immersionMode;
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ class OverlayMessage extends StatelessWidget {
|
|||
required this.maxHeight,
|
||||
this.pangeaMessageEvent,
|
||||
this.nextEvent,
|
||||
this.prevEvent,
|
||||
this.previousEvent,
|
||||
this.sizeAnimation,
|
||||
this.isTransitionAnimation = false,
|
||||
this.readingAssistanceMode,
|
||||
|
|
@ -71,15 +71,26 @@ class OverlayMessage extends StatelessWidget {
|
|||
nextEvent!.senderId == event.senderId &&
|
||||
!displayTime;
|
||||
|
||||
final previousEventSameSender = prevEvent != null &&
|
||||
final previousEventSameSender = previousEvent != null &&
|
||||
{
|
||||
EventTypes.Message,
|
||||
EventTypes.Sticker,
|
||||
EventTypes.Encrypted,
|
||||
}.contains(prevEvent!.type) &&
|
||||
prevEvent!.senderId == event.senderId &&
|
||||
prevEvent!.originServerTs.sameEnvironment(event.originServerTs);
|
||||
}.contains(previousEvent!.type) &&
|
||||
previousEvent!.senderId == event.senderId &&
|
||||
previousEvent!.originServerTs.sameEnvironment(event.originServerTs);
|
||||
|
||||
final textColor = ownMessage
|
||||
? ThemeData.dark().colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface;
|
||||
|
||||
final linkColor = theme.brightness == Brightness.light
|
||||
? theme.colorScheme.primary
|
||||
: ownMessage
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface;
|
||||
|
||||
final displayEvent = event.getDisplayEvent(timeline);
|
||||
const hardCorner = Radius.circular(4);
|
||||
const roundedCorner = Radius.circular(AppConfig.borderRadius);
|
||||
final borderRadius = BorderRadius.only(
|
||||
|
|
@ -91,7 +102,6 @@ class OverlayMessage extends StatelessWidget {
|
|||
ownMessage && previousEventSameSender ? hardCorner : roundedCorner,
|
||||
);
|
||||
|
||||
final displayEvent = event.getDisplayEvent(timeline);
|
||||
// ignore: deprecated_member_use
|
||||
var color = theme.colorScheme.surfaceContainerHigh;
|
||||
if (ownMessage) {
|
||||
|
|
@ -121,16 +131,6 @@ class OverlayMessage extends StatelessWidget {
|
|||
event.onlyEmotes &&
|
||||
event.numberEmotes > 0 &&
|
||||
event.numberEmotes <= 3);
|
||||
final noPadding = {
|
||||
MessageTypes.File,
|
||||
MessageTypes.Audio,
|
||||
}.contains(event.messageType);
|
||||
|
||||
final textColor = event.isActivityMessage
|
||||
? ThemeData.light().colorScheme.onPrimary
|
||||
: ownMessage
|
||||
? ThemeData.dark().colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface;
|
||||
|
||||
final showTranslation = overlayController.showTranslation &&
|
||||
overlayController.translationText != null;
|
||||
|
|
@ -143,12 +143,6 @@ class OverlayMessage extends StatelessWidget {
|
|||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
padding: noBubble || noPadding
|
||||
? EdgeInsets.zero
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
width: messageWidth,
|
||||
height: messageHeight,
|
||||
constraints: BoxConstraints(
|
||||
|
|
@ -185,18 +179,24 @@ class OverlayMessage extends StatelessWidget {
|
|||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 4.0,
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
),
|
||||
child: InkWell(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: ReplyContent.borderRadius,
|
||||
onTap: () => controller.scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage: ownMessage,
|
||||
timeline: timeline,
|
||||
child: InkWell(
|
||||
borderRadius: ReplyContent.borderRadius,
|
||||
onTap: () => controller.scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage: ownMessage,
|
||||
timeline: timeline,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -204,21 +204,17 @@ class OverlayMessage extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
MessageContent(
|
||||
event.getDisplayEvent(timeline),
|
||||
displayEvent,
|
||||
textColor: textColor,
|
||||
linkColor: linkColor,
|
||||
borderRadius: borderRadius,
|
||||
timeline: timeline,
|
||||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
immersionMode: immersionMode,
|
||||
overlayController: overlayController,
|
||||
controller: controller,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: prevEvent,
|
||||
borderRadius: borderRadius,
|
||||
timeline: timeline,
|
||||
linkColor: theme.brightness == Brightness.light
|
||||
? theme.colorScheme.primary
|
||||
: ownMessage
|
||||
? theme.colorScheme.onPrimary
|
||||
: theme.colorScheme.onSurface,
|
||||
prevEvent: previousEvent,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
),
|
||||
|
|
@ -228,30 +224,30 @@ class OverlayMessage extends StatelessWidget {
|
|||
))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
bottom: 8.0,
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
)) ...[
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor.withAlpha(164),
|
||||
size: 14,
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
displayEvent.originServerTs.localizedTimeShort(
|
||||
context,
|
||||
),
|
||||
Text(
|
||||
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
|
||||
style: TextStyle(
|
||||
color: textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
fontSize: 12,
|
||||
style: TextStyle(
|
||||
color: textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
fontSize: 11,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -12,21 +12,41 @@ Future<T?> showAdaptiveBottomSheet<T>({
|
|||
bool isScrollControlled = true,
|
||||
bool useRootNavigator = true,
|
||||
}) {
|
||||
final maxHeight = min(MediaQuery.of(context).size.height - 32, 600);
|
||||
final dialogMode = FluffyThemes.isColumnMode(context);
|
||||
return showModalBottomSheet(
|
||||
if (FluffyThemes.isColumnMode(context)) {
|
||||
return showDialog<T>(
|
||||
context: context,
|
||||
useRootNavigator: useRootNavigator,
|
||||
barrierDismissible: isDismissible,
|
||||
useSafeArea: true,
|
||||
builder: (context) => Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 480,
|
||||
maxHeight: 720,
|
||||
),
|
||||
child: Material(
|
||||
elevation: Theme.of(context).dialogTheme.elevation ?? 4,
|
||||
shadowColor: Theme.of(context).dialogTheme.shadowColor,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: builder(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return showModalBottomSheet<T>(
|
||||
context: context,
|
||||
builder: (context) => Padding(
|
||||
padding: dialogMode
|
||||
? const EdgeInsets.symmetric(vertical: 32.0)
|
||||
: EdgeInsets.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
child: ClipRRect(
|
||||
borderRadius: dialogMode
|
||||
? BorderRadius.circular(AppConfig.borderRadius)
|
||||
: const BorderRadius.only(
|
||||
topLeft: Radius.circular(AppConfig.borderRadius),
|
||||
topRight: Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(AppConfig.borderRadius / 2),
|
||||
topRight: Radius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
child: builder(context),
|
||||
),
|
||||
),
|
||||
|
|
@ -34,7 +54,7 @@ Future<T?> showAdaptiveBottomSheet<T>({
|
|||
isDismissible: isDismissible,
|
||||
isScrollControlled: isScrollControlled,
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: maxHeight + (dialogMode ? 64 : 0),
|
||||
maxHeight: min(MediaQuery.of(context).size.height - 32, 600),
|
||||
maxWidth: FluffyThemes.columnWidth * 1.25,
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
|
|
|
|||
|
|
@ -279,7 +279,8 @@ class BackgroundPush {
|
|||
// Pangea#
|
||||
currentPushers.first.data.url.toString() == gatewayUrl &&
|
||||
currentPushers.first.data.format ==
|
||||
AppConfig.pushNotificationsPusherFormat &&
|
||||
AppSettings.pushNotificationsPusherFormat
|
||||
.getItem(matrix!.store) &&
|
||||
mapEquals(
|
||||
currentPushers.single.data.additionalProperties,
|
||||
{"data_message": pusherDataMessageFormat},
|
||||
|
|
@ -322,7 +323,8 @@ class BackgroundPush {
|
|||
// Pangea#
|
||||
data: PusherData(
|
||||
url: Uri.parse(gatewayUrl!),
|
||||
format: AppConfig.pushNotificationsPusherFormat,
|
||||
format: AppSettings.pushNotificationsPusherFormat
|
||||
.getItem(matrix!.store),
|
||||
additionalProperties: {"data_message": pusherDataMessageFormat},
|
||||
),
|
||||
kind: 'http',
|
||||
|
|
@ -421,7 +423,8 @@ class BackgroundPush {
|
|||
}
|
||||
}
|
||||
await setupPusher(
|
||||
gatewayUrl: AppConfig.pushNotificationsGatewayUrl,
|
||||
gatewayUrl:
|
||||
AppSettings.pushNotificationsGatewayUrl.getItem(matrix!.store),
|
||||
token: _fcmToken,
|
||||
);
|
||||
}
|
||||
|
|
@ -557,7 +560,10 @@ class BackgroundPush {
|
|||
}
|
||||
|
||||
class UPFunctions extends UnifiedPushFunctions {
|
||||
final List<String> features = [/*list of features*/];
|
||||
final List<String> features = [
|
||||
/*list of features*/
|
||||
];
|
||||
|
||||
@override
|
||||
Future<String?> getDistributor() async {
|
||||
return await UnifiedPush.getDistributor();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import 'matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart';
|
|||
|
||||
abstract class ClientManager {
|
||||
static const String clientNamespace = 'im.fluffychat.store.clients';
|
||||
|
||||
static Future<List<Client>> getClients({
|
||||
bool initialize = true,
|
||||
required SharedPreferences store,
|
||||
|
|
@ -103,7 +104,7 @@ abstract class ClientManager {
|
|||
: NativeImplementationsIsolate(compute);
|
||||
|
||||
static Client createClient(String clientName, SharedPreferences store) {
|
||||
final shareKeysWith = store.getString(SettingKeys.shareKeysWith) ?? 'all';
|
||||
final shareKeysWith = AppSettings.shareKeysWith.getItem(store);
|
||||
|
||||
return Client(
|
||||
clientName,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue